---
layout: post
title: 如何制作一个能发好图的微信机器人
tags: [微信, 聊天, 机器人, PHP]
---

  微信？不好。PHP？好！<!--more-->    
  
# 起因
  前几天，我由于图片机器人做失败了所以做了一个[聊天机器人](/2021/02/06/wechatbot.html)，但是很可惜，人家图灵机器人支持一键接入微信公众号，哦，当然测试号不行，总而言之做那个什么聊天机器人毫无意义，而且我还用的是V1的API，功能根本就没发挥出来。   
  总之，既然目标是做图片机器人，那么就不能就聊天机器人而满足了，正好，今天看到群友分享了一个不错的[API](https://api.lolicon.app/)，文档写的很清晰，而且功能不错，我反正也懒得去思考要怎么搞Pixiv上面的图片，既然有人已经写好了，那就直接用就好了。   
  
# 怎么搞
  既然图源有了，那么要怎么发送图片呢？既然听别人说可以用什么客服接口，那就再仔细看看那个文档呗。我以前以为那个客服接口必须要生成客服账号啥的很麻烦就没怎么看，后来发现好像不用，而且只要用户发一句话，在48小时内都可以调用这个接口给用户发消息，那这么看来就很棒了啊，我还以为只有5秒内被动回复一种方法，这样我可以直接整个异步过去。具体API的用法可以去看[微信开放文档](https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Service_Center_messages.html#7)。   
  
# 快点上代码
```php
<?php
$appid=微信appID;
$secret=微信appsecret;
$appkey=图灵机器人APIkey;
$apikey='Lolicon API Key';

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));

$picurl=json_decode(file_get_contents('https://api.lolicon.app/setu/?r18=1&size1200=1&apikey='.$apikey),true)['data'][0]['url'];
if(!$picurl){
$picurl=json_decode(file_get_contents('https://api.lolicon.app/setu/?r18=1&size1200=1'),true)['data'][0]['url'];
}
if(!$picurl){
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":"text",
    "text":
    {
         "content":"很抱歉，获取失败，可能是API次数达到上限，请明天再试"
    }
}'))));
exit();
}


$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.png\"\r\n".
            "Content-Type: image/png\r\n\r\n".
            file_get_contents($picurl)."\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();
}

function checkSignature()
{
    $signature = $_GET["signature"];
    $timestamp = $_GET["timestamp"];
    $nonce = $_GET["nonce"];
	
    $token = 'mayx';
    $tmpArr = array($token, $timestamp, $nonce);
    sort($tmpArr, SORT_STRING);
    $tmpStr = implode( $tmpArr );
    $tmpStr = sha1( $tmpStr );
    
    if( $tmpStr == $signature ){
        return true;
    }else{
        return false;
    }
}
if(checkSignature()){
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 '<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['.json_decode(file_get_contents('https://www.tuling123.com/openapi/api', false, stream_context_create(array('http' => array('method' => 'POST','header' => 'Content-type:application/x-www-form-urlencoded','content' => http_build_query(array('key' => $appkey,'info' => $vals[$index['CONTENT'][0]]['value'],'userid' => $vals[$index['FROMUSERNAME'][0]]['value'])))))),true)['text'].']]></Content>
</xml>';
}
}
}
}else{
echo 'error';
}
```
  2021.02.19更新：当Lolicon API Key次数不够时会尝试不使用Key调用，增加了缓存`access_token`的功能。另外以后我会自己搞一个图片API，免得总是被这个每天只有300次调用的垃圾API所限制。   
  2021.02.21更新：上次不知道怎么就脑残的用了调用次数只有500次的获取用户接口作为检查`access_token`的依据，这次换了个没有上限的，另外把session的回收时间改成了2个小时，和接口对应。

# 怎么用？
  和[上一篇](/2021/02/06/wechatbot.html#%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95)一样，不过有一个不一样的地方就是这里需要一个Lolicon API Key，如果没有的话好像限制非常高，可能一天只能调用1-2次吧，有API Key好像可以每天请求300次，具体怎么用可以看[他们的文档](https://api.lolicon.app/#/setu)。    
  API Key的申请方式是去找他们的Telegram机器人[@loliconApiBot](https://t.me/loliconApiBot)，倒是还挺简单的，申请出一个API和一个测试号可以供100人使用，这一点还是挺不错的。   
  最后做好了就可以向测试号发送来点什么图？关键词可以自己在代码里改。   
  如果不想整图灵机器人也行，那个东西要实名还是挺麻烦的，不过这样聊天功能就不能正常工作了 ~~（谁用这个机器人是和聊天机器人聊天啊！）~~ 。   
  另外PHP空间很好申请的，相比Python还是非常有性价比的，至少可以不用租服务器。
  
# 总结
  这次代码用了不少有意思的特性，比如通过`file_get_contents`直接上传文件，以及PHP异步之类的，这个异步的功能还是我自己想出来的哦，现在在百度上都不一定能搜到我这样的方法。   
  总之PHP还是挺有意思的。
