@openclaw/qqbot 2026.6.5-beta.1 → 2026.6.5-beta.3
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 +7 -657
- package/dist/{channel-BLMrGYfg.js → channel-CcN43bPL.js} +32 -287
- package/dist/channel-entry-api.js +2 -0
- package/dist/channel-entry-fUBLXKPr.js +143 -0
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.setup-xBlmSU4X.js → channel.setup-CQ_DFfPx.js} +1 -1
- package/dist/config-ZEfgeoL4.js +257 -0
- package/dist/{config-schema-BI7AYP6Q.js → config-schema-BFLNZ8Nf.js} +17 -259
- package/dist/doctor-contract-BYTS8-ia.js +135 -0
- package/dist/doctor-contract-api.js +2 -0
- package/dist/{gateway-iL9SWAED.js → gateway-DdM2k3ss.js} +23 -26
- package/dist/{handler-runtime-CjXwuNlq.js → handler-runtime-JKYDlRdq.js} +4 -3
- package/dist/index.js +8 -1
- package/dist/log-SDfMMBWe.js +107 -0
- package/dist/{outbound-CEqyriPT.js → outbound-C9wV892v.js} +3 -14
- package/dist/request-context-Bm7PTBD1.js +43 -0
- package/dist/runtime-B9UoQ5NI.js +18 -0
- package/dist/runtime-api.js +1 -1
- package/dist/{runtime-BSgtMZ6G.js → sender-DIMG7jHz.js} +36 -123
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{request-context-DsuA124s.js → slash-commands-impl-FRw-Dl44.js} +3 -45
- package/dist/target-parser-BdCUmxK7.js +285 -0
- package/dist/tools-2d9t-N1b.js +518 -0
- package/dist/tools-api.js +2 -0
- package/npm-shrinkwrap.json +3 -3
- package/openclaw.plugin.json +162 -0
- package/package.json +4 -4
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as qqbotSetupAdapterShared,
|
|
3
|
-
import {
|
|
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-
|
|
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/
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
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-
|
|
670
|
+
gatewayModulePromise ??= import("./gateway-DdM2k3ss.js");
|
|
928
671
|
return gatewayModulePromise;
|
|
929
672
|
}
|
|
930
673
|
let outboundMessagingModulePromise;
|
|
931
674
|
function loadOutboundMessagingModule() {
|
|
932
|
-
outboundMessagingModulePromise ??= import("./outbound-
|
|
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 {
|
|
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,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-
|
|
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-
|
|
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`
|