QQ机器人部署及开发简易教学

1,010 阅读6分钟

📆2022/3/28

0x01 | 前言

💡做QQ机器人的原因或者说动机

源于初进BUUCTF交流群以及CTFSHOW交流群时看到的QQ机器人(BUU娘和大牛),感觉挺有意思的,然后不知道是在哪个群里有个师傅聊到了这个,提了一句go-cqhttp,我就去搜了一下,就开始研究了🤣

其实有很多QQ机器人框架,比如使用python的nonebot2,使用java的mirai,这些框架以及拓展插件的功能非常强大。但是这些框架都是写好了底层的,非常不方便用户自己重写一些功能,总的来说有一些掣肘用户自己定义自己的功能。不如自己从零开始写,不需要看别人写的繁杂的文档,同时还能练习自己的后端水平。当意识到自己的代码和别人的代码有差距时,再去观摩别人的优秀代码才能事半功倍(直接扒拉wordpress等博客系统和自己从服务器底层写博客系统是一个道理)

💬收获和感言

感受到了运维服务器的繁琐,在开发过程中因为环境配置问题多次回滚

写代码不仅仅追求代码的效率高,而且要可读性好并且容易维护,否则就是一次性的写下去,不利于后续再回来进一步开发和优化。从原生PHP开始的项目,越写到后面越能意识到框架存在的意义和作用

更加重视备份了,不管是代码的各个版本的备份还是数据库数据的导出备份。在你开发的时候你可能会认为这些没什么意义,不需要保存,心里会想的是我开发高歌猛进,现在写的都是demo,不需要记录。但是当你进行后期升级维护的时候,你会发现你进行项目重构的基础就是你以前的开发版本,仅仅盯着一个版本(最新版本)开发是不明智的行为。(git玩的好还是很有用的)所以学习的时候勤记笔记,开发项目的时候也要勤记录项目细节,重记录归档而轻开发部署更加利于学习

0x02 | 前置要求

  • 基本Linux服务器知识 + VPS一台

  • 使用一门能够在服务器上运行的脚本语言(PHP、Python、Golang等)进行后端处理操作

  • QQ号一个(最好不是全新号,为了绕过tx检测)

0x03 | go-cqhttp环境配置

简单介绍go-cqhttp

简单概括就是提供了从QQ消息到服务器后端的一层接口,打通了前后端的交互

更多内容参考go-cqhttp帮助文档:docs.go-cqhttp.org/

下载go-cqhttp

下载go-cqhttp官方的发行版(建议最新稳定版)

github.com/Mrs4s/go-cq…

选择对应服务器机型的版本,比如我的服务器是一般CentOS7,就选go-cqhttp_linux_amd64.tar.gz,可以git clone直接下载到服务器或者下载到本机上再上传到服务器

# 现在我在我用户的home目录下创建了一个Komari目录,其下仅有go-cqhttp_linux_amd64.tar.gz文件

# 解压
tar -xvzf go-cqhttp_linux_amd64.tar.gz
# 以快速方式启动go-cqhttp(正常启动会等待5s)
# 注:如果bash/zsh报错无权限,尝试输入chmod +x ./go-cqhttp,再启动
./go-cqhttp faststart
# 输出如下
未找到配置文件,正在为您生成配置文件中!
请选择你需要的通信方式:
> 0: HTTP通信
> 1: 云函数服务
> 2: 正向 Websocket 通信
> 3: 反向 Websocket 通信
> 4: pprof 性能分析服务器
请输入你需要的编号(0-9),可输入多个,同一编号也可输入多个(如: 233)
您的选择是:0 # 我们选择最基本的HTTP通信
默认配置文件已生成,请修改 config.yml 后重新启动!
^C # ctrl+c退出
# 编辑config.yml
vim config.yml

编辑go-cqhttp配置文件

这里有好几点需要注意

  • 首先是QQ账号相关
uin: 机器人的QQ号 # QQ账号
  password: 'QQ号的密码' # 密码为空时使用扫码登录
  • 然后是服务器端口配置
 # 服务端监听地址
      host: 127.0.0.1
      # 服务端监听端口
      port: 5700

监听地址没什么好说的,127.0.0.1不用改。这个服务器监听端口是用来给机器人主动行动时用到的,具体使用方式后续慢慢介绍,可以根据需要更改,但是需要记住是哪个端口

  • 最后是POST地址
# 反向HTTP POST地址列表
      post:
      - url: '脚本入口url地址' # 地址
      #  secret: ''           # 密钥
      #- url: http://127.0.0.1:5701/ # 地址
      #  secret: ''          # 密钥

将post栏中的第一行的注释去掉,填入你脚本的入口url地址 比如http://127.0.0.1/QQ_robots/Komari/index.php

注意:这里的路径不是机器的绝对路径,而是其web服务器下的路径

或者是监听端口地址 比如http://127.0.0.1:9000

至此,有两个工作目录,一个是启动go-cqhttp的/home/Komari目录,一个是编写的脚本目录

配置编写完毕后esc+:x+enter保存,重新启动

./go-cqhttp faststart

成功启动的话会弹出QQ登入扫码授权,手机登入QQ机器人的号,扫码登入即可

这个时候会进入go-cqhttp的log后台,如果此时关闭ssh连接,这个后台进程会关闭,所以我们需要服务器维持这个后台进程

使用screen维持go-cqhttp后台进程

推荐使用screen进行进程维持,查看screen版本

screen --version
# Screen version 4.01.00devel (GNU) 2-May-06

如果有回显版本,则已经安装screen,否则需要先安装screen,关于如何安装screen不再赘述

# 创建一个名为Komari的screen后台进程
screen -S Komari
# 使用Komari后台进程来跑go-cqhttp
./go-cqhttp faststart

这个时候关闭ssh连接,我们的go-cqhttp的后台进程就不会随之关闭了

screen的一些其他命令:

# 查看所有screen后台进程
screen -ls
# 回到某个screen后台进程
screen -x [进程名]

# 在screen后台进程中退出并关闭该进程,直接输入exit即可

0x04 | Demo实现

POST数据格式样例

// 在734835660群中
// FORIMOC(2097517935): test
{
    "anonymous":null,
    "font":0,
    "group_id":734835660,
    "message":"test",
    "message_id":27091168,
    "message_seq":15996,
    "message_type":"group",
    "post_type":"message",
    "raw_message":"test",
    "self_id":1686412975,
    "sender":{
        "age":0,
        "area":"",
        "card":"",
        "level":"",
        "nickname":"FORIMOC",
        "role":"admin",
        "sex":"unknown",
        "title":"",
        "user_id":2097517935
    },
    "sub_type":"normal",
    "time":1648517955,
    "user_id":2097517935
}

PHP

error_reporting(0);
header("content-type:application/json");
$content=file_get_contents('php://input');
$data=json_decode($content,true);

if(data['message']=="hello komari"){
    exit('{"reply": "hello world"}');
}

Python(flask)

import json

from flask import request, Flask

app = Flask(__name__)


@app.route('/', methods=["POST"])
def index():
    data = request.json
    if data.get('message') == "hello komari":
        result = {
            "reply": "hello world"
        }
        return json.dumps(result)


if __name__ == '__main__':
    app.run(port=9000)
  • 路由:使用/路由即可,因为go-cqhttp只有一个路由入口

  • 服务运行:使用flask框架的话配置里应该填入的是监听端口url地址,同时另开一个screen,维持flask服务

Golang(gin+gjson)

package main

import (
	"github.com/gin-gonic/gin"
	"github.com/tidwall/gjson"
	"io/ioutil"
	"net/http"
)

func main() {
	r := gin.Default()

	r.POST("/", func(context *gin.Context) {
		dataReader := context.Request.Body
		rawData, _ := ioutil.ReadAll(dataReader)

		postType := gjson.Get(string(rawData), "post_type").String()
		if postType == "message" {
			message := gjson.Get(string(rawData), "message").String()
			if message == "hello komari" {
				context.JSON(http.StatusOK, gin.H{
					"reply": "hello world",
				})
			}
		}
	})
    
	_ = r.Run(":9000")
}
  • 路由:使用/路由即可,因为go-cqhttp只有一个路由入口

  • 错误处理:Golang的准则是处理每一个可能的错误信息,这里因为demo为了简洁,没有处理错误信息,后期是应该完善的

  • 服务运行:使用gin框架的话配置里应该填入的是监听端口url地址,同时另开一个screen,维持gin服务

0x05 | 关于API结点

go-cqhttp提供了很多内置的API结点,这些功能可以让机器人主动发送消息,从而搭配crontab等实现一些诸如定时播报之类的功能

使用类似curl的方法访问本机+配置里的服务端监听端口+特定路由(和访问web页面差不多)来使用这些API结点

比如获取机器人加入的所有群聊的基本信息,使用/get_group_list路由

curl http://127.0.0.1:5700/get_group_list
{
    "data":[
        {
            "group_create_time":0,
            "group_id":734835660,
            "group_level":0,
            "group_memo":"",
            "group_name":"懒狗社历史罪人公审部",
            "max_member_count":200,
            "member_count":7
        },
        {
            "group_create_time":0,
            "group_id":781815666,
            "group_level":0,
            "group_memo":"",
            "group_name":"懒狗社姬气壬情报部",
            "max_member_count":200,
            "member_count":8
        },
    ],
    "retcode":0,
    "status":"ok"
}

再比如使用/send_group_msg发送群消息功能来代替之前使用reply的快速回复

# index.php
error_reporting(0);
header("content-type:application/json");
$content=file_get_contents('php://input');
$data=json_decode($content,true);

if(data['message']=="hello komari"){
    $msg=urlencode("hello world");
    $url="http://127.0.0.1:5700/send_group_msg?group_id=734835660&message=".$msg;
    fopen($url,'r');
}

注意:访问的时候某些字段需要url编码,例如这里的message字段

更多API结点详见go-cqhttp帮助文档:docs.go-cqhttp.org/

欢迎各位师傅指出不足