From 9270e457c6bdb6dcd91cc816bc871e56775c9ef8 Mon Sep 17 00:00:00 2001 From: anon Date: Thu, 10 Oct 2024 17:10:14 +0000 Subject: [PATCH] initial commit --- antisocial-safety.php | 460 ++++++++++++++++++++++++++++++++++++++++++ readme.md | 50 +++++ 2 files changed, 510 insertions(+) create mode 100644 antisocial-safety.php create mode 100644 readme.md diff --git a/antisocial-safety.php b/antisocial-safety.php new file mode 100644 index 0000000..0ebfcee --- /dev/null +++ b/antisocial-safety.php @@ -0,0 +1,460 @@ + '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 '' . __('Flagged', 'oai') . ''; + } else { + echo '' . __('OK', 'oai') . ''; + } + } +} + +// 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 '' . __('Pending Approval', 'oai') . ''; + } elseif ($flagged === 'flagged') { + echo '' . __('Flagged', 'oai') . ''; + } else { + echo '' . __('OK', 'oai') . ''; + } + } +} + +/** + * 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' => '' . __('This attachment has been flagged by OpenAI Moderation.', 'oai') . '', + 'helps' => __('Flagged content is blocked from being served to users.', 'oai'), + ); + } else { + $form_fields['oai_moderation'] = array( + 'label' => __('OpenAI Moderation', 'oai'), + 'input' => 'html', + 'html' => '' . __('This attachment passed OpenAI Moderation.', 'oai') . '', + ); + } + + if ($moderation_result) { + $form_fields['oai_moderation_result'] = array( + 'label' => __('Moderation Details', 'oai'), + 'input' => 'html', + 'html' => '
' . esc_html(print_r($moderation_result, true)) . '
', + ); + } + + 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 '

' . __('Status:', 'oai') . ' '; + if ($flagged === 'pending') { + echo '' . __('Pending Approval', 'oai') . ''; + } elseif ($flagged === 'flagged') { + echo '' . __('Flagged', 'oai') . ''; + } + echo '

'; + + if ($moderation_result) { + echo '

' . __('Moderation Details', 'oai') . '

'; + echo '
' . esc_html(print_r($moderation_result, true)) . '
'; + } + } else { + echo '

' . __('This comment passed OpenAI Moderation.', 'oai') . '

'; + } +} + +/** + * 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() { + ?> +
+

+
+ +
+
+ ' . esc_html__('Enter your OpenAI API key to enable moderation.', 'oai') . '

'; +} + +function oai_api_key_render() { + $api_key = get_option('oai_api_key'); + ?> + + id !== 'settings_page_openai-moderation') { + return; + } + + $api_key = get_option('oai_api_key'); + if (!$api_key) { + echo '
+

' . __('OpenAI API key is not set. Moderation features are disabled until you enter a valid API key.', 'oai') . '

+
'; + } +} \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..671ebaf --- /dev/null +++ b/readme.md @@ -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.