📆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官方的发行版(建议最新稳定版)
选择对应服务器机型的版本,比如我的服务器是一般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/
欢迎各位师傅指出不足