一直以来,本站评论区都饱受 Spam 的烦扰,于是 TE 水友群里就有小伙伴提议用一下 Cloudflare Turnstile 来解决一下。这个就是类似于 Google reCAPTCHA 吧,但区别与 Google 的是,Turnstile 没有那些烦人的找红绿灯找自行车啊什么的,几乎属于无感的。于是便着手考虑给自己站点的评论区也安排上这个。
首先,要到 Cloudflare Turnstile 官网注册一下,创建好 Turnstile 站点,获取到两个密钥(一个 Key 是放在前台的,另一个打码的是放在后台的)
免费的额度肯定够用,具体可以详见官网。但免费的版本“仅限托管模式”,也就是不能无痕插入,肯定会在前端留出一个验证状态的框,可能会对美观性造成一定影响(比如说我这边就是)
在主题目录的 comments.php
中,找到 <form>
表单,把下面的代码放到表单里面。如果实在确定不好位置,那就放在评论提交按钮的上面就好。记得把上面的前台密钥给填写进去(不是打码的那个)。
<div class="cf-turnstile" data-sitekey="写你自己站点的KEY"></div>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" defer></script>
对于进行过评论区 Ajax 提交改造的站点,需要让验证器在每次提交之后,进行一次刷新的操作。可以在适当位置添加如下代码。
// 也可以不用 try 包裹
try {
turnstile.reset();
} catch (e) {}
直接在主题目录的 functions.php
中,追加如下内容。
Typecho_Plugin::factory('Widget_Feedback')->comment = ['XComment', 'feedbackFilter'];
class XComment {
/**
* 评论发布钩子
* @param $comment
* @param $archive
* @return mixed
* @throws \Typecho\Widget\Exception
*/
public static function feedbackFilter($comment, $archive)
{
// 如果已登录,直接返回,不进行处理
if (Typecho_Widget::widget('Widget_User')->hasLogin()) {
return $comment;
}
$secret = '打码的密钥填在这里'; /* Store this somewhere secure */
$remote_addr = $_SERVER['REMOTE_ADDR'];
$cf_url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
$token = $_POST['cf-turnstile-response'];
$msg = '';
$isPass = false;
// Request data
$data = array(
"secret" => $secret,
"response" => $token,
"remoteip" => $remote_addr
);
// Initialize cURL
$curl = curl_init();
// Set the cURL options
curl_setopt($curl, CURLOPT_URL, $cf_url);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
// Execute the cURL request
$response = curl_exec($curl);
// Check for errors
if (curl_errno($curl)) {
$error_message = curl_error($curl);
// Handle the error the way you like it
$msg = '[Turnstile]cURL Error: ' . $error_message;
}else{
/* Parse Cloudflare's response and check if there are any validation errors */
$response = json_decode($response,true);
if ($response['error-codes'] && count($response['error-codes']) > 0){
$msg = '此评论已被 Cloudflare Turnstile 拦截。Error codes: ';
foreach($response['error-codes'] as $e){
$msg .= $e;
}
}else{
$isPass = true;
}
}
// Close cURL
curl_close($curl);
if ($isPass) {
return $comment;
} else {
throw new \Typecho\Widget\Exception(_t($msg, '评论失败'));
}
}
}
这样下来,应该就可以了。