qq-bot-roost
v1.0.3
Published
栖 Roost:零依赖的 QQ 官方机器人框架,填 AppID/AppSecret 即连,插件可写任意语言
Maintainers
Readme
栖 Roost
qq-bot-roost· 倦飞之鸟,终有一栖
填 AppID + AppSecret 就能连上 QQ 官方机器人。零依赖,插件可写任意语言,自带可视化面板。
快速开始 | 写插件 | 外部插件协议 | 配置参考 | 常见问题 | 贡献

用 Node.js 从零实现 QQ 开放平台 v2 协议,不依赖任何第三方包——装个 Node 22 就能跑,
不需要 npm install,装插件和更新框架也不需要 git(下载 GitHub 的 tar 包自己解,不调外部命令)。
官方接口比第三方协议严格得多:群聊被动回复 5 分钟、同一条最多回 5 次(单聊 60 分钟 / 4 次)、
发送接口 100 QPS、主动消息单会话 20 条/分钟且每群每天 1000 条。
这些限制框架已经统一兜住,写插件时不用算时间、不用管额度,调 msg.reply() 就行。
插件有两种形态,互不影响:
| | 内建插件 | 外部插件 |
|---|---|---|
| 识别 | 目录下有 index.js | 目录下有 plugin.json |
| 语言 | JavaScript | 任意语言 |
| 运行 | 主进程内 | 独立子进程 |
| 单条开销 | ~0.13 ms | ~0.09 ms(同机实测) |
| 启动开销 | 2 ms | 400 ms(Python) |
| 崩溃影响 | 拖垮主进程 | 只影响自己,自动重启 |
适合 / 不适合
适合
- 在 QQ 群里跑自己的玩法:文字游戏、群管工具、查询机器人
- 想用 Python / Go 写插件,但不想被单一语言的框架绑死
- 不想维护一堆第三方依赖,希望部署就是「复制文件夹 + 跑起来」
不适合
- 需要同时接入微信 / 飞书 / Telegram —— 本项目只做 QQ, 多平台可以看 AstrBot 或 Koishi
- 需要大模型对话、知识库、Agent —— 这里只管「消息进、消息出」,AI 能力自己接
- 需要踢人 / 禁言 / 撤回他人消息 —— 禁言和撤回已封装(要求机器人是群管理员); 踢人官方还在内邀,申请到白名单后去面板打开即可用
特性
官方接口这一层
- 限制全兜底:出站调度器统一处理被动窗口(群 5 分钟 / 单聊 60 分钟)、会话内限速、主动消息每日额度、断线拒发、审核中识别——插件只管调
msg.reply() msg_seq自动维护:同一msg_id被动回复必须递增,这是官方硬要求,漏一次就发不出去- 免@粘性:@ 过一次后 N 分钟内该会话免@ 也算在跟机器人说话
- 多轮对话:
ctx.prompt()问一句等回答,文字游戏的地基 - 流式回复:
msg.replyStream()做 AI 打字机效果,自动维护分片序号;群聊自动降级成普通消息 - 模板消息:
msg.replyCard()一次带上 markdown 正文 + 按钮 + ark 模板,不会拆成三条碎片 - 素材缓存:同一个图片 url 两天内只上传一次,省富媒体频控和流量
- 中间件管道:去重、白名单、限流、命令冷却都可插拔
身份与记忆(v1.0.0)
- QQ号 / 群号 ↔ openid 绑定:换机器人(换 AppID)之后 openid 会变,而记忆是按 openid 存的。绑定之后,读记忆会把同一个号码名下的所有键一起读——存量数据一个字节不动
- 面板「身份」页:绑定、追加、改绑、停用;未识别的人也能看到
- 群内指令(0 / 1 级):
/绑定群号 <群号>、/绑定成员 @某人 <QQ号>。只绑当前群和当前群里被 @ 的人,冲突不强制改绑 - 普通成员自助绑定:
/绑定QQ <QQ号>→ 验证码发到<QQ号>@qq.com→/QQ验证码 <6位数字>完成绑定,不需要管理员;QQ 号已有绑定记录时直接拒绝。四条绑定指令在/help的「绑定」分组里,与「管理」同级(管理员看到四条,普通成员只看到自助两条) - 消息带四个字段:
uin/groupUin/userAliases/groupAliases。插件用ctx.identity只读视图查,绑定表由框架维护 - 配套升级:
dsh-roost1.0.0(别名合并读取 + 账号迁移页)、ai-chat4.4.1(透传字段)。三个一起升,少一个号码就传不到 DSH
绑定完全自愿、非必需:QQ 官方接口不提供 QQ号 / 群号,本项目不会以任何方式获取、推算或抓取;绑定表里的号码只来自用户本人主动提供,或由管理员依据本人提供的号码代为登记(应事先取得本人同意)。不绑定不影响机器人的任何功能,未绑定时行为与绑定能力上线前完全一致;绑定信息仅保存在本机
identity.json,可在面板「身份」页随时解绑或停用,不上传、不共享,也不用于「认人认群、找回旧记忆」以外的用途。请勿把提供号码设为加群、发言或使用机器人的条件,也不要替他人报号。
连接与容错
- 两种收事件方式:WebSocket 长连接(本地默认)或 Webhook 回调(公网 / Serverless / 容器)
- 凭证自动刷新:提前 120 秒续期,401 自动重试,不用管 token
- 断线补事件:心跳 + 指数退避重连,有 session 走
Resume把掉线期间的事件补回来 - 关闭码翻译成人话:
4014→ 提示你去开通群聊能力 - 实例互斥:同一个 AppID 重复启动会被拒绝并给出 PID,避免两进程互踢(4902)
事件
- 官方 47 个事件全部登记并转交插件,含频道、成员、表情、论坛、音频、审核、互动
- 消息类事件包装成
Message,其余保留官方原始结构 - 框架不认识的未知事件也照样转发,只是日志提示补登记
- 面板「事件」页显示每个事件的订阅状态、接收次数、最后收到时间
插件系统
- 插件不在本仓库:全部放在 qq-bot-roost-plugins, 面板「插件 → 市场」一键安装,有新版会自动提示、一键更新
- 下载源可换:国内直连 GitHub 经常超时,面板里能选 GitHub 官方 / Gitee 镜像
(作者维护,内容逐字节相同)/ 自建前缀,带一键测速;
auto模式直连失败会 自动切 Gitee,插件安装和框架更新都吃这个设置 - 任意语言:内建插件跑在主进程(JS),外部插件跑在独立子进程(Python / Go / 任何语言),崩溃只影响自己
- 同会话串行、跨会话并行:插件不用自己加锁
- 独立配置、独立日志、独立存档,卸载自动清理定时器和中间件
- 依赖自动隔离:第三方包装进插件目录的
.venv,不污染系统 - 面板可改数据:插件在「设置」页自动获得配置表单和存档编辑,不需要写前端代码
- 有脚手架和测试命令:
npm run create生成骨架,npm run test:plugin直接跑命令看回复, 不连 QQ 不开面板;外部插件还有单文件 SDK(Python / Node) - 存盘即生效:改完插件代码 2 秒内自动热重载,不用重启也不用点重载;新增/删除插件目录同样自动识别
- 按群开关插件:同一个插件可以只在部分群生效(不配的群跟着全局走),
面板「群组 → 插件」、面板「插件 → 分群开关」、群里
/群插件三个入口改的是同一份配置 - 插件能加自己的面板页:
ctx.registerPage()注册一段 HTML,标签栏就多一页,样式和面板互不影响 - 面板页能读自己的数据:
ctx.registerRoute()注册 REST 接口(/api/plugins/<id>/r/…), 鉴权/CSRF/同源校验全复用面板那套,插件拿不到裸 req/res - 元数据能表达兼容性:
frameworkVersion声明需要的框架版本,不满足时在加载前拦住并说清原因,而不是跑起来才报「xxx 不是函数」 - 插件能给 AI 贡献工具(0.8.6+):
ctx.registerTool()注册一个带 JSON Schema 的工具 (名字全局唯一,建议带插件前缀),别的插件用ctx.tools()拿清单、ctx.callTool()直接调用。 于是「AI 能查斗罗玩家数据」这种事,AI 插件和斗罗插件两边不用互相认识——都只认这张工具表。 工具还能标ownerOnly/admin限权限,timeoutMs限超时,插件卸载时自动注销
统计
- 消息量、活跃人数、命令排行、会话排行,按天出柱状图
- 存在每个机器人自己的 SQLite(Node 22 内置
node:sqlite,不引第三方包) - 只记「谁、在哪、什么时候、是不是命令」,不存消息正文——统计不该以牺牲隐私为代价
- 没有 SQLite 的环境自动降级成「不统计」,其它功能不受影响
权限
- 5 种作用域 × 数字等级(0 主人 / 1 管理员 / 2 群管 / 3 助手 / 10+ 插件区)
- 一个人在不同群、不同插件里可以有完全不同的权限,互不干扰
- 只能授予数字更大的等级,不能给自己或同级提权
- 权限管的是「谁能让机器人做什么」,不是「插件代码能做什么」。 内建插件跑在主进程、外部插件跑在子进程,两者都拥有和你相同的文件与网络权限。装来路不明的插件,等于让它的作者以你的身份运行代码。
群管能力
- 禁言:单个 / 批量,自动预检机器人是不是群管理员(查
bot_state,带缓存) - 踢人 / 群成员 / 群黑名单:官方内邀接口,默认关闭,面板一键开启
- 入群申请审批 + 自动审批策略:拉取申请、通过 / 拒绝 / 拉黑,白名单自动放行
可视化面板
- 分组导航:一级只留 4 个入口(概览 / 数据 / 设置 / 插件), 统计、事件、日志收进「数据」,配置、群组、权限收进「设置」, 插件自己注册的页面也挂在「插件」下——插件装多了不会把顶栏挤爆
- 体检:打开面板先跑一遍,凭证 / 连接 / intents / 目录权限 / 下载源 / 面板暴露面 一次查完,出问题直接摆在最上面并告诉你该去哪儿改
- 实时日志流(SSE),按级别和来源着色
- 一个面板管多个机器人,顶栏随时切换
- 插件卡片直接显示它自己上报的运行数据(已回复条数、在线人数…)
- 「通知」页(在「身份」与「关于」之间):配任意邮箱服务商的 SMTP、一键发测试邮件到主人邮箱,普通成员自助绑定的说明也在这里
- 日间 / 夜间主题,启动自动开浏览器,端口被占用自动顺延
3 分钟跑起来
1. 装 Node.js 22.13+
node -v # 需要 >= 22.13用到了 Node 22 内置的全局
WebSocket,所以不用装ws,也不需要npm install。
2. 拿凭证
扫码最省事(0.8.4+):面板 → 「新建机器人」 → 点「扫码一键创建」, 用手机 QQ 扫一下,AppID / AppSecret 自动填好,不用去网页复制。 扫码用不了、或已有机器人时,同一处还有「或者手动填」:
- https://q.qq.com → 创建机器人
- 「开发设置」里拿到 AppID 和 AppSecret
- 关键:「功能配置」里开通 群聊 能力,否则连接会被拒(关闭码 4014)
3. 启动
node src/main.jsWindows 下也可以双击 start.bat(它会检查 Node 版本并给出提示)。首次运行会提示输入 AppID / AppSecret,
之后自动建好 config/ logs/ data/ 并打开浏览器面板。
看到这两行就成功了:
INFO [gateway] 已就绪,机器人: 你的机器人名
INFO [webui] 可视化面板: http://127.0.0.1:15207/4. 认领主人
把机器人拉进群,看启动日志里的「认领码: XXXXXX」,然后 @它 发送 /claim XXXXXX。
为什么要认领码:
owners为空时,群里任何人发/claim都能成为主人。 认领码每次启动随机生成,只打印在控制台和面板日志里。
也可以直接在面板「群组」页增删主人。
Docker 部署
想在服务器 / NAS 上跑,用容器更省事:
git clone https://github.com/tdcq9594/qq-bot-roost.git
cd qq-bot-roost
cp config/bot.example.json config/bot.json # 首次,然后填 AppID / AppSecret
docker compose up -d面板在 http://你的服务器:15207。
四个目录必须挂出来(docker-compose.yml 里已配好),否则容器重建后全丢:
| 宿主机目录 | 容器内 | 存什么 |
|---|---|---|
| ./config | /app/config | 配置(含 AppSecret,注意文件权限) |
| ./data | /app/data | 权限、群档案、插件存档 |
| ./logs | /app/logs | 日志 |
| ./plugins | /app/plugins | 插件代码 |
常用环境变量(优先级高于配置文件):
| 变量 | 作用 |
|---|---|
| ROOST_WEBUI_HOST / ROOST_WEBUI_PORT | 面板监听地址,容器里默认 0.0.0.0:15207 |
| ROOST_WEBUI_KEY | 面板访问密钥,对外暴露时务必设置 |
| QQ_BOT_APPID / QQ_BOT_SECRET | 机器人凭证,不想写进配置文件就用这个 |
| ROOST_WEBHOOK_ENABLED / ROOST_WEBHOOK_PORT | 开 Webhook 模式 |
| ROOST_LOG_LEVEL | 日志级别 |
镜像里没有 Python / Go。要跑对应语言的外部插件,把 Dockerfile 里那行 apk add 的注释打开。
收事件:WebSocket 还是 Webhook
官方两种都支持,框架都实现了:
| | WebSocket | Webhook | |---|---|---| | 默认 | ✅ 开箱即用 | 需配置 | | 需要公网入口 | 不用,只要出站 | 要,且必须 HTTPS | | 断线重连 | 框架自动 | 平台超时重推 | | 空闲占用 | 一条长连接 | 不占 | | 容器 / Serverless | 不太合适 | 合适 |
本地跑、NAT 后面 → 用默认的 WebSocket。
有域名、跑在服务器上、想按需唤醒 → 改 connection.mode 为 webhook,
并打开全局 webhook.enabled,到开放平台填回调地址。
回调地址的 op=13 验证请求框架会自动用 AppSecret 做 Ed25519 签名回应,
不用自己算。细节见 配置参考。
可视化面板
默认 http://127.0.0.1:15207,只监听 127.0.0.1,不对外暴露。
顶栏可以切换机器人,一个面板管全部:


| 页签 | 能做什么 | |------|----------| | 概览 | 连接状态、运行时长、凭证有效期、内存、收发统计、活跃会话 Top20、重连 / 刷新凭证、调试发消息 | | 配置 | 凭证、intents 勾选、命令前缀、白名单、限流、去重窗口、消息日志、日志级别、面板端口 / 密钥 | | 插件 | 列表、启用 / 禁用 / 重载、加载错误提示 | | 事件 | 47 个官方事件的订阅状态与接收计数,一眼看出哪个事件没收到 | | 群组 | 全局主人增删、群档案(邀请人 / 群主 / 状态)、手动登记群 | | 权限 | 等级与作用域说明、按作用域增删管理员、已授权列表 | | 日志 | SSE 实时流,按级别和来源过滤,支持关键词搜索 | | 通知 | SMTP 服务器 / 端口 / 用户名 / 授权码 / 发件人 / 主人邮箱、测试邮件通知、普通成员自助绑定说明(位于顶栏「身份」与「关于」之间) |
事件页把官方 47 个事件全列出来,灰掉的是当前 intents 没订阅的:

日志按来源着色,可按级别和来源过滤,也能搜关键词:

没有凭证也想看界面:
node src/main.js --mock --serve要对外开放:改 webui.host 为 0.0.0.0,务必同时设置 webui.accessKey,
否则任何人都能改你的机器人配置。
命令速查
| 命令 | 说明 |
|------|------|
| /help | 全局菜单:内置指令 + 所有插件入口 |
| /help <插件id> | 看某个插件的下级菜单(指令 + 玩法 + 说明) |
| /status | 运行状态:连接、凭证剩余、消息量、主动消息额度、免@窗口剩余 |
| /stats | 详细统计:收发 / 命令 / 错误 / 拦截数、活跃会话、事件计数 |
| /plugins | 插件列表与运行状态 |
| /admin | 权限总览;me / list / set / del / whois / scopes / levels |
| /enable /disable /reload [id] | 插件管理(需全局管理员) |
| /群插件 | 看 / 改这个群能用哪些插件(群管理;面板「群组 → 插件」也能改) |
| /whoami | 查自己的 openid / 群 openid |
| /绑定群号 <群号> | 把当前群绑到群号(仅 0 / 1 级;冲突不强制改绑) |
| /绑定成员 @某人 <QQ号> | 把当前群里被 @ 的人绑到 QQ号(仅 0 / 1 级) |
| /绑定QQ <QQ号> | 普通成员自助绑定:验证码发到 <QQ号>@qq.com;需先在面板「通知」页配好 SMTP |
| /QQ验证码 <6位数字> | 核对上一条的验证码,完成绑定(5 分钟有效,最多试 5 次) |
| /claim <认领码> | 认领主人身份(owners 为空时可用) |
| /ping | 连通性测试 |
权限相关的完整说明见 docs/权限模型.md。
写插件
在 plugins/ 下建个文件夹,放 index.js:
export default {
id: 'hello',
name: '打招呼',
version: '1.0.0',
// 默认配置,会自动生成 config/bots/<机器人id>/plugins/hello.json 给用户改
config: { greeting: '你好' },
setup(ctx) {
ctx.command('hello <名字>', { desc: '打个招呼' }, async (msg, { args }) => {
await msg.reply(`${ctx.config.greeting},${args[0]}!`);
});
// 多轮对话:问一句等回答
ctx.command('pick', { desc: '选一个' }, async (msg) => {
const ans = await ctx.prompt(msg, '用哪个技能?\n1 蓝银缠绕\n2 昊天锤');
if (ans) await msg.reply(`你选了「${ans.content}」`);
});
},
};发 /reload hello 立即生效。完整约定见 插件开发规范。
想用 Python / Go / Rust? 放一个 plugin.json 而不是 index.js,
插件会以独立子进程启动,通过 stdin/stdout 的 NDJSON 通信:
{
"id": "hello-py",
"name": "Python 示例",
"runtime": "python",
"entry": "main.py",
"config": { "greeting": "你好" }
}有第三方依赖就放 requirements.txt,首次加载会自动装进插件目录的 .venv。
协议细节见 外部插件协议,参考实现见 plugins/hello-py/。
常用能力
| 成员 | 说明 |
|------|------|
| ctx.command(模式, 选项, 函数) | 注册命令,<必填> 自动校验,命令自动进菜单 |
| ctx.on(事件, 函数) | 监听 message 或原始事件 |
| ctx.prompt(msg, 提示, 选项?) | 问一句等回答,多轮对话核心 |
| ctx.push(会话id, 文本) | 主动推送(受每日额度限制) |
| ctx.use(名称, 中间件) | 注册中间件,可拦截消息 |
| ctx.menu(分组, 条目) | 补充非命令玩法 |
| ctx.schedule(名称, 毫秒, 函数) | 定时任务,卸载时自动清理 |
| ctx.storage.get/set/all | 插件私有存档 |
| ctx.config / ctx.setConfig() | 插件配置读写 |
| ctx.can / ctx.levelOf / ctx.scopeOf | 权限判断 |
| ctx.identity | 只读身份视图:openidOf(uin) / uinOf(openid) / aliasesOf(uin) / groupOf(openid)。绑定表由框架维护,插件不能写 |
| ctx.registerTool(工具) | 注册一个 AI 可调用的工具(跨插件,0.8.6+),返回注销函数 |
| ctx.tools() / ctx.callTool(名, 参数) | 列出 / 调用别插件注册的工具 |
| ctx.mail.send(收件人, 内容) / ctx.mail.configured | 邮件通知:收件人填完整邮箱(任意服务商)或 QQ 号;SMTP 在面板「通知」页配,见插件开发规范 |
| msg.reply / push / replyMarkdown / replyImage / … | 各种发送方式 |
目录结构
bot/
├── config/ 用户可改的配置
│ ├── bot.json 全局配置:面板、日志、插件依赖策略
│ ├── bot.example.json 全局配置模板
│ └── bots/
│ ├── main.json 机器人 A 的连接配置
│ └── main/ 机器人 A 的插件配置
│ └── plugins/<id>.json
├── data/ 运行时数据,不要手改
│ └── bots/<id>/ 每个机器人独立,openid 不通用
│ ├── groups.json 群档案
│ ├── roles.json 权限
│ ├── quota.json 主动消息额度账本
│ ├── plugins.json 插件启用状态
│ └── plugins/<id>.json 插件存档
├── logs/
│ ├── bot.log 全局日志
│ └── bots/<id>/
│ ├── bot.log 该机器人的日志
│ └── plugins/<id>.log
├── plugins/ 插件代码,所有机器人共享一份,不复制
├── src/
│ ├── main.js 入口:环境检查、迁移、装配、启动
│ ├── bot.js BotRuntime:一个机器人的完整运行时
│ ├── paths.js 全项目路径的唯一来源(按机器人参数化)
│ ├── config.js 全局配置 + 机器人配置 + 迁移
│ ├── events.js 47 个官方事件的登记表
│ ├── endpoints.js 47 个官方 HTTP 接口清单(测试校验覆盖率)
│ ├── outbound.js 出站调度器:窗口 / 限速 / 额度 / 降级
│ ├── pipeline.js 中间件管道
│ ├── session.js 多轮对话 + 免@粘性
│ ├── instance.js 实例互斥,防止同一 AppID 双开
│ ├── gateway.js WebSocket 网关
│ ├── webhook.js Webhook 接收服务(多机器人共用一个端口)
│ ├── sign.js Webhook 回调签名(Ed25519)
│ ├── qq/ 扫码一键建机器人(qrlogin.js / qrsvg.js)
│ ├── mail/ 零依赖 SMTP 客户端、邮件服务、验证码、自助绑定命令
│ ├── tarball.js tar.gz 下载解包(不依赖 git)
│ ├── updater.js 框架自身检查更新与升级
│ ├── semver.js 版本比较与范围匹配(框架更新/市场/兼容校验共用)
│ ├── api.js HTTP API
│ ├── message.js 事件 → 统一 Message
│ ├── roles.js 权限存储
│ ├── reach.js 可达性:好友关系 + 主动推送开关(官方没有查询接口,靠事件维护)
│ ├── ledger.js 事件账本:原始消息落库,官方无历史消息接口,这是唯一的历史来源
│ ├── logger.js 日志:控制台 + 落盘 + 轮转
│ ├── services/ 后台服务:健康巡检、清理、配置热加载、定时重启、插件热重载
│ ├── storage/ SQLite 连接与两套表结构:统计库(不存正文)/ 事件账本(存原文)
│ ├── web/server.js 面板服务端(REST + SSE)
│ └── plugin/ 插件加载、配置、依赖、子进程宿主、面板页面注册
├── test/
│ ├── selftest.mjs 框架自检(622 项)
│ ├── e2e.mjs 端到端测试(243 项,起真实服务打接口)
│ ├── qrlogin.mjs 扫码建机器人(8 项)
│ └── deps.mjs 插件依赖安装(11 项)
├── web/ 面板前端(原生 JS/CSS,无构建)
└── docs/为什么数据必须按机器人隔离:openid 是每个机器人独立的——同一个用户在不同机器人下的
openid 完全不同。共享 roles.json 等于所有权限记录全部对不上。这不是设计偏好,是硬约束。
为什么插件代码共享:plugins/ 只有一份代码,每个机器人按自己的配置决定启用哪些,
加载时各建一套命令表和上下文。改一次代码,所有机器人同时生效。
文档
| 文档 | 内容 | |------|------| | 插件开发规范 | 内建插件(JS)的写法、ctx 全部能力、发布要求 | | 外部插件协议 | 任意语言插件的 NDJSON 协议、时序、示例 | | 配置参考 | 全部配置字段、环境变量、intents 位表 | | 权限模型 | 作用域 × 等级、号段分配、授权规则 | | 官方接口限制 | 官方限制清单、免@粘性原理、实现要点对照 | | 常见问题 | 收不到消息、发不出去、面板打不开、数据在哪 | | 真机联调 | 用小号(OneBot v11)在真群里跟机器人对话,测 intents / 被动回复 / msg_seq | | 对标 ElainaBot | 与主流 Python 框架的逐项对比、差距与取舍 | | 事件与接口清单 | 官方 47 个事件、47 个接口的覆盖审计(含频道生态缺口) | | 官方接口核查 | 逐页核对官方文档的结果:哪些接口真没有、官方能力怎么给 AI 用 | | DSH-Aurabot 适配 v2 | 按 v0.7.2 的真实能力重写的接入方案:通道设计、模块去留、分期 | | 架构说明 | 进程模型、数据隔离、事件流、插件加载、面板多机器人实现 | | DSH 接入指南 | 装进 DeepSeek Harness,用 AI 管机器人:安装、配置、验证、排查 | | AI 宿主接入 | 外部 AI 驱动 Roost:SSE 消息下行、通用能力上行、能力清单 | | 同类项目调研 | 与 AstrBot / NoneBot2 / Koishi / ZeroBot 的对比 |
相关仓库
| 仓库 | 说明 | |------|------| | qq-bot-roost-plugins | 插件市场源,面板「插件 → 市场」从这里装 | | dsh-roost | DSH 插件:让 AI 通过本框架住进 QQ(走官方接口,不 hook 客户端) |
开发
npm test # 636 项(selftest 617 + qrlogin 8 + deps 11)
npm run test:e2e # 端到端 168 项(起 mock 服务打全部接口,跑在临时目录)
npm run test:all # 全量约 618 项:自检 + 扫码 + 依赖 + e2e(真机驱动要真机,没配就跳过)
npm run check:secrets # 扫硬编码凭据(CI 也跑这个,提交前建议过一遍)
node src/main.js --mock # 不连 QQ,命令行发假消息调插件
node src/main.js --mock --serve # 只开面板,调界面
node src/main.js --check # 只验证凭证和网关真凭据不要写进代码,放
config/bot.json(已在 .gitignore 里)。 测试和示例里用test-secret-not-a-real-key这种明显的假值。 万一已经推到远端,光改代码没用——必须去 QQ 开放平台重置。
写插件时最常用的两条:
npm run create -- my-plugin "我的插件" # 生成骨架(含命令、菜单、配置示例)
npm run test:plugin -- my-plugin hello # 直接跑,看它回了什么
npm run test:plugin -- my-plugin --list # 列出这个插件注册了哪些命令test:plugin 不需要连 QQ、不需要凭证、不开面板:它用 src/plugin/testkit.js
造一个假 ctx 和假 msg,把命令跑一遍看回复。想写断言就在自己的测试里
import { runCommand } from './src/plugin/testkit.js'。
真机联调:mock 测不出 intents、被动回复 5 分钟窗口、msg_seq 这些只有真网关才有的东西。
用一个 QQ 小号(走 SnowLuma / NapCat 这类 OneBot v11 实现)在真群里 @ 机器人,就能测:
npm run live -- --init # 生成 config/live-test.json(已 gitignore)
npm run live -- --check # 探活:小号是谁、群在不在、机器人 uin 是几号
npm run live # 四条场景:不@ / @help / 未知命令 / 连发两条
npm run live -- --chat # 交互对话:你打字,它当小号发出去
npm run live -- --cmd help # 单发一条命令(不带斜杠,脚本补前缀)详见 真机联调。
mock 模式下:直接输入文本 = 模拟一条群 @ 消息;!as <openid> 切换发送者;
!addbot <群openid> <邀请人openid> 模拟入群。
已知限制
- 群列表:官方没有查询接口,只能靠事件被动发现
- 群成员列表 / 踢人 / 群黑名单:官方内邀中,默认关闭。 申请到白名单后去面板「配置 → 群管能力」打开,不用重启
- 禁言 / 撤回他人消息:已开放并封装(
ctx.api.muteMember等), 要求机器人是群管理员。框架默认会自动预检(查bot_state,带缓存), 确认没权限就直接给友好错误;查不到时不拦截 - 单进程:插件与框架同进程,插件里写死循环会拖垮整个机器人
- 存储是 JSON:适合中小规模,大型游戏存档建议插件里直接接 SQLite
(统计库已经用上了 Node 内置的
node:sqlite,但业务存档仍是 JSON) - 语音不能转码:官方要 SILK 格式,框架只能发已经是 silk 的音频。 要把 mp3/wav 转 silk,得自己接 ffmpeg + silk 编码器,这在插件层做 (转码器是原生二进制,塞进框架就破了零依赖)
- 图片渲染要自己装依赖:
plugins/render是示例,依赖@napi-rs/canvas可选安装, 没装时命令会明确告诉你怎么装,不会静默失败 - 面板无登录态:只有访问密钥校验,没有会话,别直接暴露到公网
细节和应对方式见 docs/官方接口限制.md。
致谢
这个项目站在不少人的肩膀上。以下项目没有直接复用代码,但架构、协议理解和文档写法上参考了它们:
架构与设计
- AstrBot —— 插件元数据、配置 Schema 的思路,以及 WebUI 的信息组织方式
- NoneBot2 —— 事件响应器分层,README 的「适合 / 不适合」写法
- Koishi —— 中间件洋葱模型、插件生命周期与副作用回收
- ZeroBot-Plugin —— 包即插件的组织方式、多级开关与优先级规则
协议与接口
- botgo(腾讯官方 Go SDK)—— 核对 HTTP 接口路径与事件清单的第一手依据
- botpy(腾讯官方 Python SDK)
- QQ 机器人官方文档 —— 所有协议细节的最终来源
经验教训
- OneBot —— 它 2023 年冻结的教训,是我们坚持直连官方接口的原因
- nonebot_plugin_xiuxian_2 —— 文字游戏玩法的模块划分参考
贡献者
(等仓库公开后,这里会自动显示所有贡献者)
贡献
欢迎 Issue 和 PR。动手前请读 贡献指南。
| 文档 | 内容 | |------|------| | CONTRIBUTING.md | 开发环境、代码风格、提交信息、PR 要求 | | CODE_OF_CONDUCT.md | 行为准则 | | SECURITY.md | 漏洞上报方式、部署时必须自己守住的三件事 |
许可
永久免费,禁止任何收费,禁止商业使用。 完整条款见 LICENSE。
| 场景 | 允许? | |------|--------| | 个人使用、学习、研究 | ✅ 随便用、随便改 | | 非营利项目、公益用途 | ✅ 可以 | | 学校、慈善组织、政府机构使用 | ✅ 可以 | | 自己改了再免费分享出去 | ✅ 可以,但须沿用同一许可并保留声明 | | 卖这个软件,或收下载费 / 授权费 / 会员费 | ❌ 禁止 | | 以「技术支持 / 培训 / 定制」名义收费 | ❌ 禁止 | | 拿它做收费服务、集成进商业产品 | ❌ 禁止 | | 设置「先打赏才能用」之类的门槛 | ❌ 禁止 |
三点要说明白:
- 任何人不得就本软件收费,包括二次分发者。这个项目也不提供任何商业授权—— 不存在「付钱就能商用」的途径。如果有人声称代表本项目出售授权,那不是我们。
- 严格说这不属于 OSI 定义的开源(开源许可证不允许限制使用领域), 准确说法是「源码公开、免费使用(source-available)」。
- 协议结构参考了 PolyForm Noncommercial 1.0.0, 并按「禁止任何收费」做了加严——所以不能直接叫 PolyForm,是一份自写协议。
自愿捐赠可以接受,但不能把任何付费行为设成使用前提。
一句话:自己用、改了再免费分享都可以;任何形式的收费和商业使用都不行。
集成:DeepSeek Harness
仓库里带了一个 DSH 插件(integrations/dsh-roost/),装进
DeepSeek Harness 之后可以直接对 AI 说:
机器人现在在线吗?收了多少消息? 看看最近的报错日志 把 dice 插件禁用了
插件通过面板 REST API 跟 Roost 对话,不把 Roost 塞进 DSH 进程—— Roost 是常驻服务,DSH 一关机器人不能跟着掉线。
npx @deepseek-ai/dsh plugin --profile desktop add "/绝对路径/qq-bot-roost/integrations/dsh-roost"六个工具:roost_status roost_plugins roost_logs(只读)、
roost_plugin_toggle roost_send roost_reconnect(写操作)。
完整步骤、配置、排查见 DSH 接入指南。
