@maorongwan/koishi-plugin-message-bridge 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.d.ts +8 -0
- package/lib/index.js +252 -0
- package/package.json +23 -0
- package/readme.md +5 -0
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Context, Schema } from 'koishi';
|
|
2
|
+
export declare const name = "message-bridge";
|
|
3
|
+
export interface Config {
|
|
4
|
+
pushBindAddress: string;
|
|
5
|
+
pullBindAddress: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const Config: Schema<Config>;
|
|
8
|
+
export declare function apply(ctx: Context, config: Config): void;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name2 in all)
|
|
10
|
+
__defProp(target, name2, { get: all[name2], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
Config: () => Config,
|
|
34
|
+
apply: () => apply,
|
|
35
|
+
name: () => name
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(src_exports);
|
|
38
|
+
var import_koishi = require("koishi");
|
|
39
|
+
var zmq = __toESM(require("zeromq"));
|
|
40
|
+
var name = "message-bridge";
|
|
41
|
+
var Config = import_koishi.Schema.object({
|
|
42
|
+
pushBindAddress: import_koishi.Schema.string().description("PUSH绑定地址(发送到C#)").default("tcp://*:5555"),
|
|
43
|
+
pullBindAddress: import_koishi.Schema.string().default("tcp://*:5556")
|
|
44
|
+
});
|
|
45
|
+
function apply(ctx, config) {
|
|
46
|
+
const logger = ctx.logger(name);
|
|
47
|
+
let pushSocket;
|
|
48
|
+
let isPushBound = false;
|
|
49
|
+
const initPushSocket = /* @__PURE__ */ __name(async () => {
|
|
50
|
+
try {
|
|
51
|
+
pushSocket = new zmq.Push();
|
|
52
|
+
await pushSocket.bind(config.pushBindAddress);
|
|
53
|
+
isPushBound = true;
|
|
54
|
+
logger.info(`PUSH socket 已绑定: ${config.pushBindAddress}`);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
logger.error(`PUSH socket 绑定失败: ${error.message}`);
|
|
57
|
+
isPushBound = false;
|
|
58
|
+
setTimeout(initPushSocket, 5e3);
|
|
59
|
+
}
|
|
60
|
+
}, "initPushSocket");
|
|
61
|
+
const sendMessage = /* @__PURE__ */ __name(async (data) => {
|
|
62
|
+
if (!isPushBound) {
|
|
63
|
+
logger.warn("无法发送消息:PUSH socket 未绑定");
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
const message = JSON.stringify(data);
|
|
68
|
+
await pushSocket.send(message);
|
|
69
|
+
return true;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
logger.warn(`发送消息失败: ${error.message}`);
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}, "sendMessage");
|
|
75
|
+
let pullSocket;
|
|
76
|
+
let isPullBound = false;
|
|
77
|
+
const initPullSocket = /* @__PURE__ */ __name(async () => {
|
|
78
|
+
try {
|
|
79
|
+
pullSocket = new zmq.Pull();
|
|
80
|
+
await pullSocket.bind(config.pullBindAddress);
|
|
81
|
+
isPullBound = true;
|
|
82
|
+
logger.info(`PULL socket 已绑定: ${config.pullBindAddress}`);
|
|
83
|
+
startReceiveLoop();
|
|
84
|
+
} catch (error) {
|
|
85
|
+
logger.error(`PULL socket 绑定失败: ${error.message}`);
|
|
86
|
+
isPullBound = false;
|
|
87
|
+
setTimeout(initPullSocket, 5e3);
|
|
88
|
+
}
|
|
89
|
+
}, "initPullSocket");
|
|
90
|
+
let receiveLoopRunning = false;
|
|
91
|
+
const startReceiveLoop = /* @__PURE__ */ __name(() => {
|
|
92
|
+
if (receiveLoopRunning) return;
|
|
93
|
+
receiveLoopRunning = true;
|
|
94
|
+
const receiveLoop = /* @__PURE__ */ __name(async () => {
|
|
95
|
+
while (receiveLoopRunning && isPullBound) {
|
|
96
|
+
try {
|
|
97
|
+
const [message] = await pullSocket.receive();
|
|
98
|
+
const messageStr = message.toString();
|
|
99
|
+
processReceivedMessage(messageStr);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (receiveLoopRunning && isPullBound) {
|
|
102
|
+
if (!error.message?.includes("Operation was not possible or timed out")) {
|
|
103
|
+
logger.error(`接收消息失败: ${error.message}`);
|
|
104
|
+
}
|
|
105
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
106
|
+
} else {
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}, "receiveLoop");
|
|
112
|
+
receiveLoop().catch((error) => {
|
|
113
|
+
logger.error(`接收循环错误: ${error.message}`);
|
|
114
|
+
});
|
|
115
|
+
}, "startReceiveLoop");
|
|
116
|
+
const processReceivedMessage = /* @__PURE__ */ __name(async (message) => {
|
|
117
|
+
try {
|
|
118
|
+
const data = JSON.parse(message);
|
|
119
|
+
switch (data.type) {
|
|
120
|
+
case "SendMessage":
|
|
121
|
+
await handleSendMessage(data);
|
|
122
|
+
break;
|
|
123
|
+
case "UploadFile":
|
|
124
|
+
await handleUploadFile(data);
|
|
125
|
+
break;
|
|
126
|
+
default:
|
|
127
|
+
logger.warn(`收到未知类型的消息: ${data.type}`);
|
|
128
|
+
logger.info(`完整消息: ${JSON.stringify(data)}`);
|
|
129
|
+
}
|
|
130
|
+
} catch (error) {
|
|
131
|
+
logger.info(`收到非JSON消息: ${message.substring(0, 100)}...`);
|
|
132
|
+
}
|
|
133
|
+
}, "processReceivedMessage");
|
|
134
|
+
const handleSendMessage = /* @__PURE__ */ __name(async (data) => {
|
|
135
|
+
const { channelId, content, botId } = data;
|
|
136
|
+
if (!content || !channelId) {
|
|
137
|
+
logger.warn("收到无效的发送消息请求,缺少 content 或 channelId");
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
let bot;
|
|
142
|
+
if (botId) {
|
|
143
|
+
for (const botItem of ctx.bots) {
|
|
144
|
+
if (botItem.selfId === botId) {
|
|
145
|
+
bot = botItem;
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (!bot) {
|
|
150
|
+
logger.warn(`指定的机器人不存在: ${botId}`);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
if (ctx.bots.length === 0) {
|
|
155
|
+
logger.warn("没有可用的机器人");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
bot = ctx.bots[0];
|
|
159
|
+
}
|
|
160
|
+
await bot.sendMessage(channelId, content);
|
|
161
|
+
logger.info(`机器人 ${bot.selfId} 发送消息到 ${channelId}: ${content}`);
|
|
162
|
+
} catch (error) {
|
|
163
|
+
logger.error(`发送消息失败: ${error.message}`);
|
|
164
|
+
}
|
|
165
|
+
}, "handleSendMessage");
|
|
166
|
+
const handleUploadFile = /* @__PURE__ */ __name(async (data) => {
|
|
167
|
+
const { channelId, url, fileName, isGuild, botId } = data;
|
|
168
|
+
if (!url || !fileName || !channelId) {
|
|
169
|
+
logger.warn("收到无效的上传文件请求,缺少必要参数");
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
let bot;
|
|
174
|
+
if (botId) {
|
|
175
|
+
for (const botItem of ctx.bots) {
|
|
176
|
+
if (botItem.selfId === botId) {
|
|
177
|
+
bot = botItem;
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (!bot) {
|
|
182
|
+
logger.warn(`指定的机器人不存在: ${botId}`);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
} else {
|
|
186
|
+
if (ctx.bots.length === 0) {
|
|
187
|
+
logger.warn("没有可用的机器人");
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
bot = ctx.bots[0];
|
|
191
|
+
}
|
|
192
|
+
if (bot.platform === "onebot") {
|
|
193
|
+
if (isGuild) {
|
|
194
|
+
await bot.internal.uploadGroupFile(channelId, url, fileName);
|
|
195
|
+
} else {
|
|
196
|
+
await bot.internal.uploadPrivateFile(channelId, url, fileName);
|
|
197
|
+
}
|
|
198
|
+
logger.info(`机器人 ${bot.selfId} 上传文件到 ${channelId}: ${fileName} (${url})`);
|
|
199
|
+
} else {
|
|
200
|
+
logger.warn(`机器人 ${bot.selfId} 的平台 ${bot.platform} 不支持文件上传功能`);
|
|
201
|
+
}
|
|
202
|
+
} catch (error) {
|
|
203
|
+
logger.error(`上传文件失败: ${error.message}`);
|
|
204
|
+
}
|
|
205
|
+
}, "handleUploadFile");
|
|
206
|
+
const middleware = /* @__PURE__ */ __name(async (session, next) => {
|
|
207
|
+
const messageData = {
|
|
208
|
+
timestamp: session.timestamp,
|
|
209
|
+
userId: session.userId,
|
|
210
|
+
username: session.username,
|
|
211
|
+
channel: session.event.channel,
|
|
212
|
+
guildId: session.guildId,
|
|
213
|
+
platform: session.platform,
|
|
214
|
+
content: session.content,
|
|
215
|
+
messageId: session.messageId,
|
|
216
|
+
botId: session.selfId
|
|
217
|
+
};
|
|
218
|
+
sendMessage(messageData);
|
|
219
|
+
}, "middleware");
|
|
220
|
+
initPushSocket();
|
|
221
|
+
initPullSocket();
|
|
222
|
+
ctx.middleware(middleware, true);
|
|
223
|
+
ctx.on("dispose", () => {
|
|
224
|
+
receiveLoopRunning = false;
|
|
225
|
+
if (pushSocket) {
|
|
226
|
+
try {
|
|
227
|
+
pushSocket.close();
|
|
228
|
+
logger.info("PUSH socket 已关闭");
|
|
229
|
+
} catch (error) {
|
|
230
|
+
logger.warn("关闭PUSH socket失败:", error.message);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (pullSocket) {
|
|
234
|
+
try {
|
|
235
|
+
pullSocket.close();
|
|
236
|
+
logger.info("PULL socket 已关闭");
|
|
237
|
+
} catch (error) {
|
|
238
|
+
logger.warn("关闭PULL socket失败:", error.message);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
logger.info("消息桥接插件已启动");
|
|
243
|
+
logger.info(`发送地址(PUSH): ${config.pushBindAddress}`);
|
|
244
|
+
logger.info(`接收地址(PULL): ${config.pullBindAddress}`);
|
|
245
|
+
}
|
|
246
|
+
__name(apply, "apply");
|
|
247
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
248
|
+
0 && (module.exports = {
|
|
249
|
+
Config,
|
|
250
|
+
apply,
|
|
251
|
+
name
|
|
252
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@maorongwan/koishi-plugin-message-bridge",
|
|
3
|
+
"description": "将聊天消息通过zeromq转发到指定网络位置",
|
|
4
|
+
"version": "1.0.1",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"typings": "lib/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"lib",
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"scripts": {},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"chatbot",
|
|
15
|
+
"koishi",
|
|
16
|
+
"plugin"
|
|
17
|
+
],
|
|
18
|
+
"devDependencies": {},
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"koishi": "^4.18.7",
|
|
21
|
+
"zeromq": "^6.5.0"
|
|
22
|
+
}
|
|
23
|
+
}
|