@openclaw/qqbot 2026.6.5-beta.2 → 2026.6.5-beta.5

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.
@@ -1,6 +1,9 @@
1
- import { c as getPlatformAdapter, t as asOptionalObjectRecord } from "./string-normalize-R_0cKO7Q.js";
2
- import { a as qqbotSetupAdapterShared, c as listQQBotAccountIds, i as qqbotMeta, m as getBridgeLogger, n as qqbotSetupWizard, o as DEFAULT_ACCOUNT_ID$1, p as ensurePlatformAdapter, r as qqbotConfigAdapter, s as applyQQBotAccountConfig, t as qqbotChannelConfigSchema, u as resolveQQBotAccount } from "./config-schema-BI7AYP6Q.js";
3
- import { T as debugWarn, t as getQQBotRuntime, w as debugLog, z as formatErrorMessage } from "./runtime-BSgtMZ6G.js";
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-BFLNZ8Nf.js";
3
+ import { a as resolveQQBotAccount, n as applyQQBotAccountConfig, r as listQQBotAccountIds, t as DEFAULT_ACCOUNT_ID$1 } from "./config-ZEfgeoL4.js";
4
+ import { t as getQQBotRuntime } from "./runtime-B9UoQ5NI.js";
5
+ import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-BYTS8-ia.js";
6
+ import { n as normalizeTarget, s as getQQBotDataPath, t as looksLikeQQBotTarget } from "./target-parser-BdCUmxK7.js";
4
7
  import { getExecApprovalReplyMetadata } from "openclaw/plugin-sdk/approval-runtime";
5
8
  import { createMessageReceiptFromOutboundResults, defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-outbound";
6
9
  import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
@@ -11,14 +14,12 @@ import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "opencl
11
14
  import { markImplicitSameChatApprovalAuthorization, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
12
15
  import { createChannelExecApprovalProfile, isChannelExecApprovalClientEnabledFromConfig, matchesApprovalRequestFilters } from "openclaw/plugin-sdk/approval-client-runtime";
13
16
  import { normalizeAccountId } from "openclaw/plugin-sdk/routing";
14
- import * as fs$1 from "node:fs";
15
17
  import fs from "node:fs";
16
- import * as os$1 from "node:os";
17
18
  import crypto from "node:crypto";
18
- import * as path$1 from "node:path";
19
19
  import path from "node:path";
20
20
  import { loadJsonFile } from "openclaw/plugin-sdk/json-store";
21
21
  import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
22
+ import { resolveChannelGroupToolsPolicy } from "openclaw/plugin-sdk/channel-policy";
22
23
  //#region extensions/qqbot/src/engine/approval/index.ts
23
24
  function buildExecApprovalText(request) {
24
25
  const expiresIn = Math.max(0, Math.round((request.expiresAtMs - Date.now()) / 1e3));
@@ -376,7 +377,7 @@ function createQQBotApprovalCapability() {
376
377
  },
377
378
  load: async () => {
378
379
  ensurePlatformAdapter();
379
- return (await import("./handler-runtime-CjXwuNlq.js")).qqbotApprovalNativeRuntime;
380
+ return (await import("./handler-runtime-JKYDlRdq.js")).qqbotApprovalNativeRuntime;
380
381
  }
381
382
  })
382
383
  });
@@ -412,6 +413,12 @@ async function writeOpenClawConfigThroughRuntime(runtime, cfg) {
412
413
  });
413
414
  }
414
415
  //#endregion
416
+ //#region extensions/qqbot/src/doctor.ts
417
+ const qqbotDoctor = {
418
+ legacyConfigRules,
419
+ normalizeCompatibilityConfig
420
+ };
421
+ //#endregion
415
422
  //#region extensions/qqbot/src/engine/utils/data-paths.ts
416
423
  /**
417
424
  * Centralised filename helpers for persisted QQBot state.
@@ -447,215 +454,6 @@ function getLegacyCredentialBackupFile() {
447
454
  return path.join(getCredentialBackupRoot(), "credential-backup.json");
448
455
  }
449
456
  //#endregion
450
- //#region extensions/qqbot/src/engine/utils/platform.ts
451
- /**
452
- * Cross-platform path and detection helpers for core/ modules.
453
- *
454
- * Provides home/data/media directory helpers, platform detection,
455
- * silk-wasm availability checks — all without importing `openclaw/plugin-sdk`.
456
- * The temp-directory fallback is delegated to the PlatformAdapter.
457
- */
458
- /**
459
- * Resolve the current user's OS home directory safely across platforms.
460
- *
461
- * Priority:
462
- * 1. `os.homedir()`
463
- * 2. `$HOME` or `%USERPROFILE%`
464
- * 3. PlatformAdapter.getTempDir() as a last resort
465
- *
466
- * This is the *operating-system* home and intentionally ignores
467
- * `OPENCLAW_HOME`. QQ Bot still checks this tree for legacy state imports and
468
- * media-path remaps from older releases.
469
- */
470
- function getHomeDir() {
471
- try {
472
- const home = os$1.homedir();
473
- if (home && fs$1.existsSync(home)) return home;
474
- } catch {}
475
- const envHome = process.env.HOME || process.env.USERPROFILE;
476
- if (envHome && fs$1.existsSync(envHome)) return envHome;
477
- return getPlatformAdapter().getTempDir();
478
- }
479
- /**
480
- * Resolve the effective OpenClaw home directory.
481
- *
482
- * Mirrors the contract from core (`src/infra/home-dir.ts::resolveEffectiveHomeDir`)
483
- * so QQ Bot media roots live under the same tree the rest of OpenClaw treats as
484
- * `~`. The extension cannot import the core helper directly (it is a separate
485
- * package with `openclaw` as a peer dependency), so this re-implements the
486
- * minimal contract:
487
- *
488
- * 1. `OPENCLAW_HOME` when set (with `~` / `~/...` expanded against the OS home).
489
- * 2. Otherwise fall back to {@link getHomeDir} so existing single-home
490
- * deployments are unaffected.
491
- *
492
- * Empty / `"undefined"` / `"null"` strings are treated as unset to match how
493
- * core normalizes the variable.
494
- */
495
- function resolveOpenClawHome() {
496
- const raw = process.env.OPENCLAW_HOME?.trim();
497
- if (!raw || raw === "undefined" || raw === "null") return getHomeDir();
498
- if (raw === "~" || raw.startsWith("~/") || raw.startsWith("~\\")) {
499
- const osHome = getHomeDir();
500
- if (raw === "~") return osHome;
501
- return path$1.join(osHome, raw.slice(2));
502
- }
503
- return raw;
504
- }
505
- /**
506
- * Return a legacy path under `~/.openclaw/qqbot` without creating it.
507
- *
508
- * Current QQ Bot runtime state lives in plugin SQLite KV. This path remains for
509
- * legacy imports and media-path remaps from older releases.
510
- */
511
- function getQQBotDataPath(...subPaths) {
512
- return path$1.join(getHomeDir(), ".openclaw", "qqbot", ...subPaths);
513
- }
514
- /** Return a path under `~/.openclaw/qqbot`, creating it on demand. */
515
- function getQQBotDataDir(...subPaths) {
516
- const dir = getQQBotDataPath(...subPaths);
517
- if (!fs$1.existsSync(dir)) fs$1.mkdirSync(dir, { recursive: true });
518
- return dir;
519
- }
520
- /**
521
- * Return a path under `<openclaw-home>/.openclaw/media/qqbot` without creating it.
522
- *
523
- * Unlike `getQQBotDataPath`, this lives under OpenClaw's core media allowlist
524
- * so downloaded images and audio can be accessed by framework media tooling.
525
- * The base honors `OPENCLAW_HOME` (when set) so files written by agents into
526
- * the OpenClaw-managed media tree are reachable by this plugin even when
527
- * `HOME` and `OPENCLAW_HOME` differ (Docker, multi-user hosts). Fixes #83562.
528
- */
529
- function getQQBotMediaPath(...subPaths) {
530
- return path$1.join(resolveOpenClawHome(), ".openclaw", "media", "qqbot", ...subPaths);
531
- }
532
- /** Return a path under `<openclaw-home>/.openclaw/media/qqbot`, creating it on demand. */
533
- function getQQBotMediaDir(...subPaths) {
534
- const dir = getQQBotMediaPath(...subPaths);
535
- if (!fs$1.existsSync(dir)) fs$1.mkdirSync(dir, { recursive: true });
536
- return dir;
537
- }
538
- /**
539
- * Return `<openclaw-home>/.openclaw/media`, OpenClaw's shared media root.
540
- *
541
- * This mirrors the directory that core's `buildMediaLocalRoots` exposes as an
542
- * allowlisted location (see `openclaw/src/media/local-roots.ts`). Using it as a
543
- * QQ Bot payload root lets the plugin trust framework-produced files that live
544
- * in sibling subdirectories such as `outbound/` (written by
545
- * `saveMediaBuffer(..., "outbound", ...)`) or `inbound/`, while still keeping
546
- * the check anchored to a single, well-known directory. Like
547
- * {@link getQQBotMediaPath}, the base honors `OPENCLAW_HOME`.
548
- */
549
- function getOpenClawMediaDir() {
550
- return path$1.join(resolveOpenClawHome(), ".openclaw", "media");
551
- }
552
- function isWindows() {
553
- return process.platform === "win32";
554
- }
555
- /** Return the preferred temporary directory. */
556
- function getTempDir() {
557
- return getPlatformAdapter().getTempDir();
558
- }
559
- let silkWasmAvailable = null;
560
- /** Check whether silk-wasm can run in the current environment. */
561
- async function checkSilkWasmAvailable() {
562
- if (silkWasmAvailable !== null) return silkWasmAvailable;
563
- try {
564
- const { isSilk } = await import("silk-wasm");
565
- isSilk(new Uint8Array(0));
566
- silkWasmAvailable = true;
567
- debugLog("[platform] silk-wasm: available");
568
- } catch (err) {
569
- silkWasmAvailable = false;
570
- debugWarn(`[platform] silk-wasm: NOT available (${formatErrorMessage(err)})`);
571
- }
572
- return silkWasmAvailable;
573
- }
574
- /** Expand `~` to the current user's home directory. */
575
- function expandTilde(p) {
576
- if (!p) return p;
577
- if (p === "~") return getHomeDir();
578
- if (p.startsWith("~/") || p.startsWith("~\\")) return path$1.join(getHomeDir(), p.slice(2));
579
- return p;
580
- }
581
- /** Normalize a user-provided path by trimming, stripping `file://`, and expanding `~`. */
582
- function normalizePath(p) {
583
- let result = p.trim();
584
- if (result.startsWith("file://")) {
585
- result = result.slice(7);
586
- try {
587
- result = decodeURIComponent(result);
588
- } catch {}
589
- }
590
- return expandTilde(result);
591
- }
592
- /** Return true when the string looks like a local filesystem path rather than a URL. */
593
- function isLocalPath(p) {
594
- if (!p) return false;
595
- if (p.startsWith("file://")) return true;
596
- if (p === "~" || p.startsWith("~/") || p.startsWith("~\\")) return true;
597
- if (p.startsWith("/")) return true;
598
- if (/^[a-zA-Z]:[\\/]/.test(p)) return true;
599
- if (p.startsWith("\\\\")) return true;
600
- if (p.startsWith("./") || p.startsWith("../")) return true;
601
- if (p.startsWith(".\\") || p.startsWith("..\\")) return true;
602
- return false;
603
- }
604
- function isPathWithinRoot(candidate, root) {
605
- const relative = path$1.relative(root, candidate);
606
- return relative === "" || !relative.startsWith("..") && !path$1.isAbsolute(relative);
607
- }
608
- /** Remap legacy or hallucinated QQ Bot local media paths to real files when possible. */
609
- function resolveQQBotLocalMediaPath(p) {
610
- const normalized = normalizePath(p);
611
- if (!isLocalPath(normalized) || fs$1.existsSync(normalized)) return normalized;
612
- const osHomeDir = getHomeDir();
613
- const openclawHomeDir = resolveOpenClawHome();
614
- const mediaRoot = getQQBotMediaPath();
615
- const dataRoot = getQQBotDataPath();
616
- const candidateRoots = [
617
- ...Array.from(new Set([path$1.join(osHomeDir, ".openclaw", "workspace", "qqbot"), path$1.join(openclawHomeDir, ".openclaw", "workspace", "qqbot")])).map((from) => ({
618
- from,
619
- to: mediaRoot
620
- })),
621
- {
622
- from: dataRoot,
623
- to: mediaRoot
624
- },
625
- {
626
- from: mediaRoot,
627
- to: dataRoot
628
- }
629
- ];
630
- for (const { from, to } of candidateRoots) {
631
- if (!isPathWithinRoot(normalized, from)) continue;
632
- const relative = path$1.relative(from, normalized);
633
- const candidate = path$1.join(to, relative);
634
- if (fs$1.existsSync(candidate)) {
635
- debugWarn(`[platform] Remapped missing QQBot media path ${normalized} -> ${candidate}`);
636
- return candidate;
637
- }
638
- }
639
- return normalized;
640
- }
641
- /**
642
- * Resolve a structured-payload local file path and enforce that it stays within
643
- * QQ Bot-owned storage roots.
644
- */
645
- function resolveQQBotPayloadLocalFilePath(p) {
646
- const candidate = resolveQQBotLocalMediaPath(p);
647
- if (!candidate.trim()) return null;
648
- const resolvedCandidate = path$1.resolve(candidate);
649
- if (!fs$1.existsSync(resolvedCandidate)) return null;
650
- const canonicalCandidate = fs$1.realpathSync(resolvedCandidate);
651
- const allowedRoots = [getOpenClawMediaDir(), getQQBotMediaPath()];
652
- for (const root of allowedRoots) {
653
- const resolvedRoot = path$1.resolve(root);
654
- if (isPathWithinRoot(canonicalCandidate, fs$1.existsSync(resolvedRoot) ? fs$1.realpathSync(resolvedRoot) : resolvedRoot)) return canonicalCandidate;
655
- }
656
- return null;
657
- }
658
- //#endregion
659
457
  //#region extensions/qqbot/src/engine/utils/sqlite-state.ts
660
458
  function resolveStoreEnv(options) {
661
459
  if (!options.stateDir) return options.env;
@@ -851,85 +649,30 @@ function clearAccountCredentials(cfg, accountId) {
851
649
  };
852
650
  }
853
651
  //#endregion
854
- //#region extensions/qqbot/src/engine/messaging/target-parser.ts
855
- /**
856
- * Parse a qqbot target string into a structured delivery target.
857
- *
858
- * Supported formats:
859
- * - `qqbot:c2c:openid` → C2C direct message
860
- * - `qqbot:group:groupid` → Group message
861
- * - `qqbot:channel:channelid` → Channel message
862
- * - `c2c:openid` → C2C (without qqbot: prefix)
863
- * - `group:groupid` → Group (without qqbot: prefix)
864
- * - `channel:channelid` → Channel (without qqbot: prefix)
865
- * - `openid` → C2C (bare openid, default)
866
- *
867
- * @param to - Raw target string.
868
- * @returns Parsed target with type and id.
869
- * @throws {Error} When the target format is invalid.
870
- */
871
- function parseTarget(to) {
872
- const id = to.replace(/^qqbot:/i, "");
873
- if (id.startsWith("c2c:")) {
874
- const userId = id.slice(4);
875
- if (!userId) throw new Error(`Invalid c2c target format: ${to} - missing user ID`);
876
- return {
877
- type: "c2c",
878
- id: userId
879
- };
880
- }
881
- if (id.startsWith("group:")) {
882
- const groupId = id.slice(6);
883
- if (!groupId) throw new Error(`Invalid group target format: ${to} - missing group ID`);
884
- return {
885
- type: "group",
886
- id: groupId
887
- };
888
- }
889
- if (id.startsWith("channel:")) {
890
- const channelId = id.slice(8);
891
- if (!channelId) throw new Error(`Invalid channel target format: ${to} - missing channel ID`);
892
- return {
893
- type: "channel",
894
- id: channelId
895
- };
896
- }
897
- if (!id) throw new Error(`Invalid target format: ${to} - empty ID after removing qqbot: prefix`);
898
- return {
899
- type: "c2c",
900
- id
901
- };
902
- }
903
- /**
904
- * Normalize a QQ Bot target string into the canonical `qqbot:...` form.
905
- *
906
- * Returns `undefined` when the target does not look like a QQ Bot address.
907
- */
908
- function normalizeTarget(target) {
909
- const id = target.replace(/^qqbot:/i, "");
910
- if (id.startsWith("c2c:") || id.startsWith("group:") || id.startsWith("channel:")) return `qqbot:${id}`;
911
- if (/^[0-9a-fA-F]{32}$/.test(id)) return `qqbot:c2c:${id}`;
912
- if (/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(id)) return `qqbot:c2c:${id}`;
913
- }
914
- /**
915
- * Return true when the string looks like a QQ Bot target ID.
916
- */
917
- function looksLikeQQBotTarget(id) {
918
- if (/^qqbot:(c2c|group|channel):/i.test(id)) return true;
919
- if (/^(c2c|group|channel):/i.test(id)) return true;
920
- if (/^[0-9a-fA-F]{32}$/.test(id)) return true;
921
- return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(id);
652
+ //#region extensions/qqbot/src/group-policy.ts
653
+ function resolveQQBotGroupToolPolicy(params) {
654
+ return resolveChannelGroupToolsPolicy({
655
+ cfg: params.cfg,
656
+ channel: "qqbot",
657
+ groupId: params.groupId,
658
+ groupIdCaseInsensitive: true,
659
+ accountId: params.accountId,
660
+ senderId: params.senderId,
661
+ senderName: params.senderName,
662
+ senderUsername: params.senderUsername,
663
+ senderE164: params.senderE164
664
+ });
922
665
  }
923
666
  //#endregion
924
667
  //#region extensions/qqbot/src/channel.ts
925
668
  let gatewayModulePromise;
926
669
  function loadGatewayModule() {
927
- gatewayModulePromise ??= import("./gateway-iL9SWAED.js");
670
+ gatewayModulePromise ??= import("./gateway-DdM2k3ss.js");
928
671
  return gatewayModulePromise;
929
672
  }
930
673
  let outboundMessagingModulePromise;
931
674
  function loadOutboundMessagingModule() {
932
- outboundMessagingModulePromise ??= import("./outbound-CEqyriPT.js").then((n) => n.t);
675
+ outboundMessagingModulePromise ??= import("./outbound-C9wV892v.js").then((n) => n.t);
933
676
  return outboundMessagingModulePromise;
934
677
  }
935
678
  function createQQBotSendReceipt(params) {
@@ -1045,6 +788,7 @@ const qqbotPlugin = {
1045
788
  },
1046
789
  reload: { configPrefixes: ["channels.qqbot"] },
1047
790
  configSchema: qqbotChannelConfigSchema,
791
+ doctor: qqbotDoctor,
1048
792
  config: {
1049
793
  ...qqbotConfigAdapter,
1050
794
  /**
@@ -1062,6 +806,7 @@ const qqbotPlugin = {
1062
806
  },
1063
807
  setup: { ...qqbotSetupAdapterShared },
1064
808
  approvalCapability: getQQBotApprovalCapability(),
809
+ groups: { resolveToolPolicy: resolveQQBotGroupToolPolicy },
1065
810
  message: qqbotMessageAdapter,
1066
811
  messaging: {
1067
812
  targetPrefixes: ["qqbot"],
@@ -1203,4 +948,4 @@ const qqbotPlugin = {
1203
948
  }
1204
949
  };
1205
950
  //#endregion
1206
- export { buildExecApprovalText as C, resolveApprovalTarget as E, buildApprovalKeyboard as S, parseApprovalButtonData as T, authorizeQQBotApprovalAction as _, checkSilkWasmAvailable as a, resolveQQBotExecApprovalConfig as b, getQQBotDataPath as c, getTempDir as d, isLocalPath as f, toGatewayAccount as g, resolveQQBotPayloadLocalFilePath as h, openQQBotSyncKeyedStore as i, getQQBotMediaDir as l, normalizePath as m, parseTarget as n, getHomeDir as o, isWindows as p, buildQQBotStateKey as r, getQQBotDataDir as s, qqbotPlugin as t, getQQBotMediaPath as u, isQQBotExecApprovalClientEnabled as v, buildPluginApprovalText as w, shouldHandleQQBotExecApprovalRequest as x, matchesQQBotApprovalAccount as y };
951
+ export { authorizeQQBotApprovalAction as a, resolveQQBotExecApprovalConfig as c, buildExecApprovalText as d, buildPluginApprovalText as f, toGatewayAccount as i, shouldHandleQQBotExecApprovalRequest as l, resolveApprovalTarget as m, buildQQBotStateKey as n, isQQBotExecApprovalClientEnabled as o, parseApprovalButtonData as p, openQQBotSyncKeyedStore as r, matchesQQBotApprovalAccount as s, qqbotPlugin as t, buildApprovalKeyboard as u };
@@ -0,0 +1,2 @@
1
+ import { t as registerQQBotFull } from "./channel-entry-fUBLXKPr.js";
2
+ export { registerQQBotFull };
@@ -0,0 +1,143 @@
1
+ import { a as resolveQQBotAccount } from "./config-ZEfgeoL4.js";
2
+ import { t as getFrameworkCommands } from "./slash-commands-impl-FRw-Dl44.js";
3
+ import { t as registerQQBotTools } from "./tools-2d9t-N1b.js";
4
+ import { h as sendDocument } from "./outbound-C9wV892v.js";
5
+ //#region extensions/qqbot/src/bridge/commands/framework-context-adapter.ts
6
+ /**
7
+ * Default queue snapshot used for framework-registered commands.
8
+ *
9
+ * Framework-side command dispatch runs outside the per-sender queue, so
10
+ * handlers observe an empty snapshot by design.
11
+ */
12
+ const DEFAULT_QUEUE_SNAPSHOT = {
13
+ totalPending: 0,
14
+ activeUsers: 0,
15
+ maxConcurrentUsers: 10,
16
+ senderPending: 0
17
+ };
18
+ function buildFrameworkSlashContext({ ctx, account, from, commandName }) {
19
+ const args = ctx.args ?? "";
20
+ const rawContent = args ? `/${commandName} ${args}` : `/${commandName}`;
21
+ return {
22
+ type: from.msgType,
23
+ senderId: ctx.senderId ?? "",
24
+ messageId: "",
25
+ eventTimestamp: (/* @__PURE__ */ new Date()).toISOString(),
26
+ receivedAt: Date.now(),
27
+ rawContent,
28
+ args,
29
+ accountId: account.accountId,
30
+ appId: account.appId,
31
+ accountConfig: account.config,
32
+ commandAuthorized: ctx.isAuthorizedSender,
33
+ queueSnapshot: { ...DEFAULT_QUEUE_SNAPSHOT }
34
+ };
35
+ }
36
+ //#endregion
37
+ //#region extensions/qqbot/src/bridge/commands/from-parser.ts
38
+ const MSG_TYPE_MAP = {
39
+ c2c: "c2c",
40
+ dm: "dm",
41
+ group: "group",
42
+ channel: "guild"
43
+ };
44
+ const TARGET_TYPE_MAP = {
45
+ c2c: "c2c",
46
+ dm: "dm",
47
+ group: "group",
48
+ channel: "channel"
49
+ };
50
+ function isFromKind(value) {
51
+ return value === "c2c" || value === "dm" || value === "group" || value === "channel";
52
+ }
53
+ /**
54
+ * Parse `ctx.from` into the structured fields the QQBot bridge expects.
55
+ *
56
+ * Unknown or missing prefixes fall back to c2c. The remainder after the first
57
+ * `:` is returned verbatim as the target id, matching what the previous inline
58
+ * implementation did.
59
+ */
60
+ function parseQQBotFrom(from) {
61
+ const stripped = (from ?? "").replace(/^qqbot:/iu, "");
62
+ const colonIdx = stripped.indexOf(":");
63
+ const rawPrefix = colonIdx === -1 ? stripped : stripped.slice(0, colonIdx);
64
+ const targetId = colonIdx === -1 ? stripped : stripped.slice(colonIdx + 1);
65
+ const kind = isFromKind(rawPrefix) ? rawPrefix : "c2c";
66
+ return {
67
+ msgType: MSG_TYPE_MAP[kind],
68
+ targetType: TARGET_TYPE_MAP[kind],
69
+ targetId
70
+ };
71
+ }
72
+ //#endregion
73
+ //#region extensions/qqbot/src/bridge/commands/result-dispatcher.ts
74
+ const UNEXPECTED_RESULT_TEXT = "⚠️ 命令返回了意外结果。";
75
+ function hasFilePath(value) {
76
+ return typeof value === "object" && value !== null && "filePath" in value && typeof value.filePath === "string";
77
+ }
78
+ function buildMediaTarget(account, from) {
79
+ return {
80
+ targetType: from.targetType,
81
+ targetId: from.targetId,
82
+ account
83
+ };
84
+ }
85
+ async function dispatchFrameworkSlashResult({ result, account, from, logger }) {
86
+ if (typeof result === "string") return { text: result };
87
+ if (hasFilePath(result)) {
88
+ const mediaCtx = buildMediaTarget(account, from);
89
+ try {
90
+ await sendDocument(mediaCtx, result.filePath, { allowQQBotDataDownloads: true });
91
+ } catch (err) {
92
+ logger?.warn(`framework slash file send failed: ${String(err)}`);
93
+ }
94
+ return { text: result.text };
95
+ }
96
+ return { text: UNEXPECTED_RESULT_TEXT };
97
+ }
98
+ //#endregion
99
+ //#region extensions/qqbot/src/bridge/commands/framework-registration.ts
100
+ const PRIVATE_CHAT_ONLY_TEXT = "💡 请在私聊中使用此指令";
101
+ function isExplicitQQBotC2cFrom(from) {
102
+ const raw = (from ?? "").trim();
103
+ const stripped = raw.replace(/^qqbot:/iu, "");
104
+ const colonIdx = stripped.indexOf(":");
105
+ if (colonIdx === -1) return false;
106
+ const kind = stripped.slice(0, colonIdx).toLowerCase();
107
+ const targetId = stripped.slice(colonIdx + 1).trim();
108
+ return /^qqbot:/iu.test(raw) && kind === "c2c" && targetId.length > 0;
109
+ }
110
+ function registerQQBotFrameworkCommands(api) {
111
+ for (const cmd of getFrameworkCommands()) api.registerCommand({
112
+ name: cmd.name,
113
+ description: cmd.description,
114
+ channels: ["qqbot"],
115
+ requireAuth: true,
116
+ acceptsArgs: true,
117
+ handler: async (ctx) => {
118
+ if (cmd.c2cOnly && !isExplicitQQBotC2cFrom(ctx.from)) return { text: PRIVATE_CHAT_ONLY_TEXT };
119
+ const from = parseQQBotFrom(ctx.from);
120
+ const account = resolveQQBotAccount(ctx.config, ctx.accountId ?? void 0);
121
+ const slashCtx = buildFrameworkSlashContext({
122
+ ctx,
123
+ account,
124
+ from,
125
+ commandName: cmd.name
126
+ });
127
+ return await dispatchFrameworkSlashResult({
128
+ result: await cmd.handler(slashCtx),
129
+ account,
130
+ from,
131
+ logger: api.logger
132
+ });
133
+ }
134
+ });
135
+ }
136
+ //#endregion
137
+ //#region extensions/qqbot/src/bridge/channel-entry.ts
138
+ function registerQQBotFull(api) {
139
+ registerQQBotTools(api);
140
+ registerQQBotFrameworkCommands(api);
141
+ }
142
+ //#endregion
143
+ export { registerQQBotFull as t };
@@ -1,2 +1,2 @@
1
- import { t as qqbotPlugin } from "./channel-BLMrGYfg.js";
1
+ import { t as qqbotPlugin } from "./channel-CcN43bPL.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-BI7AYP6Q.js";
1
+ import { a as qqbotSetupAdapterShared, i as qqbotMeta, n as qqbotSetupWizard, r as qqbotConfigAdapter, t as qqbotChannelConfigSchema } from "./config-schema-BFLNZ8Nf.js";
2
2
  //#region extensions/qqbot/src/channel.setup.ts
3
3
  /**
4
4
  * Setup-only QQBot plugin — lightweight subset used during `openclaw onboard`