@tencent-connect/openclaw-qqbot 1.7.1 → 2.0.0
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/README.md +206 -4
- package/README.zh.md +207 -4
- package/bin/qqbot-cli.js +5 -2
- package/dist/index.cjs +15549 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +890 -0
- package/index.ts +56 -18
- package/openclaw.plugin.json +19 -3
- package/package.json +14 -23
- package/preload.cjs +17 -14
- package/scripts/link-sdk-core.cjs +133 -50
- package/scripts/upgrade-via-npm.sh +400 -73
- package/scripts/upgrade-via-source.sh +126 -89
- package/skills/qqbot-channel/SKILL.md +34 -12
- package/src/adapter/contract.ts +69 -0
- package/src/adapter/gateway.ts +61 -0
- package/src/adapter/index.ts +15 -0
- package/src/adapter/lint.ts +144 -0
- package/src/adapter/media.ts +112 -0
- package/src/adapter/pairing.ts +99 -0
- package/src/adapter/resolve.ts +333 -0
- package/src/adapter/setup.ts +55 -0
- package/src/adapter/webhook.ts +248 -0
- package/src/adapter/workspace.ts +29 -0
- package/src/bot-instance.ts +60 -0
- package/src/channel.ts +202 -413
- package/src/commands/bot-approve.ts +143 -0
- package/src/commands/bot-clear-storage.ts +114 -0
- package/src/commands/bot-group-always.ts +62 -0
- package/src/commands/bot-help.ts +40 -0
- package/src/commands/bot-logs.ts +248 -0
- package/src/commands/bot-me.ts +18 -0
- package/src/commands/bot-pairing.ts +57 -0
- package/src/commands/bot-ping.ts +33 -0
- package/src/commands/bot-streaming.ts +55 -0
- package/src/commands/bot-upgrade.ts +56 -0
- package/src/commands/bot-version.ts +41 -0
- package/src/commands/config-util.ts +96 -0
- package/src/commands/index.ts +49 -0
- package/src/config.ts +59 -19
- package/src/dispatch/body-assembler.ts +356 -0
- package/src/dispatch/ctx-builder.ts +110 -0
- package/src/dispatch/dispatch.ts +352 -0
- package/src/dispatch/envelope-builder.ts +112 -0
- package/src/dispatch/index.ts +2 -0
- package/src/{approval-handler.ts → features/approval-handler.ts} +40 -94
- package/src/features/approval-utils.ts +41 -0
- package/src/features/credential-backup.ts +82 -0
- package/src/features/history-store.ts +19 -0
- package/src/features/msgid-cache.ts +55 -0
- package/src/features/onboarding.ts +38 -0
- package/src/{proactive.ts → features/proactive.ts} +68 -49
- package/src/features/ref-index-store.ts +282 -0
- package/src/{update-checker.ts → features/update-checker.ts} +15 -35
- package/src/gateway/event-handlers.ts +241 -0
- package/src/gateway/index.ts +3 -0
- package/src/gateway/lifecycle.ts +221 -0
- package/src/gateway/middleware-setup.ts +139 -0
- package/src/gateway/qqbot-gateway.ts +342 -0
- package/src/middleware/access-control.ts +130 -0
- package/src/middleware/attachment.ts +314 -0
- package/src/middleware/policy-injector.ts +66 -0
- package/src/openclaw-plugin-sdk.d.ts +81 -10
- package/src/outbound/cron-scheduler.ts +130 -0
- package/src/outbound/debounce.ts +102 -0
- package/src/outbound/deliver-pipeline.ts +235 -0
- package/src/outbound/image-size.ts +123 -0
- package/src/outbound/index.ts +3 -0
- package/src/outbound/local-file-router.ts +145 -0
- package/src/outbound/media-send.ts +377 -0
- package/src/outbound/outbound-service.ts +222 -0
- package/src/outbound/reply-limiter.ts +128 -0
- package/src/outbound/sanitize.ts +32 -0
- package/src/outbound/streaming-controller.ts +228 -0
- package/src/outbound/target.ts +63 -0
- package/src/outbound/tts-provider.ts +84 -0
- package/src/request-context.ts +9 -8
- package/src/runtime.ts +31 -6
- package/src/setup/account-key.ts +41 -0
- package/src/setup/finalize.ts +110 -0
- package/src/setup/login.ts +197 -0
- package/src/setup/surface.ts +40 -0
- package/src/tools/platform.ts +149 -0
- package/src/tools/remind.ts +1 -1
- package/src/types-augment.d.ts +54 -0
- package/src/types.ts +71 -8
- package/src/typings/openclaw-webhook-ingress.d.ts +66 -0
- package/src/utils/mention.ts +52 -0
- package/src/utils/pkg-version.ts +84 -47
- package/src/utils/platform.ts +31 -7
- package/src/utils/plugin-logger.ts +104 -0
- package/src/utils/ssrf-guard.ts +32 -2
- package/src/utils/stt.ts +137 -0
- package/src/utils/voice-text.ts +61 -0
- package/tsconfig.json +2 -1
- package/tsup.config.ts +57 -0
- package/dist/index.d.ts +0 -17
- package/dist/index.js +0 -26
- package/dist/src/admin-resolver.d.ts +0 -33
- package/dist/src/admin-resolver.js +0 -157
- package/dist/src/api.d.ts +0 -307
- package/dist/src/api.js +0 -910
- package/dist/src/approval-handler.d.ts +0 -47
- package/dist/src/approval-handler.js +0 -372
- package/dist/src/channel.d.ts +0 -29
- package/dist/src/channel.js +0 -528
- package/dist/src/config.d.ts +0 -56
- package/dist/src/config.js +0 -278
- package/dist/src/credential-backup.d.ts +0 -31
- package/dist/src/credential-backup.js +0 -66
- package/dist/src/deliver-debounce.d.ts +0 -74
- package/dist/src/deliver-debounce.js +0 -174
- package/dist/src/gateway.d.ts +0 -18
- package/dist/src/gateway.js +0 -2063
- package/dist/src/group-history.d.ts +0 -136
- package/dist/src/group-history.js +0 -226
- package/dist/src/image-server.d.ts +0 -87
- package/dist/src/image-server.js +0 -570
- package/dist/src/inbound-attachments.d.ts +0 -60
- package/dist/src/inbound-attachments.js +0 -248
- package/dist/src/known-users.d.ts +0 -100
- package/dist/src/known-users.js +0 -263
- package/dist/src/message-gating.d.ts +0 -53
- package/dist/src/message-gating.js +0 -107
- package/dist/src/message-queue.d.ts +0 -91
- package/dist/src/message-queue.js +0 -257
- package/dist/src/onboarding.d.ts +0 -10
- package/dist/src/onboarding.js +0 -203
- package/dist/src/outbound-deliver.d.ts +0 -48
- package/dist/src/outbound-deliver.js +0 -392
- package/dist/src/outbound.d.ts +0 -220
- package/dist/src/outbound.js +0 -975
- package/dist/src/proactive.d.ts +0 -170
- package/dist/src/proactive.js +0 -399
- package/dist/src/ref-index-store.d.ts +0 -101
- package/dist/src/ref-index-store.js +0 -298
- package/dist/src/reply-dispatcher.d.ts +0 -35
- package/dist/src/reply-dispatcher.js +0 -311
- package/dist/src/request-context.d.ts +0 -25
- package/dist/src/request-context.js +0 -37
- package/dist/src/runtime.d.ts +0 -3
- package/dist/src/runtime.js +0 -13
- package/dist/src/session-store.d.ts +0 -52
- package/dist/src/session-store.js +0 -254
- package/dist/src/slash-commands.d.ts +0 -82
- package/dist/src/slash-commands.js +0 -2117
- package/dist/src/startup-greeting.d.ts +0 -30
- package/dist/src/startup-greeting.js +0 -97
- package/dist/src/streaming.d.ts +0 -247
- package/dist/src/streaming.js +0 -899
- package/dist/src/stt.d.ts +0 -21
- package/dist/src/stt.js +0 -70
- package/dist/src/tools/channel.d.ts +0 -16
- package/dist/src/tools/channel.js +0 -234
- package/dist/src/tools/remind.d.ts +0 -2
- package/dist/src/tools/remind.js +0 -256
- package/dist/src/types.d.ts +0 -443
- package/dist/src/types.js +0 -22
- package/dist/src/typing-keepalive.d.ts +0 -27
- package/dist/src/typing-keepalive.js +0 -64
- package/dist/src/update-checker.d.ts +0 -36
- package/dist/src/update-checker.js +0 -171
- package/dist/src/utils/audio-convert.d.ts +0 -98
- package/dist/src/utils/audio-convert.js +0 -755
- package/dist/src/utils/chunked-upload.d.ts +0 -68
- package/dist/src/utils/chunked-upload.js +0 -341
- package/dist/src/utils/file-utils.d.ts +0 -61
- package/dist/src/utils/file-utils.js +0 -172
- package/dist/src/utils/image-size.d.ts +0 -51
- package/dist/src/utils/image-size.js +0 -234
- package/dist/src/utils/media-send.d.ts +0 -158
- package/dist/src/utils/media-send.js +0 -502
- package/dist/src/utils/media-tags.d.ts +0 -14
- package/dist/src/utils/media-tags.js +0 -165
- package/dist/src/utils/payload.d.ts +0 -112
- package/dist/src/utils/payload.js +0 -186
- package/dist/src/utils/pkg-version.d.ts +0 -5
- package/dist/src/utils/pkg-version.js +0 -61
- package/dist/src/utils/platform.d.ts +0 -137
- package/dist/src/utils/platform.js +0 -390
- package/dist/src/utils/ssrf-guard.d.ts +0 -25
- package/dist/src/utils/ssrf-guard.js +0 -91
- package/dist/src/utils/text-parsing.d.ts +0 -31
- package/dist/src/utils/text-parsing.js +0 -74
- package/dist/src/utils/upload-cache.d.ts +0 -34
- package/dist/src/utils/upload-cache.js +0 -93
- package/node_modules/@eshaz/web-worker/LICENSE +0 -201
- package/node_modules/@eshaz/web-worker/README.md +0 -134
- package/node_modules/@eshaz/web-worker/browser.js +0 -17
- package/node_modules/@eshaz/web-worker/cjs/browser.js +0 -16
- package/node_modules/@eshaz/web-worker/cjs/node.js +0 -219
- package/node_modules/@eshaz/web-worker/index.d.ts +0 -4
- package/node_modules/@eshaz/web-worker/node.js +0 -223
- package/node_modules/@eshaz/web-worker/package.json +0 -54
- package/node_modules/@wasm-audio-decoders/common/index.js +0 -5
- package/node_modules/@wasm-audio-decoders/common/package.json +0 -36
- package/node_modules/@wasm-audio-decoders/common/src/WASMAudioDecoderCommon.js +0 -231
- package/node_modules/@wasm-audio-decoders/common/src/WASMAudioDecoderWorker.js +0 -129
- package/node_modules/@wasm-audio-decoders/common/src/puff/README +0 -67
- package/node_modules/@wasm-audio-decoders/common/src/puff/build_puff.js +0 -31
- package/node_modules/@wasm-audio-decoders/common/src/puff/puff.c +0 -863
- package/node_modules/@wasm-audio-decoders/common/src/puff/puff.h +0 -35
- package/node_modules/@wasm-audio-decoders/common/src/utilities.js +0 -3
- package/node_modules/@wasm-audio-decoders/common/types.d.ts +0 -7
- package/node_modules/mpg123-decoder/README.md +0 -265
- package/node_modules/mpg123-decoder/dist/mpg123-decoder.min.js +0 -185
- package/node_modules/mpg123-decoder/dist/mpg123-decoder.min.js.map +0 -1
- package/node_modules/mpg123-decoder/index.js +0 -8
- package/node_modules/mpg123-decoder/package.json +0 -58
- package/node_modules/mpg123-decoder/src/EmscriptenWasm.js +0 -464
- package/node_modules/mpg123-decoder/src/MPEGDecoder.js +0 -200
- package/node_modules/mpg123-decoder/src/MPEGDecoderWebWorker.js +0 -21
- package/node_modules/mpg123-decoder/types.d.ts +0 -30
- package/node_modules/silk-wasm/LICENSE +0 -21
- package/node_modules/silk-wasm/README.md +0 -85
- package/node_modules/silk-wasm/lib/index.cjs +0 -16
- package/node_modules/silk-wasm/lib/index.d.ts +0 -70
- package/node_modules/silk-wasm/lib/index.mjs +0 -16
- package/node_modules/silk-wasm/lib/silk.wasm +0 -0
- package/node_modules/silk-wasm/lib/utils.d.ts +0 -4
- package/node_modules/silk-wasm/package.json +0 -39
- package/node_modules/simple-yenc/.github/FUNDING.yml +0 -1
- package/node_modules/simple-yenc/.prettierignore +0 -1
- package/node_modules/simple-yenc/LICENSE +0 -7
- package/node_modules/simple-yenc/README.md +0 -163
- package/node_modules/simple-yenc/dist/esm.js +0 -1
- package/node_modules/simple-yenc/dist/index.js +0 -1
- package/node_modules/simple-yenc/package.json +0 -50
- package/node_modules/simple-yenc/rollup.config.js +0 -27
- package/node_modules/simple-yenc/src/simple-yenc.js +0 -302
- package/node_modules/ws/LICENSE +0 -20
- package/node_modules/ws/README.md +0 -548
- package/node_modules/ws/browser.js +0 -8
- package/node_modules/ws/index.js +0 -13
- package/node_modules/ws/lib/buffer-util.js +0 -131
- package/node_modules/ws/lib/constants.js +0 -19
- package/node_modules/ws/lib/event-target.js +0 -292
- package/node_modules/ws/lib/extension.js +0 -203
- package/node_modules/ws/lib/limiter.js +0 -55
- package/node_modules/ws/lib/permessage-deflate.js +0 -528
- package/node_modules/ws/lib/receiver.js +0 -706
- package/node_modules/ws/lib/sender.js +0 -602
- package/node_modules/ws/lib/stream.js +0 -161
- package/node_modules/ws/lib/subprotocol.js +0 -62
- package/node_modules/ws/lib/validation.js +0 -152
- package/node_modules/ws/lib/websocket-server.js +0 -554
- package/node_modules/ws/lib/websocket.js +0 -1393
- package/node_modules/ws/package.json +0 -69
- package/node_modules/ws/wrapper.mjs +0 -8
- package/scripts/postinstall-link-sdk.js +0 -126
- package/skills/qqbot-media/SKILL.md +0 -60
- package/src/admin-resolver.ts +0 -181
- package/src/api.ts +0 -1319
- package/src/credential-backup.ts +0 -72
- package/src/deliver-debounce.ts +0 -229
- package/src/gateway.ts +0 -2304
- package/src/group-history.ts +0 -328
- package/src/image-server.ts +0 -675
- package/src/inbound-attachments.ts +0 -321
- package/src/known-users.ts +0 -353
- package/src/message-gating.ts +0 -190
- package/src/message-queue.ts +0 -354
- package/src/onboarding.ts +0 -274
- package/src/outbound-deliver.ts +0 -486
- package/src/outbound.ts +0 -1179
- package/src/ref-index-store.ts +0 -412
- package/src/reply-dispatcher.ts +0 -334
- package/src/session-store.ts +0 -303
- package/src/slash-commands.ts +0 -2297
- package/src/startup-greeting.ts +0 -120
- package/src/streaming.ts +0 -1077
- package/src/stt.ts +0 -86
- package/src/tools/channel.ts +0 -281
- package/src/typing-keepalive.ts +0 -59
- package/src/utils/audio-convert.ts +0 -859
- package/src/utils/chunked-upload.ts +0 -483
- package/src/utils/file-utils.ts +0 -193
- package/src/utils/image-size.ts +0 -266
- package/src/utils/media-send.ts +0 -636
- package/src/utils/media-tags.ts +0 -183
- package/src/utils/payload.ts +0 -265
- package/src/utils/text-parsing.ts +0 -85
- package/src/utils/upload-cache.ts +0 -128
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { SlashCommand } from '@tencent-connect/qqbot-nodejs';
|
|
2
|
+
|
|
3
|
+
/** /bot-ping — 测试当前 openclaw 与 QQ 连接的网络延迟 */
|
|
4
|
+
export function botPing(): SlashCommand {
|
|
5
|
+
return {
|
|
6
|
+
name: 'bot-ping',
|
|
7
|
+
description: '测试当前 openclaw 与 QQ 连接的网络延迟',
|
|
8
|
+
usage: [
|
|
9
|
+
'/bot-ping',
|
|
10
|
+
'',
|
|
11
|
+
'测试 OpenClaw 主机与 QQ 服务器之间的网络延迟。',
|
|
12
|
+
'返回网络传输耗时和插件处理耗时。',
|
|
13
|
+
].join('\n'),
|
|
14
|
+
handler: (ctx) => {
|
|
15
|
+
const now = Date.now();
|
|
16
|
+
const ts = (ctx.message as any).timestamp;
|
|
17
|
+
const eventTime = ts ? new Date(ts).getTime() : NaN;
|
|
18
|
+
if (isNaN(eventTime)) {
|
|
19
|
+
return '✅ pong!';
|
|
20
|
+
}
|
|
21
|
+
const totalMs = now - eventTime;
|
|
22
|
+
const qqToPlugin = ctx.receivedAt - eventTime;
|
|
23
|
+
const pluginProcess = now - ctx.receivedAt;
|
|
24
|
+
return [
|
|
25
|
+
'✅ pong!',
|
|
26
|
+
'',
|
|
27
|
+
`⏱ 延迟: ${totalMs}ms`,
|
|
28
|
+
` ├ 网络传输: ${qqToPlugin}ms`,
|
|
29
|
+
` └ 插件处理: ${pluginProcess}ms`,
|
|
30
|
+
].join('\n');
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { SlashCommand } from '@tencent-connect/qqbot-nodejs';
|
|
2
|
+
import type { PluginRuntime } from 'openclaw/plugin-sdk';
|
|
3
|
+
import type { ResolvedQQBotAccount } from '../types.js';
|
|
4
|
+
import { updateAccountConfig, checkCommandAuth } from './config-util.js';
|
|
5
|
+
|
|
6
|
+
/** /bot-streaming — 一键开关流式消息 */
|
|
7
|
+
export function botStreaming(account: ResolvedQQBotAccount, getRuntime: () => PluginRuntime): SlashCommand {
|
|
8
|
+
return {
|
|
9
|
+
name: 'bot-streaming',
|
|
10
|
+
description: '一键开关流式消息',
|
|
11
|
+
scope: 'c2c',
|
|
12
|
+
authorized: checkCommandAuth,
|
|
13
|
+
usage: `/bot-streaming
|
|
14
|
+
|
|
15
|
+
查看当前流式消息状态,或切换开/关。
|
|
16
|
+
流式消息仅支持 C2C(私聊)场景。`,
|
|
17
|
+
handler: async (ctx) => {
|
|
18
|
+
const args = (Array.isArray(ctx.command.args) ? ctx.command.args.join(' ') : String(ctx.command.args ?? '')).trim().toLowerCase();
|
|
19
|
+
const streaming = account.config?.streaming;
|
|
20
|
+
const currentEnabled = typeof streaming === 'boolean'
|
|
21
|
+
? streaming
|
|
22
|
+
: (streaming as any)?.mode !== 'off';
|
|
23
|
+
|
|
24
|
+
// 无参数 → 显示状态
|
|
25
|
+
if (!args) {
|
|
26
|
+
const status = currentEnabled ? '✅ 已启用' : '❌ 未启用';
|
|
27
|
+
const toggleHint = currentEnabled
|
|
28
|
+
? '<qqbot-cmd-input text="/bot-streaming off" show="关闭流式"/>'
|
|
29
|
+
: '<qqbot-cmd-input text="/bot-streaming on" show="开启流式"/>';
|
|
30
|
+
return [
|
|
31
|
+
`🌊 流式消息状态: ${status}`,
|
|
32
|
+
'',
|
|
33
|
+
'流式消息仅支持 C2C(私聊)场景。',
|
|
34
|
+
`点击 ${toggleHint} 切换。`,
|
|
35
|
+
].join('\n');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// on / off → 切换
|
|
39
|
+
const targetEnabled = args === 'on' || args === '1' || args === 'true';
|
|
40
|
+
if (targetEnabled === currentEnabled) {
|
|
41
|
+
return `ℹ️ 流式消息已经是${currentEnabled ? '开启' : '关闭'}状态,无需切换。`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const error = await updateAccountConfig(account, getRuntime, (acfg) => {
|
|
45
|
+
(acfg as any).streaming = { mode: targetEnabled ? 'partial' : 'off' };
|
|
46
|
+
});
|
|
47
|
+
if (error) return error;
|
|
48
|
+
|
|
49
|
+
account.config.streaming = { mode: targetEnabled ? 'partial' : 'off' };
|
|
50
|
+
return targetEnabled
|
|
51
|
+
? '✅ 流式消息已开启,私聊消息将以流式方式发送。'
|
|
52
|
+
: '✅ 流式消息已关闭,私聊消息将以静态方式发送。';
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { SlashCommand } from '@tencent-connect/qqbot-nodejs';
|
|
2
|
+
import type { ResolvedQQBotAccount } from '../types.js';
|
|
3
|
+
import { getPackageVersion } from '../utils/pkg-version.js';
|
|
4
|
+
import { getUpdateInfo } from '../features/update-checker.js';
|
|
5
|
+
import { checkCommandAuth } from './config-util.js';
|
|
6
|
+
|
|
7
|
+
const PLUGIN_VERSION = getPackageVersion();
|
|
8
|
+
const DEFAULT_UPGRADE_URL = 'https://docs.qq.com/doc/DSGxOZk1oVnVKVkpq';
|
|
9
|
+
const GITHUB_URL = 'https://github.com/nicepkg/openclaw';
|
|
10
|
+
|
|
11
|
+
/** /bot-upgrade — 检查更新并查看升级指引 */
|
|
12
|
+
export function botUpgrade(account: ResolvedQQBotAccount): SlashCommand {
|
|
13
|
+
return {
|
|
14
|
+
name: 'bot-upgrade',
|
|
15
|
+
description: '检查更新并查看升级指引',
|
|
16
|
+
scope: 'c2c',
|
|
17
|
+
authorized: checkCommandAuth,
|
|
18
|
+
usage: [
|
|
19
|
+
'/bot-upgrade 检查是否有新版本',
|
|
20
|
+
].join('\n'),
|
|
21
|
+
handler: async () => {
|
|
22
|
+
const url = account.config.upgradeUrl ?? DEFAULT_UPGRADE_URL;
|
|
23
|
+
const info = await getUpdateInfo();
|
|
24
|
+
|
|
25
|
+
if (info.checkedAt === 0) {
|
|
26
|
+
return '⏳ 版本检查中,请稍后再试';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (info.error) {
|
|
30
|
+
return [
|
|
31
|
+
'❌ 主机网络访问异常,无法检查更新',
|
|
32
|
+
'',
|
|
33
|
+
`查看升级指引:[点击查看](${url})`,
|
|
34
|
+
].join('\n');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!info.hasUpdate) {
|
|
38
|
+
return [
|
|
39
|
+
`✅ 当前已是最新版本 v${PLUGIN_VERSION}`,
|
|
40
|
+
'',
|
|
41
|
+
`项目地址:[GitHub](${GITHUB_URL})`,
|
|
42
|
+
].join('\n');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return [
|
|
46
|
+
'🆕 发现新版本',
|
|
47
|
+
'',
|
|
48
|
+
`当前版本:**v${PLUGIN_VERSION}**`,
|
|
49
|
+
`最新版本:**v${info.latest}**`,
|
|
50
|
+
'',
|
|
51
|
+
`📖 升级指引:[点击查看](${url})`,
|
|
52
|
+
`🌟 官方 GitHub 仓库:[点击前往](${GITHUB_URL})`,
|
|
53
|
+
].join('\n');
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { SlashCommand } from '@tencent-connect/qqbot-nodejs';
|
|
2
|
+
import type { ResolvedQQBotAccount } from '../types.js';
|
|
3
|
+
import { getPackageVersion } from '../utils/pkg-version.js';
|
|
4
|
+
import { getUpdateInfo } from '../features/update-checker.js';
|
|
5
|
+
import { getOpenClawVersion } from '../bot-instance.js';
|
|
6
|
+
|
|
7
|
+
const PLUGIN_VERSION = getPackageVersion();
|
|
8
|
+
const GITHUB_URL = 'https://github.com/nicepkg/openclaw';
|
|
9
|
+
|
|
10
|
+
/** /bot-version — 查看插件版本号 */
|
|
11
|
+
export function botVersion(_account: ResolvedQQBotAccount): SlashCommand {
|
|
12
|
+
return {
|
|
13
|
+
name: 'bot-version',
|
|
14
|
+
description: '查看插件版本号',
|
|
15
|
+
usage: [
|
|
16
|
+
'/bot-version',
|
|
17
|
+
'',
|
|
18
|
+
'查看当前 QQBot 插件版本和 OpenClaw 框架版本。',
|
|
19
|
+
'同时检查是否有新版本可用。',
|
|
20
|
+
].join('\n'),
|
|
21
|
+
handler: async () => {
|
|
22
|
+
const frameworkVersion = getOpenClawVersion();
|
|
23
|
+
const lines = [
|
|
24
|
+
`🦞框架版本:${frameworkVersion}`,
|
|
25
|
+
`🤖QQBot 插件版本:v${PLUGIN_VERSION}`,
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const info = await getUpdateInfo();
|
|
29
|
+
if (info.checkedAt === 0) {
|
|
30
|
+
lines.push('⏳ 版本检查中...');
|
|
31
|
+
} else if (info.error) {
|
|
32
|
+
lines.push('⚠️ 版本检查失败');
|
|
33
|
+
} else if (info.hasUpdate && info.latest) {
|
|
34
|
+
lines.push(`🆕最新可用版本:v${info.latest},点击 <qqbot-cmd-input text="/bot-upgrade" show="/bot-upgrade"/> 查看升级指引`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
lines.push(`🌟官方 GitHub 仓库:[点击前往](${GITHUB_URL})`);
|
|
38
|
+
return lines.join('\n');
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { ResolvedQQBotAccount } from '../types.js';
|
|
2
|
+
import type { SlashCommandHandlerContext } from '@tencent-connect/qqbot-nodejs';
|
|
3
|
+
import type { PluginRuntime } from 'openclaw/plugin-sdk';
|
|
4
|
+
import { getAdapters } from '../adapter/resolve.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 命令授权检查(用于 SlashCommand.authorized 回调)。
|
|
8
|
+
*
|
|
9
|
+
* 规则:
|
|
10
|
+
* - mode=open → 允许
|
|
11
|
+
* - allowFrom 为空或含 "*" → 允许
|
|
12
|
+
* - senderId 在 allowFrom 中 → 允许
|
|
13
|
+
* - 否则 → 返回错误消息
|
|
14
|
+
*/
|
|
15
|
+
export function checkCommandAuth(ctx: SlashCommandHandlerContext): boolean | string {
|
|
16
|
+
const p = (ctx.state as any).policy;
|
|
17
|
+
const mode = p?.c2cMode ?? 'allowlist';
|
|
18
|
+
const allowFrom: string[] = p?.allowFrom ?? [];
|
|
19
|
+
if (mode === 'open' || !allowFrom.length || allowFrom.includes('*')) return true;
|
|
20
|
+
return allowFrom.includes(ctx.message.senderId) || '⚠️ 无权限执行此命令';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface PersistResult {
|
|
24
|
+
persist: (updater: (cfg: any) => void) => Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 底层:获取 persistConfig,统一处理 runtime/adapters 判空。
|
|
29
|
+
* @returns [error, result] — error 非空时不可用;result.persist 直接调 persistConfig。
|
|
30
|
+
*/
|
|
31
|
+
async function resolvePersistFn(getRuntime: () => PluginRuntime): Promise<[string | null, PersistResult | null]> {
|
|
32
|
+
const runtime = getRuntime();
|
|
33
|
+
if (!runtime) return ['⚠️ runtime 不可用,无法修改配置。', null];
|
|
34
|
+
|
|
35
|
+
const adapters = getAdapters(runtime);
|
|
36
|
+
if (!adapters.persistConfig) {
|
|
37
|
+
return ['⚠️ 当前框架版本不支持在线修改配置,请手动编辑配置文件。', null];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return [
|
|
41
|
+
null,
|
|
42
|
+
{ persist: (updater: (cfg: any) => void) => adapters.persistConfig!(updater) },
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 持久化更新**账户**配置。自动处理命名账户 / 默认账户路径差异。
|
|
48
|
+
*/
|
|
49
|
+
export async function updateAccountConfig(
|
|
50
|
+
account: ResolvedQQBotAccount,
|
|
51
|
+
getRuntime: () => PluginRuntime,
|
|
52
|
+
updater: (accountConfig: Record<string, unknown>) => void,
|
|
53
|
+
): Promise<string | null> {
|
|
54
|
+
const [err, result] = await resolvePersistFn(getRuntime);
|
|
55
|
+
if (err || !result) return err;
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
await result.persist((cfg: any) => {
|
|
59
|
+
cfg.channels ??= {};
|
|
60
|
+
cfg.channels.qqbot ??= {};
|
|
61
|
+
|
|
62
|
+
const qqbot = cfg.channels.qqbot;
|
|
63
|
+
const accountId = account.accountId;
|
|
64
|
+
const isNamedAccount = accountId !== 'default' && qqbot.accounts?.[accountId];
|
|
65
|
+
|
|
66
|
+
if (isNamedAccount) {
|
|
67
|
+
qqbot.accounts[accountId] ??= {};
|
|
68
|
+
updater(qqbot.accounts[accountId]);
|
|
69
|
+
} else {
|
|
70
|
+
updater(qqbot);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
return null;
|
|
74
|
+
} catch (e) {
|
|
75
|
+
return `⚠️ 配置修改失败:${e instanceof Error ? e.message : String(e)}`;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 持久化更新**框架通用**配置(不从 channels.qqbot.accounts 走)。
|
|
81
|
+
* 适用于 tools.exec 等非账户级字段。
|
|
82
|
+
*/
|
|
83
|
+
export async function updateGlobalConfig(
|
|
84
|
+
getRuntime: () => PluginRuntime,
|
|
85
|
+
updater: (cfg: any) => void,
|
|
86
|
+
): Promise<string | null> {
|
|
87
|
+
const [err, result] = await resolvePersistFn(getRuntime);
|
|
88
|
+
if (err || !result) return err;
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
await result.persist(updater);
|
|
92
|
+
return null;
|
|
93
|
+
} catch (e) {
|
|
94
|
+
return `⚠️ 配置修改失败:${e instanceof Error ? e.message : String(e)}`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 斜杠命令注册表
|
|
3
|
+
*
|
|
4
|
+
* 通过 SDK 的 slashCommand 中间件统一注册所有内置命令。
|
|
5
|
+
* 每个命令拆分为独立文件,此处仅编排。
|
|
6
|
+
*/
|
|
7
|
+
import type { SlashCommand } from '@tencent-connect/qqbot-nodejs';
|
|
8
|
+
import type { PluginRuntime } from 'openclaw/plugin-sdk';
|
|
9
|
+
import type { ResolvedQQBotAccount } from '../types.js';
|
|
10
|
+
import { botHelp } from './bot-help.js';
|
|
11
|
+
import { botPing } from './bot-ping.js';
|
|
12
|
+
import { botVersion } from './bot-version.js';
|
|
13
|
+
import { botMe } from './bot-me.js';
|
|
14
|
+
import { botUpgrade } from './bot-upgrade.js';
|
|
15
|
+
import { botStreaming } from './bot-streaming.js';
|
|
16
|
+
import { botClearStorage } from './bot-clear-storage.js';
|
|
17
|
+
import { botLogs } from './bot-logs.js';
|
|
18
|
+
import { botApprove } from './bot-approve.js';
|
|
19
|
+
import { botGroupAlways } from './bot-group-always.js';
|
|
20
|
+
import { botPairing } from './bot-pairing.js';
|
|
21
|
+
|
|
22
|
+
export interface CommandBuildOptions {
|
|
23
|
+
getRuntime: () => PluginRuntime;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 构建标准命令列表(匹配后直接回复,不进入 AI)
|
|
28
|
+
*/
|
|
29
|
+
export function buildCommandList(account: ResolvedQQBotAccount, opts: CommandBuildOptions): SlashCommand[] {
|
|
30
|
+
const commands: SlashCommand[] = [];
|
|
31
|
+
|
|
32
|
+
// help 需要访问完整命令列表,延迟绑定
|
|
33
|
+
const help = botHelp(account, () => commands);
|
|
34
|
+
commands.push(
|
|
35
|
+
help,
|
|
36
|
+
botPing(),
|
|
37
|
+
botVersion(account),
|
|
38
|
+
botMe(),
|
|
39
|
+
botUpgrade(account),
|
|
40
|
+
botLogs(opts.getRuntime()),
|
|
41
|
+
botStreaming(account, opts.getRuntime),
|
|
42
|
+
botClearStorage(account),
|
|
43
|
+
botApprove(opts.getRuntime),
|
|
44
|
+
botGroupAlways(account, opts.getRuntime),
|
|
45
|
+
botPairing(opts.getRuntime),
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return commands;
|
|
49
|
+
}
|
package/src/config.ts
CHANGED
|
@@ -69,6 +69,8 @@ function evaluateMatchedGroupAccessForPolicy(params: {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
interface QQBotChannelConfig extends QQBotAccountConfig {
|
|
72
|
+
/** HTTP/WebSocket User-Agent 追加后缀 */
|
|
73
|
+
userAgentSuffix?: string;
|
|
72
74
|
accounts?: Record<string, QQBotAccountConfig>;
|
|
73
75
|
}
|
|
74
76
|
|
|
@@ -77,7 +79,10 @@ interface QQBotChannelConfig extends QQBotAccountConfig {
|
|
|
77
79
|
const DEFAULT_GROUP_POLICY: GroupPolicy = "open";
|
|
78
80
|
|
|
79
81
|
/** 群历史缓存条数默认值 */
|
|
80
|
-
const DEFAULT_GROUP_HISTORY_LIMIT =
|
|
82
|
+
const DEFAULT_GROUP_HISTORY_LIMIT = 20;
|
|
83
|
+
|
|
84
|
+
/** 单条消息默认处理超时(0 = 不限制) */
|
|
85
|
+
const DEFAULT_PROCESSING_TIMEOUT_MS = 0;
|
|
81
86
|
|
|
82
87
|
const DEFAULT_GROUP_CONFIG: Omit<Required<GroupConfig>, "prompt"> = {
|
|
83
88
|
requireMention: true,
|
|
@@ -108,8 +113,9 @@ export function resolveGroupAllowFrom(cfg: OpenClawConfig, accountId?: string):
|
|
|
108
113
|
|
|
109
114
|
/** 检查指定群是否被允许(使用标准策略引擎) */
|
|
110
115
|
export function isGroupAllowed(cfg: OpenClawConfig, groupOpenid: string, accountId?: string): boolean {
|
|
111
|
-
const
|
|
112
|
-
const
|
|
116
|
+
const account = resolveQQBotAccount(cfg, accountId);
|
|
117
|
+
const policy = account.config?.groupPolicy ?? DEFAULT_GROUP_POLICY;
|
|
118
|
+
const allowList = (account.config?.groupAllowFrom ?? []).map((id) => String(id).trim().toUpperCase());
|
|
113
119
|
const allowlistConfigured = allowList.length > 0;
|
|
114
120
|
const allowlistMatched = allowList.some((id) => id === "*" || id === groupOpenid.toUpperCase());
|
|
115
121
|
|
|
@@ -120,26 +126,28 @@ export function isGroupAllowed(cfg: OpenClawConfig, groupOpenid: string, account
|
|
|
120
126
|
}).allowed;
|
|
121
127
|
}
|
|
122
128
|
|
|
123
|
-
type ResolvedGroupConfig =
|
|
129
|
+
export type ResolvedGroupConfig = Required<GroupConfig>;
|
|
124
130
|
|
|
125
|
-
|
|
126
|
-
export function resolveGroupConfig(cfg: OpenClawConfig, groupOpenid: string, accountId?: string): ResolvedGroupConfig {
|
|
127
|
-
const account = resolveQQBotAccount(cfg, accountId);
|
|
131
|
+
export function resolveGroupConfigFromAccount(account: ResolvedQQBotAccount, groupOpenid: string): ResolvedGroupConfig {
|
|
128
132
|
const groups = account.config?.groups ?? {};
|
|
129
|
-
|
|
130
133
|
const wildcardCfg = groups["*"] ?? {};
|
|
131
134
|
const specificCfg = groups[groupOpenid] ?? {};
|
|
135
|
+
const accountDefaultRequireMention = account.config?.defaultRequireMention ?? DEFAULT_GROUP_CONFIG.requireMention;
|
|
132
136
|
|
|
133
137
|
return {
|
|
134
|
-
requireMention: specificCfg.requireMention ?? wildcardCfg.requireMention ??
|
|
138
|
+
requireMention: specificCfg.requireMention ?? wildcardCfg.requireMention ?? accountDefaultRequireMention,
|
|
135
139
|
ignoreOtherMentions: specificCfg.ignoreOtherMentions ?? wildcardCfg.ignoreOtherMentions ?? DEFAULT_GROUP_CONFIG.ignoreOtherMentions,
|
|
136
140
|
toolPolicy: specificCfg.toolPolicy ?? wildcardCfg.toolPolicy ?? DEFAULT_GROUP_CONFIG.toolPolicy,
|
|
137
141
|
name: specificCfg.name ?? wildcardCfg.name ?? DEFAULT_GROUP_CONFIG.name,
|
|
138
|
-
prompt: specificCfg.prompt ?? wildcardCfg.prompt,
|
|
142
|
+
prompt: specificCfg.prompt ?? wildcardCfg.prompt ?? DEFAULT_GROUP_PROMPT,
|
|
139
143
|
historyLimit: specificCfg.historyLimit ?? wildcardCfg.historyLimit ?? DEFAULT_GROUP_CONFIG.historyLimit,
|
|
140
144
|
};
|
|
141
145
|
}
|
|
142
146
|
|
|
147
|
+
export function resolveGroupConfig(cfg: OpenClawConfig, groupOpenid: string, accountId?: string): ResolvedGroupConfig {
|
|
148
|
+
return resolveGroupConfigFromAccount(resolveQQBotAccount(cfg, accountId), groupOpenid);
|
|
149
|
+
}
|
|
150
|
+
|
|
143
151
|
/** 解析群历史消息缓存条数 */
|
|
144
152
|
export function resolveHistoryLimit(cfg: OpenClawConfig, groupOpenid: string, accountId?: string): number {
|
|
145
153
|
return Math.max(0, resolveGroupConfig(cfg, groupOpenid, accountId).historyLimit);
|
|
@@ -147,10 +155,7 @@ export function resolveHistoryLimit(cfg: OpenClawConfig, groupOpenid: string, ac
|
|
|
147
155
|
|
|
148
156
|
/** 解析群行为 PE(具体群 > "*" > 默认值) */
|
|
149
157
|
export function resolveGroupPrompt(cfg: OpenClawConfig, groupOpenid: string, accountId?: string): string {
|
|
150
|
-
|
|
151
|
-
const groups = account.config?.groups ?? {};
|
|
152
|
-
|
|
153
|
-
return groups[groupOpenid]?.prompt ?? groups["*"]?.prompt ?? DEFAULT_GROUP_PROMPT;
|
|
158
|
+
return resolveGroupConfig(cfg, groupOpenid, accountId).prompt;
|
|
154
159
|
}
|
|
155
160
|
|
|
156
161
|
/** 解析群是否需要 @机器人才响应 */
|
|
@@ -174,6 +179,14 @@ export function resolveGroupName(cfg: OpenClawConfig, groupOpenid: string, accou
|
|
|
174
179
|
return name || groupOpenid.slice(0, 8);
|
|
175
180
|
}
|
|
176
181
|
|
|
182
|
+
/**
|
|
183
|
+
* 解析 User-Agent 追加后缀(仅通道级:channels.qqbot.userAgentSuffix)
|
|
184
|
+
*/
|
|
185
|
+
export function resolveUserAgentSuffix(cfg: OpenClawConfig): string {
|
|
186
|
+
const qqbot = cfg.channels?.qqbot as QQBotChannelConfig | undefined;
|
|
187
|
+
return qqbot?.userAgentSuffix ? String(qqbot.userAgentSuffix).trim() : "";
|
|
188
|
+
}
|
|
189
|
+
|
|
177
190
|
function normalizeAppId(raw: unknown): string {
|
|
178
191
|
if (raw === null || raw === undefined) return "";
|
|
179
192
|
return String(raw).trim();
|
|
@@ -220,6 +233,25 @@ export function resolveDefaultQQBotAccountId(cfg: OpenClawConfig): string {
|
|
|
220
233
|
return DEFAULT_ACCOUNT_ID;
|
|
221
234
|
}
|
|
222
235
|
|
|
236
|
+
/**
|
|
237
|
+
* 解析单条消息处理超时时间(ms)。
|
|
238
|
+
* 优先级:账户配置 > 环境变量 OPENCLAW_PROCESSING_TIMEOUT_MS > 默认
|
|
239
|
+
* 返回 0 表示不限制超时。
|
|
240
|
+
*/
|
|
241
|
+
export function resolveProcessingTimeoutMs(
|
|
242
|
+
accountConfig?: QQBotAccountConfig,
|
|
243
|
+
): number {
|
|
244
|
+
if (accountConfig?.processingTimeoutMs !== undefined) {
|
|
245
|
+
return accountConfig.processingTimeoutMs;
|
|
246
|
+
}
|
|
247
|
+
const env = process.env.OPENCLAW_PROCESSING_TIMEOUT_MS;
|
|
248
|
+
if (env) {
|
|
249
|
+
const v = Number(env);
|
|
250
|
+
if (!Number.isNaN(v) && v >= 0) return v;
|
|
251
|
+
}
|
|
252
|
+
return DEFAULT_PROCESSING_TIMEOUT_MS;
|
|
253
|
+
}
|
|
254
|
+
|
|
223
255
|
/**
|
|
224
256
|
* 解析 QQBot 账户配置
|
|
225
257
|
*/
|
|
@@ -276,19 +308,29 @@ export function resolveQQBotAccount(
|
|
|
276
308
|
clientSecret,
|
|
277
309
|
secretSource,
|
|
278
310
|
systemPrompt: accountConfig.systemPrompt,
|
|
279
|
-
imageServerBaseUrl: accountConfig.imageServerBaseUrl || process.env.QQBOT_IMAGE_SERVER_BASE_URL,
|
|
280
311
|
markdownSupport: accountConfig.markdownSupport !== false,
|
|
281
|
-
|
|
312
|
+
userAgentSuffix: resolveUserAgentSuffix(cfg),
|
|
313
|
+
processingTimeoutMs: resolveProcessingTimeoutMs(accountConfig),
|
|
314
|
+
config: normalizeAccountConfig(accountConfig),
|
|
282
315
|
};
|
|
283
316
|
}
|
|
284
317
|
|
|
318
|
+
/** 兼容旧版 streaming: boolean 格式 → { mode: "partial" | "off" },对齐框架 schema */
|
|
319
|
+
function normalizeAccountConfig(raw: QQBotAccountConfig): QQBotAccountConfig {
|
|
320
|
+
if (typeof (raw as any).streaming === 'boolean') {
|
|
321
|
+
const { streaming, ...rest } = raw as any;
|
|
322
|
+
return { ...rest, streaming: { mode: streaming ? 'partial' : 'off' } };
|
|
323
|
+
}
|
|
324
|
+
return raw;
|
|
325
|
+
}
|
|
326
|
+
|
|
285
327
|
/**
|
|
286
328
|
* 应用账户配置
|
|
287
329
|
*/
|
|
288
330
|
export function applyQQBotAccountConfig(
|
|
289
331
|
cfg: OpenClawConfig,
|
|
290
332
|
accountId: string,
|
|
291
|
-
input: { appId?: string; clientSecret?: string; clientSecretFile?: string; name?: string
|
|
333
|
+
input: { appId?: string; clientSecret?: string; clientSecretFile?: string; name?: string }
|
|
292
334
|
): OpenClawConfig {
|
|
293
335
|
const next = { ...cfg };
|
|
294
336
|
|
|
@@ -310,7 +352,6 @@ export function applyQQBotAccountConfig(
|
|
|
310
352
|
? { clientSecretFile: input.clientSecretFile }
|
|
311
353
|
: {}),
|
|
312
354
|
...(input.name ? { name: input.name } : {}),
|
|
313
|
-
...(input.imageServerBaseUrl ? { imageServerBaseUrl: input.imageServerBaseUrl } : {}),
|
|
314
355
|
},
|
|
315
356
|
};
|
|
316
357
|
} else {
|
|
@@ -336,7 +377,6 @@ export function applyQQBotAccountConfig(
|
|
|
336
377
|
? { clientSecretFile: input.clientSecretFile }
|
|
337
378
|
: {}),
|
|
338
379
|
...(input.name ? { name: input.name } : {}),
|
|
339
|
-
...(input.imageServerBaseUrl ? { imageServerBaseUrl: input.imageServerBaseUrl } : {}),
|
|
340
380
|
},
|
|
341
381
|
},
|
|
342
382
|
},
|