@sfmc-bds/qq-bridge 0.1.0 → 0.1.1-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 +1 -1
- package/dist/config.js +49 -53
- package/dist/config.js.map +7 -1
- package/dist/console.js +59 -65
- package/dist/console.js.map +7 -1
- package/dist/dbserver.js +65 -76
- package/dist/dbserver.js.map +7 -1
- package/dist/index.js +30 -47
- package/dist/index.js.map +7 -1
- package/dist/log.js +10 -14
- package/dist/log.js.map +7 -1
- package/dist/onebot.js +100 -141
- package/dist/onebot.js.map +7 -1
- package/dist/project-root.js +9 -0
- package/dist/project-root.js.map +7 -0
- package/dist/types.js +1 -8
- package/dist/types.js.map +7 -1
- package/dist/ws-server.js +35 -48
- package/dist/ws-server.js.map +7 -1
- package/package.json +15 -15
- package/dist/config.d.ts +0 -20
- package/dist/config.d.ts.map +0 -1
- package/dist/console.d.ts +0 -22
- package/dist/console.d.ts.map +0 -1
- package/dist/dbserver.d.ts +0 -19
- package/dist/dbserver.d.ts.map +0 -1
- package/dist/index.d.ts +0 -20
- package/dist/index.d.ts.map +0 -1
- package/dist/log.d.ts +0 -11
- package/dist/log.d.ts.map +0 -1
- package/dist/onebot.d.ts +0 -44
- package/dist/onebot.d.ts.map +0 -1
- package/dist/types.d.ts +0 -124
- package/dist/types.d.ts.map +0 -1
- package/dist/ws-server.d.ts +0 -20
- package/dist/ws-server.d.ts.map +0 -1
package/dist/onebot.js
CHANGED
|
@@ -1,172 +1,131 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* onebot.ts — OneBot 11 事件分发
|
|
3
|
-
*
|
|
4
|
-
* 职责:
|
|
5
|
-
* 1. 维护 botSelfId (从 lifecycle 元事件捕获)
|
|
6
|
-
* 2. 5s 短期 message_id 去重 (防 LLBot 偶发重发)
|
|
7
|
-
* 3. 群消息段 → 纯文本 (text / at / image / face / reply / forward / record / video / file)
|
|
8
|
-
* 4. 命中规则后调用 tryForward 投递
|
|
9
|
-
*
|
|
10
|
-
* 行为与旧 index.js 完全一致; 类型上把 sender/message 都收紧为 narrow 类型。
|
|
11
|
-
*/
|
|
12
1
|
import { log } from "./log.js";
|
|
13
2
|
import { tryForward } from "./dbserver.js";
|
|
14
|
-
// ── 类型守卫 ────────────────────────────────────────────────────
|
|
15
3
|
function isGroupMessageEvent(e) {
|
|
16
|
-
|
|
4
|
+
return e.post_type === "message" && e.message_type === "group";
|
|
17
5
|
}
|
|
18
6
|
function isLifecycleEvent(e) {
|
|
19
|
-
|
|
7
|
+
return e.post_type === "meta_event" && e.meta_event_type === "lifecycle";
|
|
20
8
|
}
|
|
21
9
|
function isTextSegment(s) {
|
|
22
|
-
|
|
10
|
+
return s?.type === "text";
|
|
23
11
|
}
|
|
24
12
|
function isAtSegment(s) {
|
|
25
|
-
|
|
13
|
+
return s?.type === "at";
|
|
26
14
|
}
|
|
27
15
|
function isImageSegment(s) {
|
|
28
|
-
|
|
16
|
+
return s?.type === "image";
|
|
29
17
|
}
|
|
30
18
|
function isFaceSegment(s) {
|
|
31
|
-
|
|
19
|
+
return s?.type === "face";
|
|
32
20
|
}
|
|
33
21
|
function isReplySegment(s) {
|
|
34
|
-
|
|
22
|
+
return s?.type === "reply";
|
|
35
23
|
}
|
|
36
24
|
function isForwardSegment(s) {
|
|
37
|
-
|
|
25
|
+
return s?.type === "forward";
|
|
38
26
|
}
|
|
39
27
|
function isRecordSegment(s) {
|
|
40
|
-
|
|
28
|
+
return s?.type === "record";
|
|
41
29
|
}
|
|
42
30
|
function isVideoSegment(s) {
|
|
43
|
-
|
|
31
|
+
return s?.type === "video";
|
|
44
32
|
}
|
|
45
33
|
function isFileSegment(s) {
|
|
46
|
-
|
|
34
|
+
return s?.type === "file";
|
|
47
35
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
else if (isImageSegment(seg)) {
|
|
76
|
-
parts.push("[图片]");
|
|
77
|
-
}
|
|
78
|
-
else if (isFaceSegment(seg)) {
|
|
79
|
-
parts.push("[表情]");
|
|
80
|
-
}
|
|
81
|
-
else if (isReplySegment(seg)) {
|
|
82
|
-
parts.push("[回复]");
|
|
83
|
-
}
|
|
84
|
-
else if (isForwardSegment(seg)) {
|
|
85
|
-
parts.push("[转发]");
|
|
86
|
-
}
|
|
87
|
-
else if (isRecordSegment(seg)) {
|
|
88
|
-
parts.push("[语音]");
|
|
89
|
-
}
|
|
90
|
-
else if (isVideoSegment(seg)) {
|
|
91
|
-
parts.push("[视频]");
|
|
92
|
-
}
|
|
93
|
-
else if (isFileSegment(seg)) {
|
|
94
|
-
parts.push("[文件]");
|
|
95
|
-
}
|
|
96
|
-
// 未知段:跳过
|
|
36
|
+
function extractText(message) {
|
|
37
|
+
if (typeof message === "string") return message;
|
|
38
|
+
if (!Array.isArray(message)) return "";
|
|
39
|
+
const parts = [];
|
|
40
|
+
for (const seg of message) {
|
|
41
|
+
if (!seg || typeof seg !== "object") continue;
|
|
42
|
+
if (isTextSegment(seg)) {
|
|
43
|
+
const text = seg.data.text;
|
|
44
|
+
if (typeof text === "string") parts.push(text);
|
|
45
|
+
} else if (isAtSegment(seg)) {
|
|
46
|
+
const name = seg.data.name ? `@${seg.data.name} ` : `@${seg.data.qq ?? "?"} `;
|
|
47
|
+
parts.push(name);
|
|
48
|
+
} else if (isImageSegment(seg)) {
|
|
49
|
+
parts.push("[\u56FE\u7247]");
|
|
50
|
+
} else if (isFaceSegment(seg)) {
|
|
51
|
+
parts.push("[\u8868\u60C5]");
|
|
52
|
+
} else if (isReplySegment(seg)) {
|
|
53
|
+
parts.push("[\u56DE\u590D]");
|
|
54
|
+
} else if (isForwardSegment(seg)) {
|
|
55
|
+
parts.push("[\u8F6C\u53D1]");
|
|
56
|
+
} else if (isRecordSegment(seg)) {
|
|
57
|
+
parts.push("[\u8BED\u97F3]");
|
|
58
|
+
} else if (isVideoSegment(seg)) {
|
|
59
|
+
parts.push("[\u89C6\u9891]");
|
|
60
|
+
} else if (isFileSegment(seg)) {
|
|
61
|
+
parts.push("[\u6587\u4EF6]");
|
|
97
62
|
}
|
|
98
|
-
|
|
63
|
+
}
|
|
64
|
+
return parts.join("").trim();
|
|
99
65
|
}
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (now - ts > DEDUP_WINDOW_MS)
|
|
115
|
-
this.map.delete(k);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
const prev = this.map.get(key);
|
|
119
|
-
if (prev !== undefined && now - prev < DEDUP_WINDOW_MS)
|
|
120
|
-
return true;
|
|
121
|
-
this.map.set(key, now);
|
|
122
|
-
return false;
|
|
66
|
+
const DEDUP_WINDOW_MS = 5e3;
|
|
67
|
+
const CLEANUP_INTERVAL_MS = 1e3;
|
|
68
|
+
class RecentMessageDedup {
|
|
69
|
+
map = /* @__PURE__ */ new Map();
|
|
70
|
+
lastCleanup = 0;
|
|
71
|
+
seen(messageId) {
|
|
72
|
+
if (messageId === void 0 || messageId === null) return false;
|
|
73
|
+
const key = String(messageId);
|
|
74
|
+
const now = Date.now();
|
|
75
|
+
if (now - this.lastCleanup > CLEANUP_INTERVAL_MS) {
|
|
76
|
+
this.lastCleanup = now;
|
|
77
|
+
for (const [k, ts] of this.map) {
|
|
78
|
+
if (now - ts > DEDUP_WINDOW_MS) this.map.delete(k);
|
|
79
|
+
}
|
|
123
80
|
}
|
|
81
|
+
const prev = this.map.get(key);
|
|
82
|
+
if (prev !== void 0 && now - prev < DEDUP_WINDOW_MS) return true;
|
|
83
|
+
this.map.set(key, now);
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
124
86
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
87
|
+
class OneBotDispatcher {
|
|
88
|
+
botSelfId = null;
|
|
89
|
+
opts;
|
|
90
|
+
dedup = new RecentMessageDedup();
|
|
91
|
+
constructor(opts) {
|
|
92
|
+
this.opts = opts;
|
|
93
|
+
}
|
|
94
|
+
/** 测试/调试用 */
|
|
95
|
+
get selfId() {
|
|
96
|
+
return this.botSelfId;
|
|
97
|
+
}
|
|
98
|
+
/** 收到一条原始 JSON 字符串(已 parse) */
|
|
99
|
+
async handle(rawEvent) {
|
|
100
|
+
const event = rawEvent;
|
|
101
|
+
if (!event || typeof event !== "object") return;
|
|
102
|
+
if (isLifecycleEvent(event)) {
|
|
103
|
+
if (event.self_id !== void 0 && this.botSelfId === null) {
|
|
104
|
+
this.botSelfId = String(event.self_id);
|
|
105
|
+
log.info(`LLBot self_id = ${this.botSelfId}`);
|
|
106
|
+
}
|
|
107
|
+
return;
|
|
131
108
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (isLifecycleEvent(event)) {
|
|
142
|
-
if (event.self_id !== undefined && this.botSelfId === null) {
|
|
143
|
-
this.botSelfId = String(event.self_id);
|
|
144
|
-
log.info(`LLBot self_id = ${this.botSelfId}`);
|
|
145
|
-
}
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
if (!isGroupMessageEvent(event))
|
|
149
|
-
return;
|
|
150
|
-
if (!this.opts.qqGroupId || String(event.group_id) !== this.opts.qqGroupId)
|
|
151
|
-
return;
|
|
152
|
-
// 循环防护 1: 跳过 bot 自己发的消息回声
|
|
153
|
-
if (this.botSelfId && String(event.sender?.user_id) === this.botSelfId)
|
|
154
|
-
return;
|
|
155
|
-
// 循环防护 2: 短期去重 (LLBot 偶发重发)
|
|
156
|
-
if (this.dedup.seen(event.message_id))
|
|
157
|
-
return;
|
|
158
|
-
const text = extractText(event.message);
|
|
159
|
-
if (!text)
|
|
160
|
-
return;
|
|
161
|
-
if (!this.opts.db.channelId) {
|
|
162
|
-
log.warn("bridge_channel_id 未配置,跳过 (可用 reload 重读配置)");
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
const senderCard = event.sender?.card;
|
|
166
|
-
const senderNick = event.sender?.nickname;
|
|
167
|
-
const fromName = senderCard || senderNick || `QQ_${event.user_id}`;
|
|
168
|
-
const fromId = `qq_${String(event.user_id)}`;
|
|
169
|
-
await tryForward(this.opts.db, fromId, fromName, text);
|
|
109
|
+
if (!isGroupMessageEvent(event)) return;
|
|
110
|
+
if (!this.opts.qqGroupId || String(event.group_id) !== this.opts.qqGroupId) return;
|
|
111
|
+
if (this.botSelfId && String(event.sender?.user_id) === this.botSelfId) return;
|
|
112
|
+
if (this.dedup.seen(event.message_id)) return;
|
|
113
|
+
const text = extractText(event.message);
|
|
114
|
+
if (!text) return;
|
|
115
|
+
if (!this.opts.db.channelId) {
|
|
116
|
+
log.warn("bridge_channel_id \u672A\u914D\u7F6E,\u8DF3\u8FC7 (\u53EF\u7528 reload \u91CD\u8BFB\u914D\u7F6E)");
|
|
117
|
+
return;
|
|
170
118
|
}
|
|
119
|
+
const senderCard = event.sender?.card;
|
|
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);
|
|
124
|
+
}
|
|
171
125
|
}
|
|
172
|
-
|
|
126
|
+
export {
|
|
127
|
+
OneBotDispatcher,
|
|
128
|
+
RecentMessageDedup,
|
|
129
|
+
extractText
|
|
130
|
+
};
|
|
131
|
+
//# sourceMappingURL=onebot.js.map
|
package/dist/onebot.js.map
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"version": 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 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 const senderCard = event.sender?.card;\n const senderNick = event.sender?.nickname;\n const fromName = senderCard || senderNick || `QQ_${event.user_id}`;\n const fromId = `qq_${String(event.user_id)}`;\n\n await tryForward(this.opts.db, fromId, fromName, text);\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { resolveRuntimeRoot } from "@sfmc-bds/sdk/node/config";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
const PROJECT_ROOT = resolveRuntimeRoot(resolve(HERE, "..", ".."));
|
|
6
|
+
export {
|
|
7
|
+
PROJECT_ROOT
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=project-root.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/project-root.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * project-root.ts \u2014 \u4ED3\u6839\u552F\u4E00\u89E3\u6790\u70B9\uFF08DRY/DIP\uFF1Aconfig / log \u52FF\u5404\u81EA\u4E0A\u6EAF\uFF09\n */\n\nimport { resolveRuntimeRoot } from \"@sfmc-bds/sdk/node/config\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst HERE = dirname(fileURLToPath(import.meta.url));\n\n/** SFMC_ROOT > \u76F8\u5BF9 qq-bridge/src \u4E0A\u6EAF\u4E24\u7EA7 */\nexport const PROJECT_ROOT: string = resolveRuntimeRoot(resolve(HERE, \"..\", \"..\"));\n"],
|
|
5
|
+
"mappings": "AAIA,SAAS,0BAA0B;AACnC,SAAS,SAAS,eAAe;AACjC,SAAS,qBAAqB;AAE9B,MAAM,OAAO,QAAQ,cAAc,YAAY,GAAG,CAAC;AAG5C,MAAM,eAAuB,mBAAmB,QAAQ,MAAM,MAAM,IAAI,CAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/types.js
CHANGED
package/dist/types.js.map
CHANGED
package/dist/ws-server.js
CHANGED
|
@@ -1,57 +1,44 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ws-server.ts — WebSocket 服务 (LLBot reverse-ws 入口)
|
|
3
|
-
*
|
|
4
|
-
* 端口: 3002 (默认)
|
|
5
|
-
* 行为与旧实现完全一致:
|
|
6
|
-
* - 优先加载本地 node_modules/ws
|
|
7
|
-
* - 加载失败时 fallback 到 ../db-server/node_modules/ws
|
|
8
|
-
* - 每个连接把消息 JSON.parse 后交给 dispatcher
|
|
9
|
-
*
|
|
10
|
-
* fallback 用 createRequire 加载: 避免 TypeScript 在编译期检查
|
|
11
|
-
* ../db-server/node_modules/ws 的相对路径(不在 tsconfig 包含范围)。
|
|
12
|
-
*/
|
|
13
1
|
import { createRequire } from "node:module";
|
|
14
2
|
import { WebSocket, WebSocketServer } from "ws";
|
|
15
3
|
import { log } from "./log.js";
|
|
16
4
|
const localRequire = createRequire(import.meta.url);
|
|
17
5
|
async function loadWs() {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return localRequire("../db-server/node_modules/ws");
|
|
24
|
-
}
|
|
6
|
+
try {
|
|
7
|
+
return localRequire("ws");
|
|
8
|
+
} catch {
|
|
9
|
+
return localRequire("../db-server/node_modules/ws");
|
|
10
|
+
}
|
|
25
11
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
log.error(`解析消息失败: ${e.message}`);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
sock.on("close", () => {
|
|
46
|
-
log.info("LLBot 已断开");
|
|
47
|
-
});
|
|
48
|
-
sock.on("error", (err) => {
|
|
49
|
-
log.error(`WebSocket 连接错误: ${err.message}`);
|
|
50
|
-
});
|
|
12
|
+
async function startWsServer(opts) {
|
|
13
|
+
const ws = await loadWs();
|
|
14
|
+
const { WebSocketServer: WSS } = ws;
|
|
15
|
+
const wss = new WSS({ port: opts.port });
|
|
16
|
+
log.info(`WebSocket \u670D\u52A1\u542F\u52A8 ws://0.0.0.0:${opts.port}`);
|
|
17
|
+
wss.on("connection", (sock, req) => {
|
|
18
|
+
const path = req.url ?? "/";
|
|
19
|
+
log.info(`LLBot \u5DF2\u8FDE\u63A5 (${path})`);
|
|
20
|
+
sock.on("message", (raw) => {
|
|
21
|
+
try {
|
|
22
|
+
const text = raw.toString("utf-8");
|
|
23
|
+
const parsed = JSON.parse(text);
|
|
24
|
+
void opts.dispatcher.handle(parsed);
|
|
25
|
+
} catch (e) {
|
|
26
|
+
log.error(`\u89E3\u6790\u6D88\u606F\u5931\u8D25: ${e.message}`);
|
|
27
|
+
}
|
|
51
28
|
});
|
|
52
|
-
|
|
53
|
-
|
|
29
|
+
sock.on("close", () => {
|
|
30
|
+
log.info("LLBot \u5DF2\u65AD\u5F00");
|
|
54
31
|
});
|
|
55
|
-
|
|
32
|
+
sock.on("error", (err) => {
|
|
33
|
+
log.error(`WebSocket \u8FDE\u63A5\u9519\u8BEF: ${err.message}`);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
wss.on("error", (err) => {
|
|
37
|
+
log.error(`WebSocket \u670D\u52A1\u5668\u9519\u8BEF: ${err.message}`);
|
|
38
|
+
});
|
|
39
|
+
return wss;
|
|
56
40
|
}
|
|
57
|
-
|
|
41
|
+
export {
|
|
42
|
+
startWsServer
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=ws-server.js.map
|
package/dist/ws-server.js.map
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"version": 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;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sfmc-bds/qq-bridge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1-beta.4",
|
|
4
4
|
"description": "SFMC QQ ↔ MC bridge — LLBot OneBot 11 reverse-WS on port 3002, forwards events to db-server",
|
|
5
5
|
"author": "Shiroha7z",
|
|
6
|
-
"type": "module",
|
|
7
6
|
"license": "ISC",
|
|
7
|
+
"type": "module",
|
|
8
8
|
"homepage": "https://github.com/DogeLakeDev/ScriptsForMinecraftServer/tree/main/qq-bridge#readme",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
@@ -15,34 +15,34 @@
|
|
|
15
15
|
"url": "https://github.com/DogeLakeDev/ScriptsForMinecraftServer/issues"
|
|
16
16
|
},
|
|
17
17
|
"keywords": [
|
|
18
|
+
"llbot",
|
|
18
19
|
"minecraft-bedrock",
|
|
19
|
-
"sfmc",
|
|
20
|
-
"qq",
|
|
21
20
|
"onebot",
|
|
22
|
-
"
|
|
23
|
-
"scriptsforminecraftserver"
|
|
21
|
+
"qq",
|
|
22
|
+
"scriptsforminecraftserver",
|
|
23
|
+
"sfmc"
|
|
24
24
|
],
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=22.13.0"
|
|
27
|
+
},
|
|
25
28
|
"main": "dist/index.js",
|
|
26
29
|
"files": [
|
|
27
30
|
"dist",
|
|
28
31
|
"README.md"
|
|
29
32
|
],
|
|
30
33
|
"scripts": {
|
|
31
|
-
"
|
|
32
|
-
"dev": "tsx src/index.ts",
|
|
33
|
-
"build": "tsc -p tsconfig.json",
|
|
34
|
-
"typecheck": "tsc --noEmit",
|
|
34
|
+
"build": "sfmc-esbuild-transpile",
|
|
35
35
|
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
36
|
+
"dev": "tsx src/index.ts",
|
|
36
37
|
"format": "prettier --write \"src/**/*.{ts,tsx,js,json}\"",
|
|
37
|
-
"prepublishOnly": "npm run build"
|
|
38
|
+
"prepublishOnly": "npm run build",
|
|
39
|
+
"start": "node dist/index.js",
|
|
40
|
+
"typecheck": "tsc7 --noEmit"
|
|
38
41
|
},
|
|
39
42
|
"dependencies": {
|
|
40
|
-
"@sfmc-bds/sdk": "^0.
|
|
43
|
+
"@sfmc-bds/sdk": "^0.2.0-beta.6",
|
|
41
44
|
"ws": "^8.21.1"
|
|
42
45
|
},
|
|
43
|
-
"engines": {
|
|
44
|
-
"node": ">=22.13.0"
|
|
45
|
-
},
|
|
46
46
|
"publishConfig": {
|
|
47
47
|
"access": "public",
|
|
48
48
|
"registry": "https://registry.npmjs.org/"
|
package/dist/config.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* config.ts — 加载 configs/qq_config.json
|
|
3
|
-
*
|
|
4
|
-
* 行为与旧 index.js 保持完全一致:
|
|
5
|
-
* - 文件不存在或解析失败: 进程退出 (旧版 process.exit(1))
|
|
6
|
-
* - 字段缺失: 走默认值
|
|
7
|
-
* - reload 时仅覆盖原对象 (mutate), 保留运行时引用的同一份对象
|
|
8
|
-
*/
|
|
9
|
-
import type { QQBridgeConfig } from "./types.js";
|
|
10
|
-
/** 统一通过 SDK 解析项目根:env SFMC_ROOT > __dirname 上溯。SEA / npm 一致。 */
|
|
11
|
-
export declare const ROOT_DIR: string;
|
|
12
|
-
export declare const CFG_PATH: string;
|
|
13
|
-
/** 进程启动时加载一次。失败直接退出,与旧实现一致。 */
|
|
14
|
-
export declare function loadInitialConfig(): QQBridgeConfig;
|
|
15
|
-
/**
|
|
16
|
-
* 重新读取配置文件,合并到传入对象上 (mutate)。
|
|
17
|
-
* 旧实现是 Object.assign(cfg, newCfg),保留运行时对原 cfg 对象的引用 — 同样行为。
|
|
18
|
-
*/
|
|
19
|
-
export declare function reloadInto(cfg: QQBridgeConfig): void;
|
|
20
|
-
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAKjD,gEAAgE;AAChE,eAAO,MAAM,QAAQ,EAAE,MAA2D,CAAC;AACnF,eAAO,MAAM,QAAQ,EAAE,MAA+C,CAAC;AAqBvE,+BAA+B;AAC/B,wBAAgB,iBAAiB,IAAI,cAAc,CAOlD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAQpD"}
|
package/dist/console.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* console.ts — 交互式控制台 (readline)
|
|
3
|
-
*
|
|
4
|
-
* 命令:
|
|
5
|
-
* help 显示帮助
|
|
6
|
-
* reload 重新读取 configs/qq_config.json
|
|
7
|
-
* status 显示当前状态
|
|
8
|
-
* stop 退出进程
|
|
9
|
-
*
|
|
10
|
-
* 行为与旧实现完全一致。
|
|
11
|
-
*/
|
|
12
|
-
import type { QQBridgeConfig } from "./types.js";
|
|
13
|
-
export interface ConsoleOptions {
|
|
14
|
-
config: QQBridgeConfig;
|
|
15
|
-
initialEnabled: boolean;
|
|
16
|
-
wsPort: number;
|
|
17
|
-
botSelfIdRef: {
|
|
18
|
-
value: string | null;
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
export declare function startConsole(opts: ConsoleOptions): void;
|
|
22
|
-
//# sourceMappingURL=console.d.ts.map
|
package/dist/console.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../src/console.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,cAAc,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE;QAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CACxC;AA8DD,wBAAgB,YAAY,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAKvD"}
|
package/dist/dbserver.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* dbserver.ts — 转发 OneBot 群消息到 db-server /api/sfmc/messages
|
|
3
|
-
*
|
|
4
|
-
* 与旧实现保持行为一致: 仅 POST 200 视为成功,否则抛错; 失败不重试 (主流程不阻塞)。
|
|
5
|
-
* 超时: 与 db-server 内部 HTTP 风格一致,使用 req.on("timeout") + req.destroy。
|
|
6
|
-
*/
|
|
7
|
-
export interface DBServerConfig {
|
|
8
|
-
host: string;
|
|
9
|
-
port: number;
|
|
10
|
-
channelId: string;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* 构造一条 IncomingChatMessage 并 POST 到 db-server。
|
|
14
|
-
* fromid 形如 `qq_<user_id>`,与旧实现保持完全一致。
|
|
15
|
-
*/
|
|
16
|
-
export declare function forwardGroupMessage(cfg: DBServerConfig, fromId: string, fromName: string, content: string, now?: number): Promise<void>;
|
|
17
|
-
/** 包装 try/catch,失败仅 log (与旧实现一致,不抛给主循环)。 */
|
|
18
|
-
export declare function tryForward(cfg: DBServerConfig, fromId: string, fromName: string, content: string): Promise<void>;
|
|
19
|
-
//# sourceMappingURL=dbserver.d.ts.map
|
package/dist/dbserver.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dbserver.d.ts","sourceRoot":"","sources":["../src/dbserver.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AA+CD;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,GAAG,GAAE,MAAmB,GACvB,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,4CAA4C;AAC5C,wBAAsB,UAAU,CAC9B,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAOf"}
|
package/dist/index.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* index.ts — QQ ↔ MC 桥接进程入口
|
|
4
|
-
*
|
|
5
|
-
* 端口: 3002 (默认) — WebSocket (LLBot reverse-ws)
|
|
6
|
-
*
|
|
7
|
-
* 数据流:
|
|
8
|
-
* QQ → MC: LLBot ──WS:3002──→ qq-bridge ──POST──→ db-server:3001/api/sfmc/messages
|
|
9
|
-
* MC → QQ: SAPI ──POST──→ db-server:3001 ──(内部)──→ LLBot:3004/send_group_msg
|
|
10
|
-
*
|
|
11
|
-
* 注意: 本进程只做 WS 入口,不再起 HTTP server。MC→QQ 由 db-server 直连 LLBot
|
|
12
|
-
* (见 db-server/src/domain/bridge.ts:forwardToQQBridge)
|
|
13
|
-
*
|
|
14
|
-
* 循环防护:
|
|
15
|
-
* 1. 跳过 sender.user_id === botSelfId 的回声
|
|
16
|
-
* (botSelfId 通过 LLBot 的 lifecycle 元事件捕获)
|
|
17
|
-
* 2. 5 秒内同 message_id 短期去重 (防 race)
|
|
18
|
-
*/
|
|
19
|
-
export {};
|
|
20
|
-
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;GAgBG"}
|