koishi-plugin-msg-router
v1.4.3
Published
Koishi OneBot 指令路由:支持手动/动态转译、权限与频率控制、双向 WebSocket 和 QQ Markdown
Downloads
627
Maintainers
Readme
koishi-plugin-msg-router
一个面向 Koishi 的 OneBot v11 指令中转路由插件。插件仅接管配置中指定的指令,将会话转换为 OneBot v11 消息事件并通过 WebSocket 交给外部后端处理;未配置的指令保持 Koishi 原有处理流程,不受影响。
后端可以通过标准 OneBot API 动作向群聊或私聊回发文本、图片、语音等消息。针对 QQ 官方适配器,插件还支持原生 Markdown 文本和 Markdown 键盘,并在不支持 Markdown 的平台上自动降级为普通文本。
主要用途
- 按需接管指令:每条路由独立配置指令列表,只转发明确指定的指令
- 手动或动态转译:可以在配置页维护指令映射,也可以由后端通过接口动态声明
- Koishi 指令控制:映射可配置权限、帮助菜单隐藏、调用频率、群聊/私聊范围和平台限制
- OneBot v11 兼容:向后端推送标准消息事件,并处理常用 OneBot API 动作
- 双向 WebSocket:支持插件主动连接后端,也支持插件监听并同时维持多个后端连接
- 多路由配置:不同指令可以连接不同后端,分别设置地址、鉴权和连接参数
- 消息类型转换:支持文本、图片、@、回复、语音、视频和表情等常用消息段
- QQ Markdown:支持 QQ 原生 Markdown 文本,其他平台自动使用普通文本降级
- 连接管理:提供令牌鉴权、心跳检测、断线重连、请求超时和调试日志
控制台配置说明
总开关
enableQQMarkdown:是否启用 QQ 原生 Markdown 文本;关闭后 Markdown 内容按普通文本降级发送enableCommandDeclaration:是否允许后端通过 WebSocket 动态声明要接管的指令,默认关闭enableCommandTranslation:是否允许把对外指令转译为不同的后端目标指令commandDeclarationAction:动态声明接口的 action,默认msg_router.declare_commandsmaxDeclaredCommands:每条路由最多允许动态声明的指令数量,默认100allowDynamicCommandPermissions:是否允许后端自行声明权限规则,默认关闭minimumDynamicAuthority:动态指令的最低权限等级,默认1;后端不得声明更低的authority权限debug:是否输出 WebSocket 收发、指令匹配和消息解析等调试信息
正向 WS 路由列表
这张表用于“插件主动连接后端”的场景。
name:路由名称enabled:是否启用mode:固定为clientendpoint:OneBot v11 WebSocket 地址token:后端鉴权令牌,可不填secret:HMAC 签名密钥,可不填commands:触发这条路由的 Koishi 指令commandMappings:手动指令转译表,可设置对外指令、后端目标指令、说明和启用状态action:发送给后端的 action 名称timeout:单次请求等待时间,单位毫秒heartbeatInterval:发送 ping 的间隔,单位毫秒reconnectInterval:断线后的重连间隔,单位毫秒maxConcurrency:同时允许的请求数量maxConnections:仅 server 模式有效,同时允许接入的后端连接数量上限,默认16
反向 WS 路由列表
这张表用于“插件监听,后端主动连进来”的场景。
name:路由名称enabled:是否启用mode:固定为serverlistenHost:监听主机listenPort:监听端口token:后端鉴权令牌,可不填secret:HMAC 签名密钥,可不填commands:触发这条路由的 Koishi 指令commandMappings:手动指令转译表,可独立于commands使用action:发送给后端的 action 名称timeout:单次请求等待时间,单位毫秒heartbeatInterval:发送 ping 的间隔,单位毫秒reconnectInterval:断线后的重连间隔,单位毫秒maxConcurrency:同时允许的请求数量maxConnections:同时允许接入的后端连接数量上限,默认16;每个指令事件会广播给全部已连接后端
推荐配置示例
{
"debug": false,
"clientRoutes": [
{
"name": "AI 后端",
"enabled": true,
"mode": "client",
"endpoint": "ws://127.0.0.1:8080",
"token": "",
"secret": "",
"commands": ["ask", "translate", "summarize"],
"action": "msg_router.forward_command",
"timeout": 10000,
"heartbeatInterval": 30000,
"reconnectInterval": 5000,
"maxConcurrency": 16,
"maxConnections": 16
}
],
"serverRoutes": [
{
"name": "反向后端",
"enabled": true,
"mode": "server",
"listenHost": "127.0.0.1",
"listenPort": 8080,
"token": "",
"secret": "",
"commands": ["reply", "summarize"],
"action": "msg_router.forward_command",
"timeout": 10000,
"heartbeatInterval": 30000,
"reconnectInterval": 5000,
"maxConcurrency": 16,
"maxConnections": 16
}
]
}模式说明
client模式:插件主动连接后端server模式:插件自己监听,等待后端连接server模式下可以同时连接多个后端;每个命令事件会广播给所有在线连接。若还没有任何后端连上来,命令会等待到超时为止
协议约定
插件向后端发送带 action / echo 外壳的请求,其中 params 使用 OneBot v11 的标准消息事件结构。当前只把文本内容封装为 message 段:
{
"action": "msg_router.forward_command",
"params": {
"time": 1710000000,
"self_id": 123456789,
"post_type": "message",
"message_type": "group",
"sub_type": "normal",
"message_id": 10001,
"user_id": 123456,
"group_id": 654321,
"message": [
{
"type": "text",
"data": {
"text": "hello"
}
}
],
"raw_message": "hello",
"font": 0,
"sender": {
"user_id": 123456,
"nickname": "Alice"
}
},
"auth": {
"algorithm": "hmac-sha256",
"signature": "hex-string",
"timestamp": 1710000000000
},
"echo": "uuid"
}后端响应示例:
{
"status": "ok",
"retcode": 0,
"data": {
"text": "reply text"
},
"echo": "uuid"
}如果 data.text、data.message 或 data.reply 存在,插件会直接把它返回给用户。data.message 支持 OneBot 风格的文本段数组。
手动配置指令转译
每条正向或反向路由都提供 commandMappings 表格,可以直接在 Koishi 配置页面添加映射:
| 配置项 | 说明 | 示例 |
| --- | --- | --- |
| enabled | 是否启用这条映射 | true |
| source | 用户实际执行、插件需要接管的指令 | 天气 |
| target | 转发给后端的目标指令 | weather |
| description | Koishi 指令系统中显示的说明 | 查询天气 |
| permissions | Koishi 权限规则 | ["authority:2"] |
| hidden | 是否从帮助菜单隐藏 | false |
| maxUsage | 每名用户每天最多调用次数,0 不限制 | 20 |
| minInterval | 同一用户连续调用的最小间隔(毫秒),0 不限制 | 3000 |
| scope | all、group 或 private | group |
| platforms | 允许的平台;空数组不限制 | ["qq"] |
配置后,用户执行 天气 上海,后端会收到 weather 上海。映射可以独立使用,不需要再把 天气 重复填写到 commands;如果两处都填写,commandMappings 中的目标指令优先。单条映射可以使用 enabled 独立关闭。
全局关闭 enableCommandTranslation 后,手动映射和动态声明仍会注册源指令,但不再替换指令名。permissions 使用 Koishi 原生权限服务;hidden 需要 help 插件,maxUsage 和 minInterval 需要 rate-limit 插件及数据库服务才能生效。
动态声明与指令转译接口
开启配置项 enableCommandDeclaration 后,已连接的后端可以通过 WebSocket 调用声明接口。接口 action 默认为 msg_router.declare_commands,也可以通过 commandDeclarationAction 修改。
下面的声明会让插件接管用户侧的 天气 和 菜单 指令。其中,用户执行 天气 上海 时,后端收到的消息内容会转译为 weather 上海;插件只替换指令名,后续参数保持不变。
{
"action": "msg_router.declare_commands",
"params": {
"replace": true,
"commands": [
{
"name": "天气",
"target": "weather",
"description": "查询天气",
"permissions": ["authority:2"],
"hidden": false,
"maxUsage": 20,
"minInterval": 3000,
"scope": "group",
"platforms": ["qq"]
},
{
"name": "菜单",
"target": "help"
}
]
},
"echo": "declare-1"
}成功响应:
{
"status": "ok",
"retcode": 0,
"data": {
"declared": [
{ "name": "天气", "target": "weather" },
{ "name": "菜单", "target": "help" }
],
"removed": [],
"rejected": []
},
"echo": "declare-1"
}接口规则:
commands可以是上述数组,也可以简写成{ "天气": "weather", "菜单": "help" }- 字符串声明(例如
"ping")表示不转译,源指令和目标指令相同 replace默认为true,会移除该路由之前动态声明、但本次未再次声明的指令replace: false表示增量更新;单项设置enabled: false可以移除动态指令- 动态声明支持与手动映射相同的
permissions、hidden、maxUsage、minInterval、scope和platforms - 默认不允许后端决定权限,动态指令统一采用
minimumDynamicAuthority;只有显式开启allowDynamicCommandPermissions后才读取后端提交的permissions - 即使允许动态权限,低于
minimumDynamicAuthority的authority规则仍会被拒绝;只声明自定义权限时,插件也会自动附加最低authority规则 - 关闭
enableCommandTranslation后仍允许动态声明,但会忽略target,按原指令转发 - 插件不会覆盖其他 Koishi 插件已经注册的指令,冲突项会出现在
rejected中 - 只配置动态声明、不填写静态
commands的路由也会启动并等待后端声明
QQ Markdown 与键盘
后端通过 send_group_msg、send_private_msg 或 send_msg 回发消息时,可以使用 QQ 原生 Markdown 文本。插件在 QQ 官方适配器上会直接提交 markdown.content,并透传 keyboard;在其他平台上会把同一内容作为普通文本降级发送。
此功能可通过配置页面的 enableQQMarkdown 开关控制。关闭后不会调用 QQ 原生 Markdown 接口,而是把 content 或 fallback_text 作为普通消息发送。
{
"action": "send_group_msg",
"params": {
"group_id": "群 openid",
"message": [
{
"type": "markdown",
"data": {
"content": "# 标题\n**加粗内容**\n[查看详情](https://example.com)",
"fallback_text": "标题\n加粗内容\nhttps://example.com",
"keyboard": {
"content": {
"rows": []
}
}
}
}
]
},
"echo": "request-id"
}如果后端本身直接使用 QQ 消息结构,也可以省略 message,直接在 params.markdown.content 中提供内容。fallback_text 可选;未提供时会直接使用 content 作为降级文本。
兼容说明
token是可选项,不填时不会附加Authorization头secret也是可选项,不填时不会生成auth- 如果后端没有返回可显示内容,插件会保持静默
- 请求超时只会记录警告,不会把命令直接抛成错误
- 同名指令不建议在多个路由里重复配置
