QQ官方机器人简介:
- 消息通知给开发者使用websocket服务
 - 发送消息,使用openapi即http
 - 官方只给了go和node.js的SDK,没有PHP的
 - 官方文档:https://bot.q.qq.com/wiki/develop/api/
 - 再主动发送消息数量有限制,不同机器人数量不同
 - 公域机器人,即其他人搜索即可添加到自己的频道或群,主动发送消息每日数量很少,好像每日就几条
 - 私域,即需要开发者设置允许哪个频道或群添加,才能使用。主动消息数量较多,好像调整到了每日100。
 - 被动回复消息都不限量
 - 准备看下面更凌乱的内容 :@(呲牙)
 
开发参考:
在gitee找到的一个的例子,其他没找到
 阿巴叭叭叭 / QbotPHP / 
程序介绍:
使用phrity/websocket库搭建websocket服务
本来想用ratchet,一直报错,没整正常 :@(害羞)
安装要求:
- PHP > 8.0
 - composer
 - phrity/websocket 版本大于等于2.0
 
安装步骤:
- 机器人目录(自定义)下新建
composer.json文件,输入 
{
    "require": {
        "phrity/websocket": "^2.0"
    }
}
- 打开ssh,进入机器人目录,输入命令 
composer install回车执行 - 在机器人目录新建
GuildSocket.php文件 
<?php
use WebSocket\Client;
use WebSocket\Connection;
use WebSocket\Message\Message;
use WebSocket\Middleware\PingResponder;
class GuildSocket
{
    private $qqGuildUrl = '';
    private $appId = '';
    private $token = '';
    private $appSecret = '';
    private $access_token = '';
    private $expires_in = '';
    private $guzzleOptions = [];
    private $s = '';
    private $session_id = '';
    private $time0 = 0;
    private $seconds = 0;
    
    /**
     * 设置最大执行时间设置为无限制
     * 设置内存限制设置为无限制
     * 初始化参数
     * 
     */
    public function __construct(String $qqGuildUrl, String $appId, String $token, String $appSecret, Array $guzzleOptions)
    {
        
        set_time_limit(0);
        ini_set('memory_limit','-1');
        $this->qqGuildUrl = $qqGuildUrl;
        $this->appId = $appId;
        $this->token = $token;
        $this->appSecret = $appSecret;
        $this->guzzleOptions = $guzzleOptions;
        
    }
    /**
     * @param $token
     * @return mixed
     * 获取Gateway
     */
    private function getGateway(String $token): string
    {
        return "wss://sandbox.api.sgroup.qq.com/websocket";
    }
    
    /**
     * @param $url  请求地址
     * @param  $method  请求方法
     * @param  $param  请求参数
     * @param  $headers  请求头
     * 构造HTTP请求
     * 
     */
    private function httpRequest($url, $method = "POST", $param = "", $header = [])
    {
    
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $param);
        $response = curl_exec($ch);
        curl_close($ch);
        $response = json_decode($response, true);
        
        return $response;
        
    }
    
    
    /**
     * 获取openapi的调用凭证access_token
     */
    private function getSendMsgToken()
    {
        
        $param = [
            "appId"=> $this->appId,
            "clientSecret"=> $this->appSecret
            ];
        $token = $this->httpRequest("https://bots.qq.com/app/getAppAccessToken", "POST", json_encode($param), ['Content-Type: application/json']);
        
        if(!empty($token['access_token'])) {
            
            // 更新token及token失效时间
            $this->access_token = $token['access_token'];
            $this->expires_in = $token['expires_in'] + time();
            echo("\033[32m[消息发送token更新]\033[0m\n" . $token['access_token'] . "\n\n");
            
        }
        
    }
    /**
     * @param String $token
     * @param Client $client
     * @return String
     * WS身份验证,返回SessionID
     */
    private function idVerify(String $token, Client $client): string
    {
        $data = [
            'op' => 2,
            'd' => [
                'token' => "Bot " . $this->appId . "." . $this->token,
                'intents' => 2081166851,
                'properties' => []
            ]
        ];
        $client->text(json_encode($data));
        
        $receive = $client->receive()->getContent();
        
        $session_id = json_decode($receive, true)['d']['session_id'];
        
        echo("\033[32m[身份鉴权完毕]\033[0m\n session_id:" . $session_id . "\n\n");
        
        return $session_id;
    }
    /**
     * 建立WS连接
     */
    public function connect()
    {
        //获取WS连接路径
        $gateway = $this->getGateway($this->token);
        //创建连接
        $this->s = '';
        
        $client = new Client($gateway);
        // //获取心跳间隔
        $this->seconds = intval($this->getHeartBeat($client));
        echo("\033[32m[连接成功]\033[0m\n心跳间隔:" . $this->seconds . "\n\n");
        
        //身份鉴权
        $this->session_id = $this->idVerify($this->token, $client);
        
        // 获取发送消息鉴权token
        $this->getSendMsgToken();
        
        //首次心跳
        $this->time0 = time();
        $client->text(json_encode(['op'=>1, 'd'=>null]));
        // //消息监听
        $client->setTimeout($this->seconds)
            // Add standard middlewares
            ->addMiddleware(new PingResponder())
            // Listen to incoming Text messages
            ->onText(function (Client $client, Connection $connection, Message $message) {
                
                //将消息转换为数组
                $receiveArr =json_decode($message->getContent(), true);
                //如果op存在
                if (isset($receiveArr['op'])){
                    //排除心跳pong
                    //if($receiveArr['op']!=11){}
                    //如果是服务端推送,将消息派发到队列处理
                    if($receiveArr['op']==0){
                            
                        // 写入最新消息识别码s
                        $this->s = $receiveArr['s'];
                        echo("\033[34m[收到消息]\033[0m\n" . $receiveArr['d']['content'] . "\n\n");
                        
                        // 传递消息给消息处理类
                        GuildMessage::msgDispatch($this->qqGuildUrl, $this->appId, $this->access_token, $receiveArr);
                        
                    }
                    //如果服务端通知重连
                    if($receiveArr['op'] == 7){
                        
                        $client->text(json_encode(['op'=>6, 'd'=>['token'=>"Bot ".$this->appId.".".$this->token, 'session_id'=>$this->session_id, 's'=>$this->s]]));
                        
                    }
                }
            })
            ->onTick(function (Client $client){
                
                //检测是否到心跳时间
                $time1 = time();
                if($time1 - $this->time0 > $this->seconds - 20){
                    $client->text(json_encode(['op'=>1, 'd'=>$this->s]));
                    echo("\033[32m[心跳成功]\033[0m\n消息识别码(s):" . $this->s . "\n\n");
                    $this->time0 = $time1;
                };
                
                // 更新openapi调用鉴token
                if($this->expires_in - $time1 < 60) {
                    $this->getSendMsgToken();
                }
            })
            ->onError(function (Client $client){
                //重新连接
                $client->text(json_encode(['op'=>6, 'd'=>['token'=>"Bot ".$this->appId.".".$this->token, 'session_id'=>$this->session_id, 's'=>$this->s]]));
            })
            ->start();
    }
    /**
     * @param $client
     * @return float
     * 获得心跳时间
     */
    public function getHeartBeat($client)
    {
        
        $receive = $client->receive()->getContent();
        $initReceive = json_decode($receive, true);
        return floor($initReceive['d']['heartbeat_interval']/1000);
        
    }
  
  
}
- 新建
qBot.php文件 
<?php
require './vendor/autoload.php';
// websocket服务管理类
require "GuildSocket.php";
// 消息处理类
require "GuildMessage.php";
// phrity/websocket库,要求2.0以上版本
use WebSocket\Client;
$qqGuildUrl='https://sandbox.api.sgroup.qq.com';  // 沙盒环境接口
// $qqGuildUrl='https://api.sgroup.qq.com';  // 正式环境接口
$appId = "";  // QQ机器人ID
$token = '';  // 机器人toekn
$appSecret = "";  // 机器人密钥
$guzzleOptions = ['verify' => false];
$guild = new GuildSocket($qqGuildUrl, $appId, $token, $appSecret, $guzzleOptions);
$guild->connect();
- 创建文件
GuildMessage.php,这个文件是消息处理文件 
<?php
/**
 * 消息处理类
 */
class GuildMessage
{
    
    /**
     * 接收消息
     * 
     */
    public static function msgDispatch(String $qqGuildUrl, String $appId, String $access_token, Array $receiveArr) {
        
        // 事件类别
        $eventType = $receiveArr['t'];
        // 消息内容
        $receiveMsgArr = $receiveArr['d'];
        // 构建发送子频道消息接口
        $postUrl = $qqGuildUrl . "/channels/" . $receiveMsgArr['channel_id'] . "/messages";
        // 构建回复消息
        $sendMsgArr = [
            "msg_id"=> $receiveArr['id'],
            ];
        
        $content = '';
        
        // @机器人的消息处理
        if($eventType == "AT_MESSAGE_CREATE") {
            
            $content =  self::msgAtBot($receiveMsgArr);
            
        }
        
        if(!empty($content)) {
            
            $sendMsgArr['content'] = $content;
            $headers = [
                  'Authorization: QQBot ' . $access_token,
                  'X-Union-Appid: ' . $appId,
                ];
            // 发送消息
            self::httpRequest($postUrl, "POST", json_encode($sendMsgArr), $headers);
            
            echo("\033[34m[发送消息]\033[0m\n".$content."]\n\n");
            
        }
        
    }
    
    
    /**
     * @机器人消息处理事件
     * return 返回消息内容(文本消息)
     * 
     */
    private static function msgAtBot(Array $receiveMsgArr) {
        
        // 消息内容
        $msgContent = preg_match('/<@!.*?>\s*(.*)/', $receiveMsgArr['content'], $matches);
        $msgContent = $matches[1];
        
        $content = self::httpRequest("https://api.lolimi.cn/API/AI/wx.php?msg=" . $msgContent, "GET")['data']['output'];
        
        return $content;
        
    }
    
    
    /**
     * 构建http请求
     * 
     */
    private static function httpRequest($url, $method = "POST", $param = "", $headers = array()) {
        
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge($headers, ['Content-Type: application/json']));
        curl_setopt($ch, CURLOPT_POSTFIELDS, $param);  
        $response = curl_exec($ch);
        curl_close($ch);
        
        $response = json_decode($response, true);
        return $response;
        
  }
    
}
- 配置
qBot.php文件内信息即可 - ssh进入机器人文件夹下,执行命令
php qBot.php,即可运行 
使用说明:
- 消息处理只给了一个简单例子,自行添加修改即可
 - webscoket库一定要大于等于2.0版本
 - PHP版本要大于等于8.0
 - PHP安装
fileinfo拓展 
持续运行机器人:
ssh执行命令screen -S qqbot php qBot.php
啊,KHmusicdownloader没了……还会有吗?暂且放个花纪念一下
https://www.sohu.com/a/859571058_122264762![$[经典表情]::(难过)](data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==)
这是能到处说的吗