@tencent-connect/openclaw-qqbot 1.6.2-alpha.0 → 1.6.2-alpha.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/dist/src/channel.d.ts +8 -0
- package/dist/src/channel.js +10 -28
- package/dist/src/gateway.js +61 -49
- package/package.json +1 -1
- package/src/channel.ts +11 -32
- package/src/gateway.ts +53 -41
package/dist/src/channel.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
import { type ChannelPlugin } from "openclaw/plugin-sdk";
|
|
2
2
|
import type { ResolvedQQBotAccount } from "./types.js";
|
|
3
|
+
/** QQ Bot 单条消息文本长度上限 */
|
|
4
|
+
export declare const TEXT_CHUNK_LIMIT = 5000;
|
|
5
|
+
/**
|
|
6
|
+
* Markdown 感知的文本分块函数
|
|
7
|
+
* 委托给 SDK 内置的 channel.text.chunkMarkdownText
|
|
8
|
+
* 支持代码块自动关闭/重开、括号感知等
|
|
9
|
+
*/
|
|
10
|
+
export declare function chunkText(text: string, limit: number): string[];
|
|
3
11
|
export declare const qqbotPlugin: ChannelPlugin<ResolvedQQBotAccount>;
|
package/dist/src/channel.js
CHANGED
|
@@ -4,34 +4,16 @@ import { sendText, sendMedia } from "./outbound.js";
|
|
|
4
4
|
import { startGateway } from "./gateway.js";
|
|
5
5
|
import { qqbotOnboardingAdapter } from "./onboarding.js";
|
|
6
6
|
import { getQQBotRuntime } from "./runtime.js";
|
|
7
|
+
/** QQ Bot 单条消息文本长度上限 */
|
|
8
|
+
export const TEXT_CHUNK_LIMIT = 5000;
|
|
7
9
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
+
* Markdown 感知的文本分块函数
|
|
11
|
+
* 委托给 SDK 内置的 channel.text.chunkMarkdownText
|
|
12
|
+
* 支持代码块自动关闭/重开、括号感知等
|
|
10
13
|
*/
|
|
11
|
-
function chunkText(text, limit) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const chunks = [];
|
|
15
|
-
let remaining = text;
|
|
16
|
-
while (remaining.length > 0) {
|
|
17
|
-
if (remaining.length <= limit) {
|
|
18
|
-
chunks.push(remaining);
|
|
19
|
-
break;
|
|
20
|
-
}
|
|
21
|
-
// 尝试在换行处分割
|
|
22
|
-
let splitAt = remaining.lastIndexOf("\n", limit);
|
|
23
|
-
if (splitAt <= 0 || splitAt < limit * 0.5) {
|
|
24
|
-
// 没找到合适的换行,尝试在空格处分割
|
|
25
|
-
splitAt = remaining.lastIndexOf(" ", limit);
|
|
26
|
-
}
|
|
27
|
-
if (splitAt <= 0 || splitAt < limit * 0.5) {
|
|
28
|
-
// 还是没找到,强制在 limit 处分割
|
|
29
|
-
splitAt = limit;
|
|
30
|
-
}
|
|
31
|
-
chunks.push(remaining.slice(0, splitAt));
|
|
32
|
-
remaining = remaining.slice(splitAt).trimStart();
|
|
33
|
-
}
|
|
34
|
-
return chunks;
|
|
14
|
+
export function chunkText(text, limit) {
|
|
15
|
+
const runtime = getQQBotRuntime();
|
|
16
|
+
return runtime.channel.text.chunkMarkdownText(text, limit);
|
|
35
17
|
}
|
|
36
18
|
export const qqbotPlugin = {
|
|
37
19
|
id: "qqbot",
|
|
@@ -205,9 +187,9 @@ export const qqbotPlugin = {
|
|
|
205
187
|
},
|
|
206
188
|
outbound: {
|
|
207
189
|
deliveryMode: "direct",
|
|
208
|
-
chunker:
|
|
190
|
+
chunker: (text, limit) => getQQBotRuntime().channel.text.chunkMarkdownText(text, limit),
|
|
209
191
|
chunkerMode: "markdown",
|
|
210
|
-
textChunkLimit:
|
|
192
|
+
textChunkLimit: 5000,
|
|
211
193
|
sendText: async ({ to, text, accountId, replyToId, cfg }) => {
|
|
212
194
|
console.log(`[qqbot:channel] sendText called — accountId=${accountId}, to=${to}, replyToId=${replyToId}, text.length=${text?.length ?? 0}`);
|
|
213
195
|
console.log(`[qqbot:channel] sendText text preview: ${text?.slice(0, 100)}${(text?.length ?? 0) > 100 ? "..." : ""}`);
|
package/dist/src/gateway.js
CHANGED
|
@@ -17,6 +17,7 @@ import { checkFileSize, readFileAsync, fileExistsAsync, formatFileSize } from ".
|
|
|
17
17
|
import { getQQBotDataDir, isLocalPath as isLocalFilePath, normalizePath, sanitizeFileName, runDiagnostics } from "./utils/platform.js";
|
|
18
18
|
import { MSG, formatMediaErrorMessage } from "./user-messages.js";
|
|
19
19
|
import { sendPhoto, sendVoice, sendVideoMsg, sendDocument, sendMedia as sendMediaAuto } from "./outbound.js";
|
|
20
|
+
import { chunkText, TEXT_CHUNK_LIMIT } from "./channel.js";
|
|
20
21
|
function resolveSTTConfig(cfg) {
|
|
21
22
|
const c = cfg;
|
|
22
23
|
// 优先使用 channels.qqbot.stt(插件专属配置)
|
|
@@ -1479,23 +1480,27 @@ export async function startGateway(ctx) {
|
|
|
1479
1480
|
};
|
|
1480
1481
|
for (const item of sendQueue) {
|
|
1481
1482
|
if (item.type === "text") {
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1483
|
+
// 对长文本进行分块发送
|
|
1484
|
+
const textChunks = getQQBotRuntime().channel.text.chunkMarkdownText(item.content, TEXT_CHUNK_LIMIT);
|
|
1485
|
+
for (const chunk of textChunks) {
|
|
1486
|
+
try {
|
|
1487
|
+
await sendWithTokenRetry(async (token) => {
|
|
1488
|
+
const ref = consumeQuoteRef();
|
|
1489
|
+
if (event.type === "c2c") {
|
|
1490
|
+
return await sendC2CMessage(token, event.senderId, chunk, event.messageId, ref);
|
|
1491
|
+
}
|
|
1492
|
+
else if (event.type === "group" && event.groupOpenid) {
|
|
1493
|
+
return await sendGroupMessage(token, event.groupOpenid, chunk, event.messageId);
|
|
1494
|
+
}
|
|
1495
|
+
else if (event.channelId) {
|
|
1496
|
+
return await sendChannelMessage(token, event.channelId, chunk, event.messageId);
|
|
1497
|
+
}
|
|
1498
|
+
});
|
|
1499
|
+
log?.info(`[qqbot:${account.accountId}] Sent text chunk (${chunk.length}/${item.content.length} chars): ${chunk.slice(0, 50)}...`);
|
|
1500
|
+
}
|
|
1501
|
+
catch (err) {
|
|
1502
|
+
log?.error(`[qqbot:${account.accountId}] Failed to send text chunk: ${err}`);
|
|
1503
|
+
}
|
|
1499
1504
|
}
|
|
1500
1505
|
}
|
|
1501
1506
|
else if (item.type === "image") {
|
|
@@ -2049,23 +2054,26 @@ export async function startGateway(ctx) {
|
|
|
2049
2054
|
}
|
|
2050
2055
|
// 🔹 第三步:发送带公网图片的 markdown 消息
|
|
2051
2056
|
if (textWithoutImages.trim()) {
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2057
|
+
const mdChunks = chunkText(textWithoutImages, TEXT_CHUNK_LIMIT);
|
|
2058
|
+
for (const chunk of mdChunks) {
|
|
2059
|
+
try {
|
|
2060
|
+
await sendWithTokenRetry(async (token) => {
|
|
2061
|
+
const ref = consumeQuoteRef();
|
|
2062
|
+
if (event.type === "c2c") {
|
|
2063
|
+
return await sendC2CMessage(token, event.senderId, chunk, event.messageId, ref);
|
|
2064
|
+
}
|
|
2065
|
+
else if (event.type === "group" && event.groupOpenid) {
|
|
2066
|
+
return await sendGroupMessage(token, event.groupOpenid, chunk, event.messageId);
|
|
2067
|
+
}
|
|
2068
|
+
else if (event.channelId) {
|
|
2069
|
+
return await sendChannelMessage(token, event.channelId, chunk, event.messageId);
|
|
2070
|
+
}
|
|
2071
|
+
});
|
|
2072
|
+
log?.info(`[qqbot:${account.accountId}] Sent markdown chunk (${chunk.length}/${textWithoutImages.length} chars) with ${httpImageUrls.length} HTTP images (${event.type})`);
|
|
2073
|
+
}
|
|
2074
|
+
catch (err) {
|
|
2075
|
+
log?.error(`[qqbot:${account.accountId}] Failed to send markdown message chunk: ${err}`);
|
|
2076
|
+
}
|
|
2069
2077
|
}
|
|
2070
2078
|
}
|
|
2071
2079
|
}
|
|
@@ -2105,21 +2113,24 @@ export async function startGateway(ctx) {
|
|
|
2105
2113
|
log?.error(`[qqbot:${account.accountId}] Failed to send image: ${imgErr}`);
|
|
2106
2114
|
}
|
|
2107
2115
|
}
|
|
2108
|
-
//
|
|
2116
|
+
// 发送文本消息(分块)
|
|
2109
2117
|
if (textWithoutImages.trim()) {
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2118
|
+
const plainChunks = chunkText(textWithoutImages, TEXT_CHUNK_LIMIT);
|
|
2119
|
+
for (const chunk of plainChunks) {
|
|
2120
|
+
await sendWithTokenRetry(async (token) => {
|
|
2121
|
+
const ref = consumeQuoteRef();
|
|
2122
|
+
if (event.type === "c2c") {
|
|
2123
|
+
return await sendC2CMessage(token, event.senderId, chunk, event.messageId, ref);
|
|
2124
|
+
}
|
|
2125
|
+
else if (event.type === "group" && event.groupOpenid) {
|
|
2126
|
+
return await sendGroupMessage(token, event.groupOpenid, chunk, event.messageId);
|
|
2127
|
+
}
|
|
2128
|
+
else if (event.channelId) {
|
|
2129
|
+
return await sendChannelMessage(token, event.channelId, chunk, event.messageId);
|
|
2130
|
+
}
|
|
2131
|
+
});
|
|
2132
|
+
log?.info(`[qqbot:${account.accountId}] Sent text chunk (${chunk.length}/${textWithoutImages.length} chars) (${event.type})`);
|
|
2133
|
+
}
|
|
2123
2134
|
}
|
|
2124
2135
|
}
|
|
2125
2136
|
catch (err) {
|
|
@@ -2204,7 +2215,7 @@ export async function startGateway(ctx) {
|
|
|
2204
2215
|
},
|
|
2205
2216
|
},
|
|
2206
2217
|
replyOptions: {
|
|
2207
|
-
disableBlockStreaming:
|
|
2218
|
+
disableBlockStreaming: true,
|
|
2208
2219
|
},
|
|
2209
2220
|
});
|
|
2210
2221
|
// 等待分发完成或超时
|
|
@@ -2339,6 +2350,7 @@ export async function startGateway(ctx) {
|
|
|
2339
2350
|
}
|
|
2340
2351
|
else if (t === "RESUMED") {
|
|
2341
2352
|
log?.info(`[qqbot:${account.accountId}] Session resumed`);
|
|
2353
|
+
onReady?.(d); // 通知框架连接已恢复,避免 health-monitor 误判 disconnected
|
|
2342
2354
|
// RESUMED 也属于首次启动(gateway restart 通常走 resume)
|
|
2343
2355
|
if (isFirstReadyGlobal) {
|
|
2344
2356
|
isFirstReadyGlobal = false;
|
package/package.json
CHANGED
package/src/channel.ts
CHANGED
|
@@ -13,38 +13,17 @@ import { startGateway } from "./gateway.js";
|
|
|
13
13
|
import { qqbotOnboardingAdapter } from "./onboarding.js";
|
|
14
14
|
import { getQQBotRuntime } from "./runtime.js";
|
|
15
15
|
|
|
16
|
+
/** QQ Bot 单条消息文本长度上限 */
|
|
17
|
+
export const TEXT_CHUNK_LIMIT = 5000;
|
|
18
|
+
|
|
16
19
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
20
|
+
* Markdown 感知的文本分块函数
|
|
21
|
+
* 委托给 SDK 内置的 channel.text.chunkMarkdownText
|
|
22
|
+
* 支持代码块自动关闭/重开、括号感知等
|
|
19
23
|
*/
|
|
20
|
-
function chunkText(text: string, limit: number): string[] {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const chunks: string[] = [];
|
|
24
|
-
let remaining = text;
|
|
25
|
-
|
|
26
|
-
while (remaining.length > 0) {
|
|
27
|
-
if (remaining.length <= limit) {
|
|
28
|
-
chunks.push(remaining);
|
|
29
|
-
break;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// 尝试在换行处分割
|
|
33
|
-
let splitAt = remaining.lastIndexOf("\n", limit);
|
|
34
|
-
if (splitAt <= 0 || splitAt < limit * 0.5) {
|
|
35
|
-
// 没找到合适的换行,尝试在空格处分割
|
|
36
|
-
splitAt = remaining.lastIndexOf(" ", limit);
|
|
37
|
-
}
|
|
38
|
-
if (splitAt <= 0 || splitAt < limit * 0.5) {
|
|
39
|
-
// 还是没找到,强制在 limit 处分割
|
|
40
|
-
splitAt = limit;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
chunks.push(remaining.slice(0, splitAt));
|
|
44
|
-
remaining = remaining.slice(splitAt).trimStart();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return chunks;
|
|
24
|
+
export function chunkText(text: string, limit: number): string[] {
|
|
25
|
+
const runtime = getQQBotRuntime();
|
|
26
|
+
return runtime.channel.text.chunkMarkdownText(text, limit);
|
|
48
27
|
}
|
|
49
28
|
|
|
50
29
|
export const qqbotPlugin: ChannelPlugin<ResolvedQQBotAccount> = {
|
|
@@ -230,9 +209,9 @@ export const qqbotPlugin: ChannelPlugin<ResolvedQQBotAccount> = {
|
|
|
230
209
|
},
|
|
231
210
|
outbound: {
|
|
232
211
|
deliveryMode: "direct",
|
|
233
|
-
chunker:
|
|
212
|
+
chunker: (text, limit) => getQQBotRuntime().channel.text.chunkMarkdownText(text, limit),
|
|
234
213
|
chunkerMode: "markdown",
|
|
235
|
-
textChunkLimit:
|
|
214
|
+
textChunkLimit: 5000,
|
|
236
215
|
sendText: async ({ to, text, accountId, replyToId, cfg }) => {
|
|
237
216
|
console.log(`[qqbot:channel] sendText called — accountId=${accountId}, to=${to}, replyToId=${replyToId}, text.length=${text?.length ?? 0}`);
|
|
238
217
|
console.log(`[qqbot:channel] sendText text preview: ${text?.slice(0, 100)}${(text?.length ?? 0) > 100 ? "..." : ""}`);
|
package/src/gateway.ts
CHANGED
|
@@ -18,6 +18,7 @@ import { checkFileSize, readFileAsync, fileExistsAsync, isLargeFile, formatFileS
|
|
|
18
18
|
import { getQQBotDataDir, isLocalPath as isLocalFilePath, normalizePath, sanitizeFileName, runDiagnostics } from "./utils/platform.js";
|
|
19
19
|
import { MSG, formatMediaErrorMessage } from "./user-messages.js";
|
|
20
20
|
import { sendPhoto, sendVoice, sendVideoMsg, sendDocument, sendMedia as sendMediaAuto, type MediaTargetContext } from "./outbound.js";
|
|
21
|
+
import { chunkText, TEXT_CHUNK_LIMIT } from "./channel.js";
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* 通用 OpenAI 兼容 STT(语音转文字)
|
|
@@ -1659,20 +1660,24 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
|
|
1659
1660
|
|
|
1660
1661
|
for (const item of sendQueue) {
|
|
1661
1662
|
if (item.type === "text") {
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1663
|
+
// 对长文本进行分块发送
|
|
1664
|
+
const textChunks = getQQBotRuntime().channel.text.chunkMarkdownText(item.content, TEXT_CHUNK_LIMIT);
|
|
1665
|
+
for (const chunk of textChunks) {
|
|
1666
|
+
try {
|
|
1667
|
+
await sendWithTokenRetry(async (token) => {
|
|
1668
|
+
const ref = consumeQuoteRef();
|
|
1669
|
+
if (event.type === "c2c") {
|
|
1670
|
+
return await sendC2CMessage(token, event.senderId, chunk, event.messageId, ref);
|
|
1671
|
+
} else if (event.type === "group" && event.groupOpenid) {
|
|
1672
|
+
return await sendGroupMessage(token, event.groupOpenid, chunk, event.messageId);
|
|
1673
|
+
} else if (event.channelId) {
|
|
1674
|
+
return await sendChannelMessage(token, event.channelId, chunk, event.messageId);
|
|
1675
|
+
}
|
|
1676
|
+
});
|
|
1677
|
+
log?.info(`[qqbot:${account.accountId}] Sent text chunk (${chunk.length}/${item.content.length} chars): ${chunk.slice(0, 50)}...`);
|
|
1678
|
+
} catch (err) {
|
|
1679
|
+
log?.error(`[qqbot:${account.accountId}] Failed to send text chunk: ${err}`);
|
|
1680
|
+
}
|
|
1676
1681
|
}
|
|
1677
1682
|
} else if (item.type === "image") {
|
|
1678
1683
|
const result = await sendPhoto(mediaTarget, item.content);
|
|
@@ -2218,20 +2223,23 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
|
|
2218
2223
|
|
|
2219
2224
|
// 🔹 第三步:发送带公网图片的 markdown 消息
|
|
2220
2225
|
if (textWithoutImages.trim()) {
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2226
|
+
const mdChunks = chunkText(textWithoutImages, TEXT_CHUNK_LIMIT);
|
|
2227
|
+
for (const chunk of mdChunks) {
|
|
2228
|
+
try {
|
|
2229
|
+
await sendWithTokenRetry(async (token) => {
|
|
2230
|
+
const ref = consumeQuoteRef();
|
|
2231
|
+
if (event.type === "c2c") {
|
|
2232
|
+
return await sendC2CMessage(token, event.senderId, chunk, event.messageId, ref);
|
|
2233
|
+
} else if (event.type === "group" && event.groupOpenid) {
|
|
2234
|
+
return await sendGroupMessage(token, event.groupOpenid, chunk, event.messageId);
|
|
2235
|
+
} else if (event.channelId) {
|
|
2236
|
+
return await sendChannelMessage(token, event.channelId, chunk, event.messageId);
|
|
2237
|
+
}
|
|
2238
|
+
});
|
|
2239
|
+
log?.info(`[qqbot:${account.accountId}] Sent markdown chunk (${chunk.length}/${textWithoutImages.length} chars) with ${httpImageUrls.length} HTTP images (${event.type})`);
|
|
2240
|
+
} catch (err) {
|
|
2241
|
+
log?.error(`[qqbot:${account.accountId}] Failed to send markdown message chunk: ${err}`);
|
|
2242
|
+
}
|
|
2235
2243
|
}
|
|
2236
2244
|
}
|
|
2237
2245
|
} else {
|
|
@@ -2271,19 +2279,22 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
|
|
2271
2279
|
}
|
|
2272
2280
|
}
|
|
2273
2281
|
|
|
2274
|
-
//
|
|
2282
|
+
// 发送文本消息(分块)
|
|
2275
2283
|
if (textWithoutImages.trim()) {
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2284
|
+
const plainChunks = chunkText(textWithoutImages, TEXT_CHUNK_LIMIT);
|
|
2285
|
+
for (const chunk of plainChunks) {
|
|
2286
|
+
await sendWithTokenRetry(async (token) => {
|
|
2287
|
+
const ref = consumeQuoteRef();
|
|
2288
|
+
if (event.type === "c2c") {
|
|
2289
|
+
return await sendC2CMessage(token, event.senderId, chunk, event.messageId, ref);
|
|
2290
|
+
} else if (event.type === "group" && event.groupOpenid) {
|
|
2291
|
+
return await sendGroupMessage(token, event.groupOpenid, chunk, event.messageId);
|
|
2292
|
+
} else if (event.channelId) {
|
|
2293
|
+
return await sendChannelMessage(token, event.channelId, chunk, event.messageId);
|
|
2294
|
+
}
|
|
2295
|
+
});
|
|
2296
|
+
log?.info(`[qqbot:${account.accountId}] Sent text chunk (${chunk.length}/${textWithoutImages.length} chars) (${event.type})`);
|
|
2297
|
+
}
|
|
2287
2298
|
}
|
|
2288
2299
|
} catch (err) {
|
|
2289
2300
|
log?.error(`[qqbot:${account.accountId}] Send failed: ${err}`);
|
|
@@ -2366,7 +2377,7 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
|
|
2366
2377
|
},
|
|
2367
2378
|
},
|
|
2368
2379
|
replyOptions: {
|
|
2369
|
-
disableBlockStreaming:
|
|
2380
|
+
disableBlockStreaming: true,
|
|
2370
2381
|
},
|
|
2371
2382
|
});
|
|
2372
2383
|
|
|
@@ -2504,6 +2515,7 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
|
|
2504
2515
|
} // end isFirstReady
|
|
2505
2516
|
} else if (t === "RESUMED") {
|
|
2506
2517
|
log?.info(`[qqbot:${account.accountId}] Session resumed`);
|
|
2518
|
+
onReady?.(d); // 通知框架连接已恢复,避免 health-monitor 误判 disconnected
|
|
2507
2519
|
// RESUMED 也属于首次启动(gateway restart 通常走 resume)
|
|
2508
2520
|
if (isFirstReadyGlobal) {
|
|
2509
2521
|
isFirstReadyGlobal = false;
|