You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
mabbs/_posts/2021-02-23-picbotpro.md

119 lines
7.0 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

---
layout: post
title: 快速自制微信图片机器人
tags: [微信, 图片, Pixiv, 机器人, PHP]
---
优化真的是很复杂啊……<!--more-->
# 起因
前段时间,我做出来了[能发图片的机器人](/2021/02/19/picbot.html)做出来之后我拿给群友们体验但是很遗憾的是那个代码实在是不太行首先有2个问题第一是微信获取`access_token`的次数是有限的,我的第一版代码在每一次调用都去获取`access_token`,这样很快次数就会消耗光,后来我稍微改进了一下,设置了个缓存,结果呢,我检测的时候用了次数更少的接口……简直是太蠢了……之后呢?结果今天发现代码里有两个获取`access_token`的地方,缓存完全没起到作用……
总之上面的问题各种波折总算是解决好了,然后还有一个问题是我的图片来源是[Lolicon API](https://api.lolicon.app/setu)然后调用限制是300次/天说实话对于一个人来说这个数量是够了但是如果有很多人像测试号最多能容纳100人那每天每人也就只有3次调用的机会。
那要怎么解决调用次数的问题呢?我首先想的就是缓存结果。
# 解决API调用次数过少的问题
因为对于图片来说,基本上没有什么变化的信息,所以如果能将每一次的结果缓存的话其实也没有问题。所以说干就干,我单独开了一个仓库[pixiv-index](https://github.com/Mabbs/pixiv-index)用来存储缓存的结果具体代码的话都在这个仓库里面每天会调用那个API直到用完次数。
考虑到大多数情况下也不需要原图所以这个API里的图片都只是长或宽最大为1200px的缩略图。
使用方法也很简单像PHP的话就可以这样写
```php
<?php
$raw=json_decode(file_get_contents("https://mabbs.github.io/pixiv-index/index.json"),true);
echo file_get_contents('https://mabbs.github.io/pixiv-index/data/'.$raw[rand(0,count($raw)-1)]);
```
虽然问题解决了但是我发现了一个巨大的缺陷我设计这个脚本的初心是想着它有非常多的数据供我调用结果我发现我错了之前没有仔细看他们的文档现在看了才发现我想要的图片他们也只有仅仅3361张而已实在是太少了而总共的图片数量也只有17285张而已即使那个站的数据也在以非常缓慢的速度增加……
我只是懒得去别的地方找而且因为这个API作者说那些图片都是Ta精心挑选的我才特意写了那个仓库的那些脚本还特地学了一下Github Action…… ~~(虽然实际上是抄的那个[给开发者账号续命的](https://github.com/wangziyingwen/AutoApiSecret)那个仓库lol~~
# 新的代码
解决了那些问题之后我又稍微优化了一下把聊天机器人的功能剥离掉了免得那个图灵机器人的API让人混乱。
```php
<?php
$appid='微信appID';
$secret='微信appsecret';
$token='和配置的Token配置一致即可';
$source='https://i.pximg.net';
ini_set('session.gc_maxlifetime', 7200);
ignore_user_abort(true);
set_time_limit(0);
session_id('Storage');
session_start();
if(!json_decode(file_get_contents('https://api.weixin.qq.com/cgi-bin/get_api_domain_ip?access_token='.$_SESSION['access_token']),true)['ip_list']){
$_SESSION['access_token']=json_decode(file_get_contents('https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='.$appid.'&secret='.$secret),true)['access_token'];
}
if($_GET["upap"]){
define('MULTIPART_BOUNDARY', '--------------------------'.microtime(true));
$retry=3;
while(!$picdata||$retry<=0){
$raw=json_decode(file_get_contents("https://mabbs.github.io/pixiv-index/index.json"),true);
$picdata=file_get_contents($source.json_decode(file_get_contents('https://mabbs.github.io/pixiv-index/data/'.$raw[rand(0,count($raw)-1)]),true)['url'], false, stream_context_create(array('http' => array('method' => 'GET','header' => "referer: https://www.pixiv.net/"))));
$retry-=1;
}
$context = stream_context_create(array(
'http' => array(
'method' => 'POST',
'header' => 'Content-Type: multipart/form-data; boundary='.MULTIPART_BOUNDARY,
'content' => "--".MULTIPART_BOUNDARY."\r\n".
"Content-Disposition: filename=\"image.jpg\"\r\n".
"Content-Type: image/jpg\r\n\r\n".
$picdata."\r\n".
"--".MULTIPART_BOUNDARY."--\r\n"
)
));
file_get_contents('https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token='.$_SESSION['access_token'] , false, stream_context_create(array('http' => array('method' => 'POST','header' => 'Content-type: application/json;charset=utf-8','content' => '{
"touser":"'.$_GET["openid"].'",
"msgtype":"image",
"image":
{
"media_id":"'.json_decode(file_get_contents('https://api.weixin.qq.com/cgi-bin/media/upload?access_token='.$_SESSION['access_token'].'&type=image', false, $context),true)['media_id'].'"
}
}'))));
exit();
}
$timestamp=$_GET["timestamp"];
$nonce=$_GET["nonce"];
$tmpArr=array($token, $timestamp, $nonce);
sort($tmpArr, SORT_STRING);
if( sha1(implode($tmpArr)) == $_GET["signature"] ){
if($_GET["echostr"]){
echo $_GET["echostr"];
}else{
// 加载XML内容
$content = file_get_contents("php://input");
$p = xml_parser_create();
xml_parse_into_struct($p, $content, $vals, $index);
xml_parser_free($p);
if($vals[$index['MSGTYPE'][0]]['value'] == 'text'){
if($vals[$index['CONTENT'][0]]['value'] == '来点色图'){
echo '<xml>
<ToUserName><![CDATA['.$vals[$index['FROMUSERNAME'][0]]['value'].']]></ToUserName>
<FromUserName><![CDATA['.$vals[$index['TOUSERNAME'][0]]['value'].']]></FromUserName>
<CreateTime>'.time().'</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[开始发起请求,请耐心等待]]></Content>
</xml>';
file_get_contents('https://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].'?upap=1&openid='.$vals[$index['FROMUSERNAME'][0]]['value'], false, stream_context_create(array('http' => array('timeout' => 0.5))));
}else{
echo 'success';
}
}
}
}else{
echo 'error';
}
```
2021.02.26更新似乎在库中的图片有一些是被删掉了所以为了提高回复的成功率增加了3次重试。
# 如何使用?
具体应该不需要我说了吧看之前的几篇关于微信机器人的文章里面的这段就行了。这里我删掉了2个参数又增加了2个一个是Token想填啥都行只要和测试号里配置一样就行。另一个是source那个是Pixiv的图片服务器如果后端服务器在国外那这个就不用管了如果在国内的话需要改成`https://i.pixiv.cat`来做反代或者如果有其他反代服务也可以自己用CloudFlare Worker建一个也没有问题。
# 结尾
那个Lolicon API实在是不好用不过我也懒得解决了所以就托学弟在做Pixiv日榜的收集回头看看效果怎么样实在不行就去研究一下各种各样的什么booru之类的图站吧用那些图片也是个不错的选择。