快速自制微信图片机器人 | Mayx的博客
Author: mayx
Description: 优化真的是很复杂啊……
Date: 2021-02-23T00:00:00+08:00

23 February 2021 - 字数统计：4456 - 阅读大约需要14分钟 - Hits: Loading...

# 快速自制微信图片机器人

by mayx

这是一篇创建于 天前的文章，其中的信息可能已经有所发展或是发生改变。

AI摘要

这篇文章讲述了作者自制微信图片发送机器人的经历，包括了遇到的问题和解决过程。主要问题包括：频繁获取微信access_token导致次数限制，以及Lolicon API的调用次数限制。为了解决这些，作者改进了代码，设置了缓存来存储API返回的结果，并创建了一个仓库来存储Pixiv API的缩略图。然而，API中的图片数量较少，这限制了可供调用的图片量。文章结尾提到将考虑使用其他图片资源作为替代。

- 起因
- 解决API调用次数过少的问题
- 新的代码
- 如何使用？
- 结尾

优化真的是很复杂啊……

#
起因

前段时间，我做出来了能发图片的机器人，做出来之后我拿给群友们体验，但是很遗憾的是那个代码实在是不太行，首先有2个问题，第一是微信获取`access_token`的次数是有限的，我的第一版代码在每一次调用都去获取`access_token`，这样很快次数就会消耗光，后来我稍微改进了一下，设置了个缓存，结果呢，我检测的时候用了次数更少的接口……简直是太蠢了……之后呢？结果今天发现代码里有两个获取`access_token`的地方，缓存完全没起到作用……

总之上面的问题各种波折总算是解决好了，然后还有一个问题是我的图片来源是Lolicon API，然后调用限制是300次/天，说实话，对于一个人来说这个数量是够了，但是如果有很多人，像测试号最多能容纳100人，那每天每人也就只有3次调用的机会。

那要怎么解决调用次数的问题呢？我首先想的就是缓存结果。

#
解决API调用次数过少的问题

因为对于图片来说，基本上没有什么变化的信息，所以如果能将每一次的结果缓存的话其实也没有问题。所以说干就干，我单独开了一个仓库pixiv-index用来存储缓存的结果，具体代码的话都在这个仓库里面，每天会调用那个API直到用完次数。

考虑到大多数情况下也不需要原图，所以这个API里的图片都只是长或宽最大为1200px的缩略图。

使用方法也很简单，像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…… （虽然实际上是抄的那个给开发者账号续命的那个仓库lol）

#
新的代码

解决了那些问题之后我又稍微优化了一下，把聊天机器人的功能剥离掉了，免得那个图灵机器人的API让人混乱。

    <?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之类的图站吧，用那些图片也是个不错的选择。

tags: 微信 - 图片 - Pixiv - 机器人 - PHP 查看原始文件

#### 推荐文章

Loading...

上一篇：如何制作一个能发好图的微信机器人
下一篇：做一个Server酱·TurboMini企业应用版
想问这篇文章
召唤伊斯特瓦尔