@kirigaya/openclaw-onebot 1.0.9 → 1.1.0
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 +52 -20
- package/dist/config.d.ts +2 -0
- package/dist/config.js +5 -0
- package/dist/connection.js +8 -3
- package/dist/handlers/process-inbound.js +63 -22
- package/dist/types.d.ts +5 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,29 +15,29 @@
|
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
+
## 安装
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
openclaw plugins install @kirigaya/openclaw-onebot
|
|
22
|
+
openclaw onebot setup
|
|
23
|
+
```
|
|
24
|
+
|
|
18
25
|
## 教程
|
|
19
26
|
|
|
20
|
-
|
|
27
|
+
[让 QQ 接入 openclaw!让你的助手掌管千人大群](https://kirigaya.cn/blog/article?seq=368)
|
|
28
|
+
|
|
29
|
+
<img src="./figure/arch.png" />
|
|
30
|
+
|
|
21
31
|
|
|
22
32
|
## 功能
|
|
23
33
|
|
|
24
34
|
- ✅ 私聊:所有消息 AI 都会回复
|
|
25
|
-
- ✅
|
|
35
|
+
- ✅ 触发:支持 @触发 和基于关键字的触发
|
|
26
36
|
- ✅ 自动获取上下文
|
|
27
|
-
- ✅
|
|
28
|
-
- ✅
|
|
29
|
-
- ✅
|
|
30
|
-
- ✅
|
|
31
|
-
- ✅ 支持文件,图像读取/上传
|
|
32
|
-
- ✅ 支持白名单系统
|
|
33
|
-
- ✅ 通过 `openclaw message send` CLI 发送(无 Agent 工具,降低 token 消耗)
|
|
34
|
-
|
|
35
|
-
## 安装
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
openclaw plugins install @kirigaya/openclaw-onebot
|
|
39
|
-
openclaw onebot setup
|
|
40
|
-
```
|
|
37
|
+
- ✅ 自定义新成员入群欢迎触发器
|
|
38
|
+
- ✅ 自动合并转发长消息:超过阈值可渲染为图片发送或者合并发送
|
|
39
|
+
- ✅ 支持文件,图像读取/发送
|
|
40
|
+
- ✅ 支持黑白名单系统
|
|
41
41
|
|
|
42
42
|
## 安装 onebot 服务端
|
|
43
43
|
|
|
@@ -48,9 +48,11 @@ openclaw onebot setup
|
|
|
48
48
|
|
|
49
49
|
| 类型 | 说明 |
|
|
50
50
|
|------|------|
|
|
51
|
-
| `forward-websocket` | 插件主动连接 OneBot(go-cqhttp、Lagrange.Core 正向 WS) |
|
|
51
|
+
| `forward-websocket` | 插件主动连接 OneBot(go-cqhttp、Lagrange.Core 正向 WS/WSS) |
|
|
52
52
|
| `backward-websocket` | 插件作为服务端,OneBot 连接过来 |
|
|
53
53
|
|
|
54
|
+
> 💡 **提示**:支持 `ws://` 和 `wss://`(WebSocket Secure)协议,可填写完整 URL 如 `wss://ws-napcatqq.example.com`
|
|
55
|
+
|
|
54
56
|
### 环境变量
|
|
55
57
|
|
|
56
58
|
可替代配置文件,适用于 Lagrange 等:
|
|
@@ -66,7 +68,31 @@ openclaw onebot setup
|
|
|
66
68
|
|
|
67
69
|
1. 安装并配置
|
|
68
70
|
2. 重启 Gateway:`openclaw gateway restart`
|
|
69
|
-
3. 在 QQ 私聊或群聊中发消息(群聊需 @
|
|
71
|
+
3. 在 QQ 私聊或群聊中发消息(群聊需 @ 机器人,或配置关键字触发)
|
|
72
|
+
|
|
73
|
+
## 关键字触发回复
|
|
74
|
+
|
|
75
|
+
除了 @ 机器人外,还可以配置关键字检测,当群消息中包含指定关键字时自动触发回复(无需 @)。
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"channels": {
|
|
80
|
+
"onebot": {
|
|
81
|
+
"keywordTriggers": {
|
|
82
|
+
"enabled": true,
|
|
83
|
+
"keywords": ["AI", "助手", "帮我问"],
|
|
84
|
+
"caseSensitive": false
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
| 配置项 | 说明 |
|
|
92
|
+
|--------|------|
|
|
93
|
+
| `enabled` | 是否启用关键字触发 |
|
|
94
|
+
| `keywords` | 关键字列表,包含任一关键字即触发 |
|
|
95
|
+
| `caseSensitive` | 是否区分大小写 |
|
|
70
96
|
|
|
71
97
|
## 长消息处理与 OG 图片渲染
|
|
72
98
|
|
|
@@ -190,7 +216,7 @@ openclaw message send --channel onebot --target group:987654321 --media "file://
|
|
|
190
216
|
}
|
|
191
217
|
```
|
|
192
218
|
|
|
193
|
-
|
|
219
|
+
## 黑名单
|
|
194
220
|
|
|
195
221
|
在群里有时候有些人需要被屏蔽,不管他怎么 @ 还是怎么,都屏蔽他的消息不触发。
|
|
196
222
|
|
|
@@ -204,7 +230,7 @@ openclaw message send --channel onebot --target group:987654321 --media "file://
|
|
|
204
230
|
}
|
|
205
231
|
```
|
|
206
232
|
|
|
207
|
-
|
|
233
|
+
**注意**:白名单优先级高于黑名单。如果同时设置了白名单和黑名单,只有白名单内的用户才能触发,且黑名单内的白名单用户也会被屏蔽。
|
|
208
234
|
|
|
209
235
|
## 新人入群触发器
|
|
210
236
|
|
|
@@ -264,6 +290,12 @@ npm run test:render-og-image -- "C:/path/to/your-theme.css"
|
|
|
264
290
|
- [Lagrange.Core](https://github.com/LSTM-Kirigaya/Lagrange.Core)
|
|
265
291
|
- [NapCat](https://github.com/NapNeko/NapCatQQ)
|
|
266
292
|
|
|
293
|
+
## 联系
|
|
294
|
+
|
|
295
|
+
zhelonghuang@qq.com
|
|
296
|
+
|
|
297
|
+
要是我不回你,可以选择进我的QQ群。782833642
|
|
298
|
+
|
|
267
299
|
## License
|
|
268
300
|
|
|
269
301
|
MIT © [LSTM-Kirigaya](https://github.com/LSTM-Kirigaya)
|
package/dist/config.d.ts
CHANGED
|
@@ -32,3 +32,5 @@ export declare function getTriggerKeywords(cfg: any): string[];
|
|
|
32
32
|
* - "contains": 消息包含关键词即可
|
|
33
33
|
*/
|
|
34
34
|
export declare function getTriggerMode(cfg: any): "prefix" | "contains";
|
|
35
|
+
/** 是否在用户不在白名单时回复“权限不足”,默认 true */
|
|
36
|
+
export declare function getReplyWhenWhitelistDenied(cfg: any): boolean;
|
package/dist/config.js
CHANGED
|
@@ -131,3 +131,8 @@ export function getTriggerMode(cfg) {
|
|
|
131
131
|
return "contains";
|
|
132
132
|
return "prefix"; // 默认为前缀匹配
|
|
133
133
|
}
|
|
134
|
+
/** 是否在用户不在白名单时回复“权限不足”,默认 true */
|
|
135
|
+
export function getReplyWhenWhitelistDenied(cfg) {
|
|
136
|
+
const v = cfg?.channels?.onebot?.replyWhenWhitelistDenied;
|
|
137
|
+
return v === undefined ? true : Boolean(v);
|
|
138
|
+
}
|
package/dist/connection.js
CHANGED
|
@@ -177,18 +177,22 @@ function getLogger() {
|
|
|
177
177
|
function sendOneBotAction(wsocket, action, params, log = getLogger()) {
|
|
178
178
|
const echo = nextEcho();
|
|
179
179
|
const payload = { action, params, echo };
|
|
180
|
+
// Log the initiation of the action with basic target info
|
|
181
|
+
const targetInfo = params.group_id ? `group=${params.group_id}` : (params.user_id ? `user=${params.user_id}` : "");
|
|
182
|
+
log.info?.(`[onebot-trace] sendOneBotAction action=${action} echo=${echo} ${targetInfo}`);
|
|
180
183
|
return new Promise((resolve, reject) => {
|
|
181
184
|
const timeout = setTimeout(() => {
|
|
182
185
|
pendingEcho.delete(echo);
|
|
183
|
-
log.warn?.(`[onebot] sendOneBotAction ${action} timeout`);
|
|
184
|
-
reject(new Error(`OneBot action ${action} timeout`));
|
|
186
|
+
log.warn?.(`[onebot-trace] sendOneBotAction ${action} timeout for echo=${echo}, ws.readyState=${wsocket.readyState}`);
|
|
187
|
+
reject(new Error(`OneBot action ${action} timeout (echo=${echo}, ws.readyState=${wsocket.readyState})`));
|
|
185
188
|
}, 15000);
|
|
186
189
|
pendingEcho.set(echo, {
|
|
187
190
|
resolve: (v) => {
|
|
188
191
|
clearTimeout(timeout);
|
|
189
192
|
pendingEcho.delete(echo);
|
|
193
|
+
log.info?.(`[onebot-trace] echo ${echo} resolved with retcode=${v?.retcode} message_id=${v?.data?.message_id ?? "unknown"}`);
|
|
190
194
|
if (v?.retcode !== 0)
|
|
191
|
-
log.warn?.(`[onebot] sendOneBotAction ${action} retcode=${v?.retcode} msg=${v?.msg ?? ""}`);
|
|
195
|
+
log.warn?.(`[onebot-trace] sendOneBotAction ${action} retcode=${v?.retcode} msg=${v?.msg ?? ""}`);
|
|
192
196
|
resolve(v);
|
|
193
197
|
},
|
|
194
198
|
});
|
|
@@ -196,6 +200,7 @@ function sendOneBotAction(wsocket, action, params, log = getLogger()) {
|
|
|
196
200
|
if (err) {
|
|
197
201
|
pendingEcho.delete(echo);
|
|
198
202
|
clearTimeout(timeout);
|
|
203
|
+
log.warn?.(`[onebot-trace] sendOneBotAction ${action} wsocket.send error for echo=${echo}: ${err.message}`);
|
|
199
204
|
reject(err);
|
|
200
205
|
}
|
|
201
206
|
});
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { getOneBotConfig } from "../config.js";
|
|
5
5
|
import { getRawText, getTextFromSegments, getReplyMessageId, getTextFromMessageContent, isMentioned, } from "../message.js";
|
|
6
|
-
import { getRenderMarkdownToPlain, getCollapseDoubleNewlines, getWhitelistUserIds, getBlacklistUserIds, getOgImageRenderTheme, getNormalModeFlushIntervalMs, getNormalModeFlushChars, getTriggerKeywords, getTriggerMode, } from "../config.js";
|
|
6
|
+
import { getRenderMarkdownToPlain, getCollapseDoubleNewlines, getWhitelistUserIds, getBlacklistUserIds, getOgImageRenderTheme, getNormalModeFlushIntervalMs, getNormalModeFlushChars, getTriggerKeywords, getTriggerMode, getReplyWhenWhitelistDenied, } from "../config.js";
|
|
7
7
|
import { markdownToPlain, collapseDoubleNewlines } from "../markdown.js";
|
|
8
8
|
import { markdownToImage } from "../og-image.js";
|
|
9
9
|
import { sendPrivateMsg, sendGroupMsg, sendPrivateImage, sendGroupImage, sendGroupForwardMsg, sendPrivateForwardMsg, setMsgEmojiLike, getMsg, } from "../connection.js";
|
|
@@ -150,15 +150,17 @@ export async function processInboundMessage(api, msg) {
|
|
|
150
150
|
// 白名单检查
|
|
151
151
|
const whitelist = getWhitelistUserIds(cfg);
|
|
152
152
|
if (whitelist.length > 0 && !whitelist.includes(Number(userId))) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
153
|
+
if (getReplyWhenWhitelistDenied(cfg)) {
|
|
154
|
+
const denyMsg = "权限不足,请向管理员申请权限";
|
|
155
|
+
const getConfig = () => getOneBotConfig(api);
|
|
156
|
+
try {
|
|
157
|
+
if (msg.message_type === "group" && msg.group_id)
|
|
158
|
+
await sendGroupMsg(msg.group_id, denyMsg, getConfig);
|
|
159
|
+
else
|
|
160
|
+
await sendPrivateMsg(userId, denyMsg, getConfig);
|
|
161
|
+
}
|
|
162
|
+
catch (_) { }
|
|
160
163
|
}
|
|
161
|
-
catch (_) { }
|
|
162
164
|
api.logger?.info?.(`[onebot] user ${userId} not in whitelist, denied`);
|
|
163
165
|
return;
|
|
164
166
|
}
|
|
@@ -185,7 +187,11 @@ export async function processInboundMessage(api, msg) {
|
|
|
185
187
|
}) ?? "";
|
|
186
188
|
const envelopeOptions = runtime.channel.reply?.resolveEnvelopeFormatOptions?.(cfg) ?? {};
|
|
187
189
|
const chatType = isGroup ? "group" : "direct";
|
|
188
|
-
|
|
190
|
+
// 优先使用群名片(card),其次是昵称(nickname),都没有则为空串
|
|
191
|
+
const senderNickname = (isGroup ? msg.sender?.card?.trim() : undefined)
|
|
192
|
+
|| msg.sender?.nickname?.trim()
|
|
193
|
+
|| "";
|
|
194
|
+
const fromLabel = senderNickname || String(userId);
|
|
189
195
|
// 添加日志:打印插件接收到的原始消息内容
|
|
190
196
|
api.logger?.info?.(`[onebot] received message from user ${userId}: "${messageText}"`);
|
|
191
197
|
const formattedBody = runtime.channel.reply?.formatInboundEnvelope?.({
|
|
@@ -289,10 +295,16 @@ export async function processInboundMessage(api, msg) {
|
|
|
289
295
|
api.logger?.warn?.("[onebot] setMsgEmojiLike failed (maybe OneBot doesn't support it)");
|
|
290
296
|
}
|
|
291
297
|
}
|
|
292
|
-
|
|
298
|
+
const traceId = `trace-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
299
|
+
const traceLog = {
|
|
300
|
+
info: (m) => api.logger?.info?.(`[${traceId}] ${m}`),
|
|
301
|
+
warn: (m) => api.logger?.warn?.(`[${traceId}] ${m}`),
|
|
302
|
+
error: (m) => api.logger?.error?.(`[${traceId}] ${m}`)
|
|
303
|
+
};
|
|
304
|
+
traceLog.info(`dispatching message for session ${sessionId}`);
|
|
293
305
|
const longMessageMode = onebotCfg.longMessageMode ?? "normal";
|
|
294
306
|
const longMessageThreshold = onebotCfg.longMessageThreshold ?? 300;
|
|
295
|
-
|
|
307
|
+
traceLog.info(`longMessageMode=${longMessageMode}, threshold=${longMessageThreshold}`);
|
|
296
308
|
const normalModeFlushIntervalMs = getNormalModeFlushIntervalMs(cfg);
|
|
297
309
|
const normalModeFlushChars = getNormalModeFlushChars(cfg);
|
|
298
310
|
const replySessionId = `onebot-reply-${Date.now()}-${sessionId}`;
|
|
@@ -307,11 +319,13 @@ export async function processInboundMessage(api, msg) {
|
|
|
307
319
|
let normalModeBufferedRawText = "";
|
|
308
320
|
let normalModeFlushTimer = null;
|
|
309
321
|
let normalModeFlushChain = Promise.resolve();
|
|
322
|
+
let receivedFinal = false;
|
|
310
323
|
const getConfig = () => getOneBotConfig(api);
|
|
311
324
|
const onReplySessionEnd = onebotCfg.onReplySessionEnd;
|
|
312
325
|
const normalModePunctuationFlushMinChars = 24;
|
|
313
|
-
const clearNormalModeFlushTimer = () => {
|
|
326
|
+
const clearNormalModeFlushTimer = (reason = "unknown") => {
|
|
314
327
|
if (normalModeFlushTimer) {
|
|
328
|
+
traceLog.info(`clearNormalModeFlushTimer: clearing timer, reason=${reason}`);
|
|
315
329
|
clearTimeout(normalModeFlushTimer);
|
|
316
330
|
normalModeFlushTimer = null;
|
|
317
331
|
}
|
|
@@ -321,7 +335,7 @@ export async function processInboundMessage(api, msg) {
|
|
|
321
335
|
normalModeFlushChain = normalModeFlushChain
|
|
322
336
|
.then(action)
|
|
323
337
|
.catch((e) => {
|
|
324
|
-
|
|
338
|
+
traceLog.error(`normal-mode flush failed: ${e?.message ?? e}`);
|
|
325
339
|
});
|
|
326
340
|
return normalModeFlushChain;
|
|
327
341
|
};
|
|
@@ -340,11 +354,12 @@ export async function processInboundMessage(api, msg) {
|
|
|
340
354
|
}
|
|
341
355
|
};
|
|
342
356
|
const flushBufferedNormalModeText = async (effectiveIsGroup, effectiveGroupId, uid) => {
|
|
343
|
-
clearNormalModeFlushTimer();
|
|
357
|
+
clearNormalModeFlushTimer("flushBufferedNormalModeText");
|
|
344
358
|
if (!hasBufferedNormalModeText())
|
|
345
359
|
return;
|
|
346
360
|
const text = normalModeBufferedText;
|
|
347
361
|
const rawText = normalModeBufferedRawText;
|
|
362
|
+
traceLog.info(`flushBufferedNormalModeText: textLen=${text.length}, textPreview="${text.slice(0, 30).replace(/\n/g, '\\n')}"`);
|
|
348
363
|
normalModeBufferedText = "";
|
|
349
364
|
normalModeBufferedRawText = "";
|
|
350
365
|
await doSendChunk(effectiveIsGroup, effectiveGroupId, uid, text, undefined);
|
|
@@ -357,7 +372,9 @@ export async function processInboundMessage(api, msg) {
|
|
|
357
372
|
const scheduleNormalModeFlush = (effectiveIsGroup, effectiveGroupId, uid) => {
|
|
358
373
|
if (normalModeFlushTimer)
|
|
359
374
|
return;
|
|
375
|
+
traceLog.info(`scheduleNormalModeFlush: scheduled (interval=${normalModeFlushIntervalMs}ms)`);
|
|
360
376
|
normalModeFlushTimer = setTimeout(() => {
|
|
377
|
+
traceLog.info(`scheduleNormalModeFlush: timer triggered`);
|
|
361
378
|
void queueNormalModeFlush(() => flushBufferedNormalModeText(effectiveIsGroup, effectiveGroupId, uid));
|
|
362
379
|
}, normalModeFlushIntervalMs);
|
|
363
380
|
};
|
|
@@ -394,6 +411,10 @@ export async function processInboundMessage(api, msg) {
|
|
|
394
411
|
const replyText = typeof p === "string" ? p : (p?.text ?? p?.body ?? "");
|
|
395
412
|
const mediaUrl = typeof p === "string" ? undefined : (p?.mediaUrl ?? p?.mediaUrls?.[0]);
|
|
396
413
|
const trimmed = (replyText || "").trim();
|
|
414
|
+
traceLog.info(`deliver entry: kind=${info.kind}, textLen=${replyText.length}, mediaUrl=${!!mediaUrl}, deliveredChunks=${deliveredChunks.length}`);
|
|
415
|
+
if (info.kind === "final") {
|
|
416
|
+
receivedFinal = true;
|
|
417
|
+
}
|
|
397
418
|
if ((!trimmed || trimmed === "NO_REPLY" || trimmed.endsWith("NO_REPLY")) && !mediaUrl)
|
|
398
419
|
return;
|
|
399
420
|
const { userId: uid, groupId: gid, isGroup: ig } = ctxPayload._onebot || {};
|
|
@@ -460,7 +481,7 @@ export async function processInboundMessage(api, msg) {
|
|
|
460
481
|
const isLong = totalLen > longMessageThreshold;
|
|
461
482
|
const isIncrementalLong = incrementalLen > longMessageThreshold;
|
|
462
483
|
const isIncremental = lastSentCount > 0;
|
|
463
|
-
|
|
484
|
+
traceLog.info(`final check: totalLen=${totalLen}, threshold=${longMessageThreshold}, isLong=${isLong}, isIncremental=${isIncremental}, deliveredChunks=${deliveredChunks.length}`);
|
|
464
485
|
if (isIncremental) {
|
|
465
486
|
setForwardSuppressDelivery(false);
|
|
466
487
|
// normal 模式下增量 chunk 已在 deliver 中实时发出;这里不能在 final 再补发一次。
|
|
@@ -544,9 +565,9 @@ export async function processInboundMessage(api, msg) {
|
|
|
544
565
|
}
|
|
545
566
|
}
|
|
546
567
|
else if (!shouldSendNow && (longMessageMode === "og_image" || longMessageMode === "forward")) {
|
|
547
|
-
|
|
568
|
+
traceLog.info(`checking og_image: isLong=${isLong}, mode=${longMessageMode}`);
|
|
548
569
|
if (isLong && longMessageMode === "og_image") {
|
|
549
|
-
|
|
570
|
+
traceLog.info(`triggering og_image for ${totalLen} chars`);
|
|
550
571
|
const fullRaw = deliveredChunks.map((c) => c.rawText ?? c.text ?? "").join("\n\n");
|
|
551
572
|
if (fullRaw.trim()) {
|
|
552
573
|
try {
|
|
@@ -645,20 +666,24 @@ export async function processInboundMessage(api, msg) {
|
|
|
645
666
|
}
|
|
646
667
|
}
|
|
647
668
|
catch (e) {
|
|
648
|
-
|
|
669
|
+
traceLog.error(`deliver failed: ${e?.message}`);
|
|
649
670
|
}
|
|
650
671
|
},
|
|
651
672
|
onError: async (err, info) => {
|
|
652
|
-
|
|
673
|
+
traceLog.error(`${info?.kind} reply failed: ${err}`);
|
|
653
674
|
await clearEmojiReaction();
|
|
654
675
|
},
|
|
655
676
|
},
|
|
656
677
|
replyOptions: { disableBlockStreaming: longMessageMode !== "normal" },
|
|
657
678
|
});
|
|
679
|
+
traceLog.info(`dispatchReplyWithBufferedBlockDispatcher returned successfully.`);
|
|
658
680
|
}
|
|
659
681
|
catch (err) {
|
|
660
682
|
await clearEmojiReaction();
|
|
661
|
-
|
|
683
|
+
// 异常时清空缓冲,避免 finally 补发半截正文后再发错误消息
|
|
684
|
+
traceLog.error(`dispatch catch block: err=${err?.message}, receivedFinal=${receivedFinal}, chunkIndex=${chunkIndex}`);
|
|
685
|
+
normalModeBufferedText = "";
|
|
686
|
+
normalModeBufferedRawText = "";
|
|
662
687
|
try {
|
|
663
688
|
const { userId: uid, groupId: gid, isGroup: ig } = ctxPayload._onebot || {};
|
|
664
689
|
if (ig && gid)
|
|
@@ -669,7 +694,23 @@ export async function processInboundMessage(api, msg) {
|
|
|
669
694
|
catch (_) { }
|
|
670
695
|
}
|
|
671
696
|
finally {
|
|
672
|
-
|
|
697
|
+
traceLog.info(`dispatch finally block: receivedFinal=${receivedFinal}, hasBuffered=${hasBufferedNormalModeText()}, bufferLen=${normalModeBufferedText.length}, hasTimer=${!!normalModeFlushTimer}, chunks=${deliveredChunks.length}`);
|
|
698
|
+
// 补发缓冲池中残留的文本(引擎未发送 final 帧时会走到这里)
|
|
699
|
+
if (hasBufferedNormalModeText()) {
|
|
700
|
+
try {
|
|
701
|
+
const { userId: uid, groupId: gid, isGroup: ig } = ctxPayload._onebot || {};
|
|
702
|
+
const sessionKey = String(ctxPayload.SessionKey ?? sessionId);
|
|
703
|
+
const groupMatch = sessionKey.match(/^onebot:group:(\d+)$/i);
|
|
704
|
+
const effectiveIsGroup = groupMatch != null || Boolean(ig);
|
|
705
|
+
const effectiveGroupId = (groupMatch ? parseInt(groupMatch[1], 10) : undefined) ?? gid;
|
|
706
|
+
queueNormalModeFlush(() => flushBufferedNormalModeText(effectiveIsGroup, effectiveGroupId, uid));
|
|
707
|
+
await normalModeFlushChain;
|
|
708
|
+
}
|
|
709
|
+
catch (e) {
|
|
710
|
+
traceLog.error(`finally flush failed: ${e?.message ?? e}`);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
clearNormalModeFlushTimer("finally");
|
|
673
714
|
setForwardSuppressDelivery(false);
|
|
674
715
|
setActiveReplySelfId(null);
|
|
675
716
|
lastSentChunkCountBySession.delete(replySessionId);
|
package/dist/types.d.ts
CHANGED
|
@@ -14,7 +14,11 @@ export interface OneBotMessage {
|
|
|
14
14
|
raw_message?: string;
|
|
15
15
|
self_id?: number;
|
|
16
16
|
time?: number;
|
|
17
|
-
|
|
17
|
+
sender?: {
|
|
18
|
+
user_id?: number;
|
|
19
|
+
nickname?: string;
|
|
20
|
+
card?: string;
|
|
21
|
+
};
|
|
18
22
|
[key: string]: unknown;
|
|
19
23
|
}
|
|
20
24
|
export interface OneBotAccountConfig {
|