Nonebot对接QQ官方机器人

lurt发布

原来基于go-cqhttp的机器人在年初的时候登录不上失效了。因为之前用的就是Nonebot就想着改一改继续用。这里记录一下QQ官方API的对接方法。

QQ官方API支持文字子频道、频道私信、群聊、单聊。截止24年8月,个人开发者还不支持群聊。但是如果只是个人/小范围使用,可以与手动添加的成员私聊,或在一个少于 20 人的沙箱群中使用(另外频道应该很少人用吧)。

优点:不用一直担心风控,无需麻烦的登录流程。

缺点:个人开发者权限有限,主动消息次数仅 4 次/月。

个人用途:主要是对接ChatGPT方便访问,还有校园卡余额不足时主动提醒。

QQ开放平台设置

进入QQ开放平台https://q.qq.com/qqbot/#/home,注册一个公域机器人账号。这一步坑比较少,简略带过。

“沙箱配置”:根据需要,配置沙箱群/沙箱频道/频道私信成员/列表私信成员。

“开发设置”:获取AppID、Token、AppSecret。注意管理平台使用管理员QQ账号登录,否则权限不足无法生成机器人密钥。

Nonebot配置

这里使用nb-cli脚手架搭建。确保所有包都正确安装并更新。

pip uninstall nonebot
pip uninstall nonebot2
pip uninstall nb-cli
pip install nb-cli

进入项目文件夹,使用 nb create 创建项目。项目配置建议如下:

  • 模板:simple
  • 驱动器:HTTPX和websockets
  • 适配器:QQ
  • 插件:在“src”文件夹中
  • 虚拟环境看自身需求。可以添加echo插件用来测试。

如果配置正确,项目文件夹下的pyproject.toml文件的adapters如下图。

踩坑:如果适配器是qqguild,这是老版本已经停止维护。请自行到适配器商店(适配器商店 | NoneBot)安装nonebot-adapters-qq。

接下来编辑.env文件。将下面这段代码粘贴到文件后,将id、token、secret更改为开发设置中自己机器人的信息。

QQ_IS_SANDBOX=flase
QQ_BOTS='
[
  {
    "id": "xxx",
    "token": "xxx",
    "secret": "xxx",
    "intent": {
      "c2c_group_at_messages": true
    }
  }
]
'

在项目文件夹下使用 nb run --reload 启动机器人(--reload 代表项目内文件有更改机器人就会热重载)。如果一切顺利,应该会见到如下画面。

如果不成功可以试试把 QQ_IS_SANDBOX 改成 true。

如果安装了echo插件,在单聊或频道私聊内,向机器人发送 /echo hello ,机器人将回复hello。 如果在群聊或子频道内,则需要发送 @机器人 /echo hello

插件开发

玩Nonebot的应该都是多多少少自己写插件的吧。

API文档:QQ 机器人 | QQ机器人文档

发送消息:发送消息 | QQ机器人文档

被动消息

  • 用户发给机器人触发的回复消息。
  • 需要携带上一条消息的msg_id(适配器自动实现)。
  • 回复消息的有效期为 5 分钟。
  • 每个消息最多回复 5 次。

Nonebot会根据被动消息触发的event类别选择不同的发送方法,所以不需要私信、频道等都分别实现。

这里放一个keyword触发的测试代码,发送的消息包含’test’就会返回当前的session_id。

from nonebot.plugin import on_keyword
from nonebot.adapters import Bot, Event

test = on_keyword(['test'], priority=5)
@test.handle()
async def test_handle(event: Event):
    await test.send(event.get_session_id())

主动消息

网上很难找到有关于Nonebot主动发送消息的资料,主要还是得看nonebot-adapter-qq的源码。

adapter-qq/nonebot/adapters/qq at master · nonebot/adapter-qq · GitHub

  • 机器人主动发送的消息,如定时消息。
  • 主动发送消息会被官方限制频率:
    • 文字子频道:20 条/天;2 个子频道/天
    • 频道私信:2 条/天/用户;200 条/天/机器人
    • 群聊:4 条/月/群
    • 单聊:4 条/月/用户
    • 主动推送消息在每个频道中,每天可以往 2 个子频道推送消息,超过后会被限制。
文字子频道频道私信群聊单聊
sessionguild+channelguild+channelgroupfriend
adapter方法send_to_channelsend_to_dmssend_to_groupsend_to_c2c
发送目标channel_idguild_idgroup_openidopenid

发送目标id与实际的QQ号、频道号、群号等不同。可以借助“被动消息”一节的测试代码,获取当前环境的id号。

这里放一个定时触发的测试代码,向指定openid的用户于每天 17:00 私聊发送主动消息‘test’。注意需要安装nonebot_plugin_apscheduler。

from nonebot import require, get_bot
from nonebot.adapters import Bot, Event

scheduler = require('nonebot_plugin_apscheduler').scheduler
@scheduler.scheduled_job('cron', hour=17, minute=0)
async def test_schedule():
    bot = get_bot()
    try:
        await bot.send_to_c2c(openid='xxx', message='test')
    except:
        print("发送失败")
分类: Code