0. 背景知识

由于QQ官方没有提供QQ的API,故而想要实现一个QQ Bot,我们需要借助第三方工具,最常用的即为酷Q(coolq)。酷Q上允许开发者自己用C/C++编写插件,然而实际体验下来会发现这样不仅开发效率低,也会遇到很多坑(如文字编码等)。

有人为酷Q开发了一个http插件cqhttp,这个插件允许我们以类似于REST API开发的方式,使用http/websocket实现QQ Bot。对WebSocket了解不多的话可以看这里的介绍,但本文并不会涉及到WebSocket相关的部分。

说回到coolq,它本身是一个运行在Windows上的应用,模拟安卓QQ协议。由于官方没有专门开发Linux版,所以在Linux上运行需要使用Wine来运行Windows程序。所幸官方提供了coolq docker,可以免除手动安装和版本更新的繁琐。但其实我们用的时候不会直接使用官方原版镜像,而是使用整合了cqhttp插件的cqhttp docker,可以进一步省掉安装插件的过程。

本篇教程将带领你完成如下架构的QQ Bot:

QQ Bot Architecture

1. 准备工作

本文以Ubuntu 18.04为例,演示在Linux上使用Docker搭建coolq+cqhttp,并使用python+flask+nginx实现消息响应,使用python实现rss/微博/b站动态等订阅和实时推送。

你需要:

  1. 一个Linux VPS,推荐使用Ubuntu 18.04镜像;
  2. (可选)一个域名,推荐使用国外域名提供商;
  3. (可选)如果你没有域名,请打开VPS防火墙的5000/5700/9000三个端口。

没有域名也不影响本篇教程,但是域名可以带来更好的安全性和自己访问的便利。

2. 安装并搭建coolq+cqhttp Docker

(1) 安装docker

Docker官方提供了详细的安装教程,非Ubuntu也可以在导航栏找到对应的系统。这里简单概括一下Ubuntu(不限版本)的安装步骤:

sudo apt-get update

sudo apt-get install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
     
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

sudo add-apt-repository \
    "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) \
    stable"
   
sudo apt-get update

sudo apt-get install -y docker-ce docker-ce-cli containerd.io

sudo docker run hello-world # 可选,验证是否安装成功

Line 1-15是为apt添加一个新的应用程序源

Line 19是正式安装

如果你是在root用户下,在运行命令时可以不加sudo,但如果你是普通用户,运行docker时不加sudo 会出现Permission denied错误,这是因为Docker运行中使用了unix socket,后者必须使用root权限才能获取。在普通用户下无sudo运行docker可以看这篇官方教程,本文就不再展开,涉及到docker的指令统一在前方加入sudo

(2) 安装cqhttp镜像

Docker安装完成后,可以开始安装整合了coolq的cqhttp镜像了。

sudo docker pull richardchien/cqhttp:latest
mkdir cqdata
sudo docker run -d --rm --name cqbot \
    -v $(pwd)/cqdata:/home/user/coolq \
    -p 9000:9000 \
    -p 5700:5700 \
    -e COOLQ_ACCOUNT=YOURQQ \ #改成自己的QQ
    -e VNC_PASSWD=PASSWORD \ # 将此处PASSWORD修改,以后网页管理qqbot使用
    -e COOLQ_URL=http://dlsec.cqp.me/cqp-tuling \ # 如果你使用的是(未付费的)Air版,去掉本行
    richardchien/cqhttp:latest

注意这里使用的命令与官方教程稍有差别,使用的是docker run -d而非docker run -t,区别在于前者可以让docker后台运行,这也是我们期望的。

此时可以使用sudo docker ps检查一下是否运行成功,如果有名为cqbot的container运行,则说明已经安装成功。运行失败时,如果提示端口被占用,那么就把被占用的端口修改掉,例如将上述9000和5700改为9001和5701,此场景同样适用于你想启动多个cqhttp实例时:

sudo docker run -d --rm --name cqbot \ #启动多个实例时记得修改此处的cqbot,不可以重名
    -v $(pwd)/cqdata:/home/user/coolq \ #启动多个实例时记得提前创建好、并修改此处的cqdata文件夹
    -p 9001:9000 \
    -p 5701:5700 \
    ... #后续与前文相同

接下来可以在浏览器里打开VNC管理页面,链接是http://VPS_IP:9000(上述端口如果修改过,这里同样要修改),进入后输入VNC_PASSWD=后面的密码即可进入。如果是CoolQ Pro版,此处需要输入授权码。然后登录上你的QQ(若提示需要网页验证则取消,需要短信验证则下一步)。

如果打开时提示拒绝访问,而运行sudo docker ps时看到的是正确结果,说明你的VPS防火墙未允许9000(或你修改后的)端口,请去VPS控制台(如阿里云、腾讯云等的控制台)修改防火墙配置。如果你有独立域名,可以直接跳到第N步,使用NGINX反向代理。

3. 使用python+flask实现QQ bot

Flask是一个基于python的微型网络应用框架,很适合开发一些轻量Web应用,同时结合python的高效开发能力,用来开发负载轻、自由度高的QQ bot再合适不过。

鉴于python2即将停止支持,本文以python3为例。

# 安装python3
sudo apt update
sudo apt install python3-pip python3-dev build-essential libssl-dev libffi-dev python3-setuptools python3-venv 

# 创建qbot工作目录和虚拟环境
mkdir qbot && cd qbot
python3 -m venv venv
source ./venv/bin/activate

# 在虚拟环境中安装flask与uwsgi
pip install -U wheel flask uwsgi

你可以尝试一下官方示例,篇幅所限,这里不过多讲述flask的内容。

接下来,创建一个qqbot.py作为bot的主程序,可以使用nano qqbot.py或者vim qqbot.py(如果提示nano未找到,则先执行sudo apt install nano),写入以下内容:

#-*- coding:utf-8 -*-
from flask import Flask, request, jsonify
import json

app = Flask(__name__)

@app.route('/')
def root():
    return 'Success.'

@app.route('/qqbot', methods=['POST'])
def qqbot_main():
    req = request.get_json(force=True)
    ret = {'block': True, 'at_sender': True}
    if req['post_type'] == 'message' and req['message'] == '/hello':
        ret['reply'] = 'QQ bot已成功配置'
        return jsonify(ret)
    return ''

以后便可以在qbot_main()函数中添加代码,来对不同的消息作出不同的回应。

4. 使用nginx转发flask请求

我们已经完成了一个最基础的QQ Bot,代码也很简单:当接收到/hello信息时,返回一个提示语。那么我们怎样将前面的cqhttp和这个flask连接起来呢?答案是nginx。

Flask并不提供可供生产环境使用的Web Server,需要nginx使用uwsgi链接。下面部分参考来自Digital Ocean的这篇教程,搭建ubuntu下flask+nginx环境。

如果你按照本篇教程前面的步骤执行,那么你已经完成了DO这篇教程的前半部分,我们可以从后续部分开始。

(1) 配置uwsgi

使用nano wsgi.py或者vim wsgi.py创建一个文件,包含以下内容:

from qqbot import app

if __name__ == "__main__":
    app.run()

在下一步之前,执行一下pwd确定一下当前路径,例如本处为/root/qqbot,记住该路径。

使用nano qqbot.ini或者vim qqbot.ini创建qqbot.ini,包含以下内容:

[uwsgi]
module = wsgi:app

master = true
processes = 4

socket = qqbot.sock
chmod-socket = 660
vacuum = true

die-on-term = true

logto = /root/qqbot/%n.log
py-autoreload = 1

简单解释一下该配置:

  • 启动4个uwsgi进程;
  • uwsgi映射到qqbot.sock下(如果修改此处,后面也要对应修改);
  • 日志保存在前面pwd所显示的路径下(注意要修改成你自己的路径),文件名为(后面即将设置的)服务名;
  • 当qqbot.py更新时自动重新加载。

接下来,我们需要在系统中创建一个qqbot服务,运行:

sudo nano /etc/systemd/system/qqbot.service
# 或者
sudo vim /etc/systemd/system/qqbot.service

输入以下内容:

[Unit]
Description=uWSGI instance to serve qqbot
After=network.target

[Service]
User=root
Group=root
WorkingDirectory=/root/qqbot
Environment="PATH=/root/qqbot/venv/bin"
ExecStart=/root/qqbot/venv/bin/uwsgi --ini qqbot.ini

[Install]
WantedBy=multi-user.target

此处,[Service]下各项要根据自己的配置(例如前文使用的pwd)进行修改。如果你不知道User和Group处该填什么,运行:

groups $USER

可能会显示如下结果:

user : group

填写到对应位置即可。这样,你就创建好了flask+uwsgi的系统服务,接下来运行

sudo systemctl start qqbot
sudo systemctl enable qqbot
sudo systemctl status qqbot  # 检查显示 Active: active (running) 即说明配置成功

来启用该服务即可。

(2) 配置nginx(有独立域名)

接下来我们需要配置nginx,由它来接收http请求并通过uwsgi转发到flask和cqhttp上。假设你有一个域名为yourdomain.com,此时你需要在域名DNS中(二选一):

  1. (推荐)添加三条A记录(A Record)到VPS的IP上,分别为:qqbot, vnc, cqhttp;
  2. 或者添加一条A记录(A Record)到VPS的IP上:*.yourdomain.com

区别在于,后者是一个泛域名解析,会将(除了其它指定好的记录以外的)所有二级域名解析到本机IP上,这不是一个推荐的选项。

无论如何,完成上述操作后,即可以开始nginx的配置。如果你还没有安装nginx,则用sudo apt install nginx进行安装。

接下来,使用sudo nano /etc/nginx/sites-available/qqbotsudo vim /etc/nginx/sites-available/qbot,写入以下内容并保存退出(如果你在前面对9000和5700端口做过修改,则这里也要对应修改):

server {
  server_name qqbot.yourdomain.com;
  listen 80;
  
  location / {
    include uwsgi_params;
    uwsgi_pass unix:/root/qqbot/qqbot.sock;
  }
}

server {
  server_name cqhttp.yourdomain.com;
  listen 80;
  
  location / {
    proxy_pass http://127.0.0.1:5700;
    proxy_redirect off;

    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
  }
}

server {
  server_name vnc.yourdomain.com;
  listen 80;
  
  location / {
    proxy_pass http://127.0.0.1:9000;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
  }
}

简单解释一下:qqbot项将所有访问qqbot.yourdomain.com的请求通过uwsgi转发到flask上处理,cqhttp项用于向QQ Bot发送消息,vnc项用于提供外部Web管理界面。

然后运行以下两个指令使其生效:

sudo nginx -t # 显示OK和Successful即为配置正确,否则请重新检查
sudo nginx -s reload # 加载新的配置文件

这样,你可以使用浏览器访问http://vnc.yourdomain.com来打开QQ Bot的GUI进行日志查看和内容管理了。

(3) 配置nginx(无独立域名)

接下来我们需要配置nginx,由它来接收http请求并通过uwsgi转发到flask。假设你已经开放了VPS上的5000/5700/9000三个TCP端口。

完成上述操作后,即可以开始nginx的配置。如果你还没有安装nginx,则用sudo apt install nginx进行安装。

接下来,使用sudo nano /etc/nginx/sites-available/qqbotsudo vim /etc/nginx/sites-available/qbot,写入以下内容并保存退出:

server {
  listen 5000;
  
  location / {
    include uwsgi_params;
    uwsgi_pass unix:/root/qqbot/qqbot.sock;
  }
}

简单解释一下:将所有访问http://your_ip:5000的请求通过uwsgi转发到flask上处理。

然后运行以下两个指令使其生效:

sudo nginx -t # 显示OK和Successful即为配置正确,否则请重新检查
sudo nginx -s reload # 加载新的配置文件

(4) 连接cqhttp与nginx

上述配置完成后,编辑cqdata/app/io.github.richardchien.coolqhttpapi/config/下的*.ini文件(*表示你登陆的QQ)。cqdata文件夹是在本文2.(2)节中创建的那个文件夹,一般保存在~/目录下。

以QQ 12345678为例,则完整路径为~/cqdata/app/io.github.richardchien.coolqhttpapi/config/12345678.ini,打开后编辑内容如下:

  1. 有域名:
[12345678]
post_url = http://qqbot.yourdomain.com/qqbot
serve_data_files = yes
  1. 无域名(将your_ip改成你VPS的ip):
[758305707]
post_url = http://your_ip:5000
serve_data_files = yes

然后打开VNC界面:http://vnc.yourdomain.comhttp://your_ip:9000,重启cqhttp插件。

此时你可以用其它QQ向QQ Bot发送私聊信息:/hello,如果能正确返回提示语,则表示配置成功。

(5) (可选)配置HTTPS(必须有独立域名)

前述配置都是基于HTTP的,存在被截获消息的危险,建议升级到HTTPS来保证数据安全性。以下内容参考Digital Ocean文档,使用其它系统或者Apache也可以在该文档库中找到对应的教程。

此处我们对前面nginx中配置的三个域名qqbot, cqhttp, vnc、或者泛域名*.yourdomain.com进行加密。

sudo add-apt-repository ppa:certbot/certbot
sudo apt install python-certbot-nginx

# 对域名独立加密
sudo certbot --nginx -d qqbot.yourdomain.com -d cqhttp.yourdomain.com -d vnc.yourdomain.com 
# 对泛域名加密
sudo certbot --nginx -d *.yourdomain.com

按照提示来,需要你输入邮箱、同意条款等,输入选项按回车即可。最后会有如下提示:

Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
-------------------------------------------------------------------------------
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel):

这里的意思是是否重定向所有http流量到https上,建议选择重定向,即[2],输入2按回车即可。

加入HTTPS后同样要回去修改4.(4)中cqhttp插件的post_url,改为https。

5. 开发指南

(1) cqhttp开发概括

cqhttp插件支持四种通信方式:

  1. 插件作为HTTP客户端(事件上报);
  2. 插件作为HTTP服务端
  3. 插件作为WebSocket服务端;
  4. 插件作为WebSocket客户端。

其中3/4与1/2类似,区别在于,WebSocket是长链接TCP双向通信,客户端和服务端都可以随时向对方发送消息;而HTTP是短链接TCP响应式,只有客户端可以向服务端发送消息,服务端只能通过响应请求的方式与客户端通信。

(2) 插件作为HTTP客户端(事件上报)

见此处文档:https://cqhttp.cc/docs/4.13/#/Post](https://cqhttp.cc/docs/4.13/#/Post)

(3) 插件作为HTTP服务端

见此处文档:https://cqhttp.cc/docs/4.13/#/API

(4) 方案选择

本文中配置完成后,可以通过1/2两种方式实现QQ Bot,两种方法可以共存。

使用方式1:当QQ收到消息时,由cqhttp向4.(4)中配置的链接中发起一个POST请求,并使用POST请求的响应内容作为回复。修改python+flask应用,可以实现对不同的消息实时响应,还可以实现包括搜图、下载、遥控等各项功能。

使用方式2:你也可以在本机或其他位置等部署自己的独立应用,向http://cqhttp.yourdomain.com(有域名配置)或http://your_ip:5700(无域名配置)发起POST/GET请求,来实现主动发送消息。

参考文献:

[1] 酷Q(coolq)

[2] CoolQ HTTP API

[3] Docker 官方文档

[4] How To Serve Flask Applications with uWSGI and Nginx on Ubuntu 18.04

[5] How To Secure Nginx with Let's Encrypt on Ubuntu 18.04

Last modification:March 4th, 2020 at 08:24 pm
本文作者:Graue Neko

本文链接:Linux VPS上搭建QQ Bot的保姆级攻略 - https://graueneko.com/archives/49/

版权声明:如无特别声明,本文即为原创文章,仅代表个人观点,版权归 灰格猫的编程日记 所有,未经允许不得转载。