需求

由于组织迎新群需求,往年使用的MyQQ机器人灵活性稍差,且频繁更新,感觉不是很稳定。因此今年尝试基于go-cqhttp框架和AliceBot框架实现一款高自由度的QQ机器人。并配套后台管理系统,可编辑回复语,编辑敏感词汇表,更改插件开关状态等。

预计实现主要功能:

  1. 入群欢迎
  2. 入群自动审批
  3. 智能聊天
  4. 自动回答问题
  5. 垃圾广告信息识别、记录、禁言

垃圾广告信息识别本准备使用NLP技术实现,但是缺暂时很难拿到足够的训练数据,所以先使用人工标记方案进行标注,待数据充足后再训练模型。

下面简单分享一下此次开发经验

两个框架

go-cqhttp

go-cqhttp文档链接

官方介绍:使用 mirai 以及 MiraiGo 开发的 cqhttp golang 原生实现, 并在 cqhttp 原版 的基础上做了部分修改和拓展。 文档目前还在撰写中。

支持协议类型:

  1. HTTP API
  2. 反向HTTP POST
  3. 正向WebSocket
  4. 反向WebSocket

我的理解是这个工具用来登录QQ,并将QQ收到的事件通过协议反馈给我们要写的项目,并且我们写的代码也可以通过接口借助这个工具实现对QQ的一些操作。

首先我们要按照文档要求进行安装,这里比较简单不再多说。

值得注意的是config.yml的修改,可以参照官方文档的注释来修改,注意此处写的端口和协议需要与我们之后定义的保持一致。

修改完成配置后,启动运行脚本(这里如果需要在服务器上运行,可以开启进程守护),第一次登录需要扫码,之后登录信息会存储在device.json中,就不需要扫码咯

AliceBot

(AliceBot文档)[https://docs.alicebot.dev/guide/]

官方介绍 AliceBot 是一个简单的 Python 异步多后端机器人框架,支持多种协议适配,可以轻松地编写易用学习和使用的插件来拓展其功能。

!注意:AliceBot 仅支持 Python 3.8+ 版本。

还是按照官方文档进行配置初始项目,也是注意config文件的编写,尤其是端口需要与上面的对应。

我的理解这个主要是我们机器人逻辑实现的一个框架,借助cqhttp(其实也支持其他框架的)与qq的交互。

原理

借助AliceBot文档的图来解释:

AliceBot 在使用时,首先需要你实例化一个 Bot 对象,Bot 对象负责加载配置文件,维护一个插件列表和一个适配器列表并提供了一个供适配器使用的通用的事件分发方法。协议适配器(Adapter)负责和协议后端进行通讯,当协议后端发送一个新的事件(Event)给适配器后,适配器会按插件优先级分发事件给各个插件进行处理。
插件(Plugin)是由你编写的用于处理事件的类,它必须具有两个方法,rule() 和 handle() ,rule() 方法要求返回一个 bool 值,表示当前事件是否要交由此插件处理,当 rule() 方法返回 True 时,handle() 方法会被调用以进行事件处理。

插件类的编写

简单来说就是需要接收事件,根据API作出响应。每一个功能是一个插件也就是一个类,这个类需要继承alicebot.plugin.Plugin,插件由三部分组成:

事件的信息和API参考go-cqhttp(或其他框架的文档)

API不一定要手写请求,AliceBot已经封装好了,参考CQHTTP 协议适配器

优先级配置

priority: int = 5  # 数字越高优先级越低
block: bool = False # 若为True当前插件执行完毕后会停止当前事件传播,更低优先级的插件将不会被执行

执行条件

async def rule(self) -> bool:
    if self.event.type == 'message':
        msg = str(self.event.message).lower()
        keyWords = ['hello', 'hi', '大家好', '你好']
        for key in keyWords:
            if msg.endswith(key) or msg.startswith(key):
                return True

该某个插件执行时,需要先检查rule,若返回了True才会执行具体内容

执行内容

满足rule后会执行handle代码

async def handle(self) -> None:

    replay = [
        {
            "type": "at",
            "data": {
                "qq": str(self.event.user_id)
            }
        },
        {
            "type": "text",
            "data": {
                "text": self.getHelloWord()
            }
        }
    ]
    await self.event.reply(replay)

一些踩过的坑

  1. QQ机器人通过API发出的操作,也会再次被接收到,如果执行条件写的不好,可能会被误处理,导致错误。
  2. 事件的有些字段可能有些类型不具备,所以为了防止报错,最好先判断一下key值存不存在。

后台管理编写思路

主要是把一下配置信息,写到文件里或者存放到Mysql or Redis 等数据库中。