@openclaw/qqbot 2026.7.1-beta.2 → 2026.7.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/dist/api.js +5 -5
- package/dist/{channel-BQM6pDlD.js → channel-D1UztsnG.js} +47 -8
- package/dist/{channel-entry-C18ypTaG.js → channel-entry-C5YdhX3Y.js} +3 -3
- package/dist/channel-entry-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{gateway-CjdB3B6n.js → gateway-pJQppxe4.js} +70 -35
- package/dist/{group-BmS1Qwst.js → group-o0GmovSf.js} +14 -11
- package/dist/{handler-runtime-rU03VA43.js → handler-runtime-zQvT6SrI.js} +2 -2
- package/dist/{log-SDfMMBWe.js → log-DEtcoDWe.js} +3 -4
- package/dist/{outbound-BZHwMFUX.js → outbound-BIrfvvFJ.js} +17 -15
- package/dist/{runtime-B4HWCOa-.js → runtime-DodcT_fQ.js} +1 -1
- package/dist/runtime-api.js +1 -1
- package/dist/{sender-BfVLJJYp.js → sender-CjDuU-uz.js} +17 -13
- package/dist/{tools-C8kTMXKc.js → tools-UJJ-tLHP.js} +19 -36
- package/dist/tools-api.js +1 -1
- package/npm-shrinkwrap.json +3 -3
- package/package.json +4 -4
package/dist/api.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as qqbotPlugin } from "./channel-
|
|
1
|
+
import { t as qqbotPlugin } from "./channel-D1UztsnG.js";
|
|
2
2
|
import { a as resolveQQBotAccount, i as resolveDefaultQQBotAccountId, n as applyQQBotAccountConfig, r as listQQBotAccountIds, t as DEFAULT_ACCOUNT_ID } from "./config-C1qZbh0K.js";
|
|
3
3
|
import { t as qqbotSetupPlugin } from "./channel.setup-2ItDYKhz.js";
|
|
4
|
-
import { o as getFrameworkCommands } from "./group-
|
|
5
|
-
import { A as OUTBOUND_ERROR_CODES, C as resolveUserFacingMediaError, D as getMessageReplyStats, E as getMessageReplyConfig, O as recordMessageReply, T as checkMessageReplyLimit, _ as sendVideoMsg, a as sendText, f as buildMediaTarget, g as sendPhoto, h as sendDocument, i as sendProactiveMessage, j as setOutboundAudioPort, k as DEFAULT_MEDIA_SEND_ERROR, m as resolveOutboundMediaPath, n as sendCronMessage, p as parseTarget, r as sendMedia, v as sendVoice, w as MESSAGE_REPLY_LIMIT } from "./outbound-
|
|
6
|
-
import { n as registerRemindTool, r as registerChannelTool, t as registerQQBotTools } from "./tools-
|
|
7
|
-
import { t as registerQQBotFull } from "./channel-entry-
|
|
4
|
+
import { o as getFrameworkCommands } from "./group-o0GmovSf.js";
|
|
5
|
+
import { A as OUTBOUND_ERROR_CODES, C as resolveUserFacingMediaError, D as getMessageReplyStats, E as getMessageReplyConfig, O as recordMessageReply, T as checkMessageReplyLimit, _ as sendVideoMsg, a as sendText, f as buildMediaTarget, g as sendPhoto, h as sendDocument, i as sendProactiveMessage, j as setOutboundAudioPort, k as DEFAULT_MEDIA_SEND_ERROR, m as resolveOutboundMediaPath, n as sendCronMessage, p as parseTarget, r as sendMedia, v as sendVoice, w as MESSAGE_REPLY_LIMIT } from "./outbound-BIrfvvFJ.js";
|
|
6
|
+
import { n as registerRemindTool, r as registerChannelTool, t as registerQQBotTools } from "./tools-UJJ-tLHP.js";
|
|
7
|
+
import { t as registerQQBotFull } from "./channel-entry-C5YdhX3Y.js";
|
|
8
8
|
export { DEFAULT_ACCOUNT_ID, DEFAULT_MEDIA_SEND_ERROR, MESSAGE_REPLY_LIMIT, OUTBOUND_ERROR_CODES, applyQQBotAccountConfig, buildMediaTarget, checkMessageReplyLimit, getFrameworkCommands, getMessageReplyConfig, getMessageReplyStats, listQQBotAccountIds, parseTarget, qqbotPlugin, qqbotSetupPlugin, recordMessageReply, registerChannelTool, registerQQBotFull, registerQQBotTools, registerRemindTool, resolveDefaultQQBotAccountId, resolveOutboundMediaPath, resolveQQBotAccount, resolveUserFacingMediaError, sendCronMessage, sendDocument, sendMedia, sendPhoto, sendProactiveMessage, sendText, sendVideoMsg, sendVoice, setOutboundAudioPort };
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { t as asOptionalObjectRecord } from "./string-normalize-R_0cKO7Q.js";
|
|
2
2
|
import { a as qqbotSetupAdapterShared, i as qqbotMeta, n as qqbotSetupWizard, o as ensurePlatformAdapter, r as qqbotConfigAdapter, s as getBridgeLogger, t as qqbotChannelConfigSchema } from "./config-schema-JZEf1dvB.js";
|
|
3
3
|
import { a as resolveQQBotAccount, n as applyQQBotAccountConfig, r as listQQBotAccountIds, t as DEFAULT_ACCOUNT_ID$1 } from "./config-C1qZbh0K.js";
|
|
4
|
-
import { t as getQQBotRuntime } from "./runtime-
|
|
4
|
+
import { t as getQQBotRuntime } from "./runtime-DodcT_fQ.js";
|
|
5
5
|
import { n as legacyConfigRules, r as normalizeCompatibilityConfig, t as buildQQBotStateKey } from "./state-keys-CQKlAFyo.js";
|
|
6
|
-
import { n as normalizeTarget, t as looksLikeQQBotTarget } from "./target-parser-DagYRQkP.js";
|
|
6
|
+
import { n as normalizeTarget, r as parseTarget, t as looksLikeQQBotTarget } from "./target-parser-DagYRQkP.js";
|
|
7
7
|
import { getExecApprovalReplyMetadata } from "openclaw/plugin-sdk/approval-runtime";
|
|
8
|
+
import { buildChannelOutboundSessionRoute } from "openclaw/plugin-sdk/channel-core";
|
|
8
9
|
import { createMessageReceiptFromOutboundResults, defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-outbound";
|
|
9
10
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
10
11
|
import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
|
|
@@ -12,16 +13,24 @@ import { createChannelApprovalCapability } from "openclaw/plugin-sdk/approval-de
|
|
|
12
13
|
import { createLazyChannelApprovalNativeRuntimeAdapter } from "openclaw/plugin-sdk/approval-handler-adapter-runtime";
|
|
13
14
|
import { resolveApprovalRequestChannelAccountId, resolveApprovalRequestSessionConversation } from "openclaw/plugin-sdk/approval-native-runtime";
|
|
14
15
|
import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
16
|
+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
15
17
|
import { markImplicitSameChatApprovalAuthorization, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
|
|
16
18
|
import { createChannelExecApprovalProfile, isChannelExecApprovalClientEnabledFromConfig, matchesApprovalRequestFilters } from "openclaw/plugin-sdk/approval-client-runtime";
|
|
17
19
|
import { normalizeAccountId } from "openclaw/plugin-sdk/routing";
|
|
18
20
|
import { resolveChannelGroupToolsPolicy } from "openclaw/plugin-sdk/channel-policy";
|
|
19
21
|
//#region extensions/qqbot/src/engine/approval/index.ts
|
|
22
|
+
/**
|
|
23
|
+
* Approval helpers — pure functions, zero framework dependencies.
|
|
24
|
+
*
|
|
25
|
+
* - Build approval message text + inline keyboard
|
|
26
|
+
* - Resolve delivery target from session metadata
|
|
27
|
+
* - Parse INTERACTION_CREATE button data
|
|
28
|
+
*/
|
|
20
29
|
function buildExecApprovalText(request) {
|
|
21
30
|
const expiresIn = Math.max(0, Math.round((request.expiresAtMs - Date.now()) / 1e3));
|
|
22
31
|
const lines = ["🔐 命令执行审批", ""];
|
|
23
32
|
const cmd = request.request.commandPreview ?? request.request.command ?? "";
|
|
24
|
-
if (cmd) lines.push(`\`\`\`\n${cmd
|
|
33
|
+
if (cmd) lines.push(`\`\`\`\n${truncateUtf16Safe(cmd, 300)}\n\`\`\``);
|
|
25
34
|
if (request.request.cwd) lines.push(`\u{1f4c1} \u76ee\u5f55: ${request.request.cwd}`);
|
|
26
35
|
if (request.request.agentId) lines.push(`\u{1f916} Agent: ${request.request.agentId}`);
|
|
27
36
|
lines.push("", `\u23f1\ufe0f \u8d85\u65f6: ${expiresIn} \u79d2`);
|
|
@@ -366,7 +375,7 @@ function createQQBotApprovalCapability() {
|
|
|
366
375
|
},
|
|
367
376
|
load: async () => {
|
|
368
377
|
ensurePlatformAdapter();
|
|
369
|
-
return (await import("./handler-runtime-
|
|
378
|
+
return (await import("./handler-runtime-zQvT6SrI.js")).qqbotApprovalNativeRuntime;
|
|
370
379
|
}
|
|
371
380
|
})
|
|
372
381
|
});
|
|
@@ -999,8 +1008,8 @@ function resolveQQBotGroupToolPolicy(params) {
|
|
|
999
1008
|
}
|
|
1000
1009
|
//#endregion
|
|
1001
1010
|
//#region extensions/qqbot/src/channel.ts
|
|
1002
|
-
const loadGatewayModule = createLazyRuntimeModule(() => import("./gateway-
|
|
1003
|
-
const loadOutboundMessagingModule = createLazyRuntimeModule(() => import("./outbound-
|
|
1011
|
+
const loadGatewayModule = createLazyRuntimeModule(() => import("./gateway-pJQppxe4.js"));
|
|
1012
|
+
const loadOutboundMessagingModule = createLazyRuntimeModule(() => import("./outbound-BIrfvvFJ.js").then((n) => n.t));
|
|
1004
1013
|
function createQQBotSendReceipt(params) {
|
|
1005
1014
|
const messageId = params.messageId?.trim();
|
|
1006
1015
|
return createMessageReceiptFromOutboundResults({
|
|
@@ -1013,6 +1022,25 @@ function createQQBotSendReceipt(params) {
|
|
|
1013
1022
|
kind: params.kind
|
|
1014
1023
|
});
|
|
1015
1024
|
}
|
|
1025
|
+
function resolveQQBotOutboundSessionRoute(params) {
|
|
1026
|
+
const target = parseTarget(params.target);
|
|
1027
|
+
const chatType = target.type === "c2c" ? "direct" : "group";
|
|
1028
|
+
const qualifiedTarget = `qqbot:${target.type}:${target.id}`;
|
|
1029
|
+
return buildChannelOutboundSessionRoute({
|
|
1030
|
+
cfg: params.cfg,
|
|
1031
|
+
agentId: params.agentId,
|
|
1032
|
+
channel: "qqbot",
|
|
1033
|
+
accountId: params.accountId,
|
|
1034
|
+
recipientSessionExact: true,
|
|
1035
|
+
peer: {
|
|
1036
|
+
kind: chatType,
|
|
1037
|
+
id: target.id
|
|
1038
|
+
},
|
|
1039
|
+
chatType,
|
|
1040
|
+
from: qualifiedTarget,
|
|
1041
|
+
to: qualifiedTarget
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1016
1044
|
async function sendQQBotText(params) {
|
|
1017
1045
|
await loadGatewayModule();
|
|
1018
1046
|
const account = resolveQQBotAccount(params.cfg, params.accountId);
|
|
@@ -1156,6 +1184,7 @@ const qqbotPlugin = {
|
|
|
1156
1184
|
targetPrefixes: ["qqbot"],
|
|
1157
1185
|
/** Normalize common QQ Bot target formats into the canonical qqbot:... form. */
|
|
1158
1186
|
normalizeTarget,
|
|
1187
|
+
resolveOutboundSessionRoute: (params) => resolveQQBotOutboundSessionRoute(params),
|
|
1159
1188
|
targetResolver: {
|
|
1160
1189
|
/** Return true when the id looks like a QQ Bot target. */
|
|
1161
1190
|
looksLikeId: looksLikeQQBotTarget,
|
|
@@ -1225,7 +1254,8 @@ const qqbotPlugin = {
|
|
|
1225
1254
|
...ctx.getStatus(),
|
|
1226
1255
|
running: true,
|
|
1227
1256
|
connected: true,
|
|
1228
|
-
lastConnectedAt: Date.now()
|
|
1257
|
+
lastConnectedAt: Date.now(),
|
|
1258
|
+
lastError: null
|
|
1229
1259
|
});
|
|
1230
1260
|
persistAccountCredentialSnapshot(account);
|
|
1231
1261
|
},
|
|
@@ -1235,7 +1265,8 @@ const qqbotPlugin = {
|
|
|
1235
1265
|
...ctx.getStatus(),
|
|
1236
1266
|
running: true,
|
|
1237
1267
|
connected: true,
|
|
1238
|
-
lastConnectedAt: Date.now()
|
|
1268
|
+
lastConnectedAt: Date.now(),
|
|
1269
|
+
lastError: null
|
|
1239
1270
|
});
|
|
1240
1271
|
persistAccountCredentialSnapshot(account);
|
|
1241
1272
|
},
|
|
@@ -1245,6 +1276,14 @@ const qqbotPlugin = {
|
|
|
1245
1276
|
...ctx.getStatus(),
|
|
1246
1277
|
lastError: error.message
|
|
1247
1278
|
});
|
|
1279
|
+
},
|
|
1280
|
+
onDisconnected: ({ reason, fatal }) => {
|
|
1281
|
+
log?.info(`[qqbot:${account.accountId}] Gateway disconnected${reason ? `: ${reason}` : ""}`);
|
|
1282
|
+
ctx.setStatus({
|
|
1283
|
+
...ctx.getStatus(),
|
|
1284
|
+
connected: false,
|
|
1285
|
+
...fatal && reason ? { lastError: reason } : {}
|
|
1286
|
+
});
|
|
1248
1287
|
}
|
|
1249
1288
|
});
|
|
1250
1289
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as resolveQQBotAccount } from "./config-C1qZbh0K.js";
|
|
2
|
-
import { d as PRIVATE_CHAT_ONLY_TEXT, n as resolveGroupCommandLevelFromAccountConfig, o as getFrameworkCommands } from "./group-
|
|
3
|
-
import { h as sendDocument } from "./outbound-
|
|
4
|
-
import { t as registerQQBotTools } from "./tools-
|
|
2
|
+
import { d as PRIVATE_CHAT_ONLY_TEXT, n as resolveGroupCommandLevelFromAccountConfig, o as getFrameworkCommands } from "./group-o0GmovSf.js";
|
|
3
|
+
import { h as sendDocument } from "./outbound-BIrfvvFJ.js";
|
|
4
|
+
import { t as registerQQBotTools } from "./tools-UJJ-tLHP.js";
|
|
5
5
|
//#region extensions/qqbot/src/bridge/commands/framework-context-adapter.ts
|
|
6
6
|
/**
|
|
7
7
|
* Default queue snapshot used for framework-registered commands.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as registerQQBotFull } from "./channel-entry-
|
|
1
|
+
import { t as registerQQBotFull } from "./channel-entry-C5YdhX3Y.js";
|
|
2
2
|
export { registerQQBotFull };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as qqbotPlugin } from "./channel-
|
|
1
|
+
import { t as qqbotPlugin } from "./channel-D1UztsnG.js";
|
|
2
2
|
export { qqbotPlugin };
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { C as getNextMsgSeq, D as downloadFile, F as StreamInputMode, I as StreamInputState, M as getMaxUploadSize, P as StreamContentType, T as openLocalFile, a as createRawInputNotifyFn, b as stopBackgroundTokenRefresh, c as getMessageApi, d as initSender, f as onMessageSent, g as sendText, h as sendMedia, i as clearTokenCache, j as getImageMimeType, k as formatFileSize, l as getPluginUserAgent, m as sendInputNotify, n as acknowledgeInteraction, o as getAccessToken, p as registerAccount, r as buildDeliveryTarget, s as getGatewayUrl, t as accountToCreds, u as initApiConfig, x as withTokenRetry, y as startBackgroundTokenRefresh } from "./sender-
|
|
1
|
+
import { C as getNextMsgSeq, D as downloadFile, F as StreamInputMode, I as StreamInputState, M as getMaxUploadSize, P as StreamContentType, T as openLocalFile, a as createRawInputNotifyFn, b as stopBackgroundTokenRefresh, c as getMessageApi, d as initSender, f as onMessageSent, g as sendText, h as sendMedia, i as clearTokenCache, j as getImageMimeType, k as formatFileSize, l as getPluginUserAgent, m as sendInputNotify, n as acknowledgeInteraction, o as getAccessToken, p as registerAccount, r as buildDeliveryTarget, s as getGatewayUrl, t as accountToCreds, u as initApiConfig, x as withTokenRetry, y as startBackgroundTokenRefresh } from "./sender-CjDuU-uz.js";
|
|
2
2
|
import { c as getPlatformAdapter, i as normalizeOptionalString$1, n as normalizeLowercaseStringOrEmpty$1, o as readStringField, s as sanitizeFileName, t as asOptionalObjectRecord } from "./string-normalize-R_0cKO7Q.js";
|
|
3
3
|
import { c as setBridgeLogger, o as ensurePlatformAdapter } from "./config-schema-JZEf1dvB.js";
|
|
4
|
-
import { a as authorizeQQBotApprovalAction, i as toGatewayAccount, n as createQQBotMarkdownChunker, p as parseApprovalButtonData, r as openQQBotSyncKeyedStore } from "./channel-
|
|
4
|
+
import { a as authorizeQQBotApprovalAction, i as toGatewayAccount, n as createQQBotMarkdownChunker, p as parseApprovalButtonData, r as openQQBotSyncKeyedStore } from "./channel-D1UztsnG.js";
|
|
5
5
|
import { d as resolveAccountBase } from "./config-C1qZbh0K.js";
|
|
6
|
-
import { a as formatErrorMessage, i as formatDuration, n as debugLog, r as debugWarn, t as debugError } from "./log-
|
|
7
|
-
import { n as getQQBotRuntimeForEngine, t as getQQBotRuntime } from "./runtime-
|
|
6
|
+
import { a as formatErrorMessage, i as formatDuration, n as debugLog, r as debugWarn, t as debugError } from "./log-DEtcoDWe.js";
|
|
7
|
+
import { n as getQQBotRuntimeForEngine, t as getQQBotRuntime } from "./runtime-DodcT_fQ.js";
|
|
8
8
|
import { t as buildQQBotStateKey } from "./state-keys-CQKlAFyo.js";
|
|
9
|
-
import { a as resolveMentionPatterns, c as getPluginVersion, d as PRIVATE_CHAT_ONLY_TEXT, f as classifyCoreCommandForGroup, i as resolveGroupSettings, l as initCommands, n as resolveGroupCommandLevelFromAccountConfig, r as resolveGroupConfig, s as getFrameworkVersion, u as matchSlashCommand } from "./group-
|
|
10
|
-
import { B as normalizePath, C as resolveUserFacingMediaError, F as getQQBotMediaDir, L as getTempDir, M as checkSilkWasmAvailable, N as getHomeDir, P as getQQBotDataDir, R as isLocalPath, S as resolveWorkspaceScopedLocalRoots, _ as sendVideoMsg, b as resolveOutboundMediaLocalRoots, c as isCronReminderPayload, d as normalizeMediaTags, g as sendPhoto, h as sendDocument, j as setOutboundAudioPort, k as DEFAULT_MEDIA_SEND_ERROR, l as isMediaPayload, o as decodeMediaPath, r as sendMedia$1, s as encodePayloadForCron, u as parseQQBotPayload, v as sendVoice, x as resolveWorkspacePathCandidates, y as resolveTrustedOutboundMediaPath, z as isWindows } from "./outbound-
|
|
9
|
+
import { a as resolveMentionPatterns, c as getPluginVersion, d as PRIVATE_CHAT_ONLY_TEXT, f as classifyCoreCommandForGroup, i as resolveGroupSettings, l as initCommands, n as resolveGroupCommandLevelFromAccountConfig, r as resolveGroupConfig, s as getFrameworkVersion, u as matchSlashCommand } from "./group-o0GmovSf.js";
|
|
10
|
+
import { B as normalizePath, C as resolveUserFacingMediaError, F as getQQBotMediaDir, L as getTempDir, M as checkSilkWasmAvailable, N as getHomeDir, P as getQQBotDataDir, R as isLocalPath, S as resolveWorkspaceScopedLocalRoots, _ as sendVideoMsg, b as resolveOutboundMediaLocalRoots, c as isCronReminderPayload, d as normalizeMediaTags, g as sendPhoto, h as sendDocument, j as setOutboundAudioPort, k as DEFAULT_MEDIA_SEND_ERROR, l as isMediaPayload, o as decodeMediaPath, r as sendMedia$1, s as encodePayloadForCron, u as parseQQBotPayload, v as sendVoice, x as resolveWorkspacePathCandidates, y as resolveTrustedOutboundMediaPath, z as isWindows } from "./outbound-BIrfvvFJ.js";
|
|
11
11
|
import { n as runWithRequestContext } from "./request-context-Bm7PTBD1.js";
|
|
12
12
|
import { uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
13
|
+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
13
14
|
import { isImplicitSameChatApprovalAuthorization } from "openclaw/plugin-sdk/approval-auth-runtime";
|
|
14
15
|
import * as fs$1 from "node:fs";
|
|
15
16
|
import fs from "node:fs";
|
|
@@ -459,7 +460,7 @@ async function trySlashCommand(msg, ctx) {
|
|
|
459
460
|
const contentLower = content.toLowerCase();
|
|
460
461
|
if (URGENT_COMMANDS.some((cmd) => contentLower === cmd.toLowerCase() || contentLower.startsWith(cmd.toLowerCase() + " "))) {
|
|
461
462
|
if (isGroup && !commandAuthorized) return "enqueue";
|
|
462
|
-
log?.info(`Urgent command detected: ${content
|
|
463
|
+
log?.info(`Urgent command detected: ${truncateUtf16Safe(content, 20)}`);
|
|
463
464
|
return "urgent";
|
|
464
465
|
}
|
|
465
466
|
const receivedAt = Date.now();
|
|
@@ -1155,7 +1156,7 @@ function createMessageQueue(ctx) {
|
|
|
1155
1156
|
for (const m of batch) if (isSlashCommand(m)) commands.push(m);
|
|
1156
1157
|
else normal.push(m);
|
|
1157
1158
|
for (const cmd of commands) {
|
|
1158
|
-
log?.debug?.(`Processing command independently for ${peerId}: ${(cmd.content ?? "").trim()
|
|
1159
|
+
log?.debug?.(`Processing command independently for ${peerId}: ${truncateUtf16Safe((cmd.content ?? "").trim(), 50)}`);
|
|
1159
1160
|
await processOne(cmd, peerId, "Command processor");
|
|
1160
1161
|
}
|
|
1161
1162
|
if (normal.length > 0) {
|
|
@@ -1488,6 +1489,10 @@ var GatewayConnection = class {
|
|
|
1488
1489
|
const { account: _account, log } = this.ctx;
|
|
1489
1490
|
if (this.isAborted || this.reconnect.isExhausted()) {
|
|
1490
1491
|
log?.error(`Max reconnect attempts reached or aborted`);
|
|
1492
|
+
if (!this.isAborted) this.ctx.onDisconnected?.({
|
|
1493
|
+
reason: "reconnect attempts exhausted",
|
|
1494
|
+
fatal: true
|
|
1495
|
+
});
|
|
1491
1496
|
return;
|
|
1492
1497
|
}
|
|
1493
1498
|
if (this.reconnectTimer) {
|
|
@@ -1579,11 +1584,20 @@ var GatewayConnection = class {
|
|
|
1579
1584
|
}
|
|
1580
1585
|
case GatewayOp.HEARTBEAT_ACK: break;
|
|
1581
1586
|
case GatewayOp.RECONNECT:
|
|
1587
|
+
this.ctx.onDisconnected?.({
|
|
1588
|
+
reason: "server requested reconnect",
|
|
1589
|
+
fatal: false
|
|
1590
|
+
});
|
|
1582
1591
|
this.cleanup();
|
|
1583
1592
|
this.scheduleReconnect();
|
|
1584
1593
|
break;
|
|
1585
|
-
case GatewayOp.INVALID_SESSION:
|
|
1586
|
-
|
|
1594
|
+
case GatewayOp.INVALID_SESSION: {
|
|
1595
|
+
const canResume = d;
|
|
1596
|
+
this.ctx.onDisconnected?.({
|
|
1597
|
+
reason: canResume ? "session resume rejected" : "session invalidated",
|
|
1598
|
+
fatal: false
|
|
1599
|
+
});
|
|
1600
|
+
if (!canResume) {
|
|
1587
1601
|
this.sessionId = null;
|
|
1588
1602
|
this.lastSeq = null;
|
|
1589
1603
|
clearSession(account.accountId);
|
|
@@ -1592,6 +1606,7 @@ var GatewayConnection = class {
|
|
|
1592
1606
|
this.cleanup();
|
|
1593
1607
|
this.scheduleReconnect(3e3);
|
|
1594
1608
|
break;
|
|
1609
|
+
}
|
|
1595
1610
|
}
|
|
1596
1611
|
} catch (err) {
|
|
1597
1612
|
log?.error(`Message parse error: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -1599,6 +1614,7 @@ var GatewayConnection = class {
|
|
|
1599
1614
|
});
|
|
1600
1615
|
ws.on("close", (code, reason) => {
|
|
1601
1616
|
log?.info(`WebSocket closed: ${code} ${reason.toString()}`);
|
|
1617
|
+
if (this.currentWs !== ws) return;
|
|
1602
1618
|
this.isConnecting = false;
|
|
1603
1619
|
this.handleClose(code);
|
|
1604
1620
|
});
|
|
@@ -1650,6 +1666,10 @@ var GatewayConnection = class {
|
|
|
1650
1666
|
}
|
|
1651
1667
|
if (action.refreshToken) this.shouldRefreshToken = true;
|
|
1652
1668
|
this.cleanup();
|
|
1669
|
+
if (!this.isAborted) this.ctx.onDisconnected?.({
|
|
1670
|
+
reason: action.reason,
|
|
1671
|
+
fatal: action.fatal
|
|
1672
|
+
});
|
|
1653
1673
|
if (action.fatal) return;
|
|
1654
1674
|
if (action.shouldReconnect) this.scheduleReconnect(action.reconnectDelay);
|
|
1655
1675
|
}
|
|
@@ -1717,7 +1737,7 @@ async function transcribeAudio(audioPath, cfg) {
|
|
|
1717
1737
|
try {
|
|
1718
1738
|
if (!resp.ok) {
|
|
1719
1739
|
const detail = await readResponseTextLimited(resp, STT_ERROR_BODY_LIMIT_BYTES).catch(() => "");
|
|
1720
|
-
throw new Error(`STT failed (HTTP ${resp.status}): ${detail
|
|
1740
|
+
throw new Error(`STT failed (HTTP ${resp.status}): ${truncateUtf16Safe(detail, 300)}`);
|
|
1721
1741
|
}
|
|
1722
1742
|
return normalizeOptionalString$1((await readProviderJsonResponse(resp, "qqbot.stt")).text) ?? null;
|
|
1723
1743
|
} finally {
|
|
@@ -1925,7 +1945,7 @@ async function processVoiceAttachment(localPath, audioPathInput, att, asrReferTe
|
|
|
1925
1945
|
try {
|
|
1926
1946
|
const transcript = await transcribeAudio(audioPath, cfg);
|
|
1927
1947
|
if (transcript) {
|
|
1928
|
-
log?.debug?.(`STT transcript: ${transcript
|
|
1948
|
+
log?.debug?.(`STT transcript: ${truncateUtf16Safe(transcript, 100)}...`);
|
|
1929
1949
|
return {
|
|
1930
1950
|
localPath,
|
|
1931
1951
|
type: "voice",
|
|
@@ -2671,7 +2691,7 @@ async function resolveQuote(event, deps) {
|
|
|
2671
2691
|
cfg: account.config,
|
|
2672
2692
|
log
|
|
2673
2693
|
}, attachmentProcessor);
|
|
2674
|
-
log?.debug?.(`Quote detected via msg_elements[0] (cache miss): id=${event.refMsgIdx}, content="${(refBody ?? ""
|
|
2694
|
+
log?.debug?.(`Quote detected via msg_elements[0] (cache miss): id=${event.refMsgIdx}, content="${truncateUtf16Safe(refBody ?? "", 80)}..."`);
|
|
2675
2695
|
return {
|
|
2676
2696
|
id: event.refMsgIdx,
|
|
2677
2697
|
body: refBody || void 0,
|
|
@@ -3297,6 +3317,13 @@ function hasQQBotImageSize(markdownImage) {
|
|
|
3297
3317
|
}
|
|
3298
3318
|
//#endregion
|
|
3299
3319
|
//#region extensions/qqbot/src/engine/messaging/outbound-deliver.ts
|
|
3320
|
+
/**
|
|
3321
|
+
* Outbound delivery helpers — core/ version.
|
|
3322
|
+
*
|
|
3323
|
+
* Uses the unified `sender.ts` business function layer for all text and
|
|
3324
|
+
* image sending. Media sends (photo/voice/video/file) are injected via
|
|
3325
|
+
* `DeliverDeps.mediaSender`.
|
|
3326
|
+
*/
|
|
3300
3327
|
/** Maximum text length for a single QQ Bot message. */
|
|
3301
3328
|
const TEXT_CHUNK_LIMIT = 5e3;
|
|
3302
3329
|
function resolveMediaTargetContext(event, actx) {
|
|
@@ -3368,7 +3395,7 @@ async function sendTextChunks(text, event, actx, sendWithRetry, consumeQuoteRef,
|
|
|
3368
3395
|
consumeQuoteRef,
|
|
3369
3396
|
allowDm: true,
|
|
3370
3397
|
log,
|
|
3371
|
-
onSuccess: (chunk) => `Sent text chunk (${chunk.length}/${text.length} chars): ${chunk
|
|
3398
|
+
onSuccess: (chunk) => `Sent text chunk (${chunk.length}/${text.length} chars): ${truncateUtf16Safe(chunk, 50)}...`,
|
|
3372
3399
|
onError: (err) => `Failed to send text chunk: ${formatErrorMessage(err)}`
|
|
3373
3400
|
});
|
|
3374
3401
|
}
|
|
@@ -3385,7 +3412,7 @@ async function sendTextOnlyReply(text, event, actx, sendWithRetry, consumeQuoteR
|
|
|
3385
3412
|
allowDm: true,
|
|
3386
3413
|
forcePlainText: true,
|
|
3387
3414
|
log,
|
|
3388
|
-
onSuccess: (chunk) => `Sent text-only chunk (${chunk.length}/${safeText.length} chars): ${chunk
|
|
3415
|
+
onSuccess: (chunk) => `Sent text-only chunk (${chunk.length}/${safeText.length} chars): ${truncateUtf16Safe(chunk, 50)}...`,
|
|
3389
3416
|
onError: (err) => `Failed to send text-only chunk: ${formatErrorMessage(err)}`
|
|
3390
3417
|
});
|
|
3391
3418
|
}
|
|
@@ -3593,7 +3620,7 @@ async function sendPlainReply(payload, replyText, event, actx, sendWithRetry, co
|
|
|
3593
3620
|
if (isRemoteHttpUrl || isDataUrl) {
|
|
3594
3621
|
if (!collectedImageUrls.includes(url)) {
|
|
3595
3622
|
collectedImageUrls.push(url);
|
|
3596
|
-
log?.debug?.(`Collected ${isDataUrl ? "Base64" : "media URL"}: ${isDataUrl ? `(length: ${url.length})` : url
|
|
3623
|
+
log?.debug?.(`Collected ${isDataUrl ? "Base64" : "media URL"}: ${isDataUrl ? `(length: ${url.length})` : truncateUtf16Safe(url, 80) + "..."}`);
|
|
3597
3624
|
}
|
|
3598
3625
|
return true;
|
|
3599
3626
|
}
|
|
@@ -3614,7 +3641,7 @@ async function sendPlainReply(payload, replyText, event, actx, sendWithRetry, co
|
|
|
3614
3641
|
if (url && !collectedImageUrls.includes(url)) {
|
|
3615
3642
|
if (isHttpUrl(url)) {
|
|
3616
3643
|
collectedImageUrls.push(url);
|
|
3617
|
-
log?.debug?.(`Extracted HTTP image from markdown: ${url
|
|
3644
|
+
log?.debug?.(`Extracted HTTP image from markdown: ${truncateUtf16Safe(url, 80)}...`);
|
|
3618
3645
|
} else if (isLocalPath(url)) {
|
|
3619
3646
|
if (!localMediaToSend.includes(url)) {
|
|
3620
3647
|
localMediaToSend.push(url);
|
|
@@ -3628,7 +3655,7 @@ async function sendPlainReply(payload, replyText, event, actx, sendWithRetry, co
|
|
|
3628
3655
|
const url = m[1];
|
|
3629
3656
|
if (url && !collectedImageUrls.includes(url)) {
|
|
3630
3657
|
collectedImageUrls.push(url);
|
|
3631
|
-
log?.debug?.(`Extracted bare image URL: ${url
|
|
3658
|
+
log?.debug?.(`Extracted bare image URL: ${truncateUtf16Safe(url, 80)}...`);
|
|
3632
3659
|
}
|
|
3633
3660
|
}
|
|
3634
3661
|
const useMarkdown = account.markdownSupport;
|
|
@@ -3676,7 +3703,7 @@ async function sendPlainReply(payload, replyText, event, actx, sendWithRetry, co
|
|
|
3676
3703
|
...actx.mediaLocalRoots ? { mediaLocalRoots: actx.mediaLocalRoots } : {},
|
|
3677
3704
|
...actx.mediaReadFile ? { mediaReadFile: actx.mediaReadFile } : {},
|
|
3678
3705
|
log,
|
|
3679
|
-
onSuccess: (mediaUrl) => `Forwarded tool media: ${mediaUrl
|
|
3706
|
+
onSuccess: (mediaUrl) => `Forwarded tool media: ${truncateUtf16Safe(mediaUrl, 80)}...`,
|
|
3680
3707
|
onResultError: (_mediaUrl, error) => `Tool media forward error: ${error}`,
|
|
3681
3708
|
onThrownError: (_mediaUrl, error) => `Tool media forward failed: ${error}`
|
|
3682
3709
|
});
|
|
@@ -3716,7 +3743,7 @@ async function sendMarkdownReply(textWithoutImages, imageUrls, mdMatches, bareUr
|
|
|
3716
3743
|
for (const url of httpImageUrls) if (!existingMdUrls.has(url)) try {
|
|
3717
3744
|
const size = await getImageSize(url);
|
|
3718
3745
|
imagesToAppend.push(formatQQBotMarkdownImage(url, size));
|
|
3719
|
-
log?.debug?.(`Formatted HTTP image: ${size ? `${size.width}x${size.height}` : "default size"} - ${url
|
|
3746
|
+
log?.debug?.(`Formatted HTTP image: ${size ? `${size.width}x${size.height}` : "default size"} - ${truncateUtf16Safe(url, 60)}...`);
|
|
3720
3747
|
} catch (err) {
|
|
3721
3748
|
log?.debug?.(`Failed to get image size, using default: ${formatErrorMessage(err)}`);
|
|
3722
3749
|
imagesToAppend.push(formatQQBotMarkdownImage(url, null));
|
|
@@ -3728,7 +3755,7 @@ async function sendMarkdownReply(textWithoutImages, imageUrls, mdMatches, bareUr
|
|
|
3728
3755
|
if (isHttpUrl(imgUrl) && !hasQQBotImageSize(fullMatch)) try {
|
|
3729
3756
|
const size = await getImageSize(imgUrl);
|
|
3730
3757
|
result = result.replace(fullMatch, formatQQBotMarkdownImage(imgUrl, size));
|
|
3731
|
-
log?.debug?.(`Updated image with size: ${size ? `${size.width}x${size.height}` : "default"} - ${imgUrl
|
|
3758
|
+
log?.debug?.(`Updated image with size: ${size ? `${size.width}x${size.height}` : "default"} - ${truncateUtf16Safe(imgUrl, 60)}...`);
|
|
3732
3759
|
} catch (err) {
|
|
3733
3760
|
log?.debug?.(`Failed to get image size for existing md, using default: ${formatErrorMessage(err)}`);
|
|
3734
3761
|
result = result.replace(fullMatch, formatQQBotMarkdownImage(imgUrl, null));
|
|
@@ -3764,7 +3791,7 @@ async function sendPlainTextReply(textWithoutImages, imageUrls, mdMatches, bareU
|
|
|
3764
3791
|
imageUrl,
|
|
3765
3792
|
mediaSender: deps.mediaSender,
|
|
3766
3793
|
log,
|
|
3767
|
-
onSuccess: (nextImageUrl) => `Sent image via sendPhoto: ${nextImageUrl
|
|
3794
|
+
onSuccess: (nextImageUrl) => `Sent image via sendPhoto: ${truncateUtf16Safe(nextImageUrl, 80)}...`,
|
|
3768
3795
|
onError: (error) => `Failed to send image: ${error}`
|
|
3769
3796
|
});
|
|
3770
3797
|
if (result.trim()) await sendTextChunksWithRetry({
|
|
@@ -3905,7 +3932,7 @@ function resolveStructuredPayloadPath(ctx, payload, mediaType) {
|
|
|
3905
3932
|
};
|
|
3906
3933
|
}
|
|
3907
3934
|
function sanitizeForLog(value, maxLen = 200) {
|
|
3908
|
-
return value.replace(/[\r\n\t]/g, " ").replaceAll("\0", " ")
|
|
3935
|
+
return truncateUtf16Safe(value.replace(/[\r\n\t]/g, " ").replaceAll("\0", " "), maxLen);
|
|
3909
3936
|
}
|
|
3910
3937
|
function describeMediaTargetForLog(pathValue, isHttpUrl) {
|
|
3911
3938
|
if (!isHttpUrl) return "<local-file>";
|
|
@@ -4053,7 +4080,7 @@ async function sendTextAsVoiceReply(ctx, text, deps) {
|
|
|
4053
4080
|
log?.error(`Voice missing text`);
|
|
4054
4081
|
return false;
|
|
4055
4082
|
}
|
|
4056
|
-
log?.debug?.(`TTS: "${ttsText
|
|
4083
|
+
log?.debug?.(`TTS: "${truncateUtf16Safe(ttsText, 50)}..."`);
|
|
4057
4084
|
const ttsResult = await deps.tts.textToSpeech({
|
|
4058
4085
|
text: ttsText,
|
|
4059
4086
|
cfg,
|
|
@@ -4212,6 +4239,12 @@ async function handleFilePayload(ctx, payload) {
|
|
|
4212
4239
|
}
|
|
4213
4240
|
//#endregion
|
|
4214
4241
|
//#region extensions/qqbot/src/engine/messaging/streaming-media-send.ts
|
|
4242
|
+
/**
|
|
4243
|
+
* 富媒体标签解析与发送队列
|
|
4244
|
+
*
|
|
4245
|
+
* 提供媒体标签(qqimg / qqvoice / qqvideo / qqfile / qqmedia)的检测、
|
|
4246
|
+
* 拆分、路径编码修复,以及统一的发送队列执行器。
|
|
4247
|
+
*/
|
|
4215
4248
|
function formatStreamSendErr(e) {
|
|
4216
4249
|
return e instanceof Error ? e.message : String(e);
|
|
4217
4250
|
}
|
|
@@ -4358,7 +4391,7 @@ async function executeSendQueue(queue, ctx, options = {}) {
|
|
|
4358
4391
|
else log?.info(`${prefix} executeSendQueue: no onSendText handler, skipping text`);
|
|
4359
4392
|
continue;
|
|
4360
4393
|
}
|
|
4361
|
-
log?.info(`${prefix} executeSendQueue: sending ${item.type}: ${item.content
|
|
4394
|
+
log?.info(`${prefix} executeSendQueue: sending ${item.type}: ${truncateUtf16Safe(item.content, 80)}...`);
|
|
4362
4395
|
if (item.type === "image") {
|
|
4363
4396
|
const result = await sendPhoto(mediaTarget, item.content);
|
|
4364
4397
|
if (result.error) {
|
|
@@ -4565,11 +4598,11 @@ const THROTTLE_CONSTANTS = {
|
|
|
4565
4598
|
BATCH_AFTER_GAP_MS: 300
|
|
4566
4599
|
};
|
|
4567
4600
|
/** 终态集合 */
|
|
4568
|
-
const TERMINAL_PHASES = new Set(["completed", "aborted"]);
|
|
4601
|
+
const TERMINAL_PHASES = /* @__PURE__ */ new Set(["completed", "aborted"]);
|
|
4569
4602
|
/** 允许的状态转换 */
|
|
4570
4603
|
const PHASE_TRANSITIONS = {
|
|
4571
|
-
idle: new Set(["streaming", "aborted"]),
|
|
4572
|
-
streaming: new Set([
|
|
4604
|
+
idle: /* @__PURE__ */ new Set(["streaming", "aborted"]),
|
|
4605
|
+
streaming: /* @__PURE__ */ new Set([
|
|
4573
4606
|
"idle",
|
|
4574
4607
|
"completed",
|
|
4575
4608
|
"aborted"
|
|
@@ -4852,7 +4885,7 @@ var StreamingController = class {
|
|
|
4852
4885
|
*/
|
|
4853
4886
|
async onDeliver(payload) {
|
|
4854
4887
|
const rawLen = payload.text?.length ?? 0;
|
|
4855
|
-
const preview = (payload.text ?? ""
|
|
4888
|
+
const preview = truncateUtf16Safe(payload.text ?? "", 60).replace(/\n/g, "\\n");
|
|
4856
4889
|
this.logDebug(`onDeliver: rawLen=${rawLen}, phase=${this.phase}, streamMsgId=${this.streamMsgId}, sentIndex=${this.sentIndex}, sentChunks=${this.sentStreamChunkCount}, firstCB=${this.firstCallbackSource}, preview="${preview}"`);
|
|
4857
4890
|
if (this.isTerminalPhase) {
|
|
4858
4891
|
this.logDebug(`onDeliver: skipped (terminal phase)`);
|
|
@@ -5000,7 +5033,7 @@ var StreamingController = class {
|
|
|
5000
5033
|
if (this.isTerminalPhase) return;
|
|
5001
5034
|
const found = findFirstClosedMediaTag(normalizedFull.slice(this.sentIndex));
|
|
5002
5035
|
if (!found) break;
|
|
5003
|
-
this.logInfo(`processMediaTags: found <${found.tagName}> at offset ${this.sentIndex}, textBefore="${found.textBefore
|
|
5036
|
+
this.logInfo(`processMediaTags: found <${found.tagName}> at offset ${this.sentIndex}, textBefore="${truncateUtf16Safe(found.textBefore, 40)}"`);
|
|
5004
5037
|
const textBeforeEndInFull = this.sentIndex + found.textBefore.length;
|
|
5005
5038
|
await this.endCurrentStreamIfNeeded("processMediaTags:closedTag", textBeforeEndInFull);
|
|
5006
5039
|
if (this.isTerminalPhase) return;
|
|
@@ -5009,7 +5042,7 @@ var StreamingController = class {
|
|
|
5009
5042
|
type: found.itemType,
|
|
5010
5043
|
content: found.mediaPath
|
|
5011
5044
|
};
|
|
5012
|
-
this.logDebug(`processMediaTags: sending ${found.itemType}: ${found.mediaPath
|
|
5045
|
+
this.logDebug(`processMediaTags: sending ${found.itemType}: ${truncateUtf16Safe(found.mediaPath, 80)}`);
|
|
5013
5046
|
await sendMediaQueue([item], this.deps.mediaContext);
|
|
5014
5047
|
this.sentMediaCount++;
|
|
5015
5048
|
this.sentStreamChunkCount++;
|
|
@@ -5334,7 +5367,7 @@ function isAudioFile(filePath, mimeType) {
|
|
|
5334
5367
|
".pcm"
|
|
5335
5368
|
].includes(ext);
|
|
5336
5369
|
}
|
|
5337
|
-
const QQ_NATIVE_VOICE_MIMES = new Set([
|
|
5370
|
+
const QQ_NATIVE_VOICE_MIMES = /* @__PURE__ */ new Set([
|
|
5338
5371
|
"audio/silk",
|
|
5339
5372
|
"audio/amr",
|
|
5340
5373
|
"audio/wav",
|
|
@@ -5343,7 +5376,7 @@ const QQ_NATIVE_VOICE_MIMES = new Set([
|
|
|
5343
5376
|
"audio/mpeg",
|
|
5344
5377
|
"audio/mp3"
|
|
5345
5378
|
]);
|
|
5346
|
-
const QQ_NATIVE_VOICE_EXTS = new Set([
|
|
5379
|
+
const QQ_NATIVE_VOICE_EXTS = /* @__PURE__ */ new Set([
|
|
5347
5380
|
".silk",
|
|
5348
5381
|
".slk",
|
|
5349
5382
|
".amr",
|
|
@@ -5807,7 +5840,7 @@ async function dispatchOutbound(inbound, deps) {
|
|
|
5807
5840
|
resultError: "Tool fallback error",
|
|
5808
5841
|
thrownError: "Tool fallback failed"
|
|
5809
5842
|
});
|
|
5810
|
-
if (toolTexts.length > 0) await sendErrorMessage(toolTexts.slice(-3).join("\n---\n")
|
|
5843
|
+
if (toolTexts.length > 0) await sendErrorMessage(truncateUtf16Safe(toolTexts.slice(-3).join("\n---\n"), 2e3));
|
|
5811
5844
|
};
|
|
5812
5845
|
const hasPendingToolFallbackPayload = () => toolTexts.length > 0 || toolMediaUrls.length > 0;
|
|
5813
5846
|
const flushPendingToolDeliveriesOnce = async () => {
|
|
@@ -6308,7 +6341,7 @@ async function startGateway$1(ctx) {
|
|
|
6308
6341
|
initApiConfig(account.appId, { markdownSupport: account.markdownSupport });
|
|
6309
6342
|
log?.debug?.(`API config: markdownSupport=${account.markdownSupport}`);
|
|
6310
6343
|
onMessageSent(account.appId, (refIdx, meta) => {
|
|
6311
|
-
log?.info(`onMessageSent called: refIdx=${refIdx}, mediaType=${meta.mediaType}, ttsText=${meta.ttsText
|
|
6344
|
+
log?.info(`onMessageSent called: refIdx=${refIdx}, mediaType=${meta.mediaType}, ttsText=${meta.ttsText === void 0 ? void 0 : truncateUtf16Safe(meta.ttsText, 30)}`);
|
|
6312
6345
|
const attachments = [];
|
|
6313
6346
|
if (meta.mediaType) {
|
|
6314
6347
|
const localPath = meta.mediaLocalPath;
|
|
@@ -6455,6 +6488,7 @@ async function startGateway$1(ctx) {
|
|
|
6455
6488
|
onReady: ctx.onReady,
|
|
6456
6489
|
onResumed: ctx.onResumed,
|
|
6457
6490
|
onError: ctx.onError,
|
|
6491
|
+
onDisconnected: ctx.onDisconnected,
|
|
6458
6492
|
onInteraction: handleInteraction,
|
|
6459
6493
|
handleMessage
|
|
6460
6494
|
}).start();
|
|
@@ -6770,6 +6804,7 @@ async function startGateway(ctx) {
|
|
|
6770
6804
|
onReady: ctx.onReady,
|
|
6771
6805
|
onResumed: ctx.onResumed,
|
|
6772
6806
|
onError: ctx.onError,
|
|
6807
|
+
onDisconnected: ctx.onDisconnected,
|
|
6773
6808
|
log: accountLogger,
|
|
6774
6809
|
runtime,
|
|
6775
6810
|
adapters: createEngineAdapters()
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { t as asOptionalObjectRecord } from "./string-normalize-R_0cKO7Q.js";
|
|
2
2
|
import { d as resolveAccountBase } from "./config-C1qZbh0K.js";
|
|
3
|
-
import { n as debugLog } from "./log-
|
|
4
|
-
import { I as getQQBotMediaPath, N as getHomeDir, P as getQQBotDataDir, z as isWindows } from "./outbound-
|
|
3
|
+
import { n as debugLog } from "./log-DEtcoDWe.js";
|
|
4
|
+
import { I as getQQBotMediaPath, N as getHomeDir, P as getQQBotDataDir, z as isWindows } from "./outbound-BIrfvvFJ.js";
|
|
5
5
|
import { asBoolean, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
6
6
|
import fs from "node:fs";
|
|
7
7
|
import path from "node:path";
|
|
8
|
+
import { formatByteSize } from "openclaw/plugin-sdk/number-runtime";
|
|
8
9
|
import { clearRuntimeConfigSnapshot } from "openclaw/plugin-sdk/runtime-config-snapshot";
|
|
9
10
|
import { loadJsonFile } from "openclaw/plugin-sdk/json-store";
|
|
10
11
|
//#region extensions/qqbot/src/engine/commands/builtin/state.ts
|
|
@@ -321,10 +322,12 @@ function scanDirectoryFiles(dirPath) {
|
|
|
321
322
|
return files;
|
|
322
323
|
}
|
|
323
324
|
function formatBytes(bytes) {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
325
|
+
return formatByteSize(bytes, {
|
|
326
|
+
style: "legacy-binary",
|
|
327
|
+
maxUnit: "giga",
|
|
328
|
+
separator: " ",
|
|
329
|
+
fractionDigits: (_value, unit) => unit === "byte" ? null : 1
|
|
330
|
+
});
|
|
328
331
|
}
|
|
329
332
|
function removeEmptyDirs(dirPath) {
|
|
330
333
|
if (!fs.existsSync(dirPath)) return;
|
|
@@ -549,7 +552,7 @@ function collectCandidateLogDirs() {
|
|
|
549
552
|
pushDir(path.join(homeDir, name));
|
|
550
553
|
pushDir(path.join(homeDir, name, "logs"));
|
|
551
554
|
}
|
|
552
|
-
const searchRoots = new Set([
|
|
555
|
+
const searchRoots = /* @__PURE__ */ new Set([
|
|
553
556
|
homeDir,
|
|
554
557
|
process.cwd(),
|
|
555
558
|
path.dirname(process.cwd())
|
|
@@ -880,14 +883,14 @@ function registerBuiltinSlashCommands(registry) {
|
|
|
880
883
|
//#endregion
|
|
881
884
|
//#region extensions/qqbot/src/engine/commands/command-visibility.ts
|
|
882
885
|
const PRIVATE_CHAT_ONLY_TEXT = "该命令仅限私聊使用,请在私聊中发送。";
|
|
883
|
-
const GROUP_VISIBLE_CORE_COMMANDS = new Set([
|
|
886
|
+
const GROUP_VISIBLE_CORE_COMMANDS = /* @__PURE__ */ new Set([
|
|
884
887
|
"help",
|
|
885
888
|
"btw",
|
|
886
889
|
"side",
|
|
887
890
|
"stop"
|
|
888
891
|
]);
|
|
889
|
-
const STRICT_CORE_COMMANDS = new Set(["new", "reset"]);
|
|
890
|
-
const GROUP_HIDDEN_CORE_COMMANDS = new Set([
|
|
892
|
+
const STRICT_CORE_COMMANDS = /* @__PURE__ */ new Set(["new", "reset"]);
|
|
893
|
+
const GROUP_HIDDEN_CORE_COMMANDS = /* @__PURE__ */ new Set([
|
|
891
894
|
"goal",
|
|
892
895
|
"usage",
|
|
893
896
|
"activation",
|
|
@@ -904,7 +907,7 @@ const GROUP_HIDDEN_CORE_COMMANDS = new Set([
|
|
|
904
907
|
"reason",
|
|
905
908
|
"queue"
|
|
906
909
|
]);
|
|
907
|
-
const PRIVATE_ONLY_CORE_COMMANDS = new Set([
|
|
910
|
+
const PRIVATE_ONLY_CORE_COMMANDS = /* @__PURE__ */ new Set([
|
|
908
911
|
"commands",
|
|
909
912
|
"tools",
|
|
910
913
|
"skill",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { c as getMessageApi, t as accountToCreds } from "./sender-
|
|
1
|
+
import { c as getMessageApi, t as accountToCreds } from "./sender-CjDuU-uz.js";
|
|
2
2
|
import { o as ensurePlatformAdapter, s as getBridgeLogger } from "./config-schema-JZEf1dvB.js";
|
|
3
|
-
import { c as resolveQQBotExecApprovalConfig, d as buildExecApprovalText, f as buildPluginApprovalText, l as shouldHandleQQBotExecApprovalRequest, m as resolveApprovalTarget, o as isQQBotExecApprovalClientEnabled, s as matchesQQBotApprovalAccount, u as buildApprovalKeyboard } from "./channel-
|
|
3
|
+
import { c as resolveQQBotExecApprovalConfig, d as buildExecApprovalText, f as buildPluginApprovalText, l as shouldHandleQQBotExecApprovalRequest, m as resolveApprovalTarget, o as isQQBotExecApprovalClientEnabled, s as matchesQQBotApprovalAccount, u as buildApprovalKeyboard } from "./channel-D1UztsnG.js";
|
|
4
4
|
import { a as resolveQQBotAccount } from "./config-C1qZbh0K.js";
|
|
5
5
|
import { resolveApprovalRequestSessionConversation } from "openclaw/plugin-sdk/approval-native-runtime";
|
|
6
6
|
import { createChannelApprovalNativeRuntimeAdapter } from "openclaw/plugin-sdk/approval-handler-runtime";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
1
2
|
//#region extensions/qqbot/src/engine/utils/format.ts
|
|
2
3
|
/**
|
|
3
4
|
* General formatting and string utilities.
|
|
@@ -22,7 +23,7 @@ function formatErrorMessage(err) {
|
|
|
22
23
|
if (err instanceof Error) {
|
|
23
24
|
let formatted = err.message || err.name || "Error";
|
|
24
25
|
let cause = err.cause;
|
|
25
|
-
const seen = new Set([err]);
|
|
26
|
+
const seen = /* @__PURE__ */ new Set([err]);
|
|
26
27
|
while (cause && !seen.has(cause)) {
|
|
27
28
|
seen.add(cause);
|
|
28
29
|
if (cause instanceof Error) {
|
|
@@ -59,8 +60,6 @@ function formatDuration(durationMs) {
|
|
|
59
60
|
*
|
|
60
61
|
* Only outputs when the QQBOT_DEBUG environment variable is set,
|
|
61
62
|
* preventing user message content from leaking in production logs.
|
|
62
|
-
*
|
|
63
|
-
* Self-contained within engine/ — no framework SDK dependency.
|
|
64
63
|
*/
|
|
65
64
|
function isQqbotDebugEnabled() {
|
|
66
65
|
const value = process.env.QQBOT_DEBUG;
|
|
@@ -86,7 +85,7 @@ function sanitizeDebugLogValue(value) {
|
|
|
86
85
|
}
|
|
87
86
|
const sanitized = text.replace(/\p{Cc}/gu, " ").replace(/\s+/g, " ").trim();
|
|
88
87
|
if (sanitized.length <= MAX_LOG_VALUE_CHARS) return sanitized;
|
|
89
|
-
return `${sanitized
|
|
88
|
+
return `${truncateUtf16Safe(sanitized, MAX_LOG_VALUE_CHARS)}...`;
|
|
90
89
|
}
|
|
91
90
|
function formatDebugLogArgs(args) {
|
|
92
91
|
return args.map(sanitizeDebugLogValue).join(" ");
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { A as getFileTypeName, D as downloadFile, E as checkFileSize, L as __exportAll, M as getMaxUploadSize, N as readFileAsync, O as fileExistsAsync, S as UploadDailyLimitExceededError, g as sendText$1, h as sendMedia$1, j as getImageMimeType, k as formatFileSize, t as accountToCreds, u as initApiConfig, w as UPLOAD_PREPARE_FALLBACK_CODE } from "./sender-
|
|
1
|
+
import { A as getFileTypeName, D as downloadFile, E as checkFileSize, L as __exportAll, M as getMaxUploadSize, N as readFileAsync, O as fileExistsAsync, S as UploadDailyLimitExceededError, g as sendText$1, h as sendMedia$1, j as getImageMimeType, k as formatFileSize, t as accountToCreds, u as initApiConfig, w as UPLOAD_PREPARE_FALLBACK_CODE } from "./sender-CjDuU-uz.js";
|
|
2
2
|
import { c as getPlatformAdapter, i as normalizeOptionalString, n as normalizeLowercaseStringOrEmpty, s as sanitizeFileName } from "./string-normalize-R_0cKO7Q.js";
|
|
3
|
-
import { a as formatErrorMessage, n as debugLog, r as debugWarn, t as debugError } from "./log-
|
|
3
|
+
import { a as formatErrorMessage, n as debugLog, r as debugWarn, t as debugError } from "./log-DEtcoDWe.js";
|
|
4
4
|
import { r as parseTarget$1 } from "./target-parser-DagYRQkP.js";
|
|
5
|
+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
5
6
|
import * as fs$2 from "node:fs";
|
|
6
7
|
import * as os$1 from "node:os";
|
|
7
8
|
import { randomUUID } from "node:crypto";
|
|
@@ -127,7 +128,7 @@ async function checkSilkWasmAvailable() {
|
|
|
127
128
|
if (silkWasmAvailable !== null) return silkWasmAvailable;
|
|
128
129
|
try {
|
|
129
130
|
const { isSilk } = await import("silk-wasm");
|
|
130
|
-
isSilk(new Uint8Array(0));
|
|
131
|
+
isSilk(/* @__PURE__ */ new Uint8Array(0));
|
|
131
132
|
silkWasmAvailable = true;
|
|
132
133
|
debugLog("[platform] silk-wasm: available");
|
|
133
134
|
} catch (err) {
|
|
@@ -179,7 +180,7 @@ function resolveQQBotLocalMediaPath(p) {
|
|
|
179
180
|
const mediaRoot = getQQBotMediaPath();
|
|
180
181
|
const dataRoot = getQQBotDataPath();
|
|
181
182
|
const candidateRoots = [
|
|
182
|
-
...Array.from(new Set([path$1.join(osHomeDir, ".openclaw", "workspace", "qqbot"), path$1.join(openclawHomeDir, ".openclaw", "workspace", "qqbot")])).map((from) => ({
|
|
183
|
+
...Array.from(/* @__PURE__ */ new Set([path$1.join(osHomeDir, ".openclaw", "workspace", "qqbot"), path$1.join(openclawHomeDir, ".openclaw", "workspace", "qqbot")])).map((from) => ({
|
|
183
184
|
from,
|
|
184
185
|
to: mediaRoot
|
|
185
186
|
})),
|
|
@@ -747,13 +748,13 @@ async function sendPhoto(ctx, imagePath) {
|
|
|
747
748
|
if (localFile) return await sendPhotoFromLocal(ctx, localFile);
|
|
748
749
|
return {
|
|
749
750
|
channel: "qqbot",
|
|
750
|
-
error: `Failed to download image: ${mediaPath
|
|
751
|
+
error: `Failed to download image: ${truncateUtf16Safe(mediaPath, 80)}`
|
|
751
752
|
};
|
|
752
753
|
}
|
|
753
754
|
if (isLocal) return await sendPhotoFromLocal(ctx, mediaPath);
|
|
754
755
|
if (!isHttp && !isData) return {
|
|
755
756
|
channel: "qqbot",
|
|
756
|
-
error: `Unsupported image source: ${mediaPath
|
|
757
|
+
error: `Unsupported image source: ${truncateUtf16Safe(mediaPath, 50)}`
|
|
757
758
|
};
|
|
758
759
|
try {
|
|
759
760
|
const creds = accountToCreds(ctx.account);
|
|
@@ -916,7 +917,7 @@ async function sendVoice(ctx, voicePath, directUploadFormats, transcodeEnabled =
|
|
|
916
917
|
if (localFile) return await sendVoiceFromLocal(ctx, localFile, directUploadFormats, transcodeEnabled);
|
|
917
918
|
return {
|
|
918
919
|
channel: "qqbot",
|
|
919
|
-
error: `Failed to download audio: ${mediaPath
|
|
920
|
+
error: `Failed to download audio: ${truncateUtf16Safe(mediaPath, 80)}`
|
|
920
921
|
};
|
|
921
922
|
}
|
|
922
923
|
return await sendVoiceFromLocal(ctx, mediaPath, directUploadFormats, transcodeEnabled);
|
|
@@ -1011,7 +1012,7 @@ async function sendVideoMsg(ctx, videoPath) {
|
|
|
1011
1012
|
if (localFile) return await sendVideoFromLocal(ctx, localFile);
|
|
1012
1013
|
return {
|
|
1013
1014
|
channel: "qqbot",
|
|
1014
|
-
error: `Failed to download video: ${mediaPath
|
|
1015
|
+
error: `Failed to download video: ${truncateUtf16Safe(mediaPath, 80)}`
|
|
1015
1016
|
};
|
|
1016
1017
|
}
|
|
1017
1018
|
try {
|
|
@@ -1125,7 +1126,7 @@ async function sendDocument(ctx, filePath, options = {}) {
|
|
|
1125
1126
|
if (localFile) return await sendDocumentFromLocal(ctx, localFile);
|
|
1126
1127
|
return {
|
|
1127
1128
|
channel: "qqbot",
|
|
1128
|
-
error: `Failed to download file: ${mediaPath
|
|
1129
|
+
error: `Failed to download file: ${truncateUtf16Safe(mediaPath, 80)}`
|
|
1129
1130
|
};
|
|
1130
1131
|
}
|
|
1131
1132
|
try {
|
|
@@ -1230,7 +1231,7 @@ async function downloadToFallbackDir(httpUrl, caller) {
|
|
|
1230
1231
|
try {
|
|
1231
1232
|
const localFile = await downloadFile(httpUrl, getQQBotMediaDir("downloads", "url-fallback"));
|
|
1232
1233
|
if (!localFile) {
|
|
1233
|
-
debugError(`${caller} fallback: download also failed for ${httpUrl
|
|
1234
|
+
debugError(`${caller} fallback: download also failed for ${truncateUtf16Safe(httpUrl, 80)}`);
|
|
1234
1235
|
return null;
|
|
1235
1236
|
}
|
|
1236
1237
|
debugLog(`${caller} fallback: downloaded → ${localFile}`);
|
|
@@ -1395,7 +1396,8 @@ function parseQQBotPayload(text) {
|
|
|
1395
1396
|
/** Encode a cron reminder payload into the stored cron-message format. */
|
|
1396
1397
|
function encodePayloadForCron(payload) {
|
|
1397
1398
|
const jsonString = JSON.stringify(payload);
|
|
1398
|
-
|
|
1399
|
+
const base64 = Buffer.from(jsonString, "utf-8").toString("base64");
|
|
1400
|
+
return `${CRON_PREFIX}${base64}`;
|
|
1399
1401
|
}
|
|
1400
1402
|
/** Decode a stored cron payload. */
|
|
1401
1403
|
function decodeCronPayload(message) {
|
|
@@ -1512,7 +1514,7 @@ function decodeMediaPath(raw, log) {
|
|
|
1512
1514
|
* These replace the inline `isImageFile` and `isVideoFile` helpers scattered
|
|
1513
1515
|
* across `outbound.ts`. Centralizing them here keeps detection consistent.
|
|
1514
1516
|
*/
|
|
1515
|
-
const IMAGE_EXTENSIONS = new Set([
|
|
1517
|
+
const IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
1516
1518
|
".jpg",
|
|
1517
1519
|
".jpeg",
|
|
1518
1520
|
".png",
|
|
@@ -1520,7 +1522,7 @@ const IMAGE_EXTENSIONS = new Set([
|
|
|
1520
1522
|
".webp",
|
|
1521
1523
|
".bmp"
|
|
1522
1524
|
]);
|
|
1523
|
-
const VIDEO_EXTENSIONS = new Set([
|
|
1525
|
+
const VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
1524
1526
|
".mp4",
|
|
1525
1527
|
".mov",
|
|
1526
1528
|
".avi",
|
|
@@ -1582,7 +1584,7 @@ async function sendText(ctx) {
|
|
|
1582
1584
|
initApiConfig(account.appId, { markdownSupport: account.markdownSupport });
|
|
1583
1585
|
debugLog("[qqbot] sendText ctx:", JSON.stringify({
|
|
1584
1586
|
to,
|
|
1585
|
-
text: text
|
|
1587
|
+
text: truncateUtf16Safe(text, 50),
|
|
1586
1588
|
replyToId,
|
|
1587
1589
|
accountId: account.accountId
|
|
1588
1590
|
}, null, 2));
|
|
@@ -1680,7 +1682,7 @@ async function sendText(ctx) {
|
|
|
1680
1682
|
timestamp: result.timestamp,
|
|
1681
1683
|
refIdx: result.ext_info?.ref_idx
|
|
1682
1684
|
};
|
|
1683
|
-
debugLog(`[qqbot] sendText: Sent text part: ${item.content
|
|
1685
|
+
debugLog(`[qqbot] sendText: Sent text part: ${truncateUtf16Safe(item.content, 30)}...`);
|
|
1684
1686
|
} else if (item.type === "image") lastResult = await sendPhoto(mediaTarget, item.content);
|
|
1685
1687
|
else if (item.type === "voice") lastResult = await sendVoice(mediaTarget, item.content, void 0, account.config?.audioFormatPolicy?.transcodeEnabled !== false);
|
|
1686
1688
|
else if (item.type === "video") lastResult = await sendVideoMsg(mediaTarget, item.content);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { v as setOpenClawVersion } from "./sender-
|
|
1
|
+
import { v as setOpenClawVersion } from "./sender-CjDuU-uz.js";
|
|
2
2
|
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
|
3
3
|
//#region extensions/qqbot/src/bridge/runtime.ts
|
|
4
4
|
const { setRuntime: _setRuntime, clearRuntime: resetQQBotRuntimeForTest, getRuntime: getQQBotRuntime } = createPluginRuntimeStore({
|
package/dist/runtime-api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { r as setQQBotRuntime, t as getQQBotRuntime } from "./runtime-
|
|
1
|
+
import { r as setQQBotRuntime, t as getQQBotRuntime } from "./runtime-DodcT_fQ.js";
|
|
2
2
|
export { getQQBotRuntime, setQQBotRuntime };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { c as getPlatformAdapter, i as normalizeOptionalString, s as sanitizeFileName } from "./string-normalize-R_0cKO7Q.js";
|
|
2
|
-
import { a as formatErrorMessage, n as debugLog, r as debugWarn, t as debugError } from "./log-
|
|
2
|
+
import { a as formatErrorMessage, n as debugLog, r as debugWarn, t as debugError } from "./log-DEtcoDWe.js";
|
|
3
|
+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
3
4
|
import * as fs$1 from "node:fs";
|
|
4
5
|
import os from "node:os";
|
|
5
6
|
import { readProviderTextResponse, readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
|
|
@@ -10,8 +11,8 @@ import { sleep } from "openclaw/plugin-sdk/runtime-env";
|
|
|
10
11
|
import { FsSafeError, openLocalFileSafely, readRegularFile, statRegularFileSync } from "openclaw/plugin-sdk/security-runtime";
|
|
11
12
|
import * as path$1 from "node:path";
|
|
12
13
|
import { mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
|
|
14
|
+
import { asDateTimestampMs, formatByteSize, isFutureDateTimestampMs, parseStrictPositiveInteger, resolveExpiresAtMsFromDurationSeconds, resolveTimestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
|
|
13
15
|
import { readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
|
|
14
|
-
import { asDateTimestampMs, isFutureDateTimestampMs, parseStrictPositiveInteger, resolveExpiresAtMsFromDurationSeconds, resolveTimestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
|
|
15
16
|
//#region \0rolldown/runtime.js
|
|
16
17
|
var __defProp = Object.defineProperty;
|
|
17
18
|
var __exportAll = (all, no_symbols) => {
|
|
@@ -176,7 +177,7 @@ var ApiClient = class {
|
|
|
176
177
|
throw new ApiError(`API Error [${path}]: ${error.message ?? rawBody}`, res.status, path, bizCode, error.message);
|
|
177
178
|
} catch (parseErr) {
|
|
178
179
|
if (parseErr instanceof ApiError) throw parseErr;
|
|
179
|
-
throw new ApiError(`API Error [${path}] HTTP ${res.status}: ${rawBody
|
|
180
|
+
throw new ApiError(`API Error [${path}] HTTP ${res.status}: ${truncateUtf16Safe(rawBody, 200)}`, res.status, path);
|
|
180
181
|
}
|
|
181
182
|
}
|
|
182
183
|
if (isHtmlResponse) throw new ApiError(`QQ 服务端返回了非 JSON 响应(${path}),可能是临时故障,请稍后重试`, res.status, path);
|
|
@@ -287,16 +288,19 @@ async function fileExistsAsync(filePath) {
|
|
|
287
288
|
}
|
|
288
289
|
/** Format a byte count into a human-readable size string. */
|
|
289
290
|
function formatFileSize(bytes) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
291
|
+
return formatByteSize(bytes, {
|
|
292
|
+
style: "legacy-binary",
|
|
293
|
+
maxUnit: "mega",
|
|
294
|
+
separator: "",
|
|
295
|
+
fractionDigits: (_value, unit) => unit === "byte" ? null : 1
|
|
296
|
+
});
|
|
293
297
|
}
|
|
294
298
|
/** Infer a MIME type from the file extension. */
|
|
295
299
|
function getMimeType(filePath) {
|
|
296
300
|
return mimeTypeFromFilePath(filePath) ?? "application/octet-stream";
|
|
297
301
|
}
|
|
298
302
|
/** Extensions accepted as image uploads by the QQ Bot media pipeline. */
|
|
299
|
-
const IMAGE_EXTENSIONS = new Set([
|
|
303
|
+
const IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
300
304
|
".jpg",
|
|
301
305
|
".jpeg",
|
|
302
306
|
".png",
|
|
@@ -495,7 +499,7 @@ async function withRetry(fn, policy, persistentPolicy, logger) {
|
|
|
495
499
|
if (policy.shouldRetry?.(lastError, attempt) === false) throw lastError;
|
|
496
500
|
if (attempt < policy.maxRetries) {
|
|
497
501
|
const delay = policy.backoff === "exponential" ? policy.baseDelayMs * 2 ** attempt : policy.baseDelayMs;
|
|
498
|
-
logger?.debug?.(`[qqbot:retry] Attempt ${attempt + 1} failed, retrying in ${delay}ms: ${lastError.message
|
|
502
|
+
logger?.debug?.(`[qqbot:retry] Attempt ${attempt + 1} failed, retrying in ${delay}ms: ${truncateUtf16Safe(lastError.message, 100)}`);
|
|
499
503
|
await sleep(delay);
|
|
500
504
|
}
|
|
501
505
|
}
|
|
@@ -571,7 +575,7 @@ function buildPartFinishPersistentPolicy(retryTimeoutMs, retryableCodes = PART_F
|
|
|
571
575
|
};
|
|
572
576
|
}
|
|
573
577
|
/** Business error codes that trigger persistent part-finish retry. */
|
|
574
|
-
const PART_FINISH_RETRYABLE_CODES = new Set([40093001]);
|
|
578
|
+
const PART_FINISH_RETRYABLE_CODES = /* @__PURE__ */ new Set([40093001]);
|
|
575
579
|
/** upload_prepare error code indicating daily limit exceeded. */
|
|
576
580
|
const UPLOAD_PREPARE_FALLBACK_CODE = 40093002;
|
|
577
581
|
//#endregion
|
|
@@ -938,8 +942,8 @@ async function putToPresignedUrl(presignedUrl, data, partIndex, totalParts, logg
|
|
|
938
942
|
const etag = response.headers.get("ETag") ?? "-";
|
|
939
943
|
if (!response.ok) {
|
|
940
944
|
const body = await readResponseTextLimited(response, PART_UPLOAD_ERROR_BODY_LIMIT_BYTES).catch(() => "");
|
|
941
|
-
logger?.error?.(`${prefix} PUT part ${partIndex}/${totalParts}: HTTP ${response.status} ${response.statusText} (${elapsed}ms, requestId=${requestId}) body=${body
|
|
942
|
-
throw new Error(`COS PUT failed: ${response.status} ${response.statusText} - ${body
|
|
945
|
+
logger?.error?.(`${prefix} PUT part ${partIndex}/${totalParts}: HTTP ${response.status} ${response.statusText} (${elapsed}ms, requestId=${requestId}) body=${truncateUtf16Safe(body, 160)}`);
|
|
946
|
+
throw new Error(`COS PUT failed: ${response.status} ${response.statusText} - ${truncateUtf16Safe(body, 120)}`);
|
|
943
947
|
}
|
|
944
948
|
logger?.debug?.(`${prefix} PUT part ${partIndex}/${totalParts} OK (${elapsed}ms ETag=${etag} requestId=${requestId})`);
|
|
945
949
|
return;
|
|
@@ -951,7 +955,7 @@ async function putToPresignedUrl(presignedUrl, data, partIndex, totalParts, logg
|
|
|
951
955
|
if (lastError.name === "AbortError") lastError = /* @__PURE__ */ new Error(`Part ${partIndex}/${totalParts} upload timeout after ${PART_UPLOAD_TIMEOUT_MS}ms`);
|
|
952
956
|
if (attempt < PART_UPLOAD_MAX_RETRIES) {
|
|
953
957
|
const delay = 1e3 * 2 ** attempt;
|
|
954
|
-
(logger?.warn ?? logger?.error)?.(`${prefix} PUT part ${partIndex}/${totalParts} attempt ${attempt + 1} failed (${lastError.message
|
|
958
|
+
(logger?.warn ?? logger?.error)?.(`${prefix} PUT part ${partIndex}/${totalParts} attempt ${attempt + 1} failed (${truncateUtf16Safe(lastError.message, 120)}), retrying in ${delay}ms`);
|
|
955
959
|
await sleep(delay);
|
|
956
960
|
}
|
|
957
961
|
} finally {
|
|
@@ -1812,7 +1816,7 @@ async function withTokenRetry(creds, sendFn, log, _accountId) {
|
|
|
1812
1816
|
}
|
|
1813
1817
|
const errMsg = formatErrorMessage(err);
|
|
1814
1818
|
if (errMsg.includes("401") || errMsg.includes("token") || errMsg.includes("access_token")) {
|
|
1815
|
-
log?.warn?.(`Token retry triggered by string heuristic (err is not ApiError). Consider propagating ApiError end-to-end. msg=${errMsg
|
|
1819
|
+
log?.warn?.(`Token retry triggered by string heuristic (err is not ApiError). Consider propagating ApiError end-to-end. msg=${truncateUtf16Safe(errMsg, 120)}`);
|
|
1816
1820
|
clearTokenCache(creds.appId);
|
|
1817
1821
|
return await sendFn(await getAccessToken(creds.appId, creds.clientSecret));
|
|
1818
1822
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { a as resolveQQBotAccount, r as listQQBotAccountIds } from "./config-C1qZbh0K.js";
|
|
2
|
-
import { a as formatErrorMessage, n as debugLog, t as debugError } from "./log-
|
|
2
|
+
import { a as formatErrorMessage, n as debugLog, t as debugError } from "./log-DEtcoDWe.js";
|
|
3
3
|
import { t as getRequestContext } from "./request-context-Bm7PTBD1.js";
|
|
4
|
+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
4
5
|
import { readProviderTextResponse, readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
|
|
5
6
|
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
6
7
|
import { resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
|
|
8
|
+
import { jsonResult } from "openclaw/plugin-sdk/tool-results";
|
|
7
9
|
import { callGatewayTool } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
8
|
-
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
9
10
|
//#region extensions/qqbot/src/engine/tools/channel-api.ts
|
|
10
11
|
/**
|
|
11
12
|
* QQ Channel API proxy tool core logic.
|
|
@@ -118,15 +119,6 @@ function validateDeleteConfirmation(params) {
|
|
|
118
119
|
if (isBulkAnnouncementDeletePath(params.path) && !params.bulkConfirmed) return "Deleting all announcements requires bulkConfirmed=true after a separate bulk-delete confirmation.";
|
|
119
120
|
return null;
|
|
120
121
|
}
|
|
121
|
-
function json$1(data) {
|
|
122
|
-
return {
|
|
123
|
-
content: [{
|
|
124
|
-
type: "text",
|
|
125
|
-
text: JSON.stringify(data, null, 2)
|
|
126
|
-
}],
|
|
127
|
-
details: data
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
122
|
/**
|
|
131
123
|
* Execute a channel API proxy request.
|
|
132
124
|
* 执行频道 API 代理请求。
|
|
@@ -136,8 +128,8 @@ function json$1(data) {
|
|
|
136
128
|
* response formatting suitable for AI tool output.
|
|
137
129
|
*/
|
|
138
130
|
async function executeChannelApi(params, options) {
|
|
139
|
-
if (!params.method) return
|
|
140
|
-
if (!params.path) return
|
|
131
|
+
if (!params.method) return jsonResult({ error: "method is required" });
|
|
132
|
+
if (!params.path) return jsonResult({ error: "path is required" });
|
|
141
133
|
const method = params.method.toUpperCase();
|
|
142
134
|
if (![
|
|
143
135
|
"GET",
|
|
@@ -145,14 +137,14 @@ async function executeChannelApi(params, options) {
|
|
|
145
137
|
"PUT",
|
|
146
138
|
"PATCH",
|
|
147
139
|
"DELETE"
|
|
148
|
-
].includes(method)) return
|
|
140
|
+
].includes(method)) return jsonResult({ error: `Unsupported HTTP method: ${method}. Allowed values: GET, POST, PUT, PATCH, DELETE` });
|
|
149
141
|
const pathError = validatePath(params.path);
|
|
150
|
-
if (pathError) return
|
|
142
|
+
if (pathError) return jsonResult({ error: pathError });
|
|
151
143
|
const confirmationError = validateDeleteConfirmation({
|
|
152
144
|
...params,
|
|
153
145
|
method
|
|
154
146
|
});
|
|
155
|
-
if (confirmationError) return
|
|
147
|
+
if (confirmationError) return jsonResult({
|
|
156
148
|
error: confirmationError,
|
|
157
149
|
path: params.path
|
|
158
150
|
});
|
|
@@ -191,13 +183,13 @@ async function executeChannelApi(params, options) {
|
|
|
191
183
|
clearTimeout(timeoutId);
|
|
192
184
|
if (err instanceof Error && err.name === "AbortError") {
|
|
193
185
|
debugError(`[qqbot-channel-api] <<< Request timeout after ${DEFAULT_TIMEOUT_MS}ms`);
|
|
194
|
-
return
|
|
186
|
+
return jsonResult({
|
|
195
187
|
error: `Request timed out after ${DEFAULT_TIMEOUT_MS}ms`,
|
|
196
188
|
path: params.path
|
|
197
189
|
});
|
|
198
190
|
}
|
|
199
191
|
debugError("[qqbot-channel-api] <<< Network error:", err);
|
|
200
|
-
return
|
|
192
|
+
return jsonResult({
|
|
201
193
|
error: `Network error: ${formatErrorMessage(err)}`,
|
|
202
194
|
path: params.path
|
|
203
195
|
});
|
|
@@ -208,12 +200,12 @@ async function executeChannelApi(params, options) {
|
|
|
208
200
|
debugLog(`[qqbot-channel-api] <<< Status: ${res.status} ${res.statusText}`);
|
|
209
201
|
const rawBody = res.ok ? await readProviderTextResponse(res, "QQ channel API response") : await readResponseTextLimited(res, CHANNEL_API_ERROR_BODY_LIMIT_BYTES);
|
|
210
202
|
if (!rawBody || rawBody.trim() === "") {
|
|
211
|
-
if (res.ok) return
|
|
203
|
+
if (res.ok) return jsonResult({
|
|
212
204
|
success: true,
|
|
213
205
|
status: res.status,
|
|
214
206
|
path: params.path
|
|
215
207
|
});
|
|
216
|
-
return
|
|
208
|
+
return jsonResult({
|
|
217
209
|
error: `API returned ${res.status} ${res.statusText}`,
|
|
218
210
|
status: res.status,
|
|
219
211
|
path: params.path
|
|
@@ -228,14 +220,14 @@ async function executeChannelApi(params, options) {
|
|
|
228
220
|
if (!res.ok) {
|
|
229
221
|
const errMsg = typeof parsed === "object" && parsed && "message" in parsed ? String(parsed.message) : `${res.status} ${res.statusText}`;
|
|
230
222
|
debugError(`[qqbot-channel-api] Error [${method} ${params.path}]: ${errMsg}`);
|
|
231
|
-
return
|
|
223
|
+
return jsonResult({
|
|
232
224
|
error: errMsg,
|
|
233
225
|
status: res.status,
|
|
234
226
|
path: params.path,
|
|
235
227
|
details: parsed
|
|
236
228
|
});
|
|
237
229
|
}
|
|
238
|
-
return
|
|
230
|
+
return jsonResult({
|
|
239
231
|
success: true,
|
|
240
232
|
status: res.status,
|
|
241
233
|
path: params.path,
|
|
@@ -245,7 +237,7 @@ async function executeChannelApi(params, options) {
|
|
|
245
237
|
await release?.();
|
|
246
238
|
}
|
|
247
239
|
} catch (err) {
|
|
248
|
-
return
|
|
240
|
+
return jsonResult({
|
|
249
241
|
error: formatErrorMessage(err),
|
|
250
242
|
path: params.path
|
|
251
243
|
});
|
|
@@ -274,7 +266,7 @@ function registerChannelTool(api) {
|
|
|
274
266
|
description: "Authenticated HTTP proxy for QQ Open Platform channel APIs. Use write and delete endpoints only after explicit user intent; DELETE requires confirmed=true, and bulk deletes require bulkConfirmed=true after confirming the exact target. Common endpoints: list guilds GET /users/@me/guilds | list channels GET /guilds/{guild_id}/channels | get channel GET /channels/{channel_id} | create channel POST /guilds/{guild_id}/channels | list members GET /guilds/{guild_id}/members?after=0&limit=100 | get member GET /guilds/{guild_id}/members/{user_id} | list threads GET /channels/{channel_id}/threads | create thread PUT /channels/{channel_id}/threads | create announce POST /guilds/{guild_id}/announces | create schedule POST /channels/{channel_id}/schedules. See the qqbot-channel skill for full endpoint details.",
|
|
275
267
|
parameters: ChannelApiSchema,
|
|
276
268
|
async execute(_toolCallId, params) {
|
|
277
|
-
const { getAccessToken } = await import("./sender-
|
|
269
|
+
const { getAccessToken } = await import("./sender-CjDuU-uz.js").then((n) => n._);
|
|
278
270
|
return executeChannelApi(params, { accessToken: await getAccessToken(account.appId, account.clientSecret) });
|
|
279
271
|
}
|
|
280
272
|
}, { name: "qqbot_channel_api" });
|
|
@@ -451,15 +443,6 @@ function formatDelay(ms) {
|
|
|
451
443
|
if (minutes === 0) return `${hours}h`;
|
|
452
444
|
return `${hours}h${minutes}m`;
|
|
453
445
|
}
|
|
454
|
-
function json(data) {
|
|
455
|
-
return {
|
|
456
|
-
content: [{
|
|
457
|
-
type: "text",
|
|
458
|
-
text: JSON.stringify(data, null, 2)
|
|
459
|
-
}],
|
|
460
|
-
details: data
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
446
|
function formatSchedulerError(error) {
|
|
464
447
|
return error instanceof Error ? error.message : String(error);
|
|
465
448
|
}
|
|
@@ -526,17 +509,17 @@ function prepareRemindCronAction(params, ctx = {}) {
|
|
|
526
509
|
}
|
|
527
510
|
async function executeScheduledRemind(params, ctx, scheduler) {
|
|
528
511
|
const plan = prepareRemindCronAction(params, ctx);
|
|
529
|
-
if (!plan.ok) return
|
|
512
|
+
if (!plan.ok) return jsonResult({ error: plan.error });
|
|
530
513
|
try {
|
|
531
514
|
const cronResult = await scheduler(plan.cronAction);
|
|
532
|
-
return
|
|
515
|
+
return jsonResult({
|
|
533
516
|
ok: true,
|
|
534
517
|
action: plan.action,
|
|
535
518
|
summary: plan.summary,
|
|
536
519
|
cronResult
|
|
537
520
|
});
|
|
538
521
|
} catch (error) {
|
|
539
|
-
return
|
|
522
|
+
return jsonResult({
|
|
540
523
|
error: `Failed to run Gateway cron action: ${formatSchedulerError(error)}`,
|
|
541
524
|
action: plan.action
|
|
542
525
|
});
|
package/dist/tools-api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as registerQQBotTools } from "./tools-
|
|
1
|
+
import { t as registerQQBotTools } from "./tools-UJJ-tLHP.js";
|
|
2
2
|
export { registerQQBotTools };
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/qqbot",
|
|
3
|
-
"version": "2026.7.1-beta.
|
|
3
|
+
"version": "2026.7.1-beta.4",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@openclaw/qqbot",
|
|
9
|
-
"version": "2026.7.1-beta.
|
|
9
|
+
"version": "2026.7.1-beta.4",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@tencent-connect/qqbot-connector": "1.1.0",
|
|
12
12
|
"mpg123-decoder": "1.0.3",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"zod": "4.4.3"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
|
-
"openclaw": ">=2026.7.1-beta.
|
|
18
|
+
"openclaw": ">=2026.7.1-beta.4"
|
|
19
19
|
},
|
|
20
20
|
"peerDependenciesMeta": {
|
|
21
21
|
"openclaw": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/qqbot",
|
|
3
|
-
"version": "2026.7.1-beta.
|
|
3
|
+
"version": "2026.7.1-beta.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "OpenClaw QQ Bot channel plugin for group and direct-message workflows.",
|
|
6
6
|
"repository": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"zod": "4.4.3"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
|
-
"openclaw": ">=2026.7.1-beta.
|
|
19
|
+
"openclaw": ">=2026.7.1-beta.4"
|
|
20
20
|
},
|
|
21
21
|
"peerDependenciesMeta": {
|
|
22
22
|
"openclaw": {
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
"minHostVersion": ">=2026.4.10"
|
|
46
46
|
},
|
|
47
47
|
"compat": {
|
|
48
|
-
"pluginApi": ">=2026.7.1-beta.
|
|
48
|
+
"pluginApi": ">=2026.7.1-beta.4"
|
|
49
49
|
},
|
|
50
50
|
"build": {
|
|
51
|
-
"openclawVersion": "2026.7.1-beta.
|
|
51
|
+
"openclawVersion": "2026.7.1-beta.4"
|
|
52
52
|
},
|
|
53
53
|
"release": {
|
|
54
54
|
"publishToClawHub": true,
|