@openclaw/qqbot 2026.6.11 → 2026.7.1-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 +6 -6
- package/dist/{channel-DvWrqcZB.js → channel-BQM6pDlD.js} +71 -126
- package/dist/{channel-entry-BGXFRmbg.js → channel-entry-C18ypTaG.js} +3 -3
- package/dist/channel-entry-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.setup-CldhUlTO.js → channel.setup-2ItDYKhz.js} +1 -1
- package/dist/{config-schema-gMW9zKvQ.js → config-schema-JZEf1dvB.js} +2 -5
- package/dist/doctor-contract-api.js +168 -2
- package/dist/{gateway-CCkrdgaK.js → gateway-CjdB3B6n.js} +284 -264
- package/dist/{group-CvVXke_k.js → group-BmS1Qwst.js} +2 -2
- package/dist/{handler-runtime--OTMkFWa.js → handler-runtime-rU03VA43.js} +3 -3
- package/dist/{outbound-BQcPCo-z.js → outbound-BZHwMFUX.js} +496 -44
- package/dist/{runtime-D1hn91-s.js → runtime-B4HWCOa-.js} +1 -1
- package/dist/runtime-api.js +1 -1
- package/dist/{sender-CRZ2af7P.js → sender-BfVLJJYp.js} +16 -14
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{doctor-contract-BYTS8-ia.js → state-keys-CQKlAFyo.js} +7 -1
- package/dist/target-parser-DagYRQkP.js +71 -0
- package/dist/{tools-CeUI9pG-.js → tools-C8kTMXKc.js} +58 -13
- package/dist/tools-api.js +1 -1
- package/npm-shrinkwrap.json +3 -3
- package/package.json +4 -4
- package/skills/qqbot-channel/SKILL.md +54 -41
- package/skills/qqbot-channel/references/api_references.md +16 -8
- package/skills/qqbot-media/SKILL.md +19 -13
- package/skills/qqbot-remind/SKILL.md +15 -14
- package/dist/target-parser-BdCUmxK7.js +0 -285
package/dist/api.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as qqbotPlugin } from "./channel-
|
|
1
|
+
import { t as qqbotPlugin } from "./channel-BQM6pDlD.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
|
-
import { t as qqbotSetupPlugin } from "./channel.setup-
|
|
4
|
-
import { o as getFrameworkCommands } from "./group-
|
|
5
|
-
import { n as
|
|
6
|
-
import { t as
|
|
7
|
-
import {
|
|
3
|
+
import { t as qqbotSetupPlugin } from "./channel.setup-2ItDYKhz.js";
|
|
4
|
+
import { o as getFrameworkCommands } from "./group-BmS1Qwst.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-BZHwMFUX.js";
|
|
6
|
+
import { n as registerRemindTool, r as registerChannelTool, t as registerQQBotTools } from "./tools-C8kTMXKc.js";
|
|
7
|
+
import { t as registerQQBotFull } from "./channel-entry-C18ypTaG.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,11 +1,12 @@
|
|
|
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-
|
|
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-
|
|
5
|
-
import { n as normalizeCompatibilityConfig, t as
|
|
6
|
-
import { n as normalizeTarget,
|
|
4
|
+
import { t as getQQBotRuntime } from "./runtime-B4HWCOa-.js";
|
|
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";
|
|
7
7
|
import { getExecApprovalReplyMetadata } from "openclaw/plugin-sdk/approval-runtime";
|
|
8
8
|
import { createMessageReceiptFromOutboundResults, defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-outbound";
|
|
9
|
+
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
9
10
|
import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
|
|
10
11
|
import { createChannelApprovalCapability } from "openclaw/plugin-sdk/approval-delivery-runtime";
|
|
11
12
|
import { createLazyChannelApprovalNativeRuntimeAdapter } from "openclaw/plugin-sdk/approval-handler-adapter-runtime";
|
|
@@ -14,11 +15,6 @@ import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "opencl
|
|
|
14
15
|
import { markImplicitSameChatApprovalAuthorization, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
|
|
15
16
|
import { createChannelExecApprovalProfile, isChannelExecApprovalClientEnabledFromConfig, matchesApprovalRequestFilters } from "openclaw/plugin-sdk/approval-client-runtime";
|
|
16
17
|
import { normalizeAccountId } from "openclaw/plugin-sdk/routing";
|
|
17
|
-
import fs from "node:fs";
|
|
18
|
-
import crypto from "node:crypto";
|
|
19
|
-
import path from "node:path";
|
|
20
|
-
import { loadJsonFile } from "openclaw/plugin-sdk/json-store";
|
|
21
|
-
import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
|
|
22
18
|
import { resolveChannelGroupToolsPolicy } from "openclaw/plugin-sdk/channel-policy";
|
|
23
19
|
//#region extensions/qqbot/src/engine/approval/index.ts
|
|
24
20
|
function buildExecApprovalText(request) {
|
|
@@ -370,7 +366,7 @@ function createQQBotApprovalCapability() {
|
|
|
370
366
|
},
|
|
371
367
|
load: async () => {
|
|
372
368
|
ensurePlatformAdapter();
|
|
373
|
-
return (await import("./handler-runtime
|
|
369
|
+
return (await import("./handler-runtime-rU03VA43.js")).qqbotApprovalNativeRuntime;
|
|
374
370
|
}
|
|
375
371
|
})
|
|
376
372
|
});
|
|
@@ -412,41 +408,6 @@ const qqbotDoctor = {
|
|
|
412
408
|
normalizeCompatibilityConfig
|
|
413
409
|
};
|
|
414
410
|
//#endregion
|
|
415
|
-
//#region extensions/qqbot/src/engine/utils/data-paths.ts
|
|
416
|
-
/**
|
|
417
|
-
* Centralised filename helpers for persisted QQBot state.
|
|
418
|
-
*
|
|
419
|
-
* Every persistence module routes file paths through these helpers so the
|
|
420
|
-
* naming convention stays in sync and legacy migrations are handled
|
|
421
|
-
* consistently.
|
|
422
|
-
*
|
|
423
|
-
* Key design decisions:
|
|
424
|
-
* - Credential backup is keyed only by `accountId` because recovery runs
|
|
425
|
-
* exactly when the appId is missing from config.
|
|
426
|
-
*/
|
|
427
|
-
/**
|
|
428
|
-
* Normalise an identifier so it is safe to embed in a filename.
|
|
429
|
-
* Keeps alphanumerics, dot, underscore, dash; everything else becomes `_`.
|
|
430
|
-
*/
|
|
431
|
-
function safeName$1(id) {
|
|
432
|
-
return id.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
433
|
-
}
|
|
434
|
-
function getCredentialBackupRoot() {
|
|
435
|
-
return path.join(resolveStateDir(process.env), "qqbot", "data");
|
|
436
|
-
}
|
|
437
|
-
/**
|
|
438
|
-
* Per-accountId credential backup file. Not keyed by appId because the
|
|
439
|
-
* whole point of this file is to recover credentials when appId is
|
|
440
|
-
* missing from the live config.
|
|
441
|
-
*/
|
|
442
|
-
function getCredentialBackupFile(accountId) {
|
|
443
|
-
return path.join(getCredentialBackupRoot(), `credential-backup-${safeName$1(accountId)}.json`);
|
|
444
|
-
}
|
|
445
|
-
/** Legacy single-file credential backup (pre-multi-account-isolation). */
|
|
446
|
-
function getLegacyCredentialBackupFile() {
|
|
447
|
-
return path.join(getCredentialBackupRoot(), "credential-backup.json");
|
|
448
|
-
}
|
|
449
|
-
//#endregion
|
|
450
411
|
//#region extensions/qqbot/src/engine/utils/sqlite-state.ts
|
|
451
412
|
function resolveStoreEnv(options) {
|
|
452
413
|
if (!options.stateDir) return options.env;
|
|
@@ -463,9 +424,6 @@ function openQQBotSyncKeyedStore(options) {
|
|
|
463
424
|
...resolveStoreEnv(options) ? { env: resolveStoreEnv(options) } : {}
|
|
464
425
|
});
|
|
465
426
|
}
|
|
466
|
-
function buildQQBotStateKey(...parts) {
|
|
467
|
-
return crypto.createHash("sha256").update(JSON.stringify(parts)).digest("hex");
|
|
468
|
-
}
|
|
469
427
|
//#endregion
|
|
470
428
|
//#region extensions/qqbot/src/engine/config/credential-backup.ts
|
|
471
429
|
/**
|
|
@@ -481,8 +439,8 @@ function buildQQBotStateKey(...parts) {
|
|
|
481
439
|
* - During plugin startup, if the live config has an empty appId or
|
|
482
440
|
* secret, the gateway consults the backup and restores the values
|
|
483
441
|
* via the config mutation API.
|
|
484
|
-
* - Legacy JSON backups are imported
|
|
485
|
-
*
|
|
442
|
+
* - Legacy JSON backups are imported by `openclaw doctor --fix`, not by
|
|
443
|
+
* runtime startup.
|
|
486
444
|
*
|
|
487
445
|
* Safety notes:
|
|
488
446
|
* - Only restore when credentials are **actually empty** — never
|
|
@@ -498,53 +456,16 @@ function createCredentialBackupStore() {
|
|
|
498
456
|
maxEntries: MAX_CREDENTIAL_BACKUPS
|
|
499
457
|
});
|
|
500
458
|
}
|
|
501
|
-
function safeName(id) {
|
|
502
|
-
return id.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
503
|
-
}
|
|
504
|
-
function getLegacyOsHomeCredentialBackupFile(accountId) {
|
|
505
|
-
return path.join(getQQBotDataPath("data"), `credential-backup-${safeName(accountId)}.json`);
|
|
506
|
-
}
|
|
507
|
-
function getLegacyOsHomeCredentialBackupFileWithoutAccount() {
|
|
508
|
-
return path.join(getQQBotDataPath("data"), "credential-backup.json");
|
|
509
|
-
}
|
|
510
459
|
function credentialBackupKey(accountId) {
|
|
511
460
|
return buildQQBotStateKey("credential-backup", accountId);
|
|
512
461
|
}
|
|
513
462
|
function isUsableBackup(data) {
|
|
514
463
|
return Boolean(data?.accountId && data.appId && data.clientSecret);
|
|
515
464
|
}
|
|
516
|
-
function loadUsableBackupFromFile(filePath) {
|
|
517
|
-
const data = loadJsonFile(filePath);
|
|
518
|
-
return isUsableBackup(data) ? data : null;
|
|
519
|
-
}
|
|
520
|
-
function removeFileQuietly(filePath) {
|
|
521
|
-
try {
|
|
522
|
-
fs.unlinkSync(filePath);
|
|
523
|
-
} catch {}
|
|
524
|
-
}
|
|
525
|
-
function findLegacyBackup(accountId) {
|
|
526
|
-
const candidates = accountId ? [
|
|
527
|
-
getCredentialBackupFile(accountId),
|
|
528
|
-
getLegacyCredentialBackupFile(),
|
|
529
|
-
getLegacyOsHomeCredentialBackupFile(accountId),
|
|
530
|
-
getLegacyOsHomeCredentialBackupFileWithoutAccount()
|
|
531
|
-
] : [getLegacyCredentialBackupFile(), getLegacyOsHomeCredentialBackupFileWithoutAccount()];
|
|
532
|
-
for (const filePath of candidates) {
|
|
533
|
-
const data = loadUsableBackupFromFile(filePath);
|
|
534
|
-
if (!data) continue;
|
|
535
|
-
if (accountId && data.accountId !== accountId) continue;
|
|
536
|
-
return {
|
|
537
|
-
data,
|
|
538
|
-
filePath
|
|
539
|
-
};
|
|
540
|
-
}
|
|
541
|
-
return null;
|
|
542
|
-
}
|
|
543
465
|
/** Persist a credential snapshot (called once gateway reaches READY). */
|
|
544
466
|
function saveCredentialBackup(accountId, appId, clientSecret) {
|
|
545
467
|
if (!appId || !clientSecret) return;
|
|
546
468
|
try {
|
|
547
|
-
const backupPath = getCredentialBackupFile(accountId);
|
|
548
469
|
const data = {
|
|
549
470
|
accountId,
|
|
550
471
|
appId,
|
|
@@ -552,14 +473,13 @@ function saveCredentialBackup(accountId, appId, clientSecret) {
|
|
|
552
473
|
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
553
474
|
};
|
|
554
475
|
createCredentialBackupStore().register(credentialBackupKey(accountId), data);
|
|
555
|
-
removeFileQuietly(backupPath);
|
|
556
476
|
} catch {}
|
|
557
477
|
}
|
|
558
478
|
/**
|
|
559
479
|
* Load a credential snapshot for `accountId`.
|
|
560
480
|
*
|
|
561
|
-
*
|
|
562
|
-
*
|
|
481
|
+
* Reads SQLite only. Legacy JSON backup import is owned by doctor/setup
|
|
482
|
+
* migration so runtime startup stays canonical-state-only.
|
|
563
483
|
*/
|
|
564
484
|
function loadCredentialBackup(accountId) {
|
|
565
485
|
try {
|
|
@@ -567,12 +487,6 @@ function loadCredentialBackup(accountId) {
|
|
|
567
487
|
const data = createCredentialBackupStore().lookup(credentialBackupKey(accountId));
|
|
568
488
|
if (isUsableBackup(data)) return data;
|
|
569
489
|
}
|
|
570
|
-
const legacy = findLegacyBackup(accountId);
|
|
571
|
-
if (legacy) {
|
|
572
|
-
createCredentialBackupStore().register(credentialBackupKey(legacy.data.accountId), legacy.data);
|
|
573
|
-
removeFileQuietly(legacy.filePath);
|
|
574
|
-
return legacy.data;
|
|
575
|
-
}
|
|
576
490
|
} catch {}
|
|
577
491
|
return null;
|
|
578
492
|
}
|
|
@@ -934,11 +848,32 @@ function isTableSeparatorLine(line) {
|
|
|
934
848
|
const cells = splitTableCells(line);
|
|
935
849
|
return cells.length > 0 && cells.every((cell) => /^:?-+:?$/.test(cell.trim()));
|
|
936
850
|
}
|
|
851
|
+
function splitTableRowCells(inner) {
|
|
852
|
+
const cells = [];
|
|
853
|
+
let current = "";
|
|
854
|
+
for (let i = 0; i < inner.length; i++) {
|
|
855
|
+
const char = inner[i];
|
|
856
|
+
if (char === "\\" && i + 1 < inner.length) {
|
|
857
|
+
const next = inner[i + 1];
|
|
858
|
+
current += next === "|" || next === "\\" ? next : `\\${next}`;
|
|
859
|
+
i++;
|
|
860
|
+
continue;
|
|
861
|
+
}
|
|
862
|
+
if (char === "|") {
|
|
863
|
+
cells.push(current);
|
|
864
|
+
current = "";
|
|
865
|
+
continue;
|
|
866
|
+
}
|
|
867
|
+
current += char;
|
|
868
|
+
}
|
|
869
|
+
cells.push(current);
|
|
870
|
+
return cells;
|
|
871
|
+
}
|
|
937
872
|
function splitTableCells(line) {
|
|
938
|
-
return line.trim().slice(1, -1)
|
|
873
|
+
return splitTableRowCells(line.trim().slice(1, -1)).map((cell) => cell.trim());
|
|
939
874
|
}
|
|
940
875
|
function splitPartialTableCells(line) {
|
|
941
|
-
return line.trim().replace(/^\|/, "")
|
|
876
|
+
return splitTableRowCells(line.trim().replace(/^\|/, "")).map((cell) => cell.trim()).filter((cell) => cell.length > 0);
|
|
942
877
|
}
|
|
943
878
|
function mergeRowFragments(pending, next) {
|
|
944
879
|
return pending ? `${pending}${next}` : next;
|
|
@@ -1064,16 +999,8 @@ function resolveQQBotGroupToolPolicy(params) {
|
|
|
1064
999
|
}
|
|
1065
1000
|
//#endregion
|
|
1066
1001
|
//#region extensions/qqbot/src/channel.ts
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
gatewayModulePromise ??= import("./gateway-CCkrdgaK.js");
|
|
1070
|
-
return gatewayModulePromise;
|
|
1071
|
-
}
|
|
1072
|
-
let outboundMessagingModulePromise;
|
|
1073
|
-
function loadOutboundMessagingModule() {
|
|
1074
|
-
outboundMessagingModulePromise ??= import("./outbound-BQcPCo-z.js").then((n) => n.t);
|
|
1075
|
-
return outboundMessagingModulePromise;
|
|
1076
|
-
}
|
|
1002
|
+
const loadGatewayModule = createLazyRuntimeModule(() => import("./gateway-CjdB3B6n.js"));
|
|
1003
|
+
const loadOutboundMessagingModule = createLazyRuntimeModule(() => import("./outbound-BZHwMFUX.js").then((n) => n.t));
|
|
1077
1004
|
function createQQBotSendReceipt(params) {
|
|
1078
1005
|
const messageId = params.messageId?.trim();
|
|
1079
1006
|
return createMessageReceiptFromOutboundResults({
|
|
@@ -1095,7 +1022,10 @@ async function sendQQBotText(params) {
|
|
|
1095
1022
|
text: params.text,
|
|
1096
1023
|
accountId: params.accountId,
|
|
1097
1024
|
replyToId: params.replyToId,
|
|
1098
|
-
account: toGatewayAccount(account)
|
|
1025
|
+
account: toGatewayAccount(account),
|
|
1026
|
+
...params.mediaAccess ? { mediaAccess: params.mediaAccess } : {},
|
|
1027
|
+
...params.mediaLocalRoots ? { mediaLocalRoots: params.mediaLocalRoots } : {},
|
|
1028
|
+
...params.mediaReadFile ? { mediaReadFile: params.mediaReadFile } : {}
|
|
1099
1029
|
});
|
|
1100
1030
|
return {
|
|
1101
1031
|
channel: "qqbot",
|
|
@@ -1118,7 +1048,10 @@ async function sendQQBotMedia(params) {
|
|
|
1118
1048
|
mediaUrl: params.mediaUrl ?? "",
|
|
1119
1049
|
accountId: params.accountId,
|
|
1120
1050
|
replyToId: params.replyToId,
|
|
1121
|
-
account: toGatewayAccount(account)
|
|
1051
|
+
account: toGatewayAccount(account),
|
|
1052
|
+
...params.mediaAccess ? { mediaAccess: params.mediaAccess } : {},
|
|
1053
|
+
...params.mediaLocalRoots ? { mediaLocalRoots: params.mediaLocalRoots } : {},
|
|
1054
|
+
...params.mediaReadFile ? { mediaReadFile: params.mediaReadFile } : {}
|
|
1122
1055
|
});
|
|
1123
1056
|
return {
|
|
1124
1057
|
channel: "qqbot",
|
|
@@ -1131,6 +1064,14 @@ async function sendQQBotMedia(params) {
|
|
|
1131
1064
|
meta: result.error ? { error: result.error } : void 0
|
|
1132
1065
|
};
|
|
1133
1066
|
}
|
|
1067
|
+
function resolveQQBotOutboundMediaAccessContext(ctx) {
|
|
1068
|
+
const record = ctx && typeof ctx === "object" ? ctx : void 0;
|
|
1069
|
+
return {
|
|
1070
|
+
...record?.mediaAccess ? { mediaAccess: record.mediaAccess } : {},
|
|
1071
|
+
...record?.mediaLocalRoots ? { mediaLocalRoots: record.mediaLocalRoots } : {},
|
|
1072
|
+
...record?.mediaReadFile ? { mediaReadFile: record.mediaReadFile } : {}
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1134
1075
|
function toQQBotMessageSendResult(result) {
|
|
1135
1076
|
if (result.meta?.error) throw new Error(result.meta.error);
|
|
1136
1077
|
if (result.receipt.platformMessageIds.length === 0) throw new Error("QQBot message adapter send did not return a platform message id");
|
|
@@ -1152,7 +1093,8 @@ const qqbotMessageAdapter = defineChannelMessageAdapter({
|
|
|
1152
1093
|
to: ctx.to,
|
|
1153
1094
|
text: ctx.text,
|
|
1154
1095
|
accountId: ctx.accountId,
|
|
1155
|
-
replyToId: ctx.replyToId
|
|
1096
|
+
replyToId: ctx.replyToId,
|
|
1097
|
+
...resolveQQBotOutboundMediaAccessContext(ctx)
|
|
1156
1098
|
})),
|
|
1157
1099
|
media: async (ctx) => toQQBotMessageSendResult(await sendQQBotMedia({
|
|
1158
1100
|
cfg: ctx.cfg,
|
|
@@ -1160,7 +1102,8 @@ const qqbotMessageAdapter = defineChannelMessageAdapter({
|
|
|
1160
1102
|
text: ctx.text,
|
|
1161
1103
|
mediaUrl: ctx.mediaUrl,
|
|
1162
1104
|
accountId: ctx.accountId,
|
|
1163
|
-
replyToId: ctx.replyToId
|
|
1105
|
+
replyToId: ctx.replyToId,
|
|
1106
|
+
...resolveQQBotOutboundMediaAccessContext(ctx)
|
|
1164
1107
|
}))
|
|
1165
1108
|
}
|
|
1166
1109
|
});
|
|
@@ -1231,20 +1174,22 @@ const qqbotPlugin = {
|
|
|
1231
1174
|
payload,
|
|
1232
1175
|
hint
|
|
1233
1176
|
}),
|
|
1234
|
-
sendText: async (
|
|
1235
|
-
cfg,
|
|
1236
|
-
to,
|
|
1237
|
-
text,
|
|
1238
|
-
accountId,
|
|
1239
|
-
replyToId
|
|
1177
|
+
sendText: async (ctx) => await sendQQBotText({
|
|
1178
|
+
cfg: ctx.cfg,
|
|
1179
|
+
to: ctx.to,
|
|
1180
|
+
text: ctx.text,
|
|
1181
|
+
accountId: ctx.accountId,
|
|
1182
|
+
replyToId: ctx.replyToId,
|
|
1183
|
+
...resolveQQBotOutboundMediaAccessContext(ctx)
|
|
1240
1184
|
}),
|
|
1241
|
-
sendMedia: async (
|
|
1242
|
-
cfg,
|
|
1243
|
-
to,
|
|
1244
|
-
text,
|
|
1245
|
-
mediaUrl,
|
|
1246
|
-
accountId,
|
|
1247
|
-
replyToId
|
|
1185
|
+
sendMedia: async (ctx) => await sendQQBotMedia({
|
|
1186
|
+
cfg: ctx.cfg,
|
|
1187
|
+
to: ctx.to,
|
|
1188
|
+
text: ctx.text,
|
|
1189
|
+
mediaUrl: ctx.mediaUrl,
|
|
1190
|
+
accountId: ctx.accountId,
|
|
1191
|
+
replyToId: ctx.replyToId,
|
|
1192
|
+
...resolveQQBotOutboundMediaAccessContext(ctx)
|
|
1248
1193
|
})
|
|
1249
1194
|
},
|
|
1250
1195
|
gateway: {
|
|
@@ -1349,4 +1294,4 @@ const qqbotPlugin = {
|
|
|
1349
1294
|
}
|
|
1350
1295
|
};
|
|
1351
1296
|
//#endregion
|
|
1352
|
-
export {
|
|
1297
|
+
export { authorizeQQBotApprovalAction as a, resolveQQBotExecApprovalConfig as c, buildExecApprovalText as d, buildPluginApprovalText as f, toGatewayAccount as i, shouldHandleQQBotExecApprovalRequest as l, resolveApprovalTarget as m, createQQBotMarkdownChunker as n, isQQBotExecApprovalClientEnabled as o, parseApprovalButtonData as p, openQQBotSyncKeyedStore as r, matchesQQBotApprovalAccount as s, qqbotPlugin as t, buildApprovalKeyboard as u };
|
|
@@ -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 {
|
|
4
|
-
import {
|
|
2
|
+
import { d as PRIVATE_CHAT_ONLY_TEXT, n as resolveGroupCommandLevelFromAccountConfig, o as getFrameworkCommands } from "./group-BmS1Qwst.js";
|
|
3
|
+
import { h as sendDocument } from "./outbound-BZHwMFUX.js";
|
|
4
|
+
import { t as registerQQBotTools } from "./tools-C8kTMXKc.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-C18ypTaG.js";
|
|
2
2
|
export { registerQQBotFull };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as qqbotPlugin } from "./channel-
|
|
1
|
+
import { t as qqbotPlugin } from "./channel-BQM6pDlD.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-JZEf1dvB.js";
|
|
2
2
|
//#region extensions/qqbot/src/channel.setup.ts
|
|
3
3
|
/**
|
|
4
4
|
* Setup-only QQBot plugin — lightweight subset used during `openclaw onboard`
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { d as registerPlatformAdapterFactory, l as hasPlatformAdapter, n as normalizeLowercaseStringOrEmpty, u as registerPlatformAdapter } from "./string-normalize-R_0cKO7Q.js";
|
|
2
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-C1qZbh0K.js";
|
|
3
|
+
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
3
4
|
import { buildSecretInputSchema, hasConfiguredSecretInput, normalizeResolvedSecretInputString, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
|
4
5
|
import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
|
|
5
6
|
import { applyAccountNameToChannelSection, deleteAccountFromConfigSection, setAccountEnabledInConfigSection } from "openclaw/plugin-sdk/core";
|
|
@@ -47,11 +48,7 @@ function getBridgeLogger() {
|
|
|
47
48
|
* statically at the top level so they work reliably in both production and
|
|
48
49
|
* vitest (which resolves bare specifiers via `resolve.alias`, not Node CJS).
|
|
49
50
|
*/
|
|
50
|
-
|
|
51
|
-
const loadMediaRuntimeModule = async () => {
|
|
52
|
-
mediaRuntimeModulePromise ??= import("openclaw/plugin-sdk/media-runtime");
|
|
53
|
-
return await mediaRuntimeModulePromise;
|
|
54
|
-
};
|
|
51
|
+
const loadMediaRuntimeModule = createLazyRuntimeModule(() => import("openclaw/plugin-sdk/media-runtime"));
|
|
55
52
|
function createBuiltinAdapter() {
|
|
56
53
|
return {
|
|
57
54
|
async validateRemoteUrl(_url, _options) {},
|
|
@@ -1,2 +1,168 @@
|
|
|
1
|
-
import { n as normalizeCompatibilityConfig, t as
|
|
2
|
-
|
|
1
|
+
import { n as legacyConfigRules, r as normalizeCompatibilityConfig, t as buildQQBotStateKey } from "./state-keys-CQKlAFyo.js";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
//#region extensions/qqbot/src/state-migrations.ts
|
|
5
|
+
const CREDENTIAL_BACKUPS_NAMESPACE = "credential-backups";
|
|
6
|
+
const MAX_CREDENTIAL_BACKUPS = 1e3;
|
|
7
|
+
function safeName(id) {
|
|
8
|
+
return id.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
9
|
+
}
|
|
10
|
+
async function fileExists(filePath) {
|
|
11
|
+
try {
|
|
12
|
+
return (await fs.lstat(filePath)).isFile();
|
|
13
|
+
} catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
async function readCredentialBackup(filePath) {
|
|
18
|
+
try {
|
|
19
|
+
const parsed = JSON.parse(await fs.readFile(filePath, "utf8"));
|
|
20
|
+
if (typeof parsed.accountId !== "string" || typeof parsed.appId !== "string" || typeof parsed.clientSecret !== "string" || !parsed.accountId || !parsed.appId || !parsed.clientSecret) return null;
|
|
21
|
+
return {
|
|
22
|
+
accountId: parsed.accountId,
|
|
23
|
+
appId: parsed.appId,
|
|
24
|
+
clientSecret: parsed.clientSecret,
|
|
25
|
+
savedAt: typeof parsed.savedAt === "string" && parsed.savedAt ? parsed.savedAt : (/* @__PURE__ */ new Date(0)).toISOString()
|
|
26
|
+
};
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function credentialBackupKey(accountId) {
|
|
32
|
+
return buildQQBotStateKey("credential-backup", accountId);
|
|
33
|
+
}
|
|
34
|
+
async function credentialBackupCandidates(stateDir) {
|
|
35
|
+
const dataDir = path.join(stateDir, "qqbot", "data");
|
|
36
|
+
const accountFiles = [];
|
|
37
|
+
try {
|
|
38
|
+
for (const entry of await fs.readdir(dataDir, { withFileTypes: true })) if (entry.isFile() && entry.name.startsWith("credential-backup-") && entry.name.endsWith(".json")) accountFiles.push({
|
|
39
|
+
sourcePath: path.join(dataDir, entry.name),
|
|
40
|
+
expectedSafeAccountId: entry.name.slice(18, -5)
|
|
41
|
+
});
|
|
42
|
+
} catch {}
|
|
43
|
+
accountFiles.sort((left, right) => left.sourcePath.localeCompare(right.sourcePath));
|
|
44
|
+
const singlePath = path.join(dataDir, "credential-backup.json");
|
|
45
|
+
return await fileExists(singlePath) ? [...accountFiles, { sourcePath: singlePath }] : accountFiles;
|
|
46
|
+
}
|
|
47
|
+
async function readLegacyCredentialBackups(stateDir) {
|
|
48
|
+
const backups = [];
|
|
49
|
+
for (const candidate of await credentialBackupCandidates(stateDir)) {
|
|
50
|
+
const value = await readCredentialBackup(candidate.sourcePath);
|
|
51
|
+
if (!value || candidate.expectedSafeAccountId !== void 0 && safeName(value.accountId) !== candidate.expectedSafeAccountId) continue;
|
|
52
|
+
backups.push({
|
|
53
|
+
sourcePath: candidate.sourcePath,
|
|
54
|
+
key: credentialBackupKey(value.accountId),
|
|
55
|
+
value
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return backups;
|
|
59
|
+
}
|
|
60
|
+
async function archiveLegacySource(params) {
|
|
61
|
+
const archivedPath = `${params.sourcePath}.migrated`;
|
|
62
|
+
if (await fileExists(archivedPath)) {
|
|
63
|
+
params.warnings.push(`Left QQBot credential backup in place because ${archivedPath} already exists`);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
await fs.chmod(params.sourcePath, 384);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
params.warnings.push(`Failed securing QQBot credential backup legacy source: ${String(err)}`);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
await fs.rename(params.sourcePath, archivedPath);
|
|
74
|
+
try {
|
|
75
|
+
await fs.chmod(archivedPath, 384);
|
|
76
|
+
} catch (err) {
|
|
77
|
+
params.warnings.push(`Failed securing archived QQBot credential backup legacy source: ${String(err)}`);
|
|
78
|
+
}
|
|
79
|
+
params.changes.push(`Archived QQBot credential backup legacy source -> ${archivedPath}`);
|
|
80
|
+
} catch (err) {
|
|
81
|
+
params.warnings.push(`Failed archiving QQBot credential backup: ${String(err)}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function sameCredentialBackup(left, right) {
|
|
85
|
+
return left?.accountId === right.accountId && left.appId === right.appId && left.clientSecret === right.clientSecret && left.savedAt === right.savedAt;
|
|
86
|
+
}
|
|
87
|
+
async function rollbackCredentialImports(store, inserted, existing) {
|
|
88
|
+
for (const [key, value] of [...inserted].toReversed()) if (sameCredentialBackup(await store.lookup(key), value)) await store.delete(key);
|
|
89
|
+
for (const [key, value] of existing) if (await store.lookup(key) === void 0) await store.registerIfAbsent(key, value);
|
|
90
|
+
}
|
|
91
|
+
function findMissingKey(expected, actual) {
|
|
92
|
+
for (const key of expected) if (!actual.has(key)) return key;
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const stateMigrations = [{
|
|
96
|
+
id: "qqbot-credential-backups-json-to-plugin-state",
|
|
97
|
+
label: "QQBot credential backups",
|
|
98
|
+
async detectLegacyState(params) {
|
|
99
|
+
const backups = await readLegacyCredentialBackups(params.stateDir);
|
|
100
|
+
if (backups.length === 0) return null;
|
|
101
|
+
return { preview: [`- QQBot credential backups: ${backups.length} ${backups.length === 1 ? "file" : "files"} -> plugin state (${CREDENTIAL_BACKUPS_NAMESPACE})`] };
|
|
102
|
+
},
|
|
103
|
+
async migrateLegacyState(params) {
|
|
104
|
+
const changes = [];
|
|
105
|
+
const warnings = [];
|
|
106
|
+
const backups = await readLegacyCredentialBackups(params.stateDir);
|
|
107
|
+
if (backups.length === 0) return {
|
|
108
|
+
changes,
|
|
109
|
+
warnings
|
|
110
|
+
};
|
|
111
|
+
const selectedByKey = /* @__PURE__ */ new Map();
|
|
112
|
+
for (const backup of backups) if (!selectedByKey.has(backup.key)) selectedByKey.set(backup.key, backup);
|
|
113
|
+
const store = params.context.openPluginStateKeyedStore({
|
|
114
|
+
namespace: CREDENTIAL_BACKUPS_NAMESPACE,
|
|
115
|
+
maxEntries: MAX_CREDENTIAL_BACKUPS
|
|
116
|
+
});
|
|
117
|
+
const existingEntries = await store.entries();
|
|
118
|
+
const existingValues = new Map(existingEntries.map((entry) => [entry.key, entry.value]));
|
|
119
|
+
const existingKeys = new Set(existingValues.keys());
|
|
120
|
+
const missing = [...selectedByKey.values()].filter((backup) => !existingKeys.has(backup.key));
|
|
121
|
+
const available = MAX_CREDENTIAL_BACKUPS - existingKeys.size;
|
|
122
|
+
if (missing.length > available) {
|
|
123
|
+
warnings.push(`Skipped QQBot credential backup migration because plugin state has room for ${available} of ${missing.length} missing entries; left legacy sources in place`);
|
|
124
|
+
return {
|
|
125
|
+
changes,
|
|
126
|
+
warnings
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
const expectedKeys = new Set(existingKeys);
|
|
130
|
+
const inserted = /* @__PURE__ */ new Map();
|
|
131
|
+
for (const backup of missing) try {
|
|
132
|
+
if (await store.registerIfAbsent(backup.key, backup.value)) inserted.set(backup.key, backup.value);
|
|
133
|
+
const missingKey = findMissingKey(new Set(expectedKeys).add(backup.key), new Set((await store.entries()).map((entry) => entry.key)));
|
|
134
|
+
if (missingKey) {
|
|
135
|
+
await rollbackCredentialImports(store, inserted, existingValues);
|
|
136
|
+
warnings.push(`Stopped QQBot credential backup migration because plugin state capacity evicted ${missingKey}; restored credential state and left legacy sources in place`);
|
|
137
|
+
return {
|
|
138
|
+
changes,
|
|
139
|
+
warnings
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
expectedKeys.add(backup.key);
|
|
143
|
+
} catch (err) {
|
|
144
|
+
try {
|
|
145
|
+
await rollbackCredentialImports(store, inserted, existingValues);
|
|
146
|
+
} catch (rollbackErr) {
|
|
147
|
+
warnings.push(`Failed restoring QQBot credential state after migration error: ${String(rollbackErr)}`);
|
|
148
|
+
}
|
|
149
|
+
warnings.push(`Failed migrating QQBot credential backup: ${String(err)}; left legacy sources in place`);
|
|
150
|
+
return {
|
|
151
|
+
changes,
|
|
152
|
+
warnings
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
if (inserted.size > 0) changes.push(`Migrated ${inserted.size} QQBot credential ${inserted.size === 1 ? "backup" : "backups"} -> plugin state`);
|
|
156
|
+
for (const backup of backups) await archiveLegacySource({
|
|
157
|
+
sourcePath: backup.sourcePath,
|
|
158
|
+
changes,
|
|
159
|
+
warnings
|
|
160
|
+
});
|
|
161
|
+
return {
|
|
162
|
+
changes,
|
|
163
|
+
warnings
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}];
|
|
167
|
+
//#endregion
|
|
168
|
+
export { legacyConfigRules, normalizeCompatibilityConfig, stateMigrations };
|