fix: 代码格式优化和临时文件清理
- 优化多个模板文件的代码格式 - 清理 tmp 目录中的临时备份文件 - 统一代码风格,符合项目规范
This commit is contained in:
@@ -1441,9 +1441,9 @@ if (argonConfig.waterflow_columns != "1") {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (commentLink != "" && !(/https?:\/\//).test(commentLink)) {
|
||||
if (commentLink != "" && commentLink.indexOf('.') == -1) {
|
||||
isError = true;
|
||||
errorMsg += __("网站格式错误 (不是 http(s):// 开头)") + "</br>";
|
||||
errorMsg += __("网站格式错误") + "</br>";
|
||||
}
|
||||
if (!$("#post_comment").hasClass("no-need-captcha")) {
|
||||
// 检查是否使用Geetest验证码
|
||||
@@ -1541,7 +1541,7 @@ if (argonConfig.waterflow_columns != "1") {
|
||||
comment: commentContent,
|
||||
author: commentName,
|
||||
email: commentEmail,
|
||||
url: commentLink,
|
||||
url: (commentLink != "" && !(/^https?:\/\//).test(commentLink) && !(/^\/\//).test(commentLink)) ? ("http://" + commentLink) : commentLink,
|
||||
comment_post_ID: postID,
|
||||
comment_parent: replyID,
|
||||
"wp-comment-cookies-consent": "yes",
|
||||
@@ -2000,9 +2000,9 @@ if (argonConfig.waterflow_columns != "1") {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (commentLink != "" && !(/https?:\/\//).test(commentLink)) {
|
||||
if (commentLink != "" && commentLink.indexOf('.') == -1) {
|
||||
isError = true;
|
||||
errorMsg += __("网站格式错误 (不是 http(s):// 开头") + "</br>";
|
||||
errorMsg += __("网站格式错误") + "</br>";
|
||||
}
|
||||
|
||||
// 如果基本表单验证失败,显示错误信息并返回
|
||||
@@ -3667,6 +3667,7 @@ if (argonConfig.disable_pjax != true && argonConfig.disable_pjax != 'true') {
|
||||
try { foldLongComments(); } catch (err) { ArgonDebug.error('foldLongComments failed:', err); }
|
||||
try { foldLongShuoshuo(); } catch (err) { ArgonDebug.error('foldLongShuoshuo failed:', err); }
|
||||
try { handleHashNavigation(); } catch (err) { ArgonDebug.error('handleHashNavigation failed:', err); }
|
||||
try { initArticleSkeletons(); } catch (err) { ArgonDebug.error('initArticleSkeletons failed:', err); }
|
||||
|
||||
$("html").trigger("resize");
|
||||
|
||||
@@ -5123,34 +5124,23 @@ setInterval(function(){
|
||||
// 7.1 骨架屏加载动画 (Skeleton Screen)
|
||||
function initArticleSkeletons() {
|
||||
if (typeof jQuery === 'undefined') return;
|
||||
var $articles = jQuery('.article-list article.post:not(.skeleton-processed)');
|
||||
if ($articles.length === 0) return;
|
||||
var $skeletons = jQuery('.article-skeleton:not(.skeleton-fading)');
|
||||
if ($skeletons.length === 0) return;
|
||||
|
||||
$articles.each(function () {
|
||||
var $this = jQuery(this);
|
||||
$this.addClass('skeleton-processed skeleton-loading');
|
||||
|
||||
// 构造骨架屏 DOM
|
||||
var skeletonHtml =
|
||||
'<div class="argon-skeleton">' +
|
||||
'<div class="argon-skeleton-item argon-skeleton-title"></div>' +
|
||||
'<div class="argon-skeleton-item argon-skeleton-meta"></div>' +
|
||||
'<div class="argon-skeleton-item argon-skeleton-line"></div>' +
|
||||
'<div class="argon-skeleton-item argon-skeleton-line"></div>' +
|
||||
'<div class="argon-skeleton-item argon-skeleton-line short"></div>' +
|
||||
'</div>';
|
||||
|
||||
var $skeleton = jQuery(skeletonHtml);
|
||||
$this.prepend($skeleton);
|
||||
$skeletons.each(function () {
|
||||
var $skeleton = jQuery(this);
|
||||
$skeleton.addClass('skeleton-fading');
|
||||
|
||||
// 给予一定的展示时长,避免闪烁并确保背景稳定性
|
||||
setTimeout(function () {
|
||||
$this.removeClass('skeleton-loading');
|
||||
$skeleton.addClass('article-skeleton-fade-out');
|
||||
setTimeout(function () {
|
||||
$skeleton.remove();
|
||||
}, 400); // 配合 CSS 的 0.4s transition
|
||||
}, 800); // 骨架屏显示 800ms
|
||||
}, 400); // 配合 CSS transition
|
||||
}, 400); // 骨架屏展示时长
|
||||
});
|
||||
}
|
||||
window.initArticleSkeletons = initArticleSkeletons;
|
||||
|
||||
// 8. 减少动画偏好检查
|
||||
function checkReducedMotion() {
|
||||
@@ -5193,3 +5183,24 @@ setInterval(function(){
|
||||
};
|
||||
}
|
||||
})();
|
||||
/* 友情链接徽章点击动画 */
|
||||
$(document).on("click", ".badge-friend", function (e) {
|
||||
let x = e.clientX;
|
||||
let y = e.clientY;
|
||||
|
||||
let $heart = $('<i class="fa fa-heart heart-bubble"></i>');
|
||||
let drift = (Math.random() - 0.5) * 120;
|
||||
let rotate = (Math.random() - 0.5) * 60;
|
||||
let duration = 2.0 + Math.random() * 1.0;
|
||||
$heart.css({
|
||||
left: x + 'px',
|
||||
top: y + 'px',
|
||||
'--drift': drift + 'px',
|
||||
'--rotate': rotate + 'deg',
|
||||
'--duration': duration + 's'
|
||||
});
|
||||
$('body').append($heart);
|
||||
setTimeout(() => {
|
||||
$heart.remove();
|
||||
}, duration * 1000);
|
||||
});
|
||||
|
||||
431
functions.php
431
functions.php
@@ -1494,6 +1494,41 @@ function get_reading_time($len){
|
||||
}
|
||||
return round($reading_time / 60 , 1) . " " . __("小时", 'argon');
|
||||
}
|
||||
//获取文章骨架屏 HTML
|
||||
function get_article_skeleton($layout = '1') {
|
||||
if ($layout == '2') {
|
||||
return '
|
||||
<div class="article-skeleton article-skeleton-layout2">
|
||||
<div class="article-skeleton-thumb"></div>
|
||||
<div class="article-skeleton-body">
|
||||
<div class="article-skeleton-title"></div>
|
||||
<div class="article-skeleton-content">
|
||||
<div class="article-skeleton-line"></div>
|
||||
<div class="article-skeleton-line"></div>
|
||||
<div class="article-skeleton-line short"></div>
|
||||
</div>
|
||||
<div class="article-skeleton-meta">
|
||||
<div class="article-skeleton-badge"></div>
|
||||
<div class="article-skeleton-badge"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>';
|
||||
}
|
||||
// 默认布局 1
|
||||
return '
|
||||
<div class="article-skeleton article-skeleton-default">
|
||||
<div class="article-skeleton-title"></div>
|
||||
<div class="article-skeleton-meta">
|
||||
<div class="article-skeleton-badge"></div>
|
||||
<div class="article-skeleton-badge"></div>
|
||||
</div>
|
||||
<div class="article-skeleton-content">
|
||||
<div class="article-skeleton-line"></div>
|
||||
<div class="article-skeleton-line"></div>
|
||||
<div class="article-skeleton-line short"></div>
|
||||
</div>
|
||||
</div>';
|
||||
}
|
||||
//当前文章是否可以生成目录
|
||||
function have_catalog(){
|
||||
if (!is_single() && !is_page()){
|
||||
@@ -2072,6 +2107,11 @@ function argon_comment_format($comment, $args, $depth){
|
||||
<div class="comment-item-title">
|
||||
<div class="comment-name">
|
||||
<div class="comment-author"><?php echo get_comment_author_link();?></div>
|
||||
<?php
|
||||
if (argon_is_url_friend_link($comment -> comment_author_url)) {
|
||||
echo '<span class="badge badge-info badge-friend" title="' . __('愿友谊长存', 'argon') . '"><i class="fa fa-heart-o"></i></span>';
|
||||
}
|
||||
?>
|
||||
<?php if (user_can($comment -> user_id , "update_core")){
|
||||
echo '<span class="badge badge-primary badge-admin">' . __('博主', 'argon') . '</span>';}
|
||||
?>
|
||||
@@ -2134,6 +2174,11 @@ function argon_comment_shuoshuo_preview_format($comment, $args, $depth){
|
||||
<div class="comment-item-inner " id="comment-inner-<?php comment_ID();?>">
|
||||
<span class="shuoshuo-comment-item-title">
|
||||
<?php echo get_comment_author_link();?>
|
||||
<?php
|
||||
if (argon_is_url_friend_link($comment -> comment_author_url)) {
|
||||
echo '<span class="badge badge-info badge-friend" title="' . __('愿友谊长存', 'argon') . '"><i class="fa fa-heart-o"></i></span>';
|
||||
}
|
||||
?>
|
||||
<?php if( user_can($comment -> user_id , "update_core") ){
|
||||
echo '<span class="badge badge-primary badge-admin">' . __('博主', 'argon') . '</span>';}
|
||||
?>
|
||||
@@ -2851,6 +2896,7 @@ function argon_comment_text_render($text){
|
||||
return argon_apply_comment_macros($text);
|
||||
}
|
||||
add_filter('comment_text', 'argon_comment_text_render', 9);
|
||||
add_filter('the_content', 'argon_apply_comment_macros', 9);
|
||||
|
||||
//评论发送处理
|
||||
function post_comment_preprocessing($comment){
|
||||
@@ -5413,6 +5459,86 @@ function argon_check_duplicate_link($url, $exclude_id = null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查一个 URL 是否在友情链接名单中
|
||||
* 支持不同协议、不同子域名的匹配
|
||||
*/
|
||||
function argon_is_url_friend_link($url) {
|
||||
if (empty($url)) {
|
||||
return false;
|
||||
}
|
||||
// 如果没有协议,补齐协议以便 parse_url 正常工作
|
||||
if (!preg_match('/^https?:\/\//', $url) && !preg_match('/^\/\//', $url)) {
|
||||
$url = 'http://' . $url;
|
||||
}
|
||||
|
||||
static $friend_hosts = null;
|
||||
if ($friend_hosts === null) {
|
||||
$links = argon_get_friend_links_raw('approved');
|
||||
$friend_hosts = array();
|
||||
foreach ($links as $link) {
|
||||
if (empty($link['url'])) continue;
|
||||
$host = parse_url($link['url'], PHP_URL_HOST);
|
||||
if ($host) {
|
||||
$host = strtolower($host);
|
||||
$friend_hosts[] = $host;
|
||||
// 也存一份移除 www. 的
|
||||
$no_www = preg_replace('/^www\./', '', $host);
|
||||
if ($no_www !== $host) {
|
||||
$friend_hosts[] = $no_www;
|
||||
}
|
||||
}
|
||||
}
|
||||
$friend_hosts = array_unique($friend_hosts);
|
||||
}
|
||||
|
||||
$comment_host = parse_url($url, PHP_URL_HOST);
|
||||
if (!$comment_host) {
|
||||
return false;
|
||||
}
|
||||
$comment_host = strtolower($comment_host);
|
||||
$comment_no_www = preg_replace('/^www\./', '', $comment_host);
|
||||
|
||||
// 1. 直系域名或 www 域名匹配
|
||||
if (in_array($comment_host, $friend_hosts) || in_array($comment_no_www, $friend_hosts)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. 匹配二级域名 (忽略子域名差异)
|
||||
foreach ($friend_hosts as $friend_host) {
|
||||
if (argon_is_same_root_domain($comment_host, $friend_host)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查两个 Host 是否拥有相同的根域名
|
||||
*/
|
||||
function argon_is_same_root_domain($host1, $host2) {
|
||||
if ($host1 === $host2) return true;
|
||||
|
||||
$get_root = function($h) {
|
||||
$h = preg_replace('/^www\./', '', $h);
|
||||
$parts = explode('.', $h);
|
||||
$count = count($parts);
|
||||
if ($count <= 2) return $h;
|
||||
|
||||
// 如果是 .com.cn, .net.cn 等三级后缀
|
||||
$last2 = $parts[$count - 2] . '.' . $parts[$count - 1];
|
||||
$three_part_suffixes = ['com.cn', 'net.cn', 'org.cn', 'gov.cn', 'edu.cn'];
|
||||
if (in_array($last2, $three_part_suffixes) && $count >= 3) {
|
||||
return $parts[$count - 3] . '.' . $last2;
|
||||
}
|
||||
|
||||
return $parts[$count - 2] . '.' . $parts[$count - 1];
|
||||
};
|
||||
|
||||
return $get_root($host1) === $get_root($host2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过代理获取网站信息(防止源站 IP 泄露)
|
||||
* 注意:由于服务器在国内,代理服务可能不可用,建议使用浏览器端获取
|
||||
@@ -6735,6 +6861,32 @@ function argon_ai_query($scenario, $prompt, $content, $context = []) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置 Prompt 基因池
|
||||
*/
|
||||
function argon_ajax_reset_prompt_pool() {
|
||||
check_ajax_referer('argon_manage_unified_apis', 'nonce');
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error(__('权限不足', 'argon'));
|
||||
}
|
||||
delete_option('argon_ai_audit_prompt_pool');
|
||||
wp_send_json_success(['message' => __('基因池已重置', 'argon')]);
|
||||
}
|
||||
add_action('wp_ajax_argon_reset_prompt_pool', 'argon_ajax_reset_prompt_pool');
|
||||
|
||||
/**
|
||||
* 清空学习到的关键词缓存
|
||||
*/
|
||||
function argon_ajax_clear_learned_keywords() {
|
||||
check_ajax_referer('argon_manage_unified_apis', 'nonce');
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error(__('权限不足', 'argon'));
|
||||
}
|
||||
delete_option('argon_comment_spam_learned_keywords');
|
||||
wp_send_json_success(['message' => __('学习缓存已清空', 'argon')]);
|
||||
}
|
||||
add_action('wp_ajax_argon_clear_learned_keywords', 'argon_ajax_clear_learned_keywords');
|
||||
|
||||
function argon_resolve_ai_provider_model($scenario, $context = []) {
|
||||
$config = null;
|
||||
$provider = '';
|
||||
@@ -9872,7 +10024,7 @@ function argon_get_siliconflow_models($api_key, $custom_endpoint = '') {
|
||||
* @param string $mode Prompt 模式:minimal, standard, enhanced
|
||||
* @return string Prompt 文本
|
||||
*/
|
||||
function argon_get_spam_detection_prompt($mode) {
|
||||
function argon_get_spam_detection_prompt($mode) {
|
||||
$prompts = [
|
||||
'minimal' => '你是严谨的内容安全专家。判断评论是否违规。
|
||||
|
||||
@@ -9973,6 +10125,166 @@ function argon_get_spam_detection_prompt($mode) {
|
||||
return isset($prompts[$mode]) ? $prompts[$mode] : $prompts['standard'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取遗传算法优化的 Prompt
|
||||
* @param string $base_mode 基础模式 (minimal/standard/enhanced)
|
||||
* @return string 优化的 Prompt
|
||||
*/
|
||||
function argon_get_genetic_audit_prompt($base_mode) {
|
||||
if (get_option('argon_ai_audit_genetic_prompt_enable', 'false') !== 'true') {
|
||||
return argon_get_spam_detection_prompt($base_mode);
|
||||
}
|
||||
|
||||
$pool = get_option('argon_ai_audit_prompt_pool', []);
|
||||
if (empty($pool) || !isset($pool[$base_mode])) {
|
||||
// 初始化 Prompt 池
|
||||
$standard_prompt = argon_get_spam_detection_prompt($base_mode);
|
||||
$pool[$base_mode] = [
|
||||
'population' => [
|
||||
[
|
||||
'content' => $standard_prompt,
|
||||
'fitness' => 100,
|
||||
'usage_count' => 0,
|
||||
'success_count' => 0,
|
||||
'error_count' => 0,
|
||||
'version' => 1
|
||||
]
|
||||
],
|
||||
'current_best_index' => 0,
|
||||
'last_mutation_time' => time()
|
||||
];
|
||||
update_option('argon_ai_audit_prompt_pool', $pool);
|
||||
return $standard_prompt;
|
||||
}
|
||||
|
||||
$population = &$pool[$base_mode]['population'];
|
||||
|
||||
// 轮盘赌选择或直接选择表现最好的
|
||||
// 为了确保收敛和探索平衡,80% 概率选择当前最好,20% 概率随机选择一个旧的或尝试变异
|
||||
if (wp_rand(1, 100) > 20) {
|
||||
$selected_index = $pool[$base_mode]['current_best_index'];
|
||||
} else {
|
||||
$selected_index = wp_rand(0, count($population) - 1);
|
||||
}
|
||||
|
||||
// 记录当前使用的索引到临时变量,用于后续更新置信度
|
||||
// 因为后续可能异步,所以这里返回时把索引也带上(通过 global 或 transient)
|
||||
global $argon_current_prompt_index;
|
||||
$argon_current_prompt_index = $selected_index;
|
||||
|
||||
return $population[$selected_index]['content'];
|
||||
}
|
||||
|
||||
function argon_genetic_correct_keywords() {
|
||||
if (get_option('argon_ai_audit_genetic_keyword_correction_enable', 'false') !== 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
$learned_keywords = get_option('argon_comment_spam_learned_keywords', []);
|
||||
if (empty($learned_keywords)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$mutation_rate = floatval(get_option('argon_ai_audit_genetic_keyword_mutation_rate', '0.05'));
|
||||
$new_keywords = [];
|
||||
$decay_factor = 0.98; // 随着时间推移,旧知识的权威性略微下降
|
||||
|
||||
foreach ($learned_keywords as $keyword => $stats) {
|
||||
$total_count = $stats['spam_count'] + $stats['normal_count'];
|
||||
|
||||
// 1. 遗传衰减:太久没出现的词置信度降低
|
||||
$days_since_added = (time() - $stats['added_time']) / (24 * 3600);
|
||||
if ($days_since_added > 30) {
|
||||
$stats['confidence'] *= pow($decay_factor, ($days_since_added - 30) / 7);
|
||||
}
|
||||
|
||||
// 2. 淘汰低频且低置信度的“基因”
|
||||
if ($total_count < 2 && $stats['confidence'] < 0.4) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 3. 特征漂变(变异保护):如果一个词被误伤太多,强制修正
|
||||
if ($stats['normal_count'] > $stats['spam_count'] * 2 && $stats['spam_count'] > 0) {
|
||||
$stats['confidence'] *= 0.5; // 大幅降低其作为垃圾特征的权威性
|
||||
}
|
||||
|
||||
// 4. 遗传修正:根据群体表现调整权重
|
||||
// 比如:如果一个极其短的词(1-2字)被认为很有用,增加一点校验惩罚以防误杀
|
||||
if (mb_strlen($keyword) <= 2) {
|
||||
$stats['confidence'] *= 0.9;
|
||||
}
|
||||
|
||||
$new_keywords[$keyword] = $stats;
|
||||
}
|
||||
|
||||
update_option('argon_comment_spam_learned_keywords', $new_keywords);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新 Prompt 适应度
|
||||
* @param string $mode 基础模式
|
||||
* @param int $prompt_index Prompt 索引
|
||||
* @param bool $is_correct AI 判断是否正确
|
||||
*/
|
||||
function argon_update_prompt_fitness($mode, $prompt_index, $is_correct) {
|
||||
if (get_option('argon_ai_audit_genetic_prompt_enable', 'false') !== 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
$pool = get_option('argon_ai_audit_prompt_pool', []);
|
||||
if (!isset($pool[$mode]['population'][$prompt_index])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$p = &$pool[$mode]['population'][$prompt_index];
|
||||
$p['usage_count']++;
|
||||
if ($is_correct) {
|
||||
$p['success_count']++;
|
||||
$p['fitness'] += 2;
|
||||
} else {
|
||||
$p['error_count']++;
|
||||
$p['fitness'] -= 5;
|
||||
}
|
||||
|
||||
// 约束范围
|
||||
$p['fitness'] = max(10, min(1000, $p['fitness']));
|
||||
|
||||
// 更新最佳索引
|
||||
$best_fitness = -1000;
|
||||
$best_idx = 0;
|
||||
foreach ($pool[$mode]['population'] as $idx => $item) {
|
||||
if ($item['fitness'] > $best_fitness) {
|
||||
$best_fitness = $item['fitness'];
|
||||
$best_idx = $idx;
|
||||
}
|
||||
}
|
||||
$pool[$mode]['current_best_index'] = $best_idx;
|
||||
|
||||
// 变异检查
|
||||
if ($p['usage_count'] > 50 && wp_rand(1, 100) < 5) {
|
||||
$new_prompt = $p['content'];
|
||||
$mutations = [
|
||||
" 请特别注意识别带有微信号的变体。",
|
||||
" 严厉拦截任何带有营销性质的外部链接。",
|
||||
" 对看起来像机器生成的重复无意义内容保持警惕。",
|
||||
" 注意识别故意错别字以逃避检测的广告。"
|
||||
];
|
||||
$new_prompt .= $mutations[wp_rand(0, count($mutations) - 1)];
|
||||
|
||||
$pool[$mode]['population'][] = [
|
||||
'content' => $new_prompt,
|
||||
'fitness' => 80,
|
||||
'usage_count' => 0,
|
||||
'success_count' => 0,
|
||||
'error_count' => 0,
|
||||
'version' => $p['version'] + 1
|
||||
];
|
||||
}
|
||||
|
||||
update_option('argon_ai_audit_prompt_pool', $pool);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建评论上下文信息
|
||||
* @param object $comment 评论对象
|
||||
@@ -10033,6 +10345,59 @@ function argon_detect_spam_comment($comment_id) {
|
||||
return argon_detect_spam_comment_sync($comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查评论内容是否匹配已学习的高置信度关键词
|
||||
* @param string $content 评论内容
|
||||
* @return array|false 匹配结果或 false
|
||||
*/
|
||||
function argon_check_keywords_match($content) {
|
||||
$learned_keywords = get_option('argon_comment_spam_learned_keywords', []);
|
||||
if (empty($learned_keywords)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($learned_keywords as $keyword => $stats) {
|
||||
// 只有置信度极高且经过多次验证的词才进入快速路径
|
||||
if ($stats['confidence'] > 0.92 && $stats['spam_count'] >= 3 && $stats['normal_count'] == 0) {
|
||||
if (mb_strpos($content, $keyword) !== false) {
|
||||
return [
|
||||
'is_spam' => true,
|
||||
'reason' => sprintf(__('命中高置信度特征词: %s', 'argon'), $keyword),
|
||||
'confidence' => $stats['confidence'],
|
||||
'suggestion' => 'auto',
|
||||
'hit_keyword' => $keyword
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 AI 确认的结果中自动学习关键词(高置信度时)
|
||||
* @param object $comment 评论对象
|
||||
* @param array $ai_result AI 返回的结果
|
||||
*/
|
||||
function argon_auto_learn_from_ai($comment, $ai_result) {
|
||||
if (get_option('argon_comment_spam_detection_ai_learn', 'false') !== 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
// 只有置信度非常高且被判定为垃圾评论时才自动提取特征
|
||||
if ($ai_result['is_spam'] && $ai_result['confidence'] > 0.98) {
|
||||
// 检查是否已经有类似关键词,避免重复提取
|
||||
$content = $comment->comment_author . ' ' . $comment->comment_content;
|
||||
if (argon_check_keywords_match($content)) {
|
||||
return; // 已经命中了,不需要再学习
|
||||
}
|
||||
|
||||
$keywords = argon_extract_keywords_from_comment($comment, true);
|
||||
if (!empty($keywords)) {
|
||||
argon_update_learned_keywords($keywords, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步检测评论是否为垃圾评论(支持临时评论对象)
|
||||
* @param object $comment 评论对象
|
||||
@@ -10044,13 +10409,46 @@ function argon_detect_spam_comment_sync($comment) {
|
||||
$prompt_mode = get_option('argon_comment_spam_detection_prompt_mode', 'standard');
|
||||
$custom_prompt = get_option('argon_comment_spam_detection_prompt', '');
|
||||
|
||||
// 根据模式选择 Prompt
|
||||
// 1. 快速路径:关键词缓存匹配(降低 AI 调用产生的成本)
|
||||
$combined_content = $comment->comment_author . ' ' . $comment->comment_content;
|
||||
$keyword_match = argon_check_keywords_match($combined_content);
|
||||
if ($keyword_match) {
|
||||
$unified_result = [
|
||||
'is_spam' => true,
|
||||
'reason' => $keyword_match['reason'],
|
||||
'username_invalid' => false,
|
||||
'username_reason' => '正常',
|
||||
'confidence' => $keyword_match['confidence'],
|
||||
'suggestion' => 'auto',
|
||||
'analysis' => __('已命中本地高置信度特征词库,自动拦截以降低 AI 额度消耗', 'argon'),
|
||||
'hit_keyword' => $keyword_match['hit_keyword']
|
||||
];
|
||||
|
||||
// 保存检测结果
|
||||
update_comment_meta($comment->comment_ID, '_argon_spam_detection_result', $unified_result);
|
||||
update_comment_meta($comment->comment_ID, '_argon_spam_detection_time', time());
|
||||
update_comment_meta($comment->comment_ID, '_argon_spam_detection_provider', 'local_cache');
|
||||
|
||||
return $unified_result;
|
||||
}
|
||||
|
||||
// 2. AI 路径
|
||||
$prompt_mode = get_option('argon_comment_spam_detection_prompt_mode', 'standard');
|
||||
$custom_prompt = get_option('argon_comment_spam_detection_prompt', '');
|
||||
|
||||
// 选择 Prompt
|
||||
global $argon_current_prompt_index;
|
||||
$argon_current_prompt_index = -1;
|
||||
|
||||
if ($prompt_mode === 'custom' && !empty($custom_prompt)) {
|
||||
$prompt = $custom_prompt;
|
||||
} else {
|
||||
$prompt = argon_get_spam_detection_prompt($prompt_mode);
|
||||
$prompt = argon_get_genetic_audit_prompt($prompt_mode);
|
||||
}
|
||||
|
||||
// 如果使用了遗传算法,记录当前索引
|
||||
$used_prompt_index = $argon_current_prompt_index;
|
||||
|
||||
// 构建评论上下文信息
|
||||
$comment_text = argon_build_comment_context($comment);
|
||||
|
||||
@@ -10096,15 +10494,23 @@ function argon_detect_spam_comment_sync($comment) {
|
||||
'username_reason' => isset($result['username_reason']) ? $result['username_reason'] : '正常',
|
||||
'confidence' => isset($result['confidence']) ? floatval($result['confidence']) : 0.8,
|
||||
'suggestion' => isset($result['suggestion']) ? $result['suggestion'] : 'auto',
|
||||
'analysis' => isset($result['analysis']) ? $result['analysis'] : ''
|
||||
'analysis' => isset($result['analysis']) ? $result['analysis'] : '',
|
||||
'prompt_mode' => $prompt_mode
|
||||
];
|
||||
|
||||
if (isset($used_prompt_index) && $used_prompt_index !== -1) {
|
||||
$unified_result['genetic_prompt_index'] = $used_prompt_index;
|
||||
}
|
||||
|
||||
// 保存检测结果
|
||||
update_comment_meta($comment->comment_ID, '_argon_spam_detection_result', $unified_result);
|
||||
update_comment_meta($comment->comment_ID, '_argon_spam_detection_time', time());
|
||||
update_comment_meta($comment->comment_ID, '_argon_spam_detection_provider', $provider);
|
||||
update_comment_meta($comment->comment_ID, '_argon_spam_detection_model', $model);
|
||||
|
||||
// 自动从 AI 结果中提取特征并缓存,降低未来成本
|
||||
argon_auto_learn_from_ai($comment, $unified_result);
|
||||
|
||||
return $unified_result;
|
||||
}
|
||||
|
||||
@@ -10215,8 +10621,16 @@ function argon_ai_learn_keywords($comment_id, $admin_decision) {
|
||||
|
||||
$ai_decision = isset($detection_result['is_spam']) ? $detection_result['is_spam'] : false;
|
||||
|
||||
// 如果 AI 和管理员判断一致,不需要学习
|
||||
if ($ai_decision === !$admin_decision) {
|
||||
// 如果 AI 和管理员判断一致,不需要学习 (AI 对了)
|
||||
$is_correct = ($ai_decision === !$admin_decision);
|
||||
|
||||
// 更新 Prompt 适应度
|
||||
if (isset($detection_result['genetic_prompt_index'])) {
|
||||
$prompt_mode = isset($detection_result['prompt_mode']) ? $detection_result['prompt_mode'] : 'standard';
|
||||
argon_update_prompt_fitness($prompt_mode, $detection_result['genetic_prompt_index'], $is_correct);
|
||||
}
|
||||
|
||||
if ($is_correct) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10331,6 +10745,9 @@ function argon_update_learned_keywords($keywords, $is_spam) {
|
||||
}
|
||||
|
||||
update_option('argon_comment_spam_detection_keywords', implode("\n", $current_keywords_array));
|
||||
|
||||
// 触发遗传修正
|
||||
argon_genetic_correct_keywords();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -10925,7 +11342,7 @@ function argon_batch_detect_spam_comments($comments_data) {
|
||||
if ($prompt_mode === 'custom' && !empty($custom_prompt)) {
|
||||
$prompt = $custom_prompt . "\n\n请对每条评论返回检测结果。";
|
||||
} else {
|
||||
$prompt = argon_get_spam_detection_prompt($prompt_mode);
|
||||
$prompt = argon_get_genetic_audit_prompt($prompt_mode);
|
||||
}
|
||||
|
||||
// 构建批量检测内容
|
||||
|
||||
135
settings.php
135
settings.php
@@ -2534,6 +2534,99 @@ function themeoptions_page(){
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th><label><?php _e('提示词遗传算法 (PGA)', 'argon');?></label></th>
|
||||
<td>
|
||||
<?php $argon_ai_audit_genetic_prompt_enable = get_option('argon_ai_audit_genetic_prompt_enable', 'false'); ?>
|
||||
<label>
|
||||
<input type="checkbox" name="argon_ai_audit_genetic_prompt_enable" value="true" <?php if ($argon_ai_audit_genetic_prompt_enable=='true'){echo 'checked';}?>/>
|
||||
<?php _e('启用 AI 审核提示词遗传算法', 'argon');?>
|
||||
</label>
|
||||
<p class="description">
|
||||
<?php _e('开启后,系统会维护一个 Prompt 基因池。根据管理员的审核反馈(对/错),自动优胜劣汰,动态调整 Prompt 以达到最佳审核效果。', 'argon');?>
|
||||
</p>
|
||||
<?php if ($argon_ai_audit_genetic_prompt_enable == 'true'): ?>
|
||||
<div class="argon-genetic-pool-status" style="margin-top: 10px; padding: 10px; background: #f8f9fe; border: 1px solid #e9ecef; border-radius: 4px;">
|
||||
<strong><?php _e('当前基因池状态', 'argon'); ?>:</strong>
|
||||
<ul style="margin: 5px 0 0 15px; font-size: 12px; color: #666;">
|
||||
<?php
|
||||
$pool = get_option('argon_ai_audit_prompt_pool', []);
|
||||
if (!empty($pool)):
|
||||
foreach ($pool as $mode => $data):
|
||||
$count = count($data['population']);
|
||||
$best = $data['population'][$data['current_best_index']];
|
||||
?>
|
||||
<li>
|
||||
<code><?php echo esc_html($mode); ?></code>:
|
||||
<?php printf(__('共 %d 个变体,当前最佳版本 v%d (适应度: %d)', 'argon'), $count, $best['version'], $best['fitness']); ?>
|
||||
</li>
|
||||
<?php
|
||||
endforeach;
|
||||
else:
|
||||
?>
|
||||
<li><?php _e('暂未积累基因数据', 'argon'); ?></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
<button type="button" class="button button-small" id="argon-reset-prompt-pool" style="margin-top: 10px;">
|
||||
<?php _e('重置基因池', 'argon'); ?>
|
||||
</button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th><label><?php _e('关键词遗传修正 (KGC)', 'argon');?></label></th>
|
||||
<td>
|
||||
<?php $argon_ai_audit_genetic_keyword_correction_enable = get_option('argon_ai_audit_genetic_keyword_correction_enable', 'false'); ?>
|
||||
<label>
|
||||
<input type="checkbox" name="argon_ai_audit_genetic_keyword_correction_enable" value="true" <?php if ($argon_ai_audit_genetic_keyword_correction_enable=='true'){echo 'checked';}?>/>
|
||||
<?php _e('启用关键词缓存遗传修正', 'argon');?>
|
||||
</label>
|
||||
<p class="description">
|
||||
<?php _e('开启后,系统会定期对学习到的关键词进行“进化”处理:自动衰减过期特征、淘汰低效词汇、修正误伤特征,使本地垃圾词库保持精简高效。', 'argon');?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th><label><?php _e('遗传变异率', 'argon');?></label></th>
|
||||
<td>
|
||||
<input type="number" name="argon_ai_audit_genetic_keyword_mutation_rate" min="1" max="50" value="<?php echo get_option('argon_ai_audit_genetic_keyword_mutation_rate', '5'); ?>" style="width: 80px;"/> %
|
||||
<p class="description">
|
||||
<?php _e('新 Prompt 变异产生的概率和关键词修正的幅度。建议维持在 5% 左右。', 'argon');?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th><label><?php _e('关键词缓存状态', 'argon');?></label></th>
|
||||
<td>
|
||||
<?php
|
||||
$learned_keywords = get_option('argon_comment_spam_learned_keywords', []);
|
||||
$high_confidence_count = 0;
|
||||
if (!empty($learned_keywords)) {
|
||||
foreach ($learned_keywords as $k => $v) {
|
||||
if (isset($v['confidence']) && $v['confidence'] > 0.9 && isset($v['spam_count']) && $v['spam_count'] >= 3) {
|
||||
$high_confidence_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
<div class="argon-keyword-cache-status" style="padding: 10px; background: #f8f9fe; border: 1px solid #e9ecef; border-radius: 4px;">
|
||||
<p style="margin: 0; font-size: 13px;">
|
||||
<?php printf(__('当前本地特征库包含 %d 个关键词,其中 %d 个已进入高置信度快速路径(可直接拦截以节省 AI 费用)。', 'argon'), count($learned_keywords), $high_confidence_count); ?>
|
||||
</p>
|
||||
<button type="button" class="button button-small" id="argon-clear-learned-keywords" style="margin-top: 10px;">
|
||||
<?php _e('清空学习缓存', 'argon'); ?>
|
||||
</button>
|
||||
</div>
|
||||
<p class="description">
|
||||
<?php _e('系统会自动从 AI 审计结果中提取共性特征并进行交叉训练和遗传修正。命中高置信度特征的评论将不再调用 AI,直接本地返回。', 'argon');?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th><label><?php _e('全站扫描', 'argon');?></label></th>
|
||||
<td>
|
||||
@@ -3035,6 +3128,48 @@ function themeoptions_page(){
|
||||
$('#argon-spam-scan-pending').on('click', function() {
|
||||
startSpamScan('pending');
|
||||
});
|
||||
|
||||
// 重置基因池
|
||||
$(document).on('click', '#argon-reset-prompt-pool', function() {
|
||||
if (!confirm('<?php _e('确定要重置所有 Prompt 基因池吗?这将删除所有已学习的变体。', 'argon'); ?>')) {
|
||||
return;
|
||||
}
|
||||
let $btn = $(this);
|
||||
$btn.prop('disabled', true).text('<?php _e('正在重置...', 'argon'); ?>');
|
||||
$.post(ajaxurl, {
|
||||
action: 'argon_reset_prompt_pool',
|
||||
nonce: '<?php echo wp_create_nonce('argon_manage_unified_apis'); ?>'
|
||||
}, function(response) {
|
||||
if (response.success) {
|
||||
alert(response.data.message);
|
||||
location.reload();
|
||||
} else {
|
||||
alert(response.data);
|
||||
$btn.prop('disabled', false).text('<?php _e('重置基因池', 'argon'); ?>');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 清空学习缓存
|
||||
$(document).on('click', '#argon-clear-learned-keywords', function() {
|
||||
if (!confirm('<?php _e('确定要清空所有已学习的关键词缓存吗?这将导致系统需要重新通过 AI 学习特征。', 'argon'); ?>')) {
|
||||
return;
|
||||
}
|
||||
let $btn = $(this);
|
||||
$btn.prop('disabled', true).text('<?php _e('正在清空...', 'argon'); ?>');
|
||||
$.post(ajaxurl, {
|
||||
action: 'argon_clear_learned_keywords',
|
||||
nonce: '<?php echo wp_create_nonce('argon_manage_unified_apis'); ?>'
|
||||
}, function(response) {
|
||||
if (response.success) {
|
||||
alert(response.data.message);
|
||||
location.reload();
|
||||
} else {
|
||||
alert(response.data);
|
||||
$btn.prop('disabled', false).text('<?php _e('清空学习缓存', 'argon'); ?>');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -159,5 +159,5 @@
|
||||
</div>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
<?php echo get_article_skeleton('1'); ?>
|
||||
</article>
|
||||
@@ -150,4 +150,5 @@
|
||||
|
||||
</div>
|
||||
|
||||
<?php echo get_article_skeleton('2'); ?>
|
||||
</article>
|
||||
@@ -151,5 +151,5 @@
|
||||
</div>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
<?php echo get_article_skeleton('1'); ?>
|
||||
</article>
|
||||
@@ -6,7 +6,7 @@
|
||||
<div class="shuoshuo-content">
|
||||
<?php the_content(); ?>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<div class="shuoshuo-preview-meta">
|
||||
<span>
|
||||
<i class="fa fa-calendar-o" aria-hidden="true"></i>
|
||||
@@ -46,4 +46,5 @@
|
||||
<a class="shuoshuo-preview-link btn btn-outline-primary btn-icon-only rounded-circle" type="button" href="<?php the_permalink(); ?>">
|
||||
<span class="btn-inner--icon"><i class="fa fa-arrow-right"></i></span>
|
||||
</a>
|
||||
<?php echo get_article_skeleton('1'); ?>
|
||||
</div>
|
||||
@@ -43,6 +43,12 @@
|
||||
<?php if (!post_password_required() && get_option("argon_show_readingtime") != "false" && is_readingtime_meta_hidden() == False) {
|
||||
echo get_article_reading_time_meta(get_the_content());
|
||||
} ?>
|
||||
<!-- 优雅内置的沉浸式阅读按钮 -->
|
||||
<div class="post-meta-devide">|</div>
|
||||
<div class="post-meta-detail post-meta-detail-immersive" id="immersive_read_toggle" title="<?php _e('沉浸阅读', 'argon'); ?>" style="cursor: pointer; transition: color 0.3s ease;">
|
||||
<i class="fa fa-television immersive-read-icon-enter" aria-hidden="true"></i>
|
||||
<span class="immersive-read-label"><?php _e('沉浸', 'argon'); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
if (has_post_thumbnail() && $show_thumbnail_in_banner != 'true'){
|
||||
@@ -51,6 +57,111 @@
|
||||
?>
|
||||
</header>
|
||||
|
||||
<!-- 简约的顶部退出按钮 -->
|
||||
<div id="immersive_exit_btn" class="immersive-exit-btn-container">
|
||||
<button class="immersive-exit-btn-fast">
|
||||
<i class="fa fa-compress" aria-hidden="true"></i> <?php _e('退出沉浸模式', 'argon'); ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 沉浸式阅读 JS -->
|
||||
<script>
|
||||
(function() {
|
||||
var toggleBtn = document.getElementById('immersive_read_toggle');
|
||||
var exitTopBtn = document.getElementById('immersive_exit_btn');
|
||||
if (!toggleBtn) return;
|
||||
|
||||
var isActive = false;
|
||||
|
||||
function enterImmersive() {
|
||||
if (isActive) return;
|
||||
isActive = true;
|
||||
|
||||
document.documentElement.classList.add('immersive-read');
|
||||
toggleBtn.classList.add('active');
|
||||
toggleBtn.title = '<?php _e("退出沉浸", "argon"); ?>';
|
||||
|
||||
var icon = toggleBtn.querySelector('i.fa');
|
||||
if (icon) {
|
||||
icon.classList.remove('fa-television');
|
||||
icon.classList.add('fa-compress');
|
||||
}
|
||||
var label = toggleBtn.querySelector('.immersive-read-label');
|
||||
if (label) {
|
||||
label.innerText = '<?php _e("退出", "argon"); ?>';
|
||||
}
|
||||
|
||||
// 快速滚动到文章头部
|
||||
var postContent = document.getElementById('post_content');
|
||||
if (postContent) {
|
||||
var rect = postContent.getBoundingClientRect();
|
||||
var headerOffset = 20;
|
||||
var scrollTarget = window.scrollY + rect.top - headerOffset;
|
||||
window.scrollTo({ top: scrollTarget, behavior: 'smooth' });
|
||||
}
|
||||
|
||||
if (exitTopBtn) {
|
||||
exitTopBtn.classList.add('visible');
|
||||
}
|
||||
}
|
||||
|
||||
function exitImmersive() {
|
||||
if (!isActive) return;
|
||||
isActive = false;
|
||||
|
||||
document.documentElement.classList.remove('immersive-read');
|
||||
|
||||
toggleBtn.classList.remove('active');
|
||||
toggleBtn.title = '<?php _e("沉浸阅读", "argon"); ?>';
|
||||
|
||||
var icon = toggleBtn.querySelector('i.fa');
|
||||
if (icon) {
|
||||
icon.classList.remove('fa-compress');
|
||||
icon.classList.add('fa-television');
|
||||
}
|
||||
var label = toggleBtn.querySelector('.immersive-read-label');
|
||||
if (label) {
|
||||
label.innerText = '<?php _e("沉浸", "argon"); ?>';
|
||||
}
|
||||
|
||||
if (exitTopBtn) {
|
||||
exitTopBtn.classList.remove('visible');
|
||||
}
|
||||
}
|
||||
|
||||
toggleBtn.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
isActive ? exitImmersive() : enterImmersive();
|
||||
});
|
||||
|
||||
if (exitTopBtn) {
|
||||
exitTopBtn.querySelector('button').addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
exitImmersive();
|
||||
});
|
||||
}
|
||||
|
||||
// Esc 快捷键支持
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape' && isActive) exitImmersive();
|
||||
});
|
||||
|
||||
// 滚动检测: 控制顶部退出按钮透明度防遮挡
|
||||
var ticking = false;
|
||||
window.addEventListener('scroll', function() {
|
||||
if (!isActive || !exitTopBtn) return;
|
||||
if (!ticking) {
|
||||
ticking = true;
|
||||
requestAnimationFrame(function() {
|
||||
// 当滚动超过一定距离时,使其半透明,鼠标悬浮时恢复
|
||||
exitTopBtn.classList.toggle('dimmed', window.scrollY > 200);
|
||||
ticking = false;
|
||||
});
|
||||
}
|
||||
}, { passive: true });
|
||||
})();
|
||||
</script>
|
||||
|
||||
<?php get_template_part('template-parts/ai-summary'); ?>
|
||||
|
||||
<div class="post-content" id="post_content">
|
||||
|
||||
@@ -207,5 +207,7 @@
|
||||
});
|
||||
}
|
||||
<?php } ?>
|
||||
|
||||
|
||||
})();
|
||||
</script>
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user