Repository: mirai-mamori/Sakurairo (language: php)

Issue:
页尾一言api无法输出自定义类型


**问题出处：**
页尾一言

**实际行为描述：**

使用比如["https://v1.hitokoto.cn/?c=i&encode=json"]保存设置并刷新缓存后，随机内容依旧是任意类型而不是i(诗词)
控制台查看对应请求为https://v1.hitokoto.cn/，发现并未传参?c=i

**预期的行为：**
正常传参

**复现步骤：**

如需要可以提供地址

**配置与环境：**

- wordpress6.7.1
- 主题版本latest

**截图（若有）**



**补充信息：**





Discussion / hints:
@Deepsea-CN，感谢你给 sakurairo 提出了 issue。ヾ(≧▽≦*)o
请根据 issue 模版确保背景信息的完善，我们将调查并尽快回复你。

Fix patch:
diff --git a/inc/classes/Captcha.php b/inc/classes/Captcha.php
index 2c2dab48..71b420c8 100644
--- a/inc/classes/Captcha.php
+++ b/inc/classes/Captcha.php
@@ -6,14 +6,16 @@
 
 class Captcha
 {
-    private $captchCode;
+    private $captchaText;
+    private $captchaResult;
 
     /**
      * CAPTCHA constructor.
      */
     public function __construct()
     {
-        $this->captchCode = '';
+        $this->captchaText = '';
+        $this->captchaResult = '';
     }
 
     /**
@@ -23,11 +25,19 @@ public function __construct()
      */
     private function create_captcha(): void
     {
-        $dict = str_split('abcdefhjkmnpqrstuvwxy12345678');
-        $randKeys = array_rand($dict, 5);
-        $this->captchCode = implode('', array_map(function ($value) use ($dict) {
-            return $dict[$value];
-        }, $randKeys));
+        $n1 = mt_rand(10, 99);
+        $n2 = mt_rand(10, 99);
+        if (mt_rand(0, 1)) {
+            //加法
+            $this->captchaText = "{$n1}+{$n2}=?";
+            $this->captchaResult = $n1 + $n2;
+        } else {
+            //减法(避免负数)
+            $min = min($n1, $n2);
+            $max = max($n1, $n2);
+            $this->captchaText = "{$max}-{$min}=?";
+            $this->captchaResult = $max - $min;
+        }
     }
 
     /**
@@ -38,7 +48,7 @@ private function create_captcha(): void
     private function crypt_captcha(): string
     {
         //return md5($this->captchCode);
-        return password_hash($this->captchCode, PASSWORD_DEFAULT);
+        return password_hash($this->captchaResult, PASSWORD_DEFAULT);
         // return wp_hash_password($this->captchCode);
     }
 
@@ -63,44 +73,79 @@ public function verify_captcha(string $captcha, string $hash): bool
      */
     public function create_captcha_img(): array
     {
+        //动态计算验证码难度
+        $level = iro_opt('iro_captcha_level') / 100;
+        $conf = array(
+          'noise' => (int)(700 + 500 * $level),
+          'curves' => (int)(8 + 6 * $level),
+          'quality' => (int)(100 - 40 * $level)
+        );
+        
+        //创建验证码
+        $this->create_captcha();
+        $font = STYLESHEETPATH . '/inc/KumoFont.ttf';
+        
         //创建画布
-        $img = imagecreatetruecolor(120, 40);
-        $file = STYLESHEETPATH . '/inc/KumoFont.ttf';
+        $image = imagecreatetruecolor(210, 60);
         //填充背景色
-        $backcolor = imagecolorallocate($img, mt_rand(200, 255), mt_rand(200, 255), mt_rand(0, 255));
-        imagefill($img, 0, 0, $backcolor);
+        $color = imagecolorallocate($image, mt_rand(200, 255), mt_rand(200, 255), mt_rand(200, 255));
+        imagefill($image, 0, 0, $color);
 
-        //创建验证码
-        $this->create_captcha();
         //绘制文字
-        for ($i = 0; $i < 5; $i++) {
-            // $span = 20;
-            $stringcolor = imagecolorallocate($img, mt_rand(0, 255), mt_rand(0, 100), mt_rand(0, 80));
-            imagefttext($img, 25, 2, $i * 20, 30, $stringcolor, $file, $this->captchCode[$i]);
+        $chars = str_split($this->captchaText);
+        for ($i = 0; $i < count($chars); $i++) {
+            $char = $chars[$i];
+            $color = imagecolorallocate($image, mt_rand(0, 150), mt_rand(0, 150), mt_rand(0, 150));
+            $x = 30 * $i + 10;
+            $y = 30 + mt_rand(-5, 5);
+            //加减符号不倾斜 并加大字体
+            $size = ($i === 2) ? 30 : 20;
+            $angle = ($i === 2) ? 0 : mt_rand(-25, 25);
+            imagettftext($image, $size, $angle, $x, $y, $color, $font, $char);
         }
-
-        //添加干扰线
-        for ($i = 0; $i < 8; $i++) {
-            $linecolor = imagecolorallocate($img, mt_rand(0, 150), mt_rand(0, 250), mt_rand(0, 255));
-            imageline($img, mt_rand(0, 179), mt_rand(0, 39), mt_rand(0, 179), mt_rand(0, 39), $linecolor);
+        
+        //添加噪点
+        for ($i = 0; $i < $conf['noise']; $i++) {
+            $color = imagecolorallocate($image, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255));
+            imagesetpixel($image, mt_rand(0, 210), mt_rand(0, 60), $color);
         }
-
-        //添加干扰点
-        for ($i = 0; $i < 144; $i++) {
-            $pixelcolor = imagecolorallocate($img, mt_rand(100, 150), mt_rand(0, 120), mt_rand(0, 255));
-            imagesetpixel($img, mt_rand(0, 179), mt_rand(0, 39), $pixelcolor);
+        
+        // 添加贝塞尔曲线
+        for ($i = 0; $i < $conf['curves']; $i++) {
+            $color = imagecolorallocate($image, mt_rand(50,150), mt_rand(50, 150), mt_rand(50, 150));
+            
+            // 贝塞尔曲线控制点
+            $x1 = mt_rand(0, 210);
+            $x2 = mt_rand(0, 210);
+            $cx1 = mt_rand(0, 210);
+            $cx2 = mt_rand(0, 210);
+            $y1 = mt_rand(0, 60);
+            $y2 = mt_rand(0, 60);
+            $cy1 = mt_rand(0, 60);
+            $cy2 = mt_rand(0, 60);
+            
+            // 绘制贝塞尔曲线
+            for ($t = 0; $t <= 1; $t += 0.01) {
+                $xt = (int)((1 - $t) * (1 - $t) * (1 - $t) * $x1 + 3 * (1 - $t) * (1 - $t) * $t * $cx1 + 3 * (1 - $t) * $t * $t * $cx2 + $t * $t * $t * $x2);
+                $yt = (int)((1 - $t) * (1 - $t) * (1 - $t) * $y1 + 3 * (1 - $t) * (1 - $t) * $t * $cy1 + 3 * (1 - $t) * $t * $t * $cy2 + $t * $t * $t * $y2);
+                imagesetpixel($image, $xt, $yt, $color);
+            }
         }
+        //启用高斯模糊，进一步降低清晰度
+        imagefilter($image, IMG_FILTER_GAUSSIAN_BLUR);
+        
         $timestamp = time();
-        $this->captchCode .= $timestamp;
+        $this->captchaResult .= $timestamp;
         //打开缓存区
         ob_start();
-        imagepng($img);
+        //降低图片质量
+        imagejpeg($image, null, $conf['quality']);
         //输出图片
         $captchaimg =  ob_get_contents();
         //销毁缓存区
         ob_end_clean();
         //销毁图片(释放资源)
-        imagedestroy($img);
+        imagedestroy($image);
         // 以json格式输出
         $captchaimg = 'data:image/png;base64,' . base64_encode($captchaimg);
         return [
@@ -126,7 +171,8 @@ public function check_captcha(string $captcha, int $timestamp, string $id): arra
         if (!isset($timestamp) || !isset($id) || !preg_match('/^[\w$.\/]+$/', $id) || !ctype_digit((string)$timestamp)) {
             $code = 3;
             $msg = __('Bad Request.',"sakurairo");//非法请求
-        } elseif (empty($captcha) || strlen($captcha) !== 5) {
+        } elseif (!preg_match('/^(?:(?!199)(?:[1-9]\d?|1\d{2}|0))$/', $captcha)) {
+            //匹配非0 ~ 198
             $code = 3;
             $msg = __("Look like you forgot to enter the captcha.","sakurairo");//请输入正确的验证码!
         } elseif ($timestamp < $timeThreshold) {
diff --git a/inc/swicher.php b/inc/swicher.php
index de7c4c78..a04e944e 100644
--- a/inc/swicher.php
+++ b/inc/swicher.php
@@ -81,7 +81,7 @@ function iro_get_the_author_name()
         'comment_upload_img' => iro_opt('img_upload_api') == 'off' ? false : true,
         'cache_cover' => check(iro_opt('cache_cover')),
         'site_bg_as_cover' => check(iro_opt('site_bg_as_cover')),
-        'yiyan_api' => empty(iro_opt('yiyan')) ? ["https://v1.hitokoto.cn/", "https://api.nmxc.ltd/yiyan/"] : json_decode(iro_opt('yiyan_api')),
+        'yiyan_api' => empty(iro_opt('yiyan_api')) ? ["https://v1.hitokoto.cn/", "https://api.nmxc.ltd/yiyan/"] : json_decode(iro_opt('yiyan_api')),
         'skin_bg0' => '',
         'skin_bg1' => $vision_resource_basepath . 'background/foreground/bg1.png',
         'skin_bg2' => $vision_resource_basepath . 'background/foreground/bg2.png',
diff --git a/opt/options/theme-options.php b/opt/options/theme-options.php
index d63e60ce..6c63b99d 100644
--- a/opt/options/theme-options.php
+++ b/opt/options/theme-options.php
@@ -1267,7 +1267,19 @@ function iro_validate_optional_url( $value ) {
         ),
         'default' => 'off',
       ),
-
+      
+      array(
+        'id' => 'iro_captcha_level',
+        'type' => 'slider',
+        'title' => __('Captcha Level', 'sakurairo_csf'),
+        'desc' => __('The difficulty level of the Theme Captcha', 'sakurairo_csf'),
+        'dependency' => array( 'captcha_select', '==', 'iro_captcha', '', 'true' ),
+        'step' => '1',
+        'min' => '0',
+        'max' => '100',
+        'default' => '60'
+      ),
+      
       array(
         'id' => 'vaptcha_vid',
         'type' => 'text',


Test patch: