@openclaw/qqbot 2026.7.1 → 2026.7.2-beta.2
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 +8 -7
- package/dist/{channel-entry-C5YdhX3Y.js → channel-entry-CA2T2sf8.js} +4 -4
- package/dist/channel-entry-api.js +1 -1
- package/dist/{channel-D1UztsnG.js → channel-nz3Mkify.js} +99 -54
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.setup-2ItDYKhz.js → channel.setup-CNoBtKXQ.js} +1 -1
- package/dist/{config-C1qZbh0K.js → config-CpOXnoEc.js} +2 -2
- package/dist/{config-schema-JZEf1dvB.js → config-schema-D7MaH5X5.js} +61 -76
- package/dist/doctor-contract-api.js +4 -10
- package/dist/{gateway-pJQppxe4.js → gateway-D8uYPtoy.js} +286 -174
- package/dist/{group-o0GmovSf.js → group-BVHG8qUZ.js} +40 -23
- package/dist/{handler-runtime-zQvT6SrI.js → handler-runtime-S1_XF8on.js} +8 -12
- package/dist/{log-DEtcoDWe.js → log-Da4jz75I.js} +4 -8
- package/dist/{outbound-BIrfvvFJ.js → outbound-FOG4zNLY.js} +8 -142
- package/dist/{runtime-DodcT_fQ.js → runtime-CyjBiGD2.js} +2 -2
- package/dist/runtime-api.js +1 -1
- package/dist/secret-contract-api.js +6 -22
- package/dist/{sender-CjDuU-uz.js → sender-BAUHZqDW.js} +225 -66
- package/dist/setup-plugin-api.js +1 -1
- package/dist/state-keys-jLJ2SmJA.js +225 -0
- package/dist/{tools-UJJ-tLHP.js → tools-CC5CKQig.js} +71 -26
- package/dist/tools-api.js +1 -1
- package/node_modules/p-map/index.d.ts +155 -0
- package/node_modules/p-map/index.js +284 -0
- package/node_modules/p-map/license +9 -0
- package/node_modules/p-map/package.json +57 -0
- package/node_modules/p-map/readme.md +190 -0
- package/node_modules/parse-ms/index.d.ts +30 -0
- package/node_modules/parse-ms/index.js +45 -0
- package/node_modules/parse-ms/license +9 -0
- package/node_modules/parse-ms/package.json +47 -0
- package/node_modules/parse-ms/readme.md +46 -0
- package/node_modules/pretty-ms/index.d.ts +157 -0
- package/node_modules/pretty-ms/index.js +149 -0
- package/node_modules/pretty-ms/license +9 -0
- package/node_modules/pretty-ms/package.json +55 -0
- package/node_modules/pretty-ms/readme.md +179 -0
- package/npm-shrinkwrap.json +44 -3
- package/openclaw.plugin.json +105 -133
- package/package.json +8 -4
- package/dist/state-keys-CQKlAFyo.js +0 -141
package/dist/api.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { t as
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { n as
|
|
7
|
-
import { t as
|
|
1
|
+
import { C as checkMessageReplyLimit, D as recordMessageReply, E as getMessageReplyStats, S as MESSAGE_REPLY_LIMIT, T as getMessageReplyConfig } from "./sender-BAUHZqDW.js";
|
|
2
|
+
import { t as qqbotPlugin } from "./channel-nz3Mkify.js";
|
|
3
|
+
import { a as resolveQQBotAccount, i as resolveDefaultQQBotAccountId, n as applyQQBotAccountConfig, r as listQQBotAccountIds, t as DEFAULT_ACCOUNT_ID } from "./config-CpOXnoEc.js";
|
|
4
|
+
import { t as qqbotSetupPlugin } from "./channel.setup-CNoBtKXQ.js";
|
|
5
|
+
import { o as getFrameworkCommands } from "./group-BVHG8qUZ.js";
|
|
6
|
+
import { C as resolveUserFacingMediaError, E as setOutboundAudioPort, T as OUTBOUND_ERROR_CODES, _ as sendVideoMsg, a as sendText, f as buildMediaTarget, g as sendPhoto, h as sendDocument, i as sendProactiveMessage, m as resolveOutboundMediaPath, n as sendCronMessage, p as parseTarget, r as sendMedia, v as sendVoice, w as DEFAULT_MEDIA_SEND_ERROR } from "./outbound-FOG4zNLY.js";
|
|
7
|
+
import { n as registerRemindTool, r as registerChannelTool, t as registerQQBotTools } from "./tools-CC5CKQig.js";
|
|
8
|
+
import { t as registerQQBotFull } from "./channel-entry-CA2T2sf8.js";
|
|
8
9
|
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,7 +1,7 @@
|
|
|
1
|
-
import { a as resolveQQBotAccount } from "./config-
|
|
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-
|
|
1
|
+
import { a as resolveQQBotAccount } from "./config-CpOXnoEc.js";
|
|
2
|
+
import { d as PRIVATE_CHAT_ONLY_TEXT, n as resolveGroupCommandLevelFromAccountConfig, o as getFrameworkCommands } from "./group-BVHG8qUZ.js";
|
|
3
|
+
import { h as sendDocument } from "./outbound-FOG4zNLY.js";
|
|
4
|
+
import { t as registerQQBotTools } from "./tools-CC5CKQig.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-CA2T2sf8.js";
|
|
2
2
|
export { registerQQBotFull };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { t as asOptionalObjectRecord } from "./string-normalize-R_0cKO7Q.js";
|
|
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-
|
|
3
|
-
import { a as resolveQQBotAccount, n as applyQQBotAccountConfig, r as listQQBotAccountIds, t as DEFAULT_ACCOUNT_ID$1 } from "./config-
|
|
4
|
-
import { t as getQQBotRuntime } from "./runtime-
|
|
5
|
-
import { n as legacyConfigRules, r as normalizeCompatibilityConfig, t as buildQQBotStateKey } from "./state-keys-
|
|
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-D7MaH5X5.js";
|
|
3
|
+
import { a as resolveQQBotAccount, n as applyQQBotAccountConfig, r as listQQBotAccountIds, t as DEFAULT_ACCOUNT_ID$1 } from "./config-CpOXnoEc.js";
|
|
4
|
+
import { t as getQQBotRuntime } from "./runtime-CyjBiGD2.js";
|
|
5
|
+
import { n as legacyConfigRules, r as normalizeCompatibilityConfig, t as buildQQBotStateKey } from "./state-keys-jLJ2SmJA.js";
|
|
6
6
|
import { n as normalizeTarget, r as parseTarget, t as looksLikeQQBotTarget } from "./target-parser-DagYRQkP.js";
|
|
7
|
-
import { getExecApprovalReplyMetadata } from "openclaw/plugin-sdk/approval-runtime";
|
|
7
|
+
import { getExecApprovalReplyMetadata, resolveExecApprovalCommandDisplay } from "openclaw/plugin-sdk/approval-runtime";
|
|
8
8
|
import { buildChannelOutboundSessionRoute } from "openclaw/plugin-sdk/channel-core";
|
|
9
9
|
import { createMessageReceiptFromOutboundResults, defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-outbound";
|
|
10
10
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
@@ -17,34 +17,66 @@ import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
|
17
17
|
import { markImplicitSameChatApprovalAuthorization, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
|
|
18
18
|
import { createChannelExecApprovalProfile, isChannelExecApprovalClientEnabledFromConfig, matchesApprovalRequestFilters } from "openclaw/plugin-sdk/approval-client-runtime";
|
|
19
19
|
import { normalizeAccountId } from "openclaw/plugin-sdk/routing";
|
|
20
|
-
import {
|
|
20
|
+
import { buildChannelGroupsScopeTree, resolveScopeKeyCaseInsensitive, resolveScopeToolsPolicy } from "openclaw/plugin-sdk/channel-policy";
|
|
21
21
|
//#region extensions/qqbot/src/engine/approval/index.ts
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
function
|
|
30
|
-
const
|
|
22
|
+
const COMMAND_PREVIEW_MAX_LENGTH = 300;
|
|
23
|
+
const COMMAND_PREVIEW_GRAPHEMES_PER_LINE = 24;
|
|
24
|
+
const COMMAND_PREVIEW_WRAP_MARKER = "↩";
|
|
25
|
+
const commandPreviewSegmenter = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter(void 0, { granularity: "grapheme" }) : null;
|
|
26
|
+
function splitCommandPreviewGraphemes(commandText) {
|
|
27
|
+
return commandPreviewSegmenter ? Array.from(commandPreviewSegmenter.segment(commandText), ({ segment }) => segment) : Array.from(commandText);
|
|
28
|
+
}
|
|
29
|
+
function formatCommandPreview(commandText) {
|
|
30
|
+
const lines = [""];
|
|
31
|
+
const displayText = commandText.replaceAll(COMMAND_PREVIEW_WRAP_MARKER, "\\u{21A9}");
|
|
32
|
+
let previewLength = 0;
|
|
33
|
+
let lineGraphemes = 0;
|
|
34
|
+
let truncated = false;
|
|
35
|
+
let wrapped = false;
|
|
36
|
+
for (const grapheme of splitCommandPreviewGraphemes(displayText)) {
|
|
37
|
+
if (previewLength + grapheme.length > COMMAND_PREVIEW_MAX_LENGTH) {
|
|
38
|
+
if (previewLength === 0) lines[0] = truncateUtf16Safe(grapheme, COMMAND_PREVIEW_MAX_LENGTH);
|
|
39
|
+
truncated = true;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
previewLength += grapheme.length;
|
|
43
|
+
if (lineGraphemes === COMMAND_PREVIEW_GRAPHEMES_PER_LINE) {
|
|
44
|
+
lines[lines.length - 1] += COMMAND_PREVIEW_WRAP_MARKER;
|
|
45
|
+
lines.push("");
|
|
46
|
+
lineGraphemes = 0;
|
|
47
|
+
wrapped = true;
|
|
48
|
+
}
|
|
49
|
+
lines[lines.length - 1] += grapheme;
|
|
50
|
+
lineGraphemes += 1;
|
|
51
|
+
}
|
|
52
|
+
const preview = `${lines.join("\n")}${truncated ? "\n…[truncated]" : ""}`;
|
|
53
|
+
const longestBacktickRun = Math.max(0, ...preview.match(/`+/g)?.map((run) => run.length) ?? []);
|
|
54
|
+
const fence = "`".repeat(Math.max(3, longestBacktickRun + 1));
|
|
55
|
+
const block = `${fence}\n${preview}\n${fence}`;
|
|
56
|
+
return wrapped ? `${COMMAND_PREVIEW_WRAP_MARKER} = display wrap only; not command text\n${block}` : block;
|
|
57
|
+
}
|
|
58
|
+
function formatApprovalMetadata(value) {
|
|
59
|
+
const sanitized = resolveExecApprovalCommandDisplay({ command: value }).commandText;
|
|
60
|
+
return formatCommandPreview(sanitized);
|
|
61
|
+
}
|
|
62
|
+
function buildExecApprovalText(view, nowMs = Date.now()) {
|
|
63
|
+
const expiresIn = Math.max(0, Math.round((view.expiresAtMs - nowMs) / 1e3));
|
|
31
64
|
const lines = ["🔐 命令执行审批", ""];
|
|
32
|
-
|
|
33
|
-
if (
|
|
34
|
-
if (
|
|
35
|
-
if (request.request.agentId) lines.push(`\u{1f916} Agent: ${request.request.agentId}`);
|
|
65
|
+
if (view.commandText) lines.push(formatCommandPreview(view.commandText));
|
|
66
|
+
if (view.cwd) lines.push(`\u{1f4c1} \u76ee\u5f55:\n${formatApprovalMetadata(view.cwd)}`);
|
|
67
|
+
if (view.agentId) lines.push(`\u{1f916} Agent:\n${formatApprovalMetadata(view.agentId)}`);
|
|
36
68
|
lines.push("", `\u23f1\ufe0f \u8d85\u65f6: ${expiresIn} \u79d2`);
|
|
37
69
|
return lines.join("\n");
|
|
38
70
|
}
|
|
39
|
-
function buildPluginApprovalText(
|
|
40
|
-
const
|
|
41
|
-
const lines = [`${
|
|
42
|
-
lines.push(`\u{1f4cb} ${
|
|
43
|
-
if (
|
|
44
|
-
if (
|
|
45
|
-
if (
|
|
46
|
-
if (
|
|
47
|
-
lines.push("", `\u23f1\ufe0f \u8d85\u65f6: ${
|
|
71
|
+
function buildPluginApprovalText(view, nowMs = Date.now()) {
|
|
72
|
+
const expiresIn = Math.max(0, Math.round((view.expiresAtMs - nowMs) / 1e3));
|
|
73
|
+
const lines = [`${view.severity === "critical" ? "🔴" : view.severity === "info" ? "🔵" : "🟡"} \u5ba1\u6279\u8bf7\u6c42`, ""];
|
|
74
|
+
lines.push(`\u{1f4cb} ${view.title}`);
|
|
75
|
+
if (view.description) lines.push(`\u{1f4dd} ${view.description}`);
|
|
76
|
+
if (view.toolName) lines.push(`\u{1f527} \u5de5\u5177: ${view.toolName}`);
|
|
77
|
+
if (view.pluginId) lines.push(`\u{1f50c} \u63d2\u4ef6: ${view.pluginId}`);
|
|
78
|
+
if (view.agentId) lines.push(`\u{1f916} Agent: ${view.agentId}`);
|
|
79
|
+
lines.push("", `\u23f1\ufe0f \u8d85\u65f6: ${expiresIn} \u79d2`);
|
|
48
80
|
return lines.join("\n");
|
|
49
81
|
}
|
|
50
82
|
/**
|
|
@@ -55,11 +87,12 @@ function buildPluginApprovalText(request) {
|
|
|
55
87
|
* click_limit=1: each user can only click once.
|
|
56
88
|
* permission.type=2: all users can interact.
|
|
57
89
|
*/
|
|
58
|
-
function buildApprovalKeyboard(approvalId, allowedDecisions = [
|
|
90
|
+
function buildApprovalKeyboard(approvalId, approvalKind, allowedDecisions = [
|
|
59
91
|
"allow-once",
|
|
60
92
|
"allow-always",
|
|
61
93
|
"deny"
|
|
62
94
|
]) {
|
|
95
|
+
const actionPrefix = `approve:v2:${approvalKind}:${encodeURIComponent(approvalId)}`;
|
|
63
96
|
const makeBtn = (id, label, visitedLabel, data, style) => ({
|
|
64
97
|
id,
|
|
65
98
|
render_data: {
|
|
@@ -76,9 +109,9 @@ function buildApprovalKeyboard(approvalId, allowedDecisions = [
|
|
|
76
109
|
group_id: "approval"
|
|
77
110
|
});
|
|
78
111
|
const buttons = [];
|
|
79
|
-
if (allowedDecisions.includes("allow-once")) buttons.push(makeBtn("allow", "✅ 允许一次", "
|
|
80
|
-
if (allowedDecisions.includes("allow-always")) buttons.push(makeBtn("always", "⭐ 始终允许", "
|
|
81
|
-
if (allowedDecisions.includes("deny")) buttons.push(makeBtn("deny", "❌ 拒绝", "
|
|
112
|
+
if (allowedDecisions.includes("allow-once")) buttons.push(makeBtn("allow", "✅ 允许一次", "已处理", `${actionPrefix}:allow-once`, 1));
|
|
113
|
+
if (allowedDecisions.includes("allow-always")) buttons.push(makeBtn("always", "⭐ 始终允许", "已处理", `${actionPrefix}:allow-always`, 1));
|
|
114
|
+
if (allowedDecisions.includes("deny")) buttons.push(makeBtn("deny", "❌ 拒绝", "已处理", `${actionPrefix}:deny`, 0));
|
|
82
115
|
return { content: { rows: [{ buttons }] } };
|
|
83
116
|
}
|
|
84
117
|
/**
|
|
@@ -96,25 +129,39 @@ function resolveApprovalTarget(sessionKey, turnSourceTo) {
|
|
|
96
129
|
if (!sk) return null;
|
|
97
130
|
const m = sk.match(/qqbot:(c2c|direct|group):([A-F0-9]+)/i);
|
|
98
131
|
if (!m) return null;
|
|
132
|
+
const scope = m[1];
|
|
133
|
+
const id = m[2];
|
|
134
|
+
if (scope === void 0 || id === void 0) return null;
|
|
99
135
|
return {
|
|
100
|
-
type:
|
|
101
|
-
id
|
|
136
|
+
type: scope.toLowerCase() === "group" ? "group" : "c2c",
|
|
137
|
+
id
|
|
102
138
|
};
|
|
103
139
|
}
|
|
104
140
|
/**
|
|
105
141
|
* Parse the button_data string from an INTERACTION_CREATE event.
|
|
106
142
|
*
|
|
107
|
-
* Expected format: `approve:<
|
|
108
|
-
* where approvalId may be prefixed with "exec:" or "plugin:".
|
|
143
|
+
* Expected format: `approve:v2:<approvalKind>:<encodedApprovalId>:<decision>`.
|
|
109
144
|
*
|
|
110
145
|
* Returns null if the data does not match the approval button format.
|
|
111
146
|
*/
|
|
112
147
|
function parseApprovalButtonData(buttonData) {
|
|
113
|
-
const m = buttonData.match(/^approve:(
|
|
114
|
-
if (!m) return null;
|
|
148
|
+
const m = buttonData.match(/^approve:v2:(exec|plugin):([^:]+):(allow-once|allow-always|deny)$/);
|
|
149
|
+
if (!m || m[0] !== buttonData) return null;
|
|
150
|
+
let approvalId;
|
|
151
|
+
const kind = m[1];
|
|
152
|
+
const encodedId = m[2];
|
|
153
|
+
const decision = m[3];
|
|
154
|
+
if (kind !== "exec" && kind !== "plugin" || encodedId === void 0 || decision !== "allow-once" && decision !== "allow-always" && decision !== "deny") return null;
|
|
155
|
+
try {
|
|
156
|
+
approvalId = decodeURIComponent(encodedId);
|
|
157
|
+
} catch {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
if (!approvalId) return null;
|
|
115
161
|
return {
|
|
116
|
-
approvalId
|
|
117
|
-
|
|
162
|
+
approvalId,
|
|
163
|
+
approvalKind: kind,
|
|
164
|
+
decision
|
|
118
165
|
};
|
|
119
166
|
}
|
|
120
167
|
//#endregion
|
|
@@ -375,7 +422,7 @@ function createQQBotApprovalCapability() {
|
|
|
375
422
|
},
|
|
376
423
|
load: async () => {
|
|
377
424
|
ensurePlatformAdapter();
|
|
378
|
-
return (await import("./handler-runtime-
|
|
425
|
+
return (await import("./handler-runtime-S1_XF8on.js")).qqbotApprovalNativeRuntime;
|
|
379
426
|
}
|
|
380
427
|
})
|
|
381
428
|
});
|
|
@@ -746,7 +793,8 @@ var QQBotMarkdownChunkingState = class {
|
|
|
746
793
|
const bodyLines = pendingFenceOpenLine ? [...this.textLines] : this.textLines.slice(1);
|
|
747
794
|
this.textLines = [];
|
|
748
795
|
this.pendingTextFenceOpenLine = null;
|
|
749
|
-
|
|
796
|
+
const lastBodyLine = bodyLines.at(-1);
|
|
797
|
+
if (lastBodyLine !== void 0 && isClosingFenceLine(lastBodyLine, fence)) bodyLines.pop();
|
|
750
798
|
if (this.activeFence && bodyLines.length === 0) return true;
|
|
751
799
|
pushFenceLineChunks({
|
|
752
800
|
chunks,
|
|
@@ -994,22 +1042,19 @@ function isClosingFenceLine(line, fence) {
|
|
|
994
1042
|
//#endregion
|
|
995
1043
|
//#region extensions/qqbot/src/group-policy.ts
|
|
996
1044
|
function resolveQQBotGroupToolPolicy(params) {
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
senderName: params.senderName,
|
|
1005
|
-
senderUsername: params.senderUsername,
|
|
1006
|
-
senderE164: params.senderE164
|
|
1045
|
+
const tree = buildChannelGroupsScopeTree(params.cfg, "qqbot", params.accountId);
|
|
1046
|
+
const scopeKey = resolveScopeKeyCaseInsensitive(tree, params.groupId);
|
|
1047
|
+
return resolveScopeToolsPolicy({
|
|
1048
|
+
...params,
|
|
1049
|
+
tree,
|
|
1050
|
+
path: scopeKey ? [scopeKey] : [],
|
|
1051
|
+
messageProvider: "qqbot"
|
|
1007
1052
|
});
|
|
1008
1053
|
}
|
|
1009
1054
|
//#endregion
|
|
1010
1055
|
//#region extensions/qqbot/src/channel.ts
|
|
1011
|
-
const loadGatewayModule = createLazyRuntimeModule(() => import("./gateway-
|
|
1012
|
-
const loadOutboundMessagingModule = createLazyRuntimeModule(() => import("./outbound-
|
|
1056
|
+
const loadGatewayModule = createLazyRuntimeModule(() => import("./gateway-D8uYPtoy.js"));
|
|
1057
|
+
const loadOutboundMessagingModule = createLazyRuntimeModule(() => import("./outbound-FOG4zNLY.js").then((n) => n.t));
|
|
1013
1058
|
function createQQBotSendReceipt(params) {
|
|
1014
1059
|
const messageId = params.messageId?.trim();
|
|
1015
1060
|
return createMessageReceiptFromOutboundResults({
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as qqbotPlugin } from "./channel-
|
|
1
|
+
import { t as qqbotPlugin } from "./channel-nz3Mkify.js";
|
|
2
2
|
export { qqbotPlugin };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as qqbotSetupAdapterShared, i as qqbotMeta, n as qqbotSetupWizard, r as qqbotConfigAdapter, t as qqbotChannelConfigSchema } from "./config-schema-
|
|
1
|
+
import { a as qqbotSetupAdapterShared, i as qqbotMeta, n as qqbotSetupWizard, r as qqbotConfigAdapter, t as qqbotChannelConfigSchema } from "./config-schema-D7MaH5X5.js";
|
|
2
2
|
//#region extensions/qqbot/src/channel.setup.ts
|
|
3
3
|
/**
|
|
4
4
|
* Setup-only QQBot plugin — lightweight subset used during `openclaw onboard`
|
|
@@ -56,8 +56,8 @@ function resolveDefaultAccountId(cfg) {
|
|
|
56
56
|
if (configuredDefaultAccountId && (configuredDefaultAccountId === "default" || Boolean(qqbot?.accounts?.[configuredDefaultAccountId]?.appId))) return configuredDefaultAccountId;
|
|
57
57
|
if (qqbot?.appId || process.env.QQBOT_APP_ID) return DEFAULT_ACCOUNT_ID$1;
|
|
58
58
|
if (qqbot?.accounts) {
|
|
59
|
-
const
|
|
60
|
-
if (
|
|
59
|
+
const firstId = Object.keys(qqbot.accounts).at(0);
|
|
60
|
+
if (firstId !== void 0) return firstId;
|
|
61
61
|
}
|
|
62
62
|
return DEFAULT_ACCOUNT_ID$1;
|
|
63
63
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { d as registerPlatformAdapterFactory, l as hasPlatformAdapter, n as normalizeLowercaseStringOrEmpty, u as registerPlatformAdapter } from "./string-normalize-R_0cKO7Q.js";
|
|
2
|
-
import { a as resolveQQBotAccount, c as describeAccount, i as resolveDefaultQQBotAccountId, l as formatAllowFrom, n as applyQQBotAccountConfig, r as listQQBotAccountIds, s as applyAccountConfig, u as isAccountConfigured } from "./config-
|
|
3
|
-
import {
|
|
2
|
+
import { a as resolveQQBotAccount, c as describeAccount, i as resolveDefaultQQBotAccountId, l as formatAllowFrom, n as applyQQBotAccountConfig, r as listQQBotAccountIds, s as applyAccountConfig, u as isAccountConfigured } from "./config-CpOXnoEc.js";
|
|
3
|
+
import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
|
|
4
4
|
import { buildSecretInputSchema, hasConfiguredSecretInput, normalizeResolvedSecretInputString, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
|
5
5
|
import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
|
|
6
6
|
import { applyAccountNameToChannelSection, deleteAccountFromConfigSection, setAccountEnabledInConfigSection } from "openclaw/plugin-sdk/core";
|
|
7
7
|
import { DEFAULT_ACCOUNT_ID, createStandardChannelSetupStatus, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup";
|
|
8
|
+
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
8
9
|
import { formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
|
|
9
|
-
import { AllowFromListSchema,
|
|
10
|
+
import { AllowFromListSchema, GroupPolicySchema, buildChannelConfigSchema, buildGroupEntrySchema, buildMultiAccountChannelSchema } from "openclaw/plugin-sdk/channel-config-schema";
|
|
10
11
|
import { z } from "zod";
|
|
11
12
|
//#region extensions/qqbot/src/bridge/logger.ts
|
|
12
13
|
let loggerInstance = null;
|
|
@@ -24,31 +25,7 @@ function getBridgeLogger() {
|
|
|
24
25
|
}
|
|
25
26
|
//#endregion
|
|
26
27
|
//#region extensions/qqbot/src/bridge/bootstrap.ts
|
|
27
|
-
|
|
28
|
-
* Bootstrap the PlatformAdapter for the built-in version.
|
|
29
|
-
*
|
|
30
|
-
* ## Design
|
|
31
|
-
*
|
|
32
|
-
* The adapter is registered via two complementary mechanisms:
|
|
33
|
-
*
|
|
34
|
-
* 1. **Factory registration** (`registerPlatformAdapterFactory`) — a lightweight
|
|
35
|
-
* callback stored in `adapter/index.ts` that is invoked lazily by
|
|
36
|
-
* `getPlatformAdapter()` on first access. This guarantees the adapter is
|
|
37
|
-
* available regardless of module evaluation order or bundler chunk splitting.
|
|
38
|
-
*
|
|
39
|
-
* 2. **Eager side-effect** (`ensurePlatformAdapter()`) — called at module
|
|
40
|
-
* evaluation time when `channel.ts` imports this file. Provides the adapter
|
|
41
|
-
* immediately for code that runs synchronously during startup.
|
|
42
|
-
*
|
|
43
|
-
* Heavy async-only dependencies (`media-runtime`, `config-runtime`,
|
|
44
|
-
* `approval-gateway-runtime`) are lazy-imported inside each async method body
|
|
45
|
-
* so that this module evaluates with minimal overhead.
|
|
46
|
-
*
|
|
47
|
-
* Synchronous dependencies (`secret-input`, `temp-path`) are imported
|
|
48
|
-
* statically at the top level so they work reliably in both production and
|
|
49
|
-
* vitest (which resolves bare specifiers via `resolve.alias`, not Node CJS).
|
|
50
|
-
*/
|
|
51
|
-
const loadMediaRuntimeModule = createLazyRuntimeModule(() => import("openclaw/plugin-sdk/media-runtime"));
|
|
28
|
+
const loadReadRemoteMediaBuffer = createLazyRuntimeNamedExport(() => import("openclaw/plugin-sdk/media-runtime"), "readRemoteMediaBuffer");
|
|
52
29
|
function createBuiltinAdapter() {
|
|
53
30
|
return {
|
|
54
31
|
async validateRemoteUrl(_url, _options) {},
|
|
@@ -56,8 +33,7 @@ function createBuiltinAdapter() {
|
|
|
56
33
|
if (typeof value === "string") return value || void 0;
|
|
57
34
|
},
|
|
58
35
|
async downloadFile(url, destDir, filename) {
|
|
59
|
-
const
|
|
60
|
-
const result = await readRemoteMediaBuffer({
|
|
36
|
+
const result = await (await loadReadRemoteMediaBuffer())({
|
|
61
37
|
url,
|
|
62
38
|
filePathHint: filename
|
|
63
39
|
});
|
|
@@ -69,12 +45,13 @@ function createBuiltinAdapter() {
|
|
|
69
45
|
return destPath;
|
|
70
46
|
},
|
|
71
47
|
async fetchMedia(options) {
|
|
72
|
-
const
|
|
73
|
-
const result = await readRemoteMediaBuffer({
|
|
48
|
+
const result = await (await loadReadRemoteMediaBuffer())({
|
|
74
49
|
url: options.url,
|
|
75
50
|
filePathHint: options.filePathHint,
|
|
76
51
|
maxBytes: options.maxBytes,
|
|
77
52
|
maxRedirects: options.maxRedirects,
|
|
53
|
+
timeoutMs: options.timeoutMs,
|
|
54
|
+
responseHeaderTimeoutMs: options.responseHeaderTimeoutMs,
|
|
78
55
|
ssrfPolicy: options.ssrfPolicy,
|
|
79
56
|
requestInit: options.requestInit
|
|
80
57
|
});
|
|
@@ -95,20 +72,20 @@ function createBuiltinAdapter() {
|
|
|
95
72
|
resolveSecretInputString(params) {
|
|
96
73
|
return normalizeResolvedSecretInputString(params) ?? void 0;
|
|
97
74
|
},
|
|
98
|
-
async resolveApproval(
|
|
75
|
+
async resolveApproval(params) {
|
|
99
76
|
try {
|
|
100
77
|
const { getRuntimeConfig } = await import("openclaw/plugin-sdk/runtime-config-snapshot");
|
|
101
78
|
const { resolveApprovalOverGateway } = await import("openclaw/plugin-sdk/approval-gateway-runtime");
|
|
102
|
-
await resolveApprovalOverGateway({
|
|
79
|
+
return await resolveApprovalOverGateway({
|
|
103
80
|
cfg: getRuntimeConfig(),
|
|
104
|
-
approvalId,
|
|
105
|
-
|
|
81
|
+
approvalId: params.approvalId,
|
|
82
|
+
approvalKind: params.approvalKind,
|
|
83
|
+
decision: params.decision,
|
|
106
84
|
clientDisplayName: "QQBot Approval Handler"
|
|
107
85
|
});
|
|
108
|
-
return true;
|
|
109
86
|
} catch (err) {
|
|
110
87
|
getBridgeLogger().error(`[qqbot] resolveApproval failed: ${String(err)}`);
|
|
111
|
-
|
|
88
|
+
throw err;
|
|
112
89
|
}
|
|
113
90
|
}
|
|
114
91
|
};
|
|
@@ -247,32 +224,43 @@ function isQQBotAccountConfigured(cfg, accountId) {
|
|
|
247
224
|
const account = resolveQQBotAccount(cfg, accountId, { allowUnresolvedSecretRef: true });
|
|
248
225
|
return Boolean(account.appId && account.clientSecret);
|
|
249
226
|
}
|
|
227
|
+
async function reportQQBotLinkFailure(params, error) {
|
|
228
|
+
params.runtime.error(`QQ Bot 绑定失败: ${String(error)}`);
|
|
229
|
+
await params.prompter.note(["绑定失败,您可以稍后手动配置。", `文档: ${formatDocsLink("/channels/qqbot", "qqbot")}`].join("\n"), "QQ Bot");
|
|
230
|
+
}
|
|
250
231
|
async function linkViaQrCode(params) {
|
|
232
|
+
let connector;
|
|
251
233
|
try {
|
|
252
|
-
|
|
253
|
-
|
|
234
|
+
connector = await import("@tencent-connect/qqbot-connector");
|
|
235
|
+
} catch (error) {
|
|
236
|
+
await reportQQBotLinkFailure(params, error);
|
|
237
|
+
return params.cfg;
|
|
238
|
+
}
|
|
239
|
+
await params.beforePersistentEffect?.();
|
|
240
|
+
try {
|
|
241
|
+
const accounts = await connector.qrConnect({ source: "openclaw" });
|
|
254
242
|
if (accounts.length === 0) {
|
|
255
243
|
await params.prompter.note("未获取到任何 QQ Bot 账号信息。", "QQ Bot");
|
|
256
244
|
return params.cfg;
|
|
257
245
|
}
|
|
258
246
|
let next = params.cfg;
|
|
259
|
-
for (
|
|
260
|
-
const { appId, appSecret } = accounts[i];
|
|
247
|
+
for (const [i, { appId, appSecret }] of accounts.entries()) {
|
|
261
248
|
const targetAccountId = i === 0 ? params.accountId : appId;
|
|
262
249
|
next = applyQQBotAccountConfig(next, targetAccountId, {
|
|
263
250
|
appId,
|
|
264
251
|
clientSecret: appSecret
|
|
265
252
|
});
|
|
266
253
|
}
|
|
267
|
-
if (accounts.length === 1)
|
|
268
|
-
|
|
254
|
+
if (accounts.length === 1) {
|
|
255
|
+
const account = expectDefined(accounts.at(0), "single linked QQ Bot account");
|
|
256
|
+
params.runtime.log(`✔ QQ Bot 绑定成功!(AppID: ${account.appId})`);
|
|
257
|
+
} else {
|
|
269
258
|
const idList = accounts.map((a) => a.appId).join(", ");
|
|
270
259
|
params.runtime.log(`✔ ${accounts.length} 个 QQ Bot 绑定成功!(AppID: ${idList})`);
|
|
271
260
|
}
|
|
272
261
|
return next;
|
|
273
262
|
} catch (error) {
|
|
274
|
-
params
|
|
275
|
-
await params.prompter.note(["绑定失败,您可以稍后手动配置。", `文档: ${formatDocsLink("/channels/qqbot", "qqbot")}`].join("\n"), "QQ Bot");
|
|
263
|
+
await reportQQBotLinkFailure(params, error);
|
|
276
264
|
return params.cfg;
|
|
277
265
|
}
|
|
278
266
|
}
|
|
@@ -319,7 +307,8 @@ async function finalizeQQBotSetup(params) {
|
|
|
319
307
|
cfg: next,
|
|
320
308
|
accountId,
|
|
321
309
|
prompter: params.prompter,
|
|
322
|
-
runtime: params.runtime
|
|
310
|
+
runtime: params.runtime,
|
|
311
|
+
beforePersistentEffect: params.options?.beforePersistentEffect
|
|
323
312
|
});
|
|
324
313
|
else if (mode === "manual") next = await linkViaManualInput({
|
|
325
314
|
cfg: next,
|
|
@@ -347,12 +336,13 @@ const qqbotSetupWizard = {
|
|
|
347
336
|
})
|
|
348
337
|
}),
|
|
349
338
|
credentials: [],
|
|
350
|
-
finalize: async ({ cfg, accountId, forceAllowFrom, prompter, runtime }) => await finalizeQQBotSetup({
|
|
339
|
+
finalize: async ({ cfg, accountId, forceAllowFrom, prompter, runtime, options }) => await finalizeQQBotSetup({
|
|
351
340
|
cfg,
|
|
352
341
|
accountId,
|
|
353
342
|
forceAllowFrom,
|
|
354
343
|
prompter,
|
|
355
|
-
runtime
|
|
344
|
+
runtime,
|
|
345
|
+
options
|
|
356
346
|
}),
|
|
357
347
|
disable: (cfg) => setSetupChannelEnabled(cfg, channel, false)
|
|
358
348
|
};
|
|
@@ -370,13 +360,12 @@ const QQBotSttSchema = z.object({
|
|
|
370
360
|
apiKey: z.string().optional(),
|
|
371
361
|
model: z.string().optional()
|
|
372
362
|
}).strict().optional();
|
|
373
|
-
|
|
374
|
-
const QQBotStreamingSchema = z.union([z.boolean(), z.object({
|
|
363
|
+
const QQBotStreamingSchema = z.object({
|
|
375
364
|
/** "partial" (default) enables block streaming; "off" disables it. */
|
|
376
365
|
mode: z.enum(["off", "partial"]).default("partial"),
|
|
377
|
-
/**
|
|
378
|
-
|
|
379
|
-
}).
|
|
366
|
+
/** Use QQ's official C2C `stream_messages` API for DM replies. */
|
|
367
|
+
nativeTransport: z.boolean().optional()
|
|
368
|
+
}).strict().optional();
|
|
380
369
|
const QQBotExecApprovalsSchema = z.object({
|
|
381
370
|
enabled: z.union([z.boolean(), z.literal("auto")]).optional(),
|
|
382
371
|
approvers: z.array(z.string()).optional(),
|
|
@@ -393,26 +382,23 @@ const QQBotDmPolicySchema = z.enum([
|
|
|
393
382
|
"allowlist",
|
|
394
383
|
"disabled"
|
|
395
384
|
]).optional();
|
|
396
|
-
const QQBotGroupPolicySchema =
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
"safety",
|
|
404
|
-
"strict"
|
|
405
|
-
]).optional();
|
|
406
|
-
const QQBotGroupSchema = z.object({
|
|
407
|
-
requireMention: z.boolean().optional(),
|
|
408
|
-
commandLevel: QQBotGroupCommandLevelSchema,
|
|
385
|
+
const QQBotGroupPolicySchema = GroupPolicySchema.optional();
|
|
386
|
+
const QQBotGroupSchema = buildGroupEntrySchema({
|
|
387
|
+
commandLevel: z.enum([
|
|
388
|
+
"all",
|
|
389
|
+
"safety",
|
|
390
|
+
"strict"
|
|
391
|
+
]).optional(),
|
|
409
392
|
ignoreOtherMentions: z.boolean().optional(),
|
|
410
393
|
historyLimit: z.number().optional(),
|
|
411
394
|
name: z.string().optional(),
|
|
412
|
-
prompt: z.string().optional()
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
395
|
+
prompt: z.string().optional()
|
|
396
|
+
}).omit({
|
|
397
|
+
skills: true,
|
|
398
|
+
enabled: true,
|
|
399
|
+
allowFrom: true,
|
|
400
|
+
systemPrompt: true
|
|
401
|
+
});
|
|
416
402
|
const QQBotGroupsSchema = z.record(z.string(), QQBotGroupSchema).optional();
|
|
417
403
|
const QQBotAccountSchema = z.object({
|
|
418
404
|
enabled: z.boolean().optional(),
|
|
@@ -435,10 +421,9 @@ const QQBotAccountSchema = z.object({
|
|
|
435
421
|
execApprovals: QQBotExecApprovalsSchema,
|
|
436
422
|
groups: QQBotGroupsSchema
|
|
437
423
|
}).passthrough();
|
|
438
|
-
const qqbotChannelConfigSchema = buildChannelConfigSchema(QQBotAccountSchema.extend({
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
}).passthrough());
|
|
424
|
+
const qqbotChannelConfigSchema = buildChannelConfigSchema(buildMultiAccountChannelSchema(QQBotAccountSchema.extend({ stt: QQBotSttSchema }).passthrough(), {
|
|
425
|
+
accountSchema: QQBotAccountSchema,
|
|
426
|
+
accountsMode: "catchall"
|
|
427
|
+
}));
|
|
443
428
|
//#endregion
|
|
444
429
|
export { qqbotSetupAdapterShared as a, setBridgeLogger as c, qqbotMeta as i, qqbotSetupWizard as n, ensurePlatformAdapter as o, qqbotConfigAdapter as r, getBridgeLogger as s, qqbotChannelConfigSchema as t };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { n as legacyConfigRules, r as normalizeCompatibilityConfig, t as buildQQBotStateKey } from "./state-keys-
|
|
1
|
+
import { n as legacyConfigRules, r as normalizeCompatibilityConfig, t as buildQQBotStateKey } from "./state-keys-jLJ2SmJA.js";
|
|
2
|
+
import { fileExists } from "openclaw/plugin-sdk/security-runtime";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import fs from "node:fs/promises";
|
|
4
5
|
//#region extensions/qqbot/src/state-migrations.ts
|
|
@@ -7,13 +8,6 @@ const MAX_CREDENTIAL_BACKUPS = 1e3;
|
|
|
7
8
|
function safeName(id) {
|
|
8
9
|
return id.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
9
10
|
}
|
|
10
|
-
async function fileExists(filePath) {
|
|
11
|
-
try {
|
|
12
|
-
return (await fs.lstat(filePath)).isFile();
|
|
13
|
-
} catch {
|
|
14
|
-
return false;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
11
|
async function readCredentialBackup(filePath) {
|
|
18
12
|
try {
|
|
19
13
|
const parsed = JSON.parse(await fs.readFile(filePath, "utf8"));
|
|
@@ -42,7 +36,7 @@ async function credentialBackupCandidates(stateDir) {
|
|
|
42
36
|
} catch {}
|
|
43
37
|
accountFiles.sort((left, right) => left.sourcePath.localeCompare(right.sourcePath));
|
|
44
38
|
const singlePath = path.join(dataDir, "credential-backup.json");
|
|
45
|
-
return
|
|
39
|
+
return fileExists(singlePath) ? [...accountFiles, { sourcePath: singlePath }] : accountFiles;
|
|
46
40
|
}
|
|
47
41
|
async function readLegacyCredentialBackups(stateDir) {
|
|
48
42
|
const backups = [];
|
|
@@ -59,7 +53,7 @@ async function readLegacyCredentialBackups(stateDir) {
|
|
|
59
53
|
}
|
|
60
54
|
async function archiveLegacySource(params) {
|
|
61
55
|
const archivedPath = `${params.sourcePath}.migrated`;
|
|
62
|
-
if (
|
|
56
|
+
if (fileExists(archivedPath)) {
|
|
63
57
|
params.warnings.push(`Left QQBot credential backup in place because ${archivedPath} already exists`);
|
|
64
58
|
return;
|
|
65
59
|
}
|