@sfmc-bds/qq-bridge 0.1.1-beta.3 → 0.2.0-beta.4
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/README.md +14 -3
- package/dist/commands/db-api.js +89 -0
- package/dist/commands/db-api.js.map +7 -0
- package/dist/commands/handlers.js +660 -0
- package/dist/commands/handlers.js.map +7 -0
- package/dist/commands/index.js +55 -0
- package/dist/commands/index.js.map +7 -0
- package/dist/commands/interaction-router.js +149 -0
- package/dist/commands/interaction-router.js.map +7 -0
- package/dist/commands/join-settings-ui.js +108 -0
- package/dist/commands/join-settings-ui.js.map +7 -0
- package/dist/commands/join-settings-ui.test.js +43 -0
- package/dist/commands/join-settings-ui.test.js.map +7 -0
- package/dist/commands/menu-format.js +45 -0
- package/dist/commands/menu-format.js.map +7 -0
- package/dist/commands/menu-panel-sync.js +130 -0
- package/dist/commands/menu-panel-sync.js.map +7 -0
- package/dist/commands/menu-panel-sync.test.js +27 -0
- package/dist/commands/menu-panel-sync.test.js.map +7 -0
- package/dist/commands/pending.js +33 -0
- package/dist/commands/pending.js.map +7 -0
- package/dist/commands/registry.js +40 -0
- package/dist/commands/registry.js.map +7 -0
- package/dist/commands/render.js +46 -0
- package/dist/commands/render.js.map +7 -0
- package/dist/commands/render.test.js +44 -0
- package/dist/commands/render.test.js.map +7 -0
- package/dist/commands/reply-llbot.js +63 -0
- package/dist/commands/reply-llbot.js.map +7 -0
- package/dist/commands/reply-official.js +66 -0
- package/dist/commands/reply-official.js.map +7 -0
- package/dist/commands/router.js +53 -0
- package/dist/commands/router.js.map +7 -0
- package/dist/commands/router.test.js +92 -0
- package/dist/commands/router.test.js.map +7 -0
- package/dist/commands/types.js +1 -0
- package/dist/commands/types.js.map +7 -0
- package/dist/config.js +12 -2
- package/dist/config.js.map +2 -2
- package/dist/console.js +40 -20
- package/dist/console.js.map +2 -2
- package/dist/index.js +117 -20
- package/dist/index.js.map +2 -2
- package/dist/llbot-ws-api.js +90 -0
- package/dist/llbot-ws-api.js.map +7 -0
- package/dist/llbot-ws-api.test.js +32 -0
- package/dist/llbot-ws-api.test.js.map +7 -0
- package/dist/official/events.js +136 -0
- package/dist/official/events.js.map +7 -0
- package/dist/official/events.test.js +123 -0
- package/dist/official/events.test.js.map +7 -0
- package/dist/official/gateway.js +226 -0
- package/dist/official/gateway.js.map +7 -0
- package/dist/onebot.js +23 -5
- package/dist/onebot.js.map +2 -2
- package/dist/project-root.js +2 -2
- package/dist/project-root.js.map +2 -2
- package/dist/runtime-status.js +45 -0
- package/dist/runtime-status.js.map +7 -0
- package/dist/ws-server.js +4 -0
- package/dist/ws-server.js.map +2 -2
- package/package.json +6 -5
package/dist/onebot.js
CHANGED
|
@@ -88,8 +88,10 @@ class OneBotDispatcher {
|
|
|
88
88
|
botSelfId = null;
|
|
89
89
|
opts;
|
|
90
90
|
dedup = new RecentMessageDedup();
|
|
91
|
+
forward;
|
|
91
92
|
constructor(opts) {
|
|
92
93
|
this.opts = opts;
|
|
94
|
+
this.forward = opts.forward ?? tryForward;
|
|
93
95
|
}
|
|
94
96
|
/** 测试/调试用 */
|
|
95
97
|
get selfId() {
|
|
@@ -112,15 +114,31 @@ class OneBotDispatcher {
|
|
|
112
114
|
if (this.dedup.seen(event.message_id)) return;
|
|
113
115
|
const text = extractText(event.message);
|
|
114
116
|
if (!text) return;
|
|
117
|
+
const senderCard = event.sender?.card;
|
|
118
|
+
const senderNick = event.sender?.nickname;
|
|
119
|
+
const fromName = senderCard || senderNick || `QQ_${event.user_id}`;
|
|
120
|
+
const userId = String(event.user_id);
|
|
121
|
+
const fromId = `qq_${userId}`;
|
|
122
|
+
const groupId = String(event.group_id);
|
|
123
|
+
if (this.opts.commandRouter) {
|
|
124
|
+
const role = String(event.sender?.role ?? "").toLowerCase();
|
|
125
|
+
const isGroupAdmin = role === "owner" || role === "admin";
|
|
126
|
+
const inbound = {
|
|
127
|
+
backend: "llbot",
|
|
128
|
+
groupId,
|
|
129
|
+
userId,
|
|
130
|
+
userName: fromName,
|
|
131
|
+
text,
|
|
132
|
+
isGroupAdmin,
|
|
133
|
+
...event.message_id !== void 0 ? { msgId: String(event.message_id) } : {}
|
|
134
|
+
};
|
|
135
|
+
if (await this.opts.commandRouter.handle(inbound)) return;
|
|
136
|
+
}
|
|
115
137
|
if (!this.opts.db.channelId) {
|
|
116
138
|
log.warn("bridge_channel_id \u672A\u914D\u7F6E,\u8DF3\u8FC7 (\u53EF\u7528 reload \u91CD\u8BFB\u914D\u7F6E)");
|
|
117
139
|
return;
|
|
118
140
|
}
|
|
119
|
-
|
|
120
|
-
const senderNick = event.sender?.nickname;
|
|
121
|
-
const fromName = senderCard || senderNick || `QQ_${event.user_id}`;
|
|
122
|
-
const fromId = `qq_${String(event.user_id)}`;
|
|
123
|
-
await tryForward(this.opts.db, fromId, fromName, text);
|
|
141
|
+
await this.forward(this.opts.db, fromId, fromName, text);
|
|
124
142
|
}
|
|
125
143
|
}
|
|
126
144
|
export {
|
package/dist/onebot.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/onebot.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * onebot.ts \u2014 OneBot 11 \u4E8B\u4EF6\u5206\u53D1\n *\n * \u804C\u8D23:\n * 1. \u7EF4\u62A4 botSelfId (\u4ECE lifecycle \u5143\u4E8B\u4EF6\u6355\u83B7)\n * 2. 5s \u77ED\u671F message_id \u53BB\u91CD (\u9632 LLBot \u5076\u53D1\u91CD\u53D1)\n * 3. \u7FA4\u6D88\u606F\u6BB5 \u2192 \u7EAF\u6587\u672C (text / at / image / face / reply / forward / record / video / file)\n * 4. \u547D\u4E2D\u89C4\u5219\u540E\u8C03\u7528 tryForward \u6295\u9012\n *\n * \u884C\u4E3A\u4E0E\u65E7 index.js \u5B8C\u5168\u4E00\u81F4; \u7C7B\u578B\u4E0A\u628A sender/message \u90FD\u6536\u7D27\u4E3A narrow \u7C7B\u578B\u3002\n */\n\nimport { log } from \"./log.js\";\nimport { tryForward, type DBServerConfig } from \"./dbserver.js\";\nimport type {\n OneBotAtSegment,\n OneBotEvent,\n OneBotFaceSegment,\n OneBotFileSegment,\n OneBotForwardSegment,\n OneBotGroupMessageEvent,\n OneBotImageSegment,\n OneBotLifecycleEvent,\n OneBotRecordSegment,\n OneBotReplySegment,\n OneBotSegment,\n OneBotTextSegment,\n OneBotVideoSegment,\n} from \"./types.js\";\n\n// \u2500\u2500 \u7C7B\u578B\u5B88\u536B \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction isGroupMessageEvent(e: OneBotEvent): e is OneBotGroupMessageEvent {\n return e.post_type === \"message\" && e.message_type === \"group\";\n}\n\nfunction isLifecycleEvent(e: OneBotEvent): e is OneBotLifecycleEvent {\n return e.post_type === \"meta_event\" && e.meta_event_type === \"lifecycle\";\n}\n\nfunction isTextSegment(s: OneBotSegment | undefined): s is OneBotTextSegment {\n return s?.type === \"text\";\n}\nfunction isAtSegment(s: OneBotSegment | undefined): s is OneBotAtSegment {\n return s?.type === \"at\";\n}\nfunction isImageSegment(s: OneBotSegment | undefined): s is OneBotImageSegment {\n return s?.type === \"image\";\n}\nfunction isFaceSegment(s: OneBotSegment | undefined): s is OneBotFaceSegment {\n return s?.type === \"face\";\n}\nfunction isReplySegment(s: OneBotSegment | undefined): s is OneBotReplySegment {\n return s?.type === \"reply\";\n}\nfunction isForwardSegment(s: OneBotSegment | undefined): s is OneBotForwardSegment {\n return s?.type === \"forward\";\n}\nfunction isRecordSegment(s: OneBotSegment | undefined): s is OneBotRecordSegment {\n return s?.type === \"record\";\n}\nfunction isVideoSegment(s: OneBotSegment | undefined): s is OneBotVideoSegment {\n return s?.type === \"video\";\n}\nfunction isFileSegment(s: OneBotSegment | undefined): s is OneBotFileSegment {\n return s?.type === \"file\";\n}\n\n// \u2500\u2500 \u6D88\u606F\u6BB5 \u2192 \u7EAF\u6587\u672C \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n/**\n * OneBot 11 \u6D88\u606F\u6BB5\u6570\u7EC4 (\u6216\u7EAF\u5B57\u7B26\u4E32) \u2192 \u7EAF\u6587\u672C\u3002\n * \u884C\u4E3A\u4E0E\u65E7\u5B9E\u73B0\u5B8C\u5168\u4E00\u81F4:\n * - text: \u539F\u6837\u62FC\u63A5\n * - at: \"@\u6635\u79F0 \" \u6216 \"@qq \" (\u6709 name \u4F18\u5148 name)\n * - \u5176\u5B83: [\u56FE\u7247]/[\u8868\u60C5]/[\u56DE\u590D]/[\u8F6C\u53D1]/[\u8BED\u97F3]/[\u89C6\u9891]/[\u6587\u4EF6]\n * - \u672A\u77E5\u6BB5: \u8DF3\u8FC7\n */\nexport function extractText(message: OneBotGroupMessageEvent[\"message\"]): string {\n if (typeof message === \"string\") return message;\n if (!Array.isArray(message)) return \"\";\n const parts: string[] = [];\n for (const seg of message) {\n if (!seg || typeof seg !== \"object\") continue;\n if (isTextSegment(seg)) {\n const text = seg.data.text;\n if (typeof text === \"string\") parts.push(text);\n } else if (isAtSegment(seg)) {\n const name = seg.data.name ? `@${seg.data.name} ` : `@${seg.data.qq ?? \"?\"} `;\n parts.push(name);\n } else if (isImageSegment(seg)) {\n parts.push(\"[\u56FE\u7247]\");\n } else if (isFaceSegment(seg)) {\n parts.push(\"[\u8868\u60C5]\");\n } else if (isReplySegment(seg)) {\n parts.push(\"[\u56DE\u590D]\");\n } else if (isForwardSegment(seg)) {\n parts.push(\"[\u8F6C\u53D1]\");\n } else if (isRecordSegment(seg)) {\n parts.push(\"[\u8BED\u97F3]\");\n } else if (isVideoSegment(seg)) {\n parts.push(\"[\u89C6\u9891]\");\n } else if (isFileSegment(seg)) {\n parts.push(\"[\u6587\u4EF6]\");\n }\n // \u672A\u77E5\u6BB5:\u8DF3\u8FC7\n }\n return parts.join(\"\").trim();\n}\n\n// \u2500\u2500 \u77ED\u671F message_id \u53BB\u91CD (\u61D2\u6E05\u7406) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst DEDUP_WINDOW_MS = 5_000;\nconst CLEANUP_INTERVAL_MS = 1_000;\n\nexport class RecentMessageDedup {\n private readonly map = new Map<string, number>();\n private lastCleanup = 0;\n\n seen(messageId: string | number | undefined): boolean {\n if (messageId === undefined || messageId === null) return false;\n const key = String(messageId);\n const now = Date.now();\n if (now - this.lastCleanup > CLEANUP_INTERVAL_MS) {\n this.lastCleanup = now;\n for (const [k, ts] of this.map) {\n if (now - ts > DEDUP_WINDOW_MS) this.map.delete(k);\n }\n }\n const prev = this.map.get(key);\n if (prev !== undefined && now - prev < DEDUP_WINDOW_MS) return true;\n this.map.set(key, now);\n return false;\n }\n}\n\n// \u2500\u2500 OneBot \u4E8B\u4EF6\u5206\u53D1\u5668 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport interface DispatcherOptions {\n /** \u7FA4 ID \u5B57\u7B26\u4E32,\u7A7A = \u63A5\u6536\u6240\u6709\u7FA4(\u65E7\u884C\u4E3A:\u7A7A\u65F6\u4E0D\u5339\u914D\u4EFB\u4F55\u7FA4,\u4E0D\u4F1A\u89E6\u53D1) */\n qqGroupId: string;\n /** db-server \u914D\u7F6E + \u76EE\u6807 channelId */\n db: DBServerConfig;\n}\n\nexport class OneBotDispatcher {\n private botSelfId: string | null = null;\n private readonly opts: DispatcherOptions;\n private readonly dedup = new RecentMessageDedup();\n\n constructor(opts: DispatcherOptions) {\n this.opts = opts;\n }\n\n /** \u6D4B\u8BD5/\u8C03\u8BD5\u7528 */\n get selfId(): string | null {\n return this.botSelfId;\n }\n\n /** \u6536\u5230\u4E00\u6761\u539F\u59CB JSON \u5B57\u7B26\u4E32(\u5DF2 parse) */\n async handle(rawEvent: unknown): Promise<void> {\n const event = rawEvent as OneBotEvent;\n if (!event || typeof event !== \"object\") return;\n\n if (isLifecycleEvent(event)) {\n if (event.self_id !== undefined && this.botSelfId === null) {\n this.botSelfId = String(event.self_id);\n log.info(`LLBot self_id = ${this.botSelfId}`);\n }\n return;\n }\n\n if (!isGroupMessageEvent(event)) return;\n if (!this.opts.qqGroupId || String(event.group_id) !== this.opts.qqGroupId) return;\n\n // \u5FAA\u73AF\u9632\u62A4 1: \u8DF3\u8FC7 bot \u81EA\u5DF1\u53D1\u7684\u6D88\u606F\u56DE\u58F0\n if (this.botSelfId && String(event.sender?.user_id) === this.botSelfId) return;\n\n // \u5FAA\u73AF\u9632\u62A4 2: \u77ED\u671F\u53BB\u91CD (LLBot \u5076\u53D1\u91CD\u53D1)\n if (this.dedup.seen(event.message_id)) return;\n\n const text = extractText(event.message);\n if (!text) return;\n\n
|
|
5
|
-
"mappings": "AAYA,SAAS,WAAW;
|
|
4
|
+
"sourcesContent": ["/**\n * onebot.ts \u2014 OneBot 11 \u4E8B\u4EF6\u5206\u53D1\n *\n * \u804C\u8D23:\n * 1. \u7EF4\u62A4 botSelfId (\u4ECE lifecycle \u5143\u4E8B\u4EF6\u6355\u83B7)\n * 2. 5s \u77ED\u671F message_id \u53BB\u91CD (\u9632 LLBot \u5076\u53D1\u91CD\u53D1)\n * 3. \u7FA4\u6D88\u606F\u6BB5 \u2192 \u7EAF\u6587\u672C (text / at / image / face / reply / forward / record / video / file)\n * 4. \u547D\u4E2D\u89C4\u5219\u540E\u8C03\u7528 tryForward \u6295\u9012\n *\n * \u884C\u4E3A\u4E0E\u65E7 index.js \u5B8C\u5168\u4E00\u81F4; \u7C7B\u578B\u4E0A\u628A sender/message \u90FD\u6536\u7D27\u4E3A narrow \u7C7B\u578B\u3002\n */\n\nimport { log } from \"./log.js\";\nimport type { CommandRouter } from \"./commands/index.js\";\nimport { tryForward, type DBServerConfig } from \"./dbserver.js\";\nimport type {\n OneBotAtSegment,\n OneBotEvent,\n OneBotFaceSegment,\n OneBotFileSegment,\n OneBotForwardSegment,\n OneBotGroupMessageEvent,\n OneBotImageSegment,\n OneBotLifecycleEvent,\n OneBotRecordSegment,\n OneBotReplySegment,\n OneBotSegment,\n OneBotTextSegment,\n OneBotVideoSegment,\n} from \"./types.js\";\n\n// \u2500\u2500 \u7C7B\u578B\u5B88\u536B \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction isGroupMessageEvent(e: OneBotEvent): e is OneBotGroupMessageEvent {\n return e.post_type === \"message\" && e.message_type === \"group\";\n}\n\nfunction isLifecycleEvent(e: OneBotEvent): e is OneBotLifecycleEvent {\n return e.post_type === \"meta_event\" && e.meta_event_type === \"lifecycle\";\n}\n\nfunction isTextSegment(s: OneBotSegment | undefined): s is OneBotTextSegment {\n return s?.type === \"text\";\n}\nfunction isAtSegment(s: OneBotSegment | undefined): s is OneBotAtSegment {\n return s?.type === \"at\";\n}\nfunction isImageSegment(s: OneBotSegment | undefined): s is OneBotImageSegment {\n return s?.type === \"image\";\n}\nfunction isFaceSegment(s: OneBotSegment | undefined): s is OneBotFaceSegment {\n return s?.type === \"face\";\n}\nfunction isReplySegment(s: OneBotSegment | undefined): s is OneBotReplySegment {\n return s?.type === \"reply\";\n}\nfunction isForwardSegment(s: OneBotSegment | undefined): s is OneBotForwardSegment {\n return s?.type === \"forward\";\n}\nfunction isRecordSegment(s: OneBotSegment | undefined): s is OneBotRecordSegment {\n return s?.type === \"record\";\n}\nfunction isVideoSegment(s: OneBotSegment | undefined): s is OneBotVideoSegment {\n return s?.type === \"video\";\n}\nfunction isFileSegment(s: OneBotSegment | undefined): s is OneBotFileSegment {\n return s?.type === \"file\";\n}\n\n// \u2500\u2500 \u6D88\u606F\u6BB5 \u2192 \u7EAF\u6587\u672C \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n/**\n * OneBot 11 \u6D88\u606F\u6BB5\u6570\u7EC4 (\u6216\u7EAF\u5B57\u7B26\u4E32) \u2192 \u7EAF\u6587\u672C\u3002\n * \u884C\u4E3A\u4E0E\u65E7\u5B9E\u73B0\u5B8C\u5168\u4E00\u81F4:\n * - text: \u539F\u6837\u62FC\u63A5\n * - at: \"@\u6635\u79F0 \" \u6216 \"@qq \" (\u6709 name \u4F18\u5148 name)\n * - \u5176\u5B83: [\u56FE\u7247]/[\u8868\u60C5]/[\u56DE\u590D]/[\u8F6C\u53D1]/[\u8BED\u97F3]/[\u89C6\u9891]/[\u6587\u4EF6]\n * - \u672A\u77E5\u6BB5: \u8DF3\u8FC7\n */\nexport function extractText(message: OneBotGroupMessageEvent[\"message\"]): string {\n if (typeof message === \"string\") return message;\n if (!Array.isArray(message)) return \"\";\n const parts: string[] = [];\n for (const seg of message) {\n if (!seg || typeof seg !== \"object\") continue;\n if (isTextSegment(seg)) {\n const text = seg.data.text;\n if (typeof text === \"string\") parts.push(text);\n } else if (isAtSegment(seg)) {\n const name = seg.data.name ? `@${seg.data.name} ` : `@${seg.data.qq ?? \"?\"} `;\n parts.push(name);\n } else if (isImageSegment(seg)) {\n parts.push(\"[\u56FE\u7247]\");\n } else if (isFaceSegment(seg)) {\n parts.push(\"[\u8868\u60C5]\");\n } else if (isReplySegment(seg)) {\n parts.push(\"[\u56DE\u590D]\");\n } else if (isForwardSegment(seg)) {\n parts.push(\"[\u8F6C\u53D1]\");\n } else if (isRecordSegment(seg)) {\n parts.push(\"[\u8BED\u97F3]\");\n } else if (isVideoSegment(seg)) {\n parts.push(\"[\u89C6\u9891]\");\n } else if (isFileSegment(seg)) {\n parts.push(\"[\u6587\u4EF6]\");\n }\n // \u672A\u77E5\u6BB5:\u8DF3\u8FC7\n }\n return parts.join(\"\").trim();\n}\n\n// \u2500\u2500 \u77ED\u671F message_id \u53BB\u91CD (\u61D2\u6E05\u7406) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst DEDUP_WINDOW_MS = 5_000;\nconst CLEANUP_INTERVAL_MS = 1_000;\n\nexport class RecentMessageDedup {\n private readonly map = new Map<string, number>();\n private lastCleanup = 0;\n\n seen(messageId: string | number | undefined): boolean {\n if (messageId === undefined || messageId === null) return false;\n const key = String(messageId);\n const now = Date.now();\n if (now - this.lastCleanup > CLEANUP_INTERVAL_MS) {\n this.lastCleanup = now;\n for (const [k, ts] of this.map) {\n if (now - ts > DEDUP_WINDOW_MS) this.map.delete(k);\n }\n }\n const prev = this.map.get(key);\n if (prev !== undefined && now - prev < DEDUP_WINDOW_MS) return true;\n this.map.set(key, now);\n return false;\n }\n}\n\n// \u2500\u2500 OneBot \u4E8B\u4EF6\u5206\u53D1\u5668 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport interface DispatcherOptions {\n /** \u7FA4 ID \u5B57\u7B26\u4E32,\u7A7A = \u63A5\u6536\u6240\u6709\u7FA4(\u65E7\u884C\u4E3A:\u7A7A\u65F6\u4E0D\u5339\u914D\u4EFB\u4F55\u7FA4,\u4E0D\u4F1A\u89E6\u53D1) */\n qqGroupId: string;\n /** db-server \u914D\u7F6E + \u76EE\u6807 channelId */\n db: DBServerConfig;\n /** QQ \u4FA7\u6307\u4EE4\u8DEF\u7531\uFF1B\u547D\u4E2D\u5219\u4E0D\u8F6C\u53D1 MC */\n commandRouter?: CommandRouter;\n /** \u53EF\u6CE8\u5165\u8F6C\u53D1\uFF08\u5355\u6D4B\uFF09 */\n forward?: typeof tryForward;\n}\n\nexport class OneBotDispatcher {\n private botSelfId: string | null = null;\n private readonly opts: DispatcherOptions;\n private readonly dedup = new RecentMessageDedup();\n private readonly forward: typeof tryForward;\n\n constructor(opts: DispatcherOptions) {\n this.opts = opts;\n this.forward = opts.forward ?? tryForward;\n }\n\n /** \u6D4B\u8BD5/\u8C03\u8BD5\u7528 */\n get selfId(): string | null {\n return this.botSelfId;\n }\n\n /** \u6536\u5230\u4E00\u6761\u539F\u59CB JSON \u5B57\u7B26\u4E32(\u5DF2 parse) */\n async handle(rawEvent: unknown): Promise<void> {\n const event = rawEvent as OneBotEvent;\n if (!event || typeof event !== \"object\") return;\n\n if (isLifecycleEvent(event)) {\n if (event.self_id !== undefined && this.botSelfId === null) {\n this.botSelfId = String(event.self_id);\n log.info(`LLBot self_id = ${this.botSelfId}`);\n }\n return;\n }\n\n if (!isGroupMessageEvent(event)) return;\n if (!this.opts.qqGroupId || String(event.group_id) !== this.opts.qqGroupId) return;\n\n // \u5FAA\u73AF\u9632\u62A4 1: \u8DF3\u8FC7 bot \u81EA\u5DF1\u53D1\u7684\u6D88\u606F\u56DE\u58F0\n if (this.botSelfId && String(event.sender?.user_id) === this.botSelfId) return;\n\n // \u5FAA\u73AF\u9632\u62A4 2: \u77ED\u671F\u53BB\u91CD (LLBot \u5076\u53D1\u91CD\u53D1)\n if (this.dedup.seen(event.message_id)) return;\n\n const text = extractText(event.message);\n if (!text) return;\n\n const senderCard = event.sender?.card;\n const senderNick = event.sender?.nickname;\n const fromName = senderCard || senderNick || `QQ_${event.user_id}`;\n const userId = String(event.user_id);\n const fromId = `qq_${userId}`;\n const groupId = String(event.group_id);\n\n if (this.opts.commandRouter) {\n const role = String(event.sender?.role ?? \"\").toLowerCase();\n const isGroupAdmin = role === \"owner\" || role === \"admin\";\n const inbound = {\n backend: \"llbot\" as const,\n groupId,\n userId,\n userName: fromName,\n text,\n isGroupAdmin,\n ...(event.message_id !== undefined ? { msgId: String(event.message_id) } : {}),\n };\n if (await this.opts.commandRouter.handle(inbound)) return;\n }\n\n if (!this.opts.db.channelId) {\n log.warn(\"bridge_channel_id \u672A\u914D\u7F6E,\u8DF3\u8FC7 (\u53EF\u7528 reload \u91CD\u8BFB\u914D\u7F6E)\");\n return;\n }\n\n await this.forward(this.opts.db, fromId, fromName, text);\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/project-root.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { resolveRuntimeRoot } from "@sfmc-bds/sdk/node/config";
|
|
2
|
-
import { dirname
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
5
|
-
const PROJECT_ROOT = resolveRuntimeRoot(
|
|
5
|
+
const PROJECT_ROOT = resolveRuntimeRoot(HERE);
|
|
6
6
|
export {
|
|
7
7
|
PROJECT_ROOT
|
|
8
8
|
};
|
package/dist/project-root.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/project-root.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * project-root.ts \u2014 \u4ED3\u6839\u552F\u4E00\u89E3\u6790\u70B9\uFF08DRY/DIP\
|
|
5
|
-
"mappings": "AAIA,SAAS,0BAA0B;AACnC,SAAS,
|
|
4
|
+
"sourcesContent": ["/**\n * project-root.ts \u2014 \u4ED3\u6839\u552F\u4E00\u89E3\u6790\u70B9\uFF08DRY/DIP\uFF1Aenv / log \u52FF\u5404\u81EA\u4E0A\u6EAF\uFF09\n */\n\nimport { resolveRuntimeRoot } from \"@sfmc-bds/sdk/node/config\";\nimport { dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst HERE = dirname(fileURLToPath(import.meta.url));\n\n/** SFMC_ROOT > \u4ECE\u672C\u6587\u4EF6\u5411\u4E0A\u627E sfmc-monorepo */\nexport const PROJECT_ROOT: string = resolveRuntimeRoot(HERE);\n"],
|
|
5
|
+
"mappings": "AAIA,SAAS,0BAA0B;AACnC,SAAS,eAAe;AACxB,SAAS,qBAAqB;AAE9B,MAAM,OAAO,QAAQ,cAAc,YAAY,GAAG,CAAC;AAG5C,MAAM,eAAuB,mBAAmB,IAAI;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
qqRuntimeStatusPath
|
|
3
|
+
} from "@sfmc-bds/sdk/node/config";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { PROJECT_ROOT } from "./project-root.js";
|
|
7
|
+
function writeQqRuntimeStatus(backend) {
|
|
8
|
+
const status = {
|
|
9
|
+
pid: process.pid,
|
|
10
|
+
backend,
|
|
11
|
+
startedAt: Date.now()
|
|
12
|
+
};
|
|
13
|
+
const file = qqRuntimeStatusPath(PROJECT_ROOT);
|
|
14
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
15
|
+
fs.writeFileSync(file, `${JSON.stringify(status, null, 2)}
|
|
16
|
+
`, "utf8");
|
|
17
|
+
}
|
|
18
|
+
function clearQqRuntimeStatus() {
|
|
19
|
+
const file = qqRuntimeStatusPath(PROJECT_ROOT);
|
|
20
|
+
try {
|
|
21
|
+
if (fs.existsSync(file)) fs.unlinkSync(file);
|
|
22
|
+
} catch {
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function installQqRuntimeStatusHooks(backend) {
|
|
26
|
+
writeQqRuntimeStatus(backend);
|
|
27
|
+
const clear = () => {
|
|
28
|
+
clearQqRuntimeStatus();
|
|
29
|
+
};
|
|
30
|
+
process.once("exit", clear);
|
|
31
|
+
process.once("SIGINT", () => {
|
|
32
|
+
clear();
|
|
33
|
+
process.exit(0);
|
|
34
|
+
});
|
|
35
|
+
process.once("SIGTERM", () => {
|
|
36
|
+
clear();
|
|
37
|
+
process.exit(0);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
clearQqRuntimeStatus,
|
|
42
|
+
installQqRuntimeStatusHooks,
|
|
43
|
+
writeQqRuntimeStatus
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=runtime-status.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/runtime-status.ts"],
|
|
4
|
+
"sourcesContent": ["/**\r\n * runtime-status.ts \u2014 \u5199/\u6E05 `.sfmc/qq.runtime.json`\uFF0C\u4F9B CLI \u63A2\u6D4B\u5916\u90E8 qq\r\n */\r\n\r\nimport {\r\n qqRuntimeStatusPath,\r\n type QqRuntimeStatus,\r\n} from \"@sfmc-bds/sdk/node/config\";\r\nimport fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport { PROJECT_ROOT } from \"./project-root.js\";\r\n\r\nexport function writeQqRuntimeStatus(backend: \"official\" | \"llbot\"): void {\r\n const status: QqRuntimeStatus = {\r\n pid: process.pid,\r\n backend,\r\n startedAt: Date.now(),\r\n };\r\n const file = qqRuntimeStatusPath(PROJECT_ROOT);\r\n fs.mkdirSync(path.dirname(file), { recursive: true });\r\n fs.writeFileSync(file, `${JSON.stringify(status, null, 2)}\\n`, \"utf8\");\r\n}\r\n\r\nexport function clearQqRuntimeStatus(): void {\r\n const file = qqRuntimeStatusPath(PROJECT_ROOT);\r\n try {\r\n if (fs.existsSync(file)) fs.unlinkSync(file);\r\n } catch {\r\n /* ignore */\r\n }\r\n}\r\n\r\n/** \u6CE8\u518C\u8FDB\u7A0B\u9000\u51FA\u65F6\u6E05\u7406\u5FC3\u8DF3\u6587\u4EF6 */\r\nexport function installQqRuntimeStatusHooks(backend: \"official\" | \"llbot\"): void {\r\n writeQqRuntimeStatus(backend);\r\n const clear = (): void => {\r\n clearQqRuntimeStatus();\r\n };\r\n process.once(\"exit\", clear);\r\n process.once(\"SIGINT\", () => {\r\n clear();\r\n process.exit(0);\r\n });\r\n process.once(\"SIGTERM\", () => {\r\n clear();\r\n process.exit(0);\r\n });\r\n}\r\n\r"],
|
|
5
|
+
"mappings": "AAIA;AAAA,EACE;AAAA,OAEK;AACP,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,oBAAoB;AAEtB,SAAS,qBAAqB,SAAqC;AACxE,QAAM,SAA0B;AAAA,IAC9B,KAAK,QAAQ;AAAA,IACb;AAAA,IACA,WAAW,KAAK,IAAI;AAAA,EACtB;AACA,QAAM,OAAO,oBAAoB,YAAY;AAC7C,KAAG,UAAU,KAAK,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AACpD,KAAG,cAAc,MAAM,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,GAAM,MAAM;AACvE;AAEO,SAAS,uBAA6B;AAC3C,QAAM,OAAO,oBAAoB,YAAY;AAC7C,MAAI;AACF,QAAI,GAAG,WAAW,IAAI,EAAG,IAAG,WAAW,IAAI;AAAA,EAC7C,QAAQ;AAAA,EAER;AACF;AAGO,SAAS,4BAA4B,SAAqC;AAC/E,uBAAqB,OAAO;AAC5B,QAAM,QAAQ,MAAY;AACxB,yBAAqB;AAAA,EACvB;AACA,UAAQ,KAAK,QAAQ,KAAK;AAC1B,UAAQ,KAAK,UAAU,MAAM;AAC3B,UAAM;AACN,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACD,UAAQ,KAAK,WAAW,MAAM;AAC5B,UAAM;AACN,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACH;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/ws-server.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { WebSocket, WebSocketServer } from "ws";
|
|
3
|
+
import { llbotWsApi } from "./llbot-ws-api.js";
|
|
3
4
|
import { log } from "./log.js";
|
|
4
5
|
const localRequire = createRequire(import.meta.url);
|
|
5
6
|
async function loadWs() {
|
|
@@ -17,16 +18,19 @@ async function startWsServer(opts) {
|
|
|
17
18
|
wss.on("connection", (sock, req) => {
|
|
18
19
|
const path = req.url ?? "/";
|
|
19
20
|
log.info(`LLBot \u5DF2\u8FDE\u63A5 (${path})`);
|
|
21
|
+
llbotWsApi.attach(sock);
|
|
20
22
|
sock.on("message", (raw) => {
|
|
21
23
|
try {
|
|
22
24
|
const text = raw.toString("utf-8");
|
|
23
25
|
const parsed = JSON.parse(text);
|
|
26
|
+
if (llbotWsApi.tryHandleResponse(parsed)) return;
|
|
24
27
|
void opts.dispatcher.handle(parsed);
|
|
25
28
|
} catch (e) {
|
|
26
29
|
log.error(`\u89E3\u6790\u6D88\u606F\u5931\u8D25: ${e.message}`);
|
|
27
30
|
}
|
|
28
31
|
});
|
|
29
32
|
sock.on("close", () => {
|
|
33
|
+
llbotWsApi.detach(sock);
|
|
30
34
|
log.info("LLBot \u5DF2\u65AD\u5F00");
|
|
31
35
|
});
|
|
32
36
|
sock.on("error", (err) => {
|
package/dist/ws-server.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/ws-server.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * ws-server.ts \u2014 WebSocket \u670D\u52A1 (LLBot reverse-ws \u5165\u53E3)\n *\n * \u7AEF\u53E3: 3002 (\u9ED8\u8BA4)\n * \u884C\u4E3A\u4E0E\u65E7\u5B9E\u73B0\u5B8C\u5168\u4E00\u81F4:\n * - \u4F18\u5148\u52A0\u8F7D\u672C\u5730 node_modules/ws\n * - \u52A0\u8F7D\u5931\u8D25\u65F6 fallback \u5230 ../db-server/node_modules/ws\n * - \u6BCF\u4E2A\u8FDE\u63A5\u628A\u6D88\u606F JSON.parse \u540E\u4EA4\u7ED9 dispatcher\n *\n * fallback \u7528 createRequire \u52A0\u8F7D: \u907F\u514D TypeScript \u5728\u7F16\u8BD1\u671F\u68C0\u67E5\n * ../db-server/node_modules/ws \u7684\u76F8\u5BF9\u8DEF\u5F84(\u4E0D\u5728 tsconfig \u5305\u542B\u8303\u56F4)\u3002\n */\n\nimport type { IncomingMessage } from \"node:http\";\nimport { createRequire } from \"node:module\";\nimport { WebSocket, WebSocketServer } from \"ws\";\nimport { log } from \"./log.js\";\nimport type { OneBotDispatcher } from \"./onebot.js\";\n\nexport interface WsServerOptions {\n port: number;\n dispatcher: OneBotDispatcher;\n}\n\nconst localRequire = createRequire(import.meta.url);\n\nasync function loadWs(): Promise<typeof import(\"ws\")> {\n try {\n return localRequire(\"ws\") as typeof import(\"ws\");\n } catch {\n // \u515C\u5E95:\u590D\u7528 db-server \u7684 ws\n return localRequire(\"../db-server/node_modules/ws\") as typeof import(\"ws\");\n }\n}\n\nexport async function startWsServer(opts: WsServerOptions): Promise<WebSocketServer> {\n const ws = await loadWs();\n const { WebSocketServer: WSS } = ws;\n const wss = new WSS({ port: opts.port });\n log.info(`WebSocket \u670D\u52A1\u542F\u52A8 ws://0.0.0.0:${opts.port}`);\n\n wss.on(\"connection\", (sock: WebSocket, req: IncomingMessage) => {\n const path = req.url ?? \"/\";\n log.info(`LLBot \u5DF2\u8FDE\u63A5 (${path})`);\n sock.on(\"message\", (raw) => {\n try {\n const text = raw.toString(\"utf-8\");\n const parsed: unknown = JSON.parse(text);\n // \u5F02\u6B65\u4F46\u4E0D\u963B\u585E socket \u5FAA\u73AF\n void opts.dispatcher.handle(parsed);\n } catch (e) {\n log.error(`\u89E3\u6790\u6D88\u606F\u5931\u8D25: ${(e as Error).message}`);\n }\n });\n sock.on(\"close\", () => {\n log.info(\"LLBot \u5DF2\u65AD\u5F00\");\n });\n sock.on(\"error\", (err) => {\n log.error(`WebSocket \u8FDE\u63A5\u9519\u8BEF: ${err.message}`);\n });\n });\n\n wss.on(\"error\", (err) => {\n log.error(`WebSocket \u670D\u52A1\u5668\u9519\u8BEF: ${err.message}`);\n });\n\n return wss;\n}\n"],
|
|
5
|
-
"mappings": "AAcA,SAAS,qBAAqB;AAC9B,SAAS,WAAW,uBAAuB;AAC3C,SAAS,WAAW;AAQpB,MAAM,eAAe,cAAc,YAAY,GAAG;AAElD,eAAe,SAAuC;AACpD,MAAI;AACF,WAAO,aAAa,IAAI;AAAA,EAC1B,QAAQ;AAEN,WAAO,aAAa,8BAA8B;AAAA,EACpD;AACF;AAEA,eAAsB,cAAc,MAAiD;AACnF,QAAM,KAAK,MAAM,OAAO;AACxB,QAAM,EAAE,iBAAiB,IAAI,IAAI;AACjC,QAAM,MAAM,IAAI,IAAI,EAAE,MAAM,KAAK,KAAK,CAAC;AACvC,MAAI,KAAK,mDAA+B,KAAK,IAAI,EAAE;AAEnD,MAAI,GAAG,cAAc,CAAC,MAAiB,QAAyB;AAC9D,UAAM,OAAO,IAAI,OAAO;AACxB,QAAI,KAAK,6BAAc,IAAI,GAAG;AAC9B,SAAK,GAAG,WAAW,CAAC,QAAQ;AAC1B,UAAI;AACF,cAAM,OAAO,IAAI,SAAS,OAAO;AACjC,cAAM,SAAkB,KAAK,MAAM,IAAI;AAEvC,aAAK,KAAK,WAAW,OAAO,MAAM;AAAA,MACpC,SAAS,GAAG;AACV,YAAI,MAAM,yCAAY,EAAY,OAAO,EAAE;AAAA,MAC7C;AAAA,IACF,CAAC;AACD,SAAK,GAAG,SAAS,MAAM;AACrB,UAAI,KAAK,0BAAW;AAAA,IACtB,CAAC;AACD,SAAK,GAAG,SAAS,CAAC,QAAQ;AACxB,UAAI,MAAM,uCAAmB,IAAI,OAAO,EAAE;AAAA,IAC5C,CAAC;AAAA,EACH,CAAC;AAED,MAAI,GAAG,SAAS,CAAC,QAAQ;AACvB,QAAI,MAAM,6CAAoB,IAAI,OAAO,EAAE;AAAA,EAC7C,CAAC;AAED,SAAO;AACT;",
|
|
4
|
+
"sourcesContent": ["/**\n * ws-server.ts \u2014 WebSocket \u670D\u52A1 (LLBot reverse-ws \u5165\u53E3)\n *\n * \u7AEF\u53E3: 3002 (\u9ED8\u8BA4)\n * \u884C\u4E3A\u4E0E\u65E7\u5B9E\u73B0\u5B8C\u5168\u4E00\u81F4:\n * - \u4F18\u5148\u52A0\u8F7D\u672C\u5730 node_modules/ws\n * - \u52A0\u8F7D\u5931\u8D25\u65F6 fallback \u5230 ../db-server/node_modules/ws\n * - \u6BCF\u4E2A\u8FDE\u63A5\u628A\u6D88\u606F JSON.parse \u540E\u4EA4\u7ED9 dispatcher\n *\n * fallback \u7528 createRequire \u52A0\u8F7D: \u907F\u514D TypeScript \u5728\u7F16\u8BD1\u671F\u68C0\u67E5\n * ../db-server/node_modules/ws \u7684\u76F8\u5BF9\u8DEF\u5F84(\u4E0D\u5728 tsconfig \u5305\u542B\u8303\u56F4)\u3002\n */\n\nimport type { IncomingMessage } from \"node:http\";\nimport { createRequire } from \"node:module\";\nimport { WebSocket, WebSocketServer } from \"ws\";\nimport { llbotWsApi } from \"./llbot-ws-api.js\";\nimport { log } from \"./log.js\";\nimport type { OneBotDispatcher } from \"./onebot.js\";\n\nexport interface WsServerOptions {\n port: number;\n dispatcher: OneBotDispatcher;\n}\n\nconst localRequire = createRequire(import.meta.url);\n\nasync function loadWs(): Promise<typeof import(\"ws\")> {\n try {\n return localRequire(\"ws\") as typeof import(\"ws\");\n } catch {\n // \u515C\u5E95:\u590D\u7528 db-server \u7684 ws\n return localRequire(\"../db-server/node_modules/ws\") as typeof import(\"ws\");\n }\n}\n\nexport async function startWsServer(opts: WsServerOptions): Promise<WebSocketServer> {\n const ws = await loadWs();\n const { WebSocketServer: WSS } = ws;\n const wss = new WSS({ port: opts.port });\n log.info(`WebSocket \u670D\u52A1\u542F\u52A8 ws://0.0.0.0:${opts.port}`);\n\n wss.on(\"connection\", (sock: WebSocket, req: IncomingMessage) => {\n const path = req.url ?? \"/\";\n log.info(`LLBot \u5DF2\u8FDE\u63A5 (${path})`);\n llbotWsApi.attach(sock);\n sock.on(\"message\", (raw) => {\n try {\n const text = raw.toString(\"utf-8\");\n const parsed: unknown = JSON.parse(text);\n // API \u56DE\u5305\uFF08echo\uFF09\u4F18\u5148\u6D88\u8D39\uFF0C\u907F\u514D\u8BEF\u5165\u4E8B\u4EF6\u5206\u53D1\n if (llbotWsApi.tryHandleResponse(parsed)) return;\n // \u5F02\u6B65\u4F46\u4E0D\u963B\u585E socket \u5FAA\u73AF\n void opts.dispatcher.handle(parsed);\n } catch (e) {\n log.error(`\u89E3\u6790\u6D88\u606F\u5931\u8D25: ${(e as Error).message}`);\n }\n });\n sock.on(\"close\", () => {\n llbotWsApi.detach(sock);\n log.info(\"LLBot \u5DF2\u65AD\u5F00\");\n });\n sock.on(\"error\", (err) => {\n log.error(`WebSocket \u8FDE\u63A5\u9519\u8BEF: ${err.message}`);\n });\n });\n\n wss.on(\"error\", (err) => {\n log.error(`WebSocket \u670D\u52A1\u5668\u9519\u8BEF: ${err.message}`);\n });\n\n return wss;\n}\n"],
|
|
5
|
+
"mappings": "AAcA,SAAS,qBAAqB;AAC9B,SAAS,WAAW,uBAAuB;AAC3C,SAAS,kBAAkB;AAC3B,SAAS,WAAW;AAQpB,MAAM,eAAe,cAAc,YAAY,GAAG;AAElD,eAAe,SAAuC;AACpD,MAAI;AACF,WAAO,aAAa,IAAI;AAAA,EAC1B,QAAQ;AAEN,WAAO,aAAa,8BAA8B;AAAA,EACpD;AACF;AAEA,eAAsB,cAAc,MAAiD;AACnF,QAAM,KAAK,MAAM,OAAO;AACxB,QAAM,EAAE,iBAAiB,IAAI,IAAI;AACjC,QAAM,MAAM,IAAI,IAAI,EAAE,MAAM,KAAK,KAAK,CAAC;AACvC,MAAI,KAAK,mDAA+B,KAAK,IAAI,EAAE;AAEnD,MAAI,GAAG,cAAc,CAAC,MAAiB,QAAyB;AAC9D,UAAM,OAAO,IAAI,OAAO;AACxB,QAAI,KAAK,6BAAc,IAAI,GAAG;AAC9B,eAAW,OAAO,IAAI;AACtB,SAAK,GAAG,WAAW,CAAC,QAAQ;AAC1B,UAAI;AACF,cAAM,OAAO,IAAI,SAAS,OAAO;AACjC,cAAM,SAAkB,KAAK,MAAM,IAAI;AAEvC,YAAI,WAAW,kBAAkB,MAAM,EAAG;AAE1C,aAAK,KAAK,WAAW,OAAO,MAAM;AAAA,MACpC,SAAS,GAAG;AACV,YAAI,MAAM,yCAAY,EAAY,OAAO,EAAE;AAAA,MAC7C;AAAA,IACF,CAAC;AACD,SAAK,GAAG,SAAS,MAAM;AACrB,iBAAW,OAAO,IAAI;AACtB,UAAI,KAAK,0BAAW;AAAA,IACtB,CAAC;AACD,SAAK,GAAG,SAAS,CAAC,QAAQ;AACxB,UAAI,MAAM,uCAAmB,IAAI,OAAO,EAAE;AAAA,IAC5C,CAAC;AAAA,EACH,CAAC;AAED,MAAI,GAAG,SAAS,CAAC,QAAQ;AACvB,QAAI,MAAM,6CAAoB,IAAI,OAAO,EAAE;AAAA,EAC7C,CAAC;AAED,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sfmc-bds/qq-bridge",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "SFMC QQ ↔ MC bridge —
|
|
3
|
+
"version": "0.2.0-beta.4",
|
|
4
|
+
"description": "SFMC QQ ↔ MC bridge — official QQ Open Platform Bot and/or LLBot OneBot 11",
|
|
5
5
|
"author": "Shiroha7z",
|
|
6
6
|
"license": "ISC",
|
|
7
7
|
"type": "module",
|
|
8
|
-
"homepage": "https://github.com/DogeLakeDev/ScriptsForMinecraftServer/tree/main/qq-bridge#readme",
|
|
8
|
+
"homepage": "https://github.com/DogeLakeDev/ScriptsForMinecraftServer/tree/main/packages/qq-bridge#readme",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
11
|
"url": "git+https://github.com/DogeLakeDev/ScriptsForMinecraftServer.git",
|
|
12
|
-
"directory": "qq-bridge"
|
|
12
|
+
"directory": "packages/qq-bridge"
|
|
13
13
|
},
|
|
14
14
|
"bugs": {
|
|
15
15
|
"url": "https://github.com/DogeLakeDev/ScriptsForMinecraftServer/issues"
|
|
@@ -37,10 +37,11 @@
|
|
|
37
37
|
"format": "prettier --write \"src/**/*.{ts,tsx,js,json}\"",
|
|
38
38
|
"prepublishOnly": "npm run build",
|
|
39
39
|
"start": "node dist/index.js",
|
|
40
|
+
"test": "npm run build && node --test dist/official/events.test.js dist/commands/router.test.js dist/commands/render.test.js dist/commands/menu-panel-sync.test.js dist/commands/join-settings-ui.test.js dist/llbot-ws-api.test.js",
|
|
40
41
|
"typecheck": "tsc7 --noEmit"
|
|
41
42
|
},
|
|
42
43
|
"dependencies": {
|
|
43
|
-
"@sfmc-bds/sdk": "^0.2.0-beta.
|
|
44
|
+
"@sfmc-bds/sdk": "^0.2.0-beta.9",
|
|
44
45
|
"ws": "^8.21.1"
|
|
45
46
|
},
|
|
46
47
|
"publishConfig": {
|