initial commit
This commit is contained in:
commit
9270e457c6
460
antisocial-safety.php
Normal file
460
antisocial-safety.php
Normal file
@ -0,0 +1,460 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: antisocial-safety
|
||||
* Description: Secures attachment uploads and comments with the OpenAI moderation endpoint
|
||||
* Version: 1.6
|
||||
*/
|
||||
|
||||
// Prevent direct access to the file
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Global variable to store moderation results temporarily
|
||||
*/
|
||||
global $oai_moderation_results;
|
||||
$oai_moderation_results = array();
|
||||
|
||||
/**
|
||||
* Attachment Moderation
|
||||
*/
|
||||
|
||||
// Hook into 'add_attachment' to moderate attachments upon upload
|
||||
add_action('add_attachment', 'oai_moderate_attachment');
|
||||
|
||||
function oai_moderate_attachment($post_ID) {
|
||||
$attachment = get_post($post_ID);
|
||||
$mime_type = get_post_mime_type($post_ID);
|
||||
|
||||
// Only moderate images
|
||||
if (strpos($mime_type, 'image/') !== false) {
|
||||
// Get the API key
|
||||
$api_key = get_option('oai_api_key');
|
||||
if (!$api_key) {
|
||||
// API key not set; skip moderation
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the file URL
|
||||
$file_url = wp_get_attachment_url($post_ID);
|
||||
|
||||
// Moderate the image
|
||||
$result = oai_moderate_image($file_url);
|
||||
|
||||
if (isset($result['error'])) {
|
||||
// Handle error (optional: log the error)
|
||||
// For graceful failure, do not flag the attachment
|
||||
return;
|
||||
}
|
||||
|
||||
// Save the moderation result
|
||||
update_post_meta($post_ID, '_oai_moderation_result', $result);
|
||||
|
||||
if ($result['flagged']) {
|
||||
// Mark as flagged
|
||||
update_post_meta($post_ID, '_oai_moderation_flagged', true);
|
||||
} else {
|
||||
// Remove any previous flag
|
||||
delete_post_meta($post_ID, '_oai_moderation_flagged');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function oai_moderate_image($image_url) {
|
||||
$api_key = get_option('oai_api_key');
|
||||
if (!$api_key) {
|
||||
// API key not set; skip moderation
|
||||
return array();
|
||||
}
|
||||
|
||||
$endpoint = 'https://api.openai.com/v1/moderations';
|
||||
|
||||
$data = array(
|
||||
'model' => 'omni-moderation-latest',
|
||||
'input' => array(
|
||||
array(
|
||||
'type' => 'image_url',
|
||||
'image_url' => array('url' => $image_url),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$args = array(
|
||||
'headers' => array(
|
||||
'Content-Type' => 'application/json',
|
||||
'Authorization' => 'Bearer ' . $api_key,
|
||||
),
|
||||
'body' => wp_json_encode($data),
|
||||
'timeout' => 60,
|
||||
);
|
||||
|
||||
$response = wp_remote_post($endpoint, $args);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
// For graceful failure, return an empty array
|
||||
return array();
|
||||
}
|
||||
|
||||
$body = wp_remote_retrieve_body($response);
|
||||
$result = json_decode($body, true);
|
||||
|
||||
if (isset($result['results'][0])) {
|
||||
return $result['results'][0];
|
||||
} else {
|
||||
// Invalid response; return an empty array
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
// Hook into 'template_redirect' to block access to flagged attachments
|
||||
add_action('template_redirect', 'oai_block_flagged_attachments');
|
||||
|
||||
function oai_block_flagged_attachments() {
|
||||
if (is_attachment()) {
|
||||
global $post;
|
||||
if ($post) {
|
||||
$flagged = get_post_meta($post->ID, '_oai_moderation_flagged', true);
|
||||
if ($flagged) {
|
||||
// Send 403 Forbidden header and exit
|
||||
status_header(403);
|
||||
nocache_headers();
|
||||
// Optionally, display a message
|
||||
wp_die(__('You are not allowed to access this content.', 'oai'), __('Forbidden', 'oai'), array('response' => 403));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Comments Moderation
|
||||
*/
|
||||
|
||||
// Moderate comments before saving
|
||||
add_filter('preprocess_comment', 'oai_moderate_comment');
|
||||
|
||||
function oai_moderate_comment($commentdata) {
|
||||
$api_key = get_option('oai_api_key');
|
||||
if (!$api_key) {
|
||||
return $commentdata; // If API key not set, let the comment pass
|
||||
}
|
||||
|
||||
$comment_content = $commentdata['comment_content'];
|
||||
|
||||
// Moderate the comment text
|
||||
$result = oai_moderate_text($comment_content);
|
||||
|
||||
if (isset($result['error'])) {
|
||||
// Handle error (optional: log the error)
|
||||
// For graceful failure, let the comment pass
|
||||
return $commentdata;
|
||||
}
|
||||
|
||||
// Store the moderation result in a global variable indexed by comment content hash
|
||||
$hash = md5($comment_content);
|
||||
global $oai_moderation_results;
|
||||
$oai_moderation_results[$hash] = $result;
|
||||
|
||||
return $commentdata;
|
||||
}
|
||||
|
||||
// Set the comment approval status based on moderation result
|
||||
add_filter('pre_comment_approved', 'oai_set_comment_approval_status', 10, 2);
|
||||
|
||||
function oai_set_comment_approval_status($approved, $commentdata) {
|
||||
$api_key = get_option('oai_api_key');
|
||||
if (!$api_key) {
|
||||
return $approved; // Let the comment pass if API key not set
|
||||
}
|
||||
|
||||
$comment_content = $commentdata['comment_content'];
|
||||
$hash = md5($comment_content);
|
||||
|
||||
global $oai_moderation_results;
|
||||
if (isset($oai_moderation_results[$hash])) {
|
||||
$result = $oai_moderation_results[$hash];
|
||||
if ($result['flagged']) {
|
||||
// Set comment to unapproved
|
||||
return '0'; // Unapproved
|
||||
}
|
||||
}
|
||||
return $approved;
|
||||
}
|
||||
|
||||
// Save the moderation result after the comment is inserted
|
||||
add_action('comment_post', 'oai_save_comment_moderation_meta', 10, 2);
|
||||
|
||||
function oai_save_comment_moderation_meta($comment_ID, $comment_approved) {
|
||||
$comment = get_comment($comment_ID);
|
||||
$comment_content = $comment->comment_content;
|
||||
$hash = md5($comment_content);
|
||||
|
||||
global $oai_moderation_results;
|
||||
if (isset($oai_moderation_results[$hash])) {
|
||||
$result = $oai_moderation_results[$hash];
|
||||
update_comment_meta($comment_ID, '_oai_moderation_result', $result);
|
||||
update_comment_meta($comment_ID, '_oai_moderation_flagged', $result['flagged'] ? 'pending' : 'ok');
|
||||
// Remove from global variable
|
||||
unset($oai_moderation_results[$hash]);
|
||||
}
|
||||
}
|
||||
|
||||
function oai_moderate_text($text) {
|
||||
$api_key = get_option('oai_api_key');
|
||||
if (!$api_key) {
|
||||
// API key not set; skip moderation
|
||||
return array(); // Return an empty array indicating no moderation
|
||||
}
|
||||
|
||||
$endpoint = 'https://api.openai.com/v1/moderations';
|
||||
|
||||
$data = array(
|
||||
'model' => 'omni-moderation-latest',
|
||||
'input' => array(
|
||||
array(
|
||||
'type' => 'text',
|
||||
'text' => $text,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$args = array(
|
||||
'headers' => array(
|
||||
'Content-Type' => 'application/json',
|
||||
'Authorization' => 'Bearer ' . $api_key,
|
||||
),
|
||||
'body' => wp_json_encode($data),
|
||||
'timeout' => 60,
|
||||
);
|
||||
|
||||
$response = wp_remote_post($endpoint, $args);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
// For graceful failure, return an empty array
|
||||
return array();
|
||||
}
|
||||
|
||||
$body = wp_remote_retrieve_body($response);
|
||||
$result = json_decode($body, true);
|
||||
|
||||
if (isset($result['results'][0])) {
|
||||
return $result['results'][0];
|
||||
} else {
|
||||
// Invalid response; return an empty array
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin Columns for Media and Comments
|
||||
*/
|
||||
|
||||
// Add a custom column to the media library
|
||||
add_filter('manage_media_columns', 'oai_add_media_column');
|
||||
function oai_add_media_column($columns) {
|
||||
$columns['oai_moderation'] = __('Moderation', 'oai');
|
||||
return $columns;
|
||||
}
|
||||
|
||||
add_action('manage_media_custom_column', 'oai_media_column_content', 10, 2);
|
||||
function oai_media_column_content($column_name, $post_ID) {
|
||||
if ($column_name == 'oai_moderation') {
|
||||
$flagged = get_post_meta($post_ID, '_oai_moderation_flagged', true);
|
||||
if ($flagged) {
|
||||
echo '<span style="color:red;">' . __('Flagged', 'oai') . '</span>';
|
||||
} else {
|
||||
echo '<span style="color:green;">' . __('OK', 'oai') . '</span>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add a custom column to the comments list
|
||||
add_filter('manage_edit-comments_columns', 'oai_add_comments_column');
|
||||
function oai_add_comments_column($columns) {
|
||||
$columns['oai_moderation'] = __('Moderation', 'oai');
|
||||
return $columns;
|
||||
}
|
||||
|
||||
add_action('manage_comments_custom_column', 'oai_comments_column_content', 10, 2);
|
||||
function oai_comments_column_content($column_name, $comment_ID) {
|
||||
if ($column_name == 'oai_moderation') {
|
||||
$flagged = get_comment_meta($comment_ID, '_oai_moderation_flagged', true);
|
||||
if ($flagged === 'pending') {
|
||||
echo '<span style="color:orange;">' . __('Pending Approval', 'oai') . '</span>';
|
||||
} elseif ($flagged === 'flagged') {
|
||||
echo '<span style="color:red;">' . __('Flagged', 'oai') . '</span>';
|
||||
} else {
|
||||
echo '<span style="color:green;">' . __('OK', 'oai') . '</span>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display Moderation Details in Admin Screens
|
||||
*/
|
||||
|
||||
// Add moderation info to the attachment edit screen
|
||||
add_filter('attachment_fields_to_edit', 'oai_attachment_fields', 10, 2);
|
||||
function oai_attachment_fields($form_fields, $post) {
|
||||
$api_key = get_option('oai_api_key');
|
||||
if (!$api_key) {
|
||||
// API key not set; do not display moderation fields
|
||||
return $form_fields;
|
||||
}
|
||||
|
||||
$flagged = get_post_meta($post->ID, '_oai_moderation_flagged', true);
|
||||
$moderation_result = get_post_meta($post->ID, '_oai_moderation_result', true);
|
||||
|
||||
if ($flagged) {
|
||||
$form_fields['oai_moderation'] = array(
|
||||
'label' => __('OpenAI Moderation', 'oai'),
|
||||
'input' => 'html',
|
||||
'html' => '<span style="color:red;">' . __('This attachment has been flagged by OpenAI Moderation.', 'oai') . '</span>',
|
||||
'helps' => __('Flagged content is blocked from being served to users.', 'oai'),
|
||||
);
|
||||
} else {
|
||||
$form_fields['oai_moderation'] = array(
|
||||
'label' => __('OpenAI Moderation', 'oai'),
|
||||
'input' => 'html',
|
||||
'html' => '<span style="color:green;">' . __('This attachment passed OpenAI Moderation.', 'oai') . '</span>',
|
||||
);
|
||||
}
|
||||
|
||||
if ($moderation_result) {
|
||||
$form_fields['oai_moderation_result'] = array(
|
||||
'label' => __('Moderation Details', 'oai'),
|
||||
'input' => 'html',
|
||||
'html' => '<pre>' . esc_html(print_r($moderation_result, true)) . '</pre>',
|
||||
);
|
||||
}
|
||||
|
||||
return $form_fields;
|
||||
}
|
||||
|
||||
// Add a meta box to the comment edit screen for moderation details
|
||||
add_action('add_meta_boxes_comment', 'oai_add_comment_meta_box');
|
||||
function oai_add_comment_meta_box() {
|
||||
add_meta_box(
|
||||
'oai_comment_moderation',
|
||||
__('OpenAI Moderation Details', 'oai'),
|
||||
'oai_comment_moderation_meta_box',
|
||||
'comment',
|
||||
'normal',
|
||||
'high'
|
||||
);
|
||||
}
|
||||
|
||||
function oai_comment_moderation_meta_box($comment) {
|
||||
$api_key = get_option('oai_api_key');
|
||||
if (!$api_key) {
|
||||
// API key not set; do not display moderation details
|
||||
return;
|
||||
}
|
||||
|
||||
$moderation_result = get_comment_meta($comment->comment_ID, '_oai_moderation_result', true);
|
||||
$flagged = get_comment_meta($comment->comment_ID, '_oai_moderation_flagged', true);
|
||||
|
||||
if ($flagged) {
|
||||
echo '<p><strong>' . __('Status:', 'oai') . '</strong> ';
|
||||
if ($flagged === 'pending') {
|
||||
echo '<span style="color:orange;">' . __('Pending Approval', 'oai') . '</span>';
|
||||
} elseif ($flagged === 'flagged') {
|
||||
echo '<span style="color:red;">' . __('Flagged', 'oai') . '</span>';
|
||||
}
|
||||
echo '</p>';
|
||||
|
||||
if ($moderation_result) {
|
||||
echo '<h4>' . __('Moderation Details', 'oai') . '</h4>';
|
||||
echo '<pre>' . esc_html(print_r($moderation_result, true)) . '</pre>';
|
||||
}
|
||||
} else {
|
||||
echo '<p><span style="color:green;">' . __('This comment passed OpenAI Moderation.', 'oai') . '</span></p>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings Page
|
||||
*/
|
||||
|
||||
// Add settings page under Settings menu
|
||||
add_action('admin_menu', 'oai_add_admin_menu');
|
||||
|
||||
function oai_add_admin_menu() {
|
||||
add_options_page(
|
||||
__('OpenAI Moderation', 'oai'),
|
||||
__('OpenAI Moderation', 'oai'),
|
||||
'manage_options',
|
||||
'openai-moderation',
|
||||
'oai_options_page'
|
||||
);
|
||||
}
|
||||
|
||||
function oai_options_page() {
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php esc_html_e('OpenAI Moderation Settings', 'oai'); ?></h1>
|
||||
<form method="post" action="options.php">
|
||||
<?php
|
||||
settings_fields('oai_options_group');
|
||||
do_settings_sections('openai-moderation');
|
||||
submit_button();
|
||||
?>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
// Initialize settings
|
||||
add_action('admin_init', 'oai_settings_init');
|
||||
|
||||
function oai_settings_init() {
|
||||
register_setting('oai_options_group', 'oai_api_key');
|
||||
|
||||
add_settings_section(
|
||||
'oai_settings_section',
|
||||
__('OpenAI API Settings', 'oai'),
|
||||
'oai_settings_section_callback',
|
||||
'openai-moderation'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'oai_api_key_field',
|
||||
__('OpenAI API Key', 'oai'),
|
||||
'oai_api_key_render',
|
||||
'openai-moderation',
|
||||
'oai_settings_section'
|
||||
);
|
||||
}
|
||||
|
||||
function oai_settings_section_callback() {
|
||||
echo '<p>' . esc_html__('Enter your OpenAI API key to enable moderation.', 'oai') . '</p>';
|
||||
}
|
||||
|
||||
function oai_api_key_render() {
|
||||
$api_key = get_option('oai_api_key');
|
||||
?>
|
||||
<input type="text" name="oai_api_key" value="<?php echo esc_attr($api_key); ?>" size="50">
|
||||
<?php
|
||||
}
|
||||
|
||||
// Display admin notice if API key is not set
|
||||
add_action('admin_notices', 'oai_admin_notices');
|
||||
|
||||
function oai_admin_notices() {
|
||||
if (!current_user_can('manage_options')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we're on the OpenAI Moderation settings page
|
||||
$screen = get_current_screen();
|
||||
if ($screen->id !== 'settings_page_openai-moderation') {
|
||||
return;
|
||||
}
|
||||
|
||||
$api_key = get_option('oai_api_key');
|
||||
if (!$api_key) {
|
||||
echo '<div class="notice notice-warning is-dismissible">
|
||||
<p>' . __('OpenAI API key is not set. Moderation features are disabled until you enter a valid API key.', 'oai') . '</p>
|
||||
</div>';
|
||||
}
|
||||
}
|
50
readme.md
Normal file
50
readme.md
Normal file
@ -0,0 +1,50 @@
|
||||
# openai moderation for attachments and comments
|
||||
|
||||
running a small website can be a hassle when idiots upload nasty stuff or spam your comments. this plugin uses openai's moderation api to automatically check images and comments for bad content.
|
||||
|
||||
## why use this plugin?
|
||||
|
||||
it keeps your site clean by blocking offensive or illegal images and comments before they become a problem. saves you headaches.
|
||||
|
||||
## features
|
||||
|
||||
- **attachment moderation**: images are checked during upload. flagged images are blocked from being accessed.
|
||||
- **comment moderation**: comments are reviewed before they're saved. flagged comments are set to unapproved.
|
||||
- **admin integration**: see moderation status directly in your media library and comments list. detailed info available in edit screens.
|
||||
- **easy setup**: just add your openai api key in the settings.
|
||||
|
||||
## installation
|
||||
|
||||
1. upload the plugin files to `/wp-content/plugins/`, or install through the wordpress plugins screen.
|
||||
2. activate the plugin.
|
||||
3. go to `settings` > `openai moderation` and enter your openai api key.
|
||||
|
||||
## how it works
|
||||
|
||||
### attachment moderation
|
||||
|
||||
- when an image is uploaded, the plugin sends it to openai's moderation api using the `omni-moderation-latest` model.
|
||||
- if the image is flagged, the plugin blocks access to it by sending a 403 forbidden response when someone tries to view it.
|
||||
|
||||
### comment moderation
|
||||
|
||||
- before a comment is saved, the plugin checks the content with openai's moderation api.
|
||||
- flagged comments are set to unapproved, so they won't show up on your site unless you approve them.
|
||||
|
||||
### backend functionality
|
||||
|
||||
- hooks into `add_attachment` to moderate images upon upload.
|
||||
- uses `template_redirect` to block access to flagged attachments.
|
||||
- hooks into `preprocess_comment` and `pre_comment_approved` to moderate comments before saving.
|
||||
- stores moderation results in post and comment meta.
|
||||
- adds custom columns in admin screens to display moderation status.
|
||||
- provides detailed moderation info in the attachment and comment edit screens.
|
||||
|
||||
## requirements
|
||||
|
||||
- wordpress 5.0 or higher
|
||||
- an openai api key
|
||||
|
||||
## notes
|
||||
|
||||
- if the openai api key isn't set, the plugin won't function.
|
Loading…
Reference in New Issue
Block a user