@cxyhhhhh/openclaw-qqbot 2.0.0-dev.202607081621 → 2.0.0-dev.202607082155
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/index.cjs +606 -427
- package/dist/index.cjs.map +1 -1
- package/index.ts +1 -1
- package/package.json +2 -2
- package/skills/qqbot-channel/SKILL.md +33 -11
- package/src/{runtime-adapter/contract-check.ts → adapter/contract.ts} +4 -7
- package/src/{runtime-adapter/gateway-runtime.ts → adapter/gateway.ts} +2 -3
- package/src/{runtime-adapter → adapter}/index.ts +3 -3
- package/src/{runtime-adapter/lint-runtime-access.ts → adapter/lint.ts} +7 -7
- package/src/adapter/media.ts +112 -0
- package/src/adapter/setup.ts +55 -0
- package/src/adapter/workspace.ts +29 -0
- package/src/bot-instance.ts +1 -1
- package/src/channel.ts +1 -1
- package/src/commands/bot-approve.ts +1 -1
- package/src/commands/bot-logs.ts +1 -1
- package/src/commands/bot-pairing.ts +1 -1
- package/src/commands/config-util.ts +1 -1
- package/src/dispatch/body-assembler.ts +1 -1
- package/src/dispatch/dispatch.ts +75 -45
- package/src/features/approval-handler.ts +1 -1
- package/src/gateway/event-handlers.ts +1 -1
- package/src/gateway/lifecycle.ts +1 -1
- package/src/gateway/qqbot-gateway.ts +1 -1
- package/src/middleware/access-control.ts +1 -1
- package/src/middleware/attachment.ts +4 -4
- package/src/openclaw-plugin-sdk.d.ts +41 -0
- package/src/outbound/media-send.ts +4 -6
- package/src/runtime.ts +3 -1
- package/src/setup/finalize.ts +1 -2
- package/src/setup/surface.ts +5 -6
- package/src/utils/pkg-version.ts +100 -49
- package/tsup.config.ts +1 -1
- /package/src/{runtime-adapter/pairing-runtime.ts → adapter/pairing.ts} +0 -0
- /package/src/{runtime-adapter → adapter}/resolve.ts +0 -0
- /package/src/{runtime-adapter/webhook-adapter.ts → adapter/webhook.ts} +0 -0
|
@@ -22,7 +22,7 @@ import { setupMiddlewares } from './middleware-setup.js';
|
|
|
22
22
|
import { handleMessage, handleInteraction } from './event-handlers.js';
|
|
23
23
|
import { getQQBotDataDir } from '../utils/platform.js';
|
|
24
24
|
import { buildUserAgent } from '../bot-instance.js';
|
|
25
|
-
import { createPluginWebhookAdapter } from '../
|
|
25
|
+
import { createPluginWebhookAdapter } from '../adapter/webhook.js';
|
|
26
26
|
import { getPersistedRefIndexStore } from '../features/ref-index-store.js';
|
|
27
27
|
import { getCachedMsgId } from '../features/msgid-cache.js';
|
|
28
28
|
|
|
@@ -14,11 +14,11 @@ import {
|
|
|
14
14
|
convertSilkToWav,
|
|
15
15
|
isVoiceAttachment,
|
|
16
16
|
} from '@tencent-connect/qqbot-nodejs/protocol';
|
|
17
|
-
import { saveRemoteMedia } from 'openclaw/plugin-sdk/media-runtime';
|
|
18
17
|
import type { MessageAttachment } from '../types.js';
|
|
19
18
|
import { transcribeAudio, resolveSTTConfig } from '../utils/stt.js';
|
|
20
19
|
import { formatVoiceText, formatDuration, type VoiceTranscript, type TranscriptSource } from '../utils/voice-text.js';
|
|
21
|
-
import {
|
|
20
|
+
import { downloadRemoteMedia } from '../adapter/media.js';
|
|
21
|
+
import { getAdapters } from '../adapter/resolve.js';
|
|
22
22
|
|
|
23
23
|
export { formatVoiceText, formatDuration };
|
|
24
24
|
export type { VoiceTranscript, TranscriptSource };
|
|
@@ -298,12 +298,12 @@ async function downloadMediaFile(
|
|
|
298
298
|
}
|
|
299
299
|
|
|
300
300
|
try {
|
|
301
|
-
const result = await
|
|
301
|
+
const result = await downloadRemoteMedia({
|
|
302
302
|
url,
|
|
303
303
|
subdir: 'qqbot/downloads',
|
|
304
304
|
originalFilename: filename,
|
|
305
305
|
maxBytes: 500 * 1024 * 1024,
|
|
306
|
-
timeoutMs:
|
|
306
|
+
timeoutMs: 120_000,
|
|
307
307
|
});
|
|
308
308
|
log?.debug?.(`Downloaded: ${result.path}`);
|
|
309
309
|
return result.path;
|
|
@@ -675,6 +675,47 @@ declare module "openclaw/plugin-sdk" {
|
|
|
675
675
|
export function normalizeAccountId(accountId: string | undefined | null): string;
|
|
676
676
|
}
|
|
677
677
|
|
|
678
|
+
declare module "openclaw/plugin-sdk/setup" {
|
|
679
|
+
export interface ChannelSetupWizardStatus {
|
|
680
|
+
channelLabel: string;
|
|
681
|
+
configuredLabel: string;
|
|
682
|
+
unconfiguredLabel: string;
|
|
683
|
+
configuredHint?: string;
|
|
684
|
+
unconfiguredHint?: string;
|
|
685
|
+
configuredScore?: number;
|
|
686
|
+
unconfiguredScore?: number;
|
|
687
|
+
includeStatusLine?: boolean;
|
|
688
|
+
resolveConfigured: (params: { cfg: unknown; accountId: string }) => boolean;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
export interface ChannelSetupWizard {
|
|
692
|
+
channel: string;
|
|
693
|
+
status: ChannelSetupWizardStatus;
|
|
694
|
+
credentials?: Array<{ id: string; label: string }>;
|
|
695
|
+
onStatusChange?: (params: { cfg: unknown }) => void | Promise<void>;
|
|
696
|
+
configure?: (params: { cfg: unknown; accountId: string; prompter: unknown; runtime: unknown }) => Promise<unknown>;
|
|
697
|
+
finalize: (params: { cfg: unknown; accountId: string; prompter: unknown; runtime: unknown }) => Promise<unknown>;
|
|
698
|
+
enable?: (cfg: unknown) => unknown;
|
|
699
|
+
disable?: (cfg: unknown) => unknown;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
export function createStandardChannelSetupStatus(
|
|
703
|
+
opts: ChannelSetupWizardStatus,
|
|
704
|
+
): ChannelSetupWizardStatus;
|
|
705
|
+
|
|
706
|
+
export function setSetupChannelEnabled(
|
|
707
|
+
cfg: unknown,
|
|
708
|
+
channel: string,
|
|
709
|
+
enabled: boolean,
|
|
710
|
+
): void;
|
|
711
|
+
|
|
712
|
+
export const DEFAULT_ACCOUNT_ID: string;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
declare module "openclaw/plugin-sdk/setup-tools" {
|
|
716
|
+
export function formatDocsLink(href: string, text?: string): string;
|
|
717
|
+
}
|
|
718
|
+
|
|
678
719
|
declare module "openclaw/plugin-sdk/approval-runtime" {
|
|
679
720
|
export interface ExecApprovalReplyMetadata {
|
|
680
721
|
approvalId: string;
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
import * as path from 'node:path';
|
|
14
14
|
import * as fs from 'node:fs';
|
|
15
15
|
import * as os from 'node:os';
|
|
16
|
-
import {
|
|
16
|
+
import { resolveAgentWorkspace } from '../adapter/workspace.js';
|
|
17
17
|
import type { ReplyTarget } from '@tencent-connect/qqbot-nodejs';
|
|
18
18
|
import type { QQBotGateway } from '../gateway/index.js';
|
|
19
19
|
import { tryGetQQBotRuntime } from '../runtime.js';
|
|
20
|
-
import { getAdapters } from '../
|
|
20
|
+
import { getAdapters } from '../adapter/resolve.js';
|
|
21
21
|
import type { PluginLogger } from '../utils/plugin-logger.js';
|
|
22
22
|
import { getGateway } from './outbound-service.js';
|
|
23
23
|
import { parseTarget } from './target.js';
|
|
@@ -174,13 +174,11 @@ function resolveMediaPath(source: string, log?: SendMediaParams['log'], workspac
|
|
|
174
174
|
return { ok: true, path: real, isLocal: true };
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
/** Agent ID → workspaceDir
|
|
177
|
+
/** Agent ID → workspaceDir(动态加载 plugin-sdk/health) */
|
|
178
178
|
function resolveWorkspaceFromAgent(agentId?: string): string | undefined {
|
|
179
179
|
const cfg = resolveConfigViaAdapter();
|
|
180
180
|
if (!cfg) return undefined;
|
|
181
|
-
|
|
182
|
-
return resolveAgentWorkspaceDir(cfg, agentId ?? resolveDefaultAgentId(cfg));
|
|
183
|
-
} catch { return undefined; }
|
|
181
|
+
return resolveAgentWorkspace(cfg, agentId);
|
|
184
182
|
}
|
|
185
183
|
|
|
186
184
|
/** 通过 adapter 获取配置 */
|
package/src/runtime.ts
CHANGED
|
@@ -7,13 +7,15 @@
|
|
|
7
7
|
import type { PluginRuntime } from "openclaw/plugin-sdk";
|
|
8
8
|
import { setOpenClawVersion } from "./bot-instance.js";
|
|
9
9
|
import { flushAllRefIndexStores } from "./features/ref-index-store.js";
|
|
10
|
+
import { getOpenclawVersion } from "./utils/pkg-version.js";
|
|
10
11
|
|
|
11
12
|
let runtime: PluginRuntime | null = null;
|
|
12
13
|
let exitHooksInstalled = false;
|
|
13
14
|
|
|
14
15
|
export function setQQBotRuntime(next: PluginRuntime) {
|
|
15
16
|
runtime = next;
|
|
16
|
-
|
|
17
|
+
const version = getOpenclawVersion(next.version);
|
|
18
|
+
setOpenClawVersion(version);
|
|
17
19
|
installExitHooksOnce();
|
|
18
20
|
}
|
|
19
21
|
|
package/src/setup/finalize.ts
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
* openclaw setup 引导 — QQ Bot 扫码/手动绑定
|
|
3
3
|
*/
|
|
4
4
|
import type { OpenClawConfig } from 'openclaw/plugin-sdk';
|
|
5
|
-
import { DEFAULT_ACCOUNT_ID } from '
|
|
6
|
-
import { formatDocsLink } from 'openclaw/plugin-sdk/setup-tools';
|
|
5
|
+
import { DEFAULT_ACCOUNT_ID, formatDocsLink } from '../adapter/setup.js';
|
|
7
6
|
import { qrConnect } from '@tencent-connect/qqbot-connector';
|
|
8
7
|
import { applyQQBotAccountConfig, resolveQQBotAccount } from '../config.js';
|
|
9
8
|
|
package/src/setup/surface.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OpenClaw setup 向导 — QQ Bot 配置界面
|
|
3
3
|
*/
|
|
4
|
-
import type { ChannelSetupWizard } from '
|
|
5
|
-
import { createStandardChannelSetupStatus } from '
|
|
4
|
+
import type { ChannelSetupWizard } from '../adapter/setup.js';
|
|
5
|
+
import { createStandardChannelSetupStatus, setSetupChannelEnabled } from '../adapter/setup.js';
|
|
6
6
|
import { listQQBotAccountIds, resolveQQBotAccount } from '../config.js';
|
|
7
7
|
import { finalizeQQBotSetup } from './finalize.js';
|
|
8
|
-
import { setSetupChannelEnabled } from 'openclaw/plugin-sdk/setup';
|
|
9
8
|
|
|
10
9
|
const CHANNEL = 'qqbot' as const;
|
|
11
10
|
|
|
@@ -20,8 +19,8 @@ export const qqbotSetupWizard: ChannelSetupWizard = {
|
|
|
20
19
|
configuredScore: 1,
|
|
21
20
|
unconfiguredScore: 6,
|
|
22
21
|
resolveConfigured: ({ cfg, accountId }) =>
|
|
23
|
-
(accountId ? [accountId] : listQQBotAccountIds(cfg)).some((id) => {
|
|
24
|
-
const account = resolveQQBotAccount(cfg, id);
|
|
22
|
+
(accountId ? [accountId] : listQQBotAccountIds(cfg as any)).some((id) => {
|
|
23
|
+
const account = resolveQQBotAccount(cfg as any, id);
|
|
25
24
|
return Boolean(account.appId && account.clientSecret);
|
|
26
25
|
}),
|
|
27
26
|
}),
|
|
@@ -29,5 +28,5 @@ export const qqbotSetupWizard: ChannelSetupWizard = {
|
|
|
29
28
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
29
|
finalize: (async ({ cfg, accountId, prompter, runtime }: any) =>
|
|
31
30
|
finalizeQQBotSetup({ cfg, accountId, prompter: prompter as any, runtime: runtime as any })) as any,
|
|
32
|
-
disable: (cfg) => setSetupChannelEnabled(cfg, CHANNEL, false),
|
|
31
|
+
disable: (cfg) => { setSetupChannelEnabled(cfg, CHANNEL, false); return cfg; },
|
|
33
32
|
};
|
package/src/utils/pkg-version.ts
CHANGED
|
@@ -1,35 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* 不依赖硬编码的 "../" 层级,无论编译输出结构如何变化都能可靠找到。
|
|
2
|
+
* 版本号获取工具。
|
|
4
3
|
*
|
|
5
|
-
*
|
|
4
|
+
* - getPackageVersion(): 插件自身版本(@tencent-connect/openclaw-qqbot)
|
|
5
|
+
* - getOpenclawVersion(): OpenClaw 框架版本降级链
|
|
6
|
+
*
|
|
7
|
+
* 兼容 CJS(tsup bundle)环境,不依赖 require.resolve。
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
|
-
import { fileURLToPath } from "node:url";
|
|
9
|
-
import { createRequire } from "node:module";
|
|
10
10
|
import path from "node:path";
|
|
11
11
|
import fs from "node:fs";
|
|
12
12
|
|
|
13
|
-
/**
|
|
13
|
+
/** 已定位到的插件 package.json 路径,避免重复遍历目录树 */
|
|
14
14
|
let _resolvedPkgPath: string | null = null;
|
|
15
15
|
|
|
16
|
+
/** OpenClaw 框架版本缓存(一次查完不再重复 io) */
|
|
17
|
+
let _cachedOpenclawVersion: string | undefined;
|
|
18
|
+
|
|
16
19
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
20
|
+
* 获取插件自身版本号(@tencent-connect/openclaw-qqbot)。
|
|
21
|
+
* 从 __filename 向上遍历目录树找同名 package.json。
|
|
19
22
|
*/
|
|
20
|
-
function
|
|
21
|
-
if (metaUrl) {
|
|
22
|
-
return fileURLToPath(metaUrl);
|
|
23
|
-
}
|
|
24
|
-
// CJS 环境:__filename 由 tsup 注入
|
|
25
|
-
if (typeof __filename !== "undefined") {
|
|
26
|
-
return __filename;
|
|
27
|
-
}
|
|
28
|
-
return process.cwd();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function getPackageVersion(metaUrl?: string): string {
|
|
32
|
-
// 如果之前已定位到 package.json 路径,直接重新读取(快速路径)
|
|
23
|
+
export function getPackageVersion(): string {
|
|
33
24
|
if (_resolvedPkgPath) {
|
|
34
25
|
try {
|
|
35
26
|
const pkg = JSON.parse(fs.readFileSync(_resolvedPkgPath, "utf8"));
|
|
@@ -37,45 +28,105 @@ export function getPackageVersion(metaUrl?: string): string {
|
|
|
37
28
|
return pkg.version as string;
|
|
38
29
|
}
|
|
39
30
|
} catch {
|
|
40
|
-
// 文件可能已被删除(升级过程中),清除路径缓存,走完整查找
|
|
41
31
|
_resolvedPkgPath = null;
|
|
42
32
|
}
|
|
43
33
|
}
|
|
44
34
|
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return pkg.version as string;
|
|
35
|
+
// 从 __filename 向上遍历目录树
|
|
36
|
+
if (typeof __filename === "string") {
|
|
37
|
+
let dir = path.dirname(__filename);
|
|
38
|
+
const root = path.parse(dir).root;
|
|
39
|
+
while (dir !== root) {
|
|
40
|
+
const candidate = path.join(dir, "package.json");
|
|
41
|
+
try {
|
|
42
|
+
if (fs.existsSync(candidate)) {
|
|
43
|
+
const pkg = JSON.parse(fs.readFileSync(candidate, "utf8"));
|
|
44
|
+
if (pkg.name === "@tencent-connect/openclaw-qqbot" && pkg.version) {
|
|
45
|
+
_resolvedPkgPath = candidate;
|
|
46
|
+
return pkg.version as string;
|
|
47
|
+
}
|
|
59
48
|
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// ignore and try parent
|
|
49
|
+
} catch { /* next */ }
|
|
50
|
+
dir = path.dirname(dir);
|
|
63
51
|
}
|
|
64
|
-
dir = path.dirname(dir);
|
|
65
52
|
}
|
|
66
53
|
|
|
67
|
-
|
|
54
|
+
return "unknown";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ── OpenClaw 框架版本 ──
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* OpenClaw 框架版本降级链(优先后)。
|
|
61
|
+
* 1. PluginRuntime.version(3.31+,非 "unknown")
|
|
62
|
+
* 2. OPENCLAW_VERSION / OPENCLAW_SERVICE_VERSION 环境变量
|
|
63
|
+
* 3. 文件系统搜索 openclaw/package.json
|
|
64
|
+
* 4. 兜底 "unknown"
|
|
65
|
+
*/
|
|
66
|
+
export function getOpenclawVersion(runtimeVersion?: string): string {
|
|
67
|
+
if (_cachedOpenclawVersion) return _cachedOpenclawVersion;
|
|
68
|
+
|
|
69
|
+
// 1. runtime.version(排除兜底值)
|
|
70
|
+
if (runtimeVersion && runtimeVersion !== "unknown") {
|
|
71
|
+
return _cachedOpenclawVersion = runtimeVersion;
|
|
72
|
+
}
|
|
73
|
+
// 2. 环境变量
|
|
74
|
+
if (process.env.OPENCLAW_VERSION) {
|
|
75
|
+
return _cachedOpenclawVersion = process.env.OPENCLAW_VERSION;
|
|
76
|
+
}
|
|
77
|
+
if (process.env.OPENCLAW_SERVICE_VERSION) {
|
|
78
|
+
return _cachedOpenclawVersion = process.env.OPENCLAW_SERVICE_VERSION;
|
|
79
|
+
}
|
|
80
|
+
// 3. 文件系统
|
|
81
|
+
const pkgVer = readOpenclawPackageVersion();
|
|
82
|
+
if (pkgVer) return _cachedOpenclawVersion = pkgVer;
|
|
83
|
+
|
|
84
|
+
return "unknown";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function readOpenclawPackageVersion(): string | undefined {
|
|
68
88
|
try {
|
|
69
|
-
const
|
|
70
|
-
for (const
|
|
89
|
+
const dirs = searchRoots();
|
|
90
|
+
for (const dir of dirs) {
|
|
91
|
+
const pkgPath = path.join(dir, "package.json");
|
|
71
92
|
try {
|
|
72
|
-
const pkg =
|
|
73
|
-
if (pkg
|
|
74
|
-
return pkg.version as string;
|
|
75
|
-
}
|
|
93
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8")) as { name?: string; version?: string };
|
|
94
|
+
if (pkg.name === "openclaw" && pkg.version) return pkg.version;
|
|
76
95
|
} catch { /* next */ }
|
|
77
96
|
}
|
|
78
|
-
} catch { /*
|
|
97
|
+
} catch { /* fallthrough */ }
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
79
100
|
|
|
80
|
-
|
|
101
|
+
function searchRoots(): string[] {
|
|
102
|
+
const roots: string[] = [];
|
|
103
|
+
|
|
104
|
+
// 从插件自身路径向上查找
|
|
105
|
+
if (typeof __filename === "string") {
|
|
106
|
+
let dir = path.dirname(__filename);
|
|
107
|
+
for (let i = 0; i < 10; i++) {
|
|
108
|
+
roots.push(dir);
|
|
109
|
+
const parent = path.dirname(dir);
|
|
110
|
+
if (parent === dir) break;
|
|
111
|
+
dir = parent;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// STATE_DIR 指向的 openclaw 安装目录附近
|
|
116
|
+
for (const key of ["OPENCLAW_STATE_DIR", "CLAWDBOT_STATE_DIR", "MOLTBOT_STATE_DIR"]) {
|
|
117
|
+
const v = process.env[key];
|
|
118
|
+
if (v) {
|
|
119
|
+
roots.push(path.dirname(v));
|
|
120
|
+
roots.push(path.resolve(v, ".."));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 常见安装路径
|
|
125
|
+
const home = process.env.HOME || process.env.USERPROFILE || "/tmp";
|
|
126
|
+
for (const name of ["openclaw", "clawdbot", "moltbot"]) {
|
|
127
|
+
roots.push(path.join(home, `.${name}`));
|
|
128
|
+
}
|
|
129
|
+
roots.push(process.cwd());
|
|
130
|
+
|
|
131
|
+
return [...new Set(roots.filter((r) => typeof r === "string" && r.length > 0))];
|
|
81
132
|
}
|
package/tsup.config.ts
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|