@maorongwan/koishi-plugin-message-bridge
v2.2.0
Published
将聊天消息通过zeromq转发到指定网络位置
Readme
@maorongwan/koishi-plugin-message-bridge v2.0
(https://www.npmjs.com/package/@maorongwan/koishi-plugin-message-bridge)
通过 ZeroMQ 在 Koishi 与外部程序(如 C#)之间双向转发聊天消息。
v2.0 新特性
- 🏗️ Per-bot 隔离:通过
bot-added/bot-removed事件自动管理,每个 bot 独享独立端口(basePort + botIndex * 10) - 📡 17 个 API 方法:覆盖消息发送、文件上传、群管理、查询等完整操作集
- 🔄 统一 JSON 响应:
{ code, data/error }格式,错误处理一致 - 🧪 测试 REP:独立 socket 端口用于开发调试
- 🔗 SendChain 防并发:per-bot Promise 链式发送,避免 ZeroMQ "Socket is busy writing"
- 🔁 指数退避重试:最多 3 次,间隔 1s / 2s / 4s
功能
- 📤 消息转发(PUSH):通过 middleware 将收到的消息序列化后 PUSH 到 ZeroMQ socket
- 📥 指令下发(PULL):监听 PULL socket,接收外部指令并调用 Bot API 发送消息
- 🔁 请求-应答(REP):监听 REP socket,处理查询/操作请求并返回结构化响应
- 🧪 测试 REP:独立 REP socket,用于开发/调试时手动测试 API
配置项
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| basePort | number | 5555 | 基端口,实际端口为 basePort + botIndex * 10(PUSH/PULL/REP 分别占用 3 个连续端口) |
| testRepEnabled | boolean | true | 是否启用测试 REP socket |
| testRepPort | number | 15559 | 测试 REP 端口 |
端口分配规则
Bot 索引从 0 开始,按 ctx.bots 顺序排列:
| Bot 索引 | PUSH 端口 | PULL 端口 | REP 端口 |
|----------|-----------|-----------|----------|
| 0 | basePort | basePort+1 | basePort+2 |
| 1 | basePort+10 | basePort+11 | basePort+12 |
| 2 | basePort+20 | basePort+21 | basePort+22 |
传输协议
Koishi → 外部(PUSH)
{
"timestamp": 1700000000,
"userId": "123456",
"username": "user",
"channel": { "id": "...", "type": 0 },
"guildId": null,
"platform": "onebot",
"content": "消息内容",
"messageId": "msg-123",
"botId": "1740451858"
}外部 → Koishi(PULL)
发送私聊消息:
{
"type": "SendMessage",
"botId": "1740451858",
"channelId": "123456",
"content": "你好",
"isGroup": false
}发送群消息(两种方式):
方式一 — SendMessage + isGroup:
{
"type": "SendMessage",
"botId": "1740451858",
"channelId": "987654",
"content": "群发消息",
"isGroup": true
}方式二 — SendGroupMessage(语义别名):
{
"type": "SendGroupMessage",
"botId": "1740451858",
"groupId": "987654",
"content": "群发消息"
}外部 ⇄ Koishi(REP)
上传文件:
// 请求
{
"type": "UploadFile",
"botId": "1740451858",
"params": {
"channelId": "123456",
"url": "https://example.com/file.txt",
"fileName": "file.txt",
"isGroup": false
}
}
// 响应
{ "code": 0, "data": { "channelId": "123456", "fileName": "file.txt" } }查询类 API:
// GetFriendList
{ "type": "GetFriendList", "botId": "1740451858", "params": {} }
// GetGroupList
{ "type": "GetGroupList", "botId": "1740451858", "params": {} }
// GetGroupMemberList
{ "type": "GetGroupMemberList", "botId": "1740451858", "params": { "groupId": "987654" } }
// GetGroupMemberInfo
{ "type": "GetGroupMemberInfo", "botId": "1740451858", "params": { "groupId": "987654", "userId": "123456" } }
// GetGroupInfo
{ "type": "GetGroupInfo", "botId": "1740451858", "params": { "groupId": "987654" } }
// GetLoginInfo
{ "type": "GetLoginInfo", "botId": "1740451858", "params": {} }群管理类 API:
// SetGroupName
{ "type": "SetGroupName", "botId": "1740451858", "params": { "groupId": "987654", "name": "新群名" } }
// SetGroupAdmin
{ "type": "SetGroupAdmin", "botId": "1740451858", "params": { "groupId": "987654", "userId": "123456", "enable": true } }
// SetGroupCard
{ "type": "SetGroupCard", "botId": "1740451858", "params": { "groupId": "987654", "userId": "123456", "card": "新名片" } }
// SetGroupKick
{ "type": "SetGroupKick", "botId": "1740451858", "params": { "groupId": "987654", "userId": "123456", "rejectAddRequest": false } }
// SetGroupBan
{ "type": "SetGroupBan", "botId": "1740451858", "params": { "groupId": "987654", "userId": "123456", "duration": 600 } }
// SetGroupWholeBan
{ "type": "SetGroupWholeBan", "botId": "1740451858", "params": { "groupId": "987654", "enable": true } }
// SetGroupLeave
{ "type": "SetGroupLeave", "botId": "1740451858", "params": { "groupId": "987654", "isDismiss": false } }
// SetGroupAnnouncement
{ "type": "SetGroupAnnouncement", "botId": "1740451858", "params": { "groupId": "987654", "content": "公告内容" } }响应码
| 码 | 名称 | 说明 | |----|------|------| | -1 | INIT | 初始/无效 | | 0 | SUCCESS | 成功 | | 1 | MISSING_PARAMS | 缺少必要参数 | | 2 | FAILED | 操作失败 | | 3 | UNKNOWN_TYPE | 未知消息类型 | | 4 | NOT_FRIEND | 非好友(文件上传受限) | | 5 | BOT_NOT_FOUND | Bot 不存在 | | 6 | UNSUPPORTED_METHOD | 不支持的方法 |
测试 REP
测试 REP socket 独立于 per-bot REP,提供一个直接调用 API 的入口。
- 端口:
15559(默认,可通过testRepPort配置) - 安全约束:仅绑定
selfId=1740451858的 bot(硬编码,不可配置)
请求格式
{
"type": "TestMethod",
"method": "GetFriendList",
"botId": "1740451858",
"params": {}
}method大小写不敏感(如getfriendlist自动映射到GetFriendList)botId必须匹配已上线的 bot
示例
# 使用 zeromq 客户端测试
node -e "
const zmq = require('zeromq');
(async () => {
const req = new zmq.Request();
req.connect('tcp://localhost:15559');
await req.send(JSON.stringify({type:'TestMethod', method:'GetFriendList', botId:'1740451858', params:{}}));
const [resp] = await req.receive();
console.log(resp.toString());
})()
"安装
npm install @maorongwan/koishi-plugin-message-bridge依赖
koishi ^4.18.7(peer)zeromq ^6.5.0- Node.js ≥ 18
