对接 IM 平台
你想让 ZCBOT 连上一个真实的聊天软件,让机器人能收发消息——这就是这篇要解决的问题。
框架本身协议无关:「消息怎么来、怎么发」全由接入端插件负责。所有接入端默认都是关闭的,需显式启用;最常用的是 onebot_adapter(OneBot 11,常用来接 QQ);热路径性能敏感时可换 rust_accel(同一协议的 Rust 加速接入端,默认关,见下表);另外还有 QQ 官方、Telegram、Discord、出站 WS 等,同样默认关闭,填好凭证再开。
先确认要不要读完
框架能启动、命令能注册、定时任务能跑——都不需要接聊天平台。 只有「要和某个聊天软件收发消息」时才需要这篇。
接入端是怎么工作的(以默认 OneBot 为例)
┌──────────┐ 反向 WebSocket ┌─────────────────┐
│ NapCat / │ ────────────────► │ ZCBOT │
│ Lagrange │ 连到 :6830 │ onebot_adapter │
│ (OneBot) │ ◄──────────────── │ → 内核 → 插件 │
└──────────┘ 回复也走这条 └─────────────────┘- 框架起一个 WebSocket 服务端(默认
0.0.0.0:6830); - OneBot 实现端(NapCat、Lagrange…)以反向 WebSocket 连进来;
- 消息推给框架,插件处理完再经同一连接发回平台。
换一个 ProtocolAdapter 就能接别的事件源。这里默认走 onebot_adapter。
其它官方接入端(默认全关)
| 插件 | 平台 | 怎么开(core_plugins.yaml) |
|---|---|---|
onebot_adapter | QQ(经 NapCat 等) | 默认关闭,需启用;设 access_token 更安全 |
rust_accel | QQ(同上,OneBot 11) | Rust 加速接入端(默认关):enabled: true;启用前先停 onebot_adapter,监听独立端口 6831 |
qq_official | QQ 官方机器人 | enabled: true + app_id / app_secret |
telegram | Telegram | enabled: true + token(找 @BotFather 要) |
discord | Discord | enabled: true + token(开发者门户;MESSAGE_CONTENT Intent 需开) |
kook | KOOK | enabled: true + token(开发者后台,连接模式选 WebSocket) |
matrix | Matrix(含自建 homeserver) | enabled: true + homeserver / access_token / user_id |
ws_client | 任意 WS 总线 | enabled: true + url(主动连出去) |
http_inject | 不接 IM / Webhook | enabled: true + 地址端口 |
这些接入端都实现了统一连接自描述,后台「接入端连接」页可以改配置;图片统一支持 base64://。
能力差异(先看再选):
- 收发消息、文本回复:各端统一走事件与
send_text/ 动作名。 - 群管理(踢人 / 禁言等):目前完整支持在 OneBot 11;QQ 官方 / Telegram / Discord 等端对这类动作多返回「不支持」,群管类插件换端后请先验证。
- 媒体出站:Telegram / Discord 支持本地上传与 base64;QQ 官方需先走富媒体上传接口;KOOK 要求资源由机器人自己上传后引用,Matrix 需先
/_matrix/media/v3/upload(后两者本端暂只发文本)。
失败是怎么报给你的
各平台的错误模型不一样,接入端会把它们统一成同一套 retcode,插件不需要按平台分支:
| retcode | 含义 | 触发例子 |
|---|---|---|
0 | 成功 | — |
-1 | 失败(平台业务错误,或接入端未连接) | Telegram chat not found、Discord 403 缺权限、KOOK code=40000 |
-2 | 请求超时 | OneBot 等待响应超 10s |
-3 | 网络/连接异常 | 发不出去、连接已断 |
-4 | 被平台限流 | HTTP 429;带 throttled: true 与 retry_after(秒) |
-10 | 该端不支持此动作 | 例如 KOOK 主动私聊、Matrix 媒体出站 |
几个容易踩的点,接入端已经处理:
- HTTP 200 不代表成功。 QQ 官方失败时仍返回 200,业务码在 body 的
code里; 官方文档明确写了message只用于人工排查、内容可能随时调整,所以判据只取code。 - 限流必须按平台给的等待时间。 自己拍一个更短的间隔重试会被继续限,越试越久。 各端都会读
retry_after/retry_after_ms/Retry-After,原样放在返回值里。 - 凭证类错误不重试。 Discord 的 4004/4013/4014、KOOK 的 40101/40103、 Matrix 的
M_UNKNOWN_TOKEN、Telegram 的 401,都会停止重连并打印原因—— 这类错误重连一万次也不会好,还会把配额烧光(Discord 限 1000 次 IDENTIFY/天)。 - Telegram
getUpdates与 webhook 互斥。 配过 webhook 的 bot 轮询会一直返回 409, 接入端会只提示一次并给出deleteWebhook的修法。
默认路线:OneBot 11(QQ)
1. 确认接入端已启用
编辑根目录 core_plugins.yaml(首次启动会自动生成):
core_plugins:
onebot_adapter:
enabled: true
listen_host: 0.0.0.0
listen_port: 6830
access_token: '' # 公网务必改成强随机字符串| 字段 | 说明 |
|---|---|
enabled | 关掉则完全不启这个接入端(纯定时场景可关) |
listen_host / listen_port | 服务端监听地址——客户端连这里 |
access_token | 连入校验令牌;本地调试可空,公网必须设 |
改完重启框架,日志应出现类似:
OneBot 适配器已启动 (ws://0.0.0.0:6830)2. 让客户端连进来
以 NapCat 为例:网络配置 → 新增「反向 WebSocket 客户端」:
| 设置项 | 填什么 |
|---|---|
| 目标地址 | ws://127.0.0.1:6830 |
| AccessToken | 与 access_token 一致(本地可留空) |
等价 JSON:
{
"WebSocketReverse": {
"Enable": true,
"URL": "ws://127.0.0.1:6830",
"AccessToken": ""
}
}Lagrange、go-cqhttp 等同理:填同一个 ws://地址 和令牌即可。
3. 验证连上了
框架日志出现:
[bot_xxxxxx] OneBot 客户端已连接多账号时每个客户端独立;回复会自动用「消息来源那个号」,插件一般不用手动选 bot。
4. 发一条消息试试
对机器人发:
/echo 你好回 你好 就通了。/help 看全部命令。
这条
/echo由内置的 echo 自检插件(core_plugins/echo,默认开启)提供, 覆盖所有接入端(OneBot / QQ 官方 / Telegram / Discord / WS / HTTP 注入), 发什么回什么——专门用来验证「消息进得来、命令路由得通、回复发得出」整条链路。 若你手动关掉了echo插件,需重新启用才会响应。同理,
/help(及别名/帮助、/菜单)由内置的 help 插件(core_plugins/help,默认开启) 提供,列出当前所有已注册命令——这也是教程让你「发/help看命令清单」的对应实现,覆盖全部接入端。
其它平台(简要)
以 Telegram 为例,三步:
core_plugins.yaml里telegram.enabled: true,填token- 重启,日志确认接入端就绪
- 给机器人发
/echo 你好(内置 echo 自检插件响应)
Discord、QQ 官方同理:开插件 → 填 token/app 凭证 → 重启 → 发 /echo 你好 自测。 详细字段说明见 配置系统 · 各接入端。
插件里怎么发消息
消息进框架后,插件只用统一的 ctx,不必关心底下是 OneBot 还是别的协议。
通用文本
ctx.send_msg(user_id=uid, group_id=None, message="你好") # 私聊
ctx.send_msg(group_id=gid, user_id=None, message="大家好") # 群
# 异步 handler 里用 await ctx.asend_msg(...)富媒体(OneBot / CQ 码)
ctx.send_msg(group_id=gid, message="[CQ:image,file=file:///path/to/a.png]")
ctx.send_msg(group_id=gid, message=f"[CQ:at,qq={uid}] 看这里")
ctx.send_msg(group_id=gid, message=f"[CQ:reply,id={msg_id}] 收到")CQ 码是 OneBot 11 的写法。其它平台图片语法不同,但文本发送接口不变。
需要跨协议统一处理图片/语音/视频的插件,请改用规范消息段:
ev.images/ev.voices/ev.videos/ev.files在所有接入端上都能读到, 详见 接入端契约与规范消息。
群管 / 查询(依赖 OneBot 接入端)
ctx.ban(group_id, user_id, duration=600) # 禁言(秒,0 解禁)
await ctx.akick(group_id, user_id) # 踢出
ctx.mute_all(group_id, True) # 全员禁言
members = ctx.get_member_list(group_id)
# 或经 ctx.onebot 调任意 OneBot action
ctx.onebot.send_group_msg(group_id=123, message="hi")
info = ctx.onebot.get_login_info()在命令匹配前截获原始消息
def register(ctx):
ctx.on_raw_message(raw_handler)
async def raw_handler(raw_event, bot_name):
if 需要接管:
return True # True = 吃掉这条,后续命令/关键词都不跑
# 其它情况返回 None/False,继续正常流程多个原始处理器按插件 priority 升序执行,单个异常不影响别人。
安全须知
- 公网部署:
web.host保持127.0.0.1+ 反代;access_token设强随机值;按需 IP 白名单 - 启动时若提示「监听 0.0.0.0 且 token 为空」,请立刻处理
- 多账号时注意权限边界,别把超管能力随便开给所有 bot