feat: 实现基于 AI 反馈的智能关键字优化系统

- 黑名单拦截时向评论者发送通知邮件(带 AI 复审按钮)
- 用户可申请 AI 复审,AI 重新评估评论内容
- AI 判定无误时自动恢复评论并优化关键字规则
- 自动移除不合理的黑名单关键字
- 记录所有优化操作,可在设置页查看日志
- 新增黑名单拦截通知邮件模板
- 新增反馈处理页面,显示 AI 评估结果
- 设置页新增黑名单拦截通知开关
- 设置页新增关键字优化日志查看和清除功能
- 充分利用 AI 智能,实现自我学习和优化
This commit is contained in:
2026-01-27 11:21:44 +08:00
parent 17c5f8365d
commit e990cb81de
3 changed files with 624 additions and 0 deletions

View File

@@ -0,0 +1,100 @@
<?php
/**
* Argon 黑名单关键字拦截通知邮件
*
* 当评论被黑名单关键字拦截时发送给评论者
*/
if (!defined('ABSPATH')) {
exit;
}
/**
* 发送黑名单拦截通知邮件给评论者
*
* @param WP_Comment $comment 评论对象
* @return bool 发送是否成功
*/
function argon_send_blacklist_spam_notify_email($comment) {
// 检查评论者是否留了邮箱
if (empty($comment->comment_author_email)) {
return false;
}
// 获取文章信息
$post = get_post($comment->comment_post_ID);
if (!$post) {
return false;
}
// 获取触发的关键字
$blacklist_keywords = get_comment_meta($comment->comment_ID, '_argon_spam_blacklist_keywords', true);
$keywords_array = json_decode($blacklist_keywords, true);
$keywords_text = is_array($keywords_array) ? implode('、', $keywords_array) : '';
// 生成反馈 token
$feedback_token = wp_hash($comment->comment_ID . $comment->comment_author_email . wp_salt());
update_comment_meta($comment->comment_ID, '_argon_spam_feedback_token', $feedback_token);
// 生成反馈链接
$feedback_url = add_query_arg([
'action' => 'argon_spam_feedback',
'comment_id' => $comment->comment_ID,
'token' => $feedback_token
], home_url());
// 邮件设置
$settings = argon_get_email_settings();
$site_name = get_bloginfo('name');
$site_url = home_url();
// 邮件主题
$subject = sprintf('[%s] 您的评论被系统拦截', $site_name);
// 邮件内容
$message = argon_get_email_template('base', [
'site_name' => $site_name,
'site_url' => $site_url,
'header_color' => $settings['header_color'],
'content' => sprintf('
<h2 style="color: #333; font-size: 20px; margin-bottom: 20px;">评论拦截通知</h2>
<p style="color: #666; line-height: 1.8; margin-bottom: 15px;">
您好,%s
</p>
<p style="color: #666; line-height: 1.8; margin-bottom: 15px;">
您在文章《<a href="%s" style="color: #5e72e4; text-decoration: none;">%s</a>》中的评论因触发关键字过滤规则而被系统自动拦截。
</p>
<div style="background: #f8f9fa; border-left: 4px solid #ffc107; padding: 15px; margin: 20px 0; border-radius: 4px;">
<p style="color: #666; margin: 0 0 10px 0;"><strong>您的评论内容:</strong></p>
<p style="color: #666; margin: 0; font-style: italic;">%s</p>
</div>
<div style="background: #fff3cd; border: 1px solid #ffc107; padding: 15px; margin: 20px 0; border-radius: 4px;">
<p style="color: #856404; margin: 0 0 10px 0;"><strong>触发的关键字:</strong>%s</p>
</div>
<p style="color: #666; line-height: 1.8; margin-bottom: 15px;">
如果您认为这是一次误判,可以点击下方按钮申请 AI 复审。我们的 AI 系统会重新评估您的评论,如果确认无误,将自动恢复您的评论并优化关键字规则。
</p>
<div style="text-align: center; margin: 30px 0;">
<a href="%s" style="display: inline-block; padding: 12px 30px; background: #5e72e4; color: #fff; text-decoration: none; border-radius: 4px; font-weight: bold;">申请 AI 复审</a>
</div>
<p style="color: #999; font-size: 12px; line-height: 1.6; margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee;">
此邮件由系统自动发送,请勿直接回复。如有疑问,请联系网站管理员。
</p>
',
esc_html($comment->comment_author),
esc_url(get_permalink($post->ID)),
esc_html($post->post_title),
esc_html($comment->comment_content),
esc_html($keywords_text),
esc_url($feedback_url)
)
]);
// 发送邮件
$headers = ['Content-Type: text/html; charset=UTF-8'];
if (!empty($settings['from_email'])) {
$headers[] = 'From: ' . $settings['from_name'] . ' <' . $settings['from_email'] . '>';
}
return wp_mail($comment->comment_author_email, $subject, $message, $headers);
}

View File

@@ -284,6 +284,7 @@ require_once(get_template_directory() . '/email-templates/comment-notify.php');
require_once(get_template_directory() . '/email-templates/reply-notify.php');
require_once(get_template_directory() . '/email-templates/feedback-notify.php');
require_once(get_template_directory() . '/email-templates/spam-notify.php');
require_once(get_template_directory() . '/email-templates/blacklist-spam-notify.php');
require_once(get_template_directory() . '/email-templates/username-change-notify.php');
//检测更新
@@ -2868,6 +2869,7 @@ function post_comment_preprocessing($comment){
if (isset($keyword_check['is_blacklist']) && $keyword_check['is_blacklist']) {
$comment['comment_approved'] = 'spam';
$_POST['_argon_spam_blacklist_keywords'] = json_encode($keyword_check['keywords']);
$_POST['_argon_spam_by_blacklist'] = 'true';
} else {
// 触发关键字,需要 AI 检测
$should_check = true;
@@ -2986,6 +2988,29 @@ add_action('argon_async_comment_mail_notify', 'argon_async_comment_mail_notify_h
function post_comment_updatemetas($id){
$parentID = $_POST['comment_parent'];
$comment = get_comment($id);
// 保存黑名单关键字标记
if (isset($_POST['_argon_spam_blacklist_keywords'])) {
update_comment_meta($id, '_argon_spam_blacklist_keywords', $_POST['_argon_spam_blacklist_keywords']);
}
if (isset($_POST['_argon_spam_by_blacklist'])) {
update_comment_meta($id, '_argon_spam_by_blacklist', 'true');
// 发送黑名单拦截通知邮件(带反馈按钮)
if (get_option('argon_comment_spam_blacklist_notify', 'true') === 'true') {
argon_send_blacklist_spam_notify_email($comment);
}
}
// 保存触发的关键字
if (isset($_POST['_argon_spam_triggered_keywords'])) {
update_comment_meta($id, '_argon_spam_triggered_keywords', $_POST['_argon_spam_triggered_keywords']);
}
// 保存需要 AI 检测的标记
if (isset($_POST['_argon_needs_spam_check'])) {
update_comment_meta($id, '_argon_needs_spam_check', 'true');
}
$commentPostID = $comment -> comment_post_ID;
$commentAuthor = $comment -> comment_author;
$mailnoticeUnsubscribeKey = get_random_token();
@@ -12680,3 +12705,399 @@ function argon_clear_spam_error_log() {
}
}
add_action('wp_ajax_argon_clear_spam_error_log', 'argon_clear_spam_error_log');
// ==========================================================================
// 黑名单关键字智能优化系统
// ==========================================================================
/**
* 处理黑名单拦截反馈请求
*/
function argon_handle_spam_feedback() {
// 验证参数
if (!isset($_GET['action']) || $_GET['action'] !== 'argon_spam_feedback') {
return;
}
if (!isset($_GET['comment_id']) || !isset($_GET['token'])) {
wp_die('无效的反馈链接');
}
$comment_id = intval($_GET['comment_id']);
$token = sanitize_text_field($_GET['token']);
// 获取评论
$comment = get_comment($comment_id);
if (!$comment) {
wp_die('评论不存在');
}
// 验证 token
$saved_token = get_comment_meta($comment_id, '_argon_spam_feedback_token', true);
if ($token !== $saved_token) {
wp_die('无效的反馈链接');
}
// 检查是否已经处理过
$feedback_processed = get_comment_meta($comment_id, '_argon_spam_feedback_processed', true);
if ($feedback_processed === 'true') {
wp_die('此反馈已经处理过了');
}
// 标记为正在处理
update_comment_meta($comment_id, '_argon_spam_feedback_processing', 'true');
// 使用 AI 重新评估
$result = argon_ai_review_blacklist_spam($comment);
if ($result['success']) {
// 标记为已处理
update_comment_meta($comment_id, '_argon_spam_feedback_processed', 'true');
update_comment_meta($comment_id, '_argon_spam_feedback_result', json_encode($result));
if ($result['action'] === 'restore') {
// 显示成功页面
argon_show_feedback_result_page($comment, $result, 'success');
} else {
// 显示维持拦截页面
argon_show_feedback_result_page($comment, $result, 'rejected');
}
} else {
// 显示错误页面
argon_show_feedback_result_page($comment, $result, 'error');
}
exit;
}
add_action('template_redirect', 'argon_handle_spam_feedback');
/**
* 使用 AI 重新评估被黑名单拦截的评论
*
* @param WP_Comment $comment 评论对象
* @return array 评估结果
*/
function argon_ai_review_blacklist_spam($comment) {
// 获取触发的黑名单关键字
$blacklist_keywords = get_comment_meta($comment->comment_ID, '_argon_spam_blacklist_keywords', true);
$keywords_array = json_decode($blacklist_keywords, true);
if (empty($keywords_array)) {
return [
'success' => false,
'error' => '无法获取触发的关键字'
];
}
// 构建 AI 评估提示词
$prompt = '你是专业的内容审核专家。一条评论因触发黑名单关键字而被拦截,现在需要你重新评估。
评估要求:
1. 判断这条评论是否真的是垃圾评论(广告、违法、色情、暴力、恶意攻击等)
2. 分析触发的关键字是否合理
3. 如果评论正常,建议如何优化关键字规则
请返回 JSON 格式:
{
"is_spam": true/false,
"confidence": 0-100,
"reason": "判断理由50字以内",
"keyword_analysis": {
"keyword": "关键字",
"is_reasonable": true/false,
"suggestion": "优化建议(如果不合理)"
}[],
"action_suggestion": "restore恢复评论或 maintain维持拦截"
}';
$content = sprintf(
"触发的关键字:%s\n\n用户名%s\n评论内容%s",
implode('、', $keywords_array),
$comment->comment_author,
$comment->comment_content
);
// 调用 AI
$ai_result = argon_ai_query('blacklist_review', $prompt, $content, [
'comment_id' => $comment->comment_ID,
'keywords' => $keywords_array
]);
if ($ai_result === false) {
return [
'success' => false,
'error' => 'AI 评估失败'
];
}
// 解析 AI 结果
$review = json_decode($ai_result, true);
if (!$review || !isset($review['is_spam'])) {
return [
'success' => false,
'error' => 'AI 返回格式错误'
];
}
// 保存 AI 评估结果
update_comment_meta($comment->comment_ID, '_argon_spam_ai_review', json_encode($review));
// 根据 AI 判断执行操作
if (!$review['is_spam'] && $review['confidence'] >= 70) {
// AI 认为这不是垃圾评论,恢复评论
wp_set_comment_status($comment->comment_ID, 'approve');
// 优化关键字
argon_optimize_blacklist_keywords($keywords_array, $review['keyword_analysis'], $comment);
// 记录日志
error_log(sprintf(
'Argon: 黑名单拦截误判,已恢复评论 #%d优化关键字%s',
$comment->comment_ID,
implode('、', $keywords_array)
));
return [
'success' => true,
'action' => 'restore',
'reason' => $review['reason'],
'confidence' => $review['confidence'],
'optimizations' => $review['keyword_analysis']
];
} else {
// AI 认为确实是垃圾评论,维持拦截
return [
'success' => true,
'action' => 'maintain',
'reason' => $review['reason'],
'confidence' => $review['confidence']
];
}
}
/**
* 优化黑名单关键字
*
* @param array $triggered_keywords 触发的关键字
* @param array $keyword_analysis AI 分析结果
* @param WP_Comment $comment 评论对象
*/
function argon_optimize_blacklist_keywords($triggered_keywords, $keyword_analysis, $comment) {
// 获取当前黑名单关键字
$blacklist_text = get_option('argon_comment_spam_detection_keywords', '');
$blacklist_array = array_filter(array_map('trim', explode("\n", $blacklist_text)));
$removed_keywords = [];
$modified_keywords = [];
// 根据 AI 分析优化关键字
foreach ($keyword_analysis as $analysis) {
$keyword = $analysis['keyword'];
if (!$analysis['is_reasonable']) {
// 关键字不合理,移除
$key = array_search($keyword, $blacklist_array);
if ($key !== false) {
unset($blacklist_array[$key]);
$removed_keywords[] = $keyword;
}
} elseif (!empty($analysis['suggestion'])) {
// 有优化建议,记录但不自动修改
$modified_keywords[] = [
'original' => $keyword,
'suggestion' => $analysis['suggestion']
];
}
}
// 更新黑名单
if (!empty($removed_keywords)) {
$new_blacklist = implode("\n", array_values($blacklist_array));
update_option('argon_comment_spam_detection_keywords', $new_blacklist);
// 记录优化历史
$optimization_log = get_option('argon_spam_keyword_optimization_log', []);
$optimization_log[] = [
'time' => time(),
'comment_id' => $comment->comment_ID,
'removed_keywords' => $removed_keywords,
'modified_suggestions' => $modified_keywords,
'comment_author' => $comment->comment_author,
'comment_content' => mb_substr($comment->comment_content, 0, 100)
];
// 只保留最近 50 条记录
if (count($optimization_log) > 50) {
$optimization_log = array_slice($optimization_log, -50);
}
update_option('argon_spam_keyword_optimization_log', $optimization_log);
}
}
/**
* 显示反馈结果页面
*
* @param WP_Comment $comment 评论对象
* @param array $result 处理结果
* @param string $type 结果类型success, rejected, error
*/
function argon_show_feedback_result_page($comment, $result, $type) {
$site_name = get_bloginfo('name');
$post = get_post($comment->comment_post_ID);
?>
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>反馈处理结果 - <?php echo esc_html($site_name); ?></title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.container {
background: white;
border-radius: 12px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
max-width: 600px;
width: 100%;
padding: 40px;
}
.icon {
width: 80px;
height: 80px;
margin: 0 auto 20px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 40px;
}
.icon.success { background: #d4edda; color: #28a745; }
.icon.rejected { background: #fff3cd; color: #ffc107; }
.icon.error { background: #f8d7da; color: #dc3545; }
h1 {
text-align: center;
color: #333;
margin-bottom: 20px;
font-size: 24px;
}
.message {
color: #666;
line-height: 1.8;
margin-bottom: 20px;
text-align: center;
}
.detail-box {
background: #f8f9fa;
border-left: 4px solid #5e72e4;
padding: 15px;
margin: 20px 0;
border-radius: 4px;
}
.detail-box p {
color: #666;
margin: 8px 0;
line-height: 1.6;
}
.detail-box strong {
color: #333;
}
.btn {
display: inline-block;
padding: 12px 30px;
background: #5e72e4;
color: white;
text-decoration: none;
border-radius: 6px;
margin-top: 20px;
transition: background 0.3s;
}
.btn:hover {
background: #4c63d2;
}
.btn-container {
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<?php if ($type === 'success'): ?>
<div class="icon success">✓</div>
<h1>评论已恢复</h1>
<p class="message">
经过 AI 重新评估,您的评论没有违规内容,已经成功恢复并发布。
</p>
<div class="detail-box">
<p><strong>AI 评估结果:</strong><?php echo esc_html($result['reason']); ?></p>
<p><strong>置信度:</strong><?php echo esc_html($result['confidence']); ?>%</p>
<?php if (!empty($result['optimizations'])): ?>
<p><strong>关键字优化:</strong>系统已自动优化相关关键字规则,避免类似误判。</p>
<?php endif; ?>
</div>
<p class="message">
感谢您的反馈,这有助于我们改进系统!
</p>
<?php elseif ($type === 'rejected'): ?>
<div class="icon rejected">!</div>
<h1>维持拦截决定</h1>
<p class="message">
经过 AI 重新评估,您的评论仍然被判定为不符合社区规范。
</p>
<div class="detail-box">
<p><strong>AI 评估结果:</strong><?php echo esc_html($result['reason']); ?></p>
<p><strong>置信度:</strong><?php echo esc_html($result['confidence']); ?>%</p>
</div>
<p class="message">
如有疑问,请联系网站管理员。
</p>
<?php else: ?>
<div class="icon error">×</div>
<h1>处理失败</h1>
<p class="message">
抱歉,处理您的反馈时出现了错误。
</p>
<div class="detail-box">
<p><strong>错误信息:</strong><?php echo esc_html($result['error']); ?></p>
</div>
<p class="message">
请稍后重试或联系网站管理员。
</p>
<?php endif; ?>
<div class="btn-container">
<a href="<?php echo esc_url(get_permalink($post->ID)); ?>" class="btn">返回文章</a>
</div>
</div>
</body>
</html>
<?php
}
/**
* AJAX: 清除关键字优化日志
*/
function argon_ajax_clear_keyword_optimization_log() {
check_ajax_referer('argon_clear_keyword_optimization_log', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error(['message' => '权限不足']);
}
delete_option('argon_spam_keyword_optimization_log');
wp_send_json_success(['message' => '关键字优化日志已清除']);
}
add_action('wp_ajax_argon_clear_keyword_optimization_log', 'argon_ajax_clear_keyword_optimization_log');

View File

@@ -2421,6 +2421,81 @@ function themeoptions_page(){
</td>
</tr>
<tr>
<th><label><?php _e('黑名单拦截通知', 'argon');?></label></th>
<td>
<?php $argon_comment_spam_blacklist_notify = get_option('argon_comment_spam_blacklist_notify', 'true'); ?>
<label>
<input type="checkbox" name="argon_comment_spam_blacklist_notify" value="true" <?php if ($argon_comment_spam_blacklist_notify=='true'){echo 'checked';}?>/>
<?php _e('向评论者发送拦截通知邮件(带 AI 复审按钮)', 'argon');?>
</label>
<p class="description">
<?php _e('当评论被黑名单关键字拦截时,向评论者发送通知邮件。评论者可以申请 AI 复审,如果 AI 判定无误,将自动恢复评论并优化关键字规则。', 'argon');?>
</p>
</td>
</tr>
<tr>
<th><label><?php _e('关键字优化日志', 'argon');?></label></th>
<td>
<?php
$optimization_log = get_option('argon_spam_keyword_optimization_log', []);
if (!empty($optimization_log)):
// 倒序显示,最新的在前
$optimization_log = array_reverse($optimization_log);
?>
<div style="max-height: 400px; overflow-y: auto; border: 1px solid #ddd; border-radius: 4px; padding: 15px; background: #f9f9f9;">
<?php foreach ($optimization_log as $log): ?>
<div style="background: white; padding: 12px; margin-bottom: 10px; border-left: 3px solid #5e72e4; border-radius: 3px;">
<p style="margin: 0 0 8px 0; color: #666; font-size: 12px;">
<strong><?php echo date('Y-m-d H:i:s', $log['time']); ?></strong>
<?php if (!empty($log['comment_id'])): ?>
| 评论 #<?php echo $log['comment_id']; ?>
<?php endif; ?>
</p>
<?php if (!empty($log['removed_keywords'])): ?>
<p style="margin: 0 0 5px 0; color: #333;">
<span style="color: #dc3545;">✗ 移除关键字:</span>
<code style="background: #f8d7da; padding: 2px 6px; border-radius: 3px; font-size: 12px;">
<?php echo esc_html(implode('、', $log['removed_keywords'])); ?>
</code>
</p>
<?php endif; ?>
<?php if (!empty($log['modified_suggestions'])): ?>
<p style="margin: 0 0 5px 0; color: #333;">
<span style="color: #ffc107;">⚠ 优化建议:</span>
</p>
<?php foreach ($log['modified_suggestions'] as $suggestion): ?>
<p style="margin: 0 0 5px 0; padding-left: 20px; font-size: 12px; color: #666;">
<code style="background: #fff3cd; padding: 2px 6px; border-radius: 3px;">
<?php echo esc_html($suggestion['original']); ?>
</code>
→ <?php echo esc_html($suggestion['suggestion']); ?>
</p>
<?php endforeach; ?>
<?php endif; ?>
<?php if (!empty($log['comment_author']) && !empty($log['comment_content'])): ?>
<p style="margin: 5px 0 0 0; padding-top: 8px; border-top: 1px solid #eee; font-size: 12px; color: #999;">
<strong><?php echo esc_html($log['comment_author']); ?></strong>
<?php echo esc_html($log['comment_content']); ?>
</p>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<p class="description" style="margin-top: 10px;">
<?php printf(__('共 %d 条优化记录(最多保留 50 条)', 'argon'), count($optimization_log)); ?>
<button type="button" class="button" id="argon-clear-keyword-optimization-log" style="margin-left: 10px;">
<span class="dashicons dashicons-trash" style="margin-top: 3px;"></span>
<?php _e('清除日志', 'argon'); ?>
</button>
</p>
<?php else: ?>
<p class="description"><?php _e('暂无优化记录。当用户反馈黑名单误判并通过 AI 复审后,系统会自动优化关键字并记录在此。', 'argon');?></p>
<?php endif; ?>
</td>
</tr>
<tr>
<th><label><?php _e('Prompt 模式', 'argon');?></label></th>
<td>
@@ -2745,6 +2820,33 @@ function themeoptions_page(){
});
});
// ========== 清除关键字优化日志 ==========
$('#argon-clear-keyword-optimization-log').on('click', function() {
if (!confirm('<?php _e('确定要清除所有关键字优化日志吗?', 'argon'); ?>')) {
return;
}
let $btn = $(this);
let originalHtml = $btn.html();
$btn.prop('disabled', true).html('<span class="dashicons dashicons-update dashicons-spin"></span> <?php _e('清除中...', 'argon'); ?>');
$.post(ajaxurl, {
action: 'argon_clear_keyword_optimization_log',
nonce: '<?php echo wp_create_nonce('argon_clear_keyword_optimization_log'); ?>'
}, function(response) {
if (response.success) {
alert('✓ ' + response.data.message);
location.reload();
} else {
alert('✗ <?php _e('清除失败:', 'argon'); ?> ' + response.data.message);
}
$btn.prop('disabled', false).html(originalHtml);
}).fail(function() {
alert('<?php _e('请求失败,请重试', 'argon'); ?>');
$btn.prop('disabled', false).html(originalHtml);
});
});
// ========== Prompt 模式切换 ==========
$('select[name="argon_comment_spam_detection_prompt_mode"]').on('change', function() {
if ($(this).val() === 'custom') {
@@ -6990,6 +7092,7 @@ function argon_update_themeoptions(){
argon_update_option('argon_comment_spam_detection_prompt_mode');
argon_update_option('argon_comment_spam_detection_custom_prompt');
argon_update_option('argon_comment_spam_detection_auto_threshold');
argon_update_option_checkbox('argon_comment_spam_blacklist_notify');
}