@jerryliang122/openclaw-qqbot 1.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.
Files changed (120) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +967 -0
  3. package/README.zh.md +790 -0
  4. package/dist/index.cjs +18072 -0
  5. package/dist/index.cjs.map +1 -0
  6. package/dist/index.d.cts +1222 -0
  7. package/index.ts +92 -0
  8. package/openclaw.plugin.json +38 -0
  9. package/package.json +69 -0
  10. package/preload.cjs +19 -0
  11. package/scripts/link-sdk-core.cjs +268 -0
  12. package/scripts/proactive-api-server.ts +369 -0
  13. package/scripts/send-proactive.ts +293 -0
  14. package/scripts/test-sendmedia.ts +116 -0
  15. package/skills/qqbot-channel/SKILL.md +285 -0
  16. package/skills/qqbot-channel/references/api_references.md +521 -0
  17. package/skills/qqbot-remind/SKILL.md +159 -0
  18. package/skills/qqbot-upgrade/SKILL.md +56 -0
  19. package/src/adapter/contract.ts +63 -0
  20. package/src/adapter/lint.ts +144 -0
  21. package/src/adapter/media.ts +40 -0
  22. package/src/adapter/pairing.ts +95 -0
  23. package/src/adapter/resolve.ts +255 -0
  24. package/src/adapter/setup.ts +13 -0
  25. package/src/adapter/webhook.ts +248 -0
  26. package/src/adapter/workspace.ts +21 -0
  27. package/src/agent-prompt-adapter.ts +26 -0
  28. package/src/bot-instance.ts +60 -0
  29. package/src/channel.ts +230 -0
  30. package/src/commands/bot-approve.ts +143 -0
  31. package/src/commands/bot-clear-storage.ts +114 -0
  32. package/src/commands/bot-group-always.ts +62 -0
  33. package/src/commands/bot-group-info.ts +48 -0
  34. package/src/commands/bot-help.ts +40 -0
  35. package/src/commands/bot-logs.ts +248 -0
  36. package/src/commands/bot-me.ts +18 -0
  37. package/src/commands/bot-pairing.ts +50 -0
  38. package/src/commands/bot-ping.ts +33 -0
  39. package/src/commands/bot-streaming.ts +55 -0
  40. package/src/commands/bot-upgrade.ts +56 -0
  41. package/src/commands/bot-version.ts +41 -0
  42. package/src/commands/config-util.ts +96 -0
  43. package/src/commands/index.ts +51 -0
  44. package/src/config.ts +403 -0
  45. package/src/constants.ts +6 -0
  46. package/src/dispatch/body-assembler.ts +308 -0
  47. package/src/dispatch/ctx-builder.ts +127 -0
  48. package/src/dispatch/dispatch.ts +667 -0
  49. package/src/dispatch/envelope-builder.ts +112 -0
  50. package/src/dispatch/index.ts +2 -0
  51. package/src/features/approval-capability.ts +302 -0
  52. package/src/features/approval-helpers.ts +271 -0
  53. package/src/features/approval-utils.ts +21 -0
  54. package/src/features/command-panel.ts +301 -0
  55. package/src/features/credential-backup.ts +74 -0
  56. package/src/features/group-mode-store.ts +79 -0
  57. package/src/features/history-store.ts +75 -0
  58. package/src/features/msgid-cache.ts +55 -0
  59. package/src/features/outbound-echo-store.ts +46 -0
  60. package/src/features/proactive-budget.ts +57 -0
  61. package/src/features/proactive.ts +549 -0
  62. package/src/features/question-helpers.ts +771 -0
  63. package/src/features/quota-manager.ts +173 -0
  64. package/src/features/ref-index-store.ts +289 -0
  65. package/src/features/secret-input-store.ts +118 -0
  66. package/src/features/secret-store-cli.ts +324 -0
  67. package/src/features/typing-refresh.ts +51 -0
  68. package/src/features/update-checker.ts +166 -0
  69. package/src/gateway/event-handlers.ts +456 -0
  70. package/src/gateway/index.ts +3 -0
  71. package/src/gateway/lifecycle.ts +236 -0
  72. package/src/gateway/middleware-setup.ts +173 -0
  73. package/src/gateway/qqbot-gateway.ts +458 -0
  74. package/src/gateway-adapter.ts +44 -0
  75. package/src/heartbeat-adapter.ts +57 -0
  76. package/src/message-adapter.ts +40 -0
  77. package/src/messaging-adapter.ts +78 -0
  78. package/src/middleware/access-control.ts +125 -0
  79. package/src/middleware/attachment.ts +373 -0
  80. package/src/middleware/inbound-guard.ts +102 -0
  81. package/src/middleware/policy-injector.ts +71 -0
  82. package/src/middleware/secret-capture.ts +161 -0
  83. package/src/middleware/typing.ts +110 -0
  84. package/src/openclaw-plugin-sdk.d.ts +543 -0
  85. package/src/outbound/chunker.ts +80 -0
  86. package/src/outbound/debounce.ts +102 -0
  87. package/src/outbound/deliver-pipeline.ts +235 -0
  88. package/src/outbound/index.ts +3 -0
  89. package/src/outbound/local-file-router.ts +145 -0
  90. package/src/outbound/media-send.ts +408 -0
  91. package/src/outbound/outbound-service.ts +298 -0
  92. package/src/outbound/reply-limiter.ts +139 -0
  93. package/src/outbound/sanitize.ts +32 -0
  94. package/src/outbound/streaming-controller.ts +332 -0
  95. package/src/outbound/target.ts +109 -0
  96. package/src/outbound-adapter.ts +323 -0
  97. package/src/plugin-base.ts +42 -0
  98. package/src/request-context.ts +50 -0
  99. package/src/runtime.ts +42 -0
  100. package/src/setup/account-key.ts +41 -0
  101. package/src/setup/finalize.ts +110 -0
  102. package/src/setup/login.ts +197 -0
  103. package/src/setup/surface.ts +40 -0
  104. package/src/status-adapter.ts +56 -0
  105. package/src/tools/platform.ts +149 -0
  106. package/src/tools/remind.ts +308 -0
  107. package/src/tools/secret-input.ts +185 -0
  108. package/src/types-augment.d.ts +54 -0
  109. package/src/types-plugin.ts +82 -0
  110. package/src/types.ts +620 -0
  111. package/src/typing-lifecycle.ts +182 -0
  112. package/src/utils/mention.ts +52 -0
  113. package/src/utils/pkg-version.ts +23 -0
  114. package/src/utils/platform.ts +459 -0
  115. package/src/utils/plugin-logger.ts +104 -0
  116. package/src/utils/ssrf-guard.ts +132 -0
  117. package/src/utils/stt.ts +150 -0
  118. package/src/utils/voice-text.ts +61 -0
  119. package/tsconfig.json +17 -0
  120. package/tsup.config.ts +64 -0
@@ -0,0 +1,50 @@
1
+ import type { SlashCommand } from '@tencent-connect/qqbot-nodejs';
2
+ import type { PluginRuntime } from 'openclaw/plugin-sdk';
3
+ import { getPairingApi } from '../adapter/pairing.js';
4
+ import { checkCommandAuth } from './config-util.js';
5
+
6
+ /** /bot-pairing — DM 配对审批管理 */
7
+ export function botPairing(_getRuntime: () => PluginRuntime): SlashCommand {
8
+ return {
9
+ name: 'bot-pairing',
10
+ description: '管理 DM 配对审批',
11
+ scope: 'c2c',
12
+ hidden: true,
13
+ usage: `/bot-pairing approve <code>
14
+
15
+ 批准指定配对码,允许对应用户私聊机器人。
16
+ 配对码由用户首次私聊时自动生成。`,
17
+ authorized: checkCommandAuth,
18
+ handler: async (ctx) => {
19
+ const args = (Array.isArray(ctx.command.args) ? ctx.command.args.join(' ') : String(ctx.command.args ?? '')).trim();
20
+ const parts = args.split(/\s+/);
21
+ const subCmd = parts[0]?.toLowerCase();
22
+ const code = parts[1]?.trim();
23
+
24
+ if (subCmd !== 'approve' || !code) {
25
+ return '⚠️ 用法: /bot-pairing approve <配对码>';
26
+ }
27
+
28
+ const api = getPairingApi();
29
+
30
+ try {
31
+ const result = await api.approveCode({
32
+ channel: 'qqbot',
33
+ code,
34
+ });
35
+
36
+ if (!result?.approved) {
37
+ return [
38
+ `⚠️ 配对码 \`${code}\` 无效或已过期。`,
39
+ '',
40
+ '每个配对码有效期为 1 小时。',
41
+ ].join('\n');
42
+ }
43
+
44
+ return '✅ 已批准用户访问。';
45
+ } catch (err) {
46
+ return `❌ 审批失败: ${(err as Error).message}`;
47
+ }
48
+ },
49
+ };
50
+ }
@@ -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://github.com/jerryliang122/qqbot-openclaw/blob/main/CHANGELOG.md';
9
+ const GITHUB_URL = 'https://github.com/jerryliang122/qqbot-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,51 @@
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 { botGroupInfo } from './bot-group-info.js';
21
+ import { botPairing } from './bot-pairing.js';
22
+
23
+ export interface CommandBuildOptions {
24
+ getRuntime: () => PluginRuntime;
25
+ }
26
+
27
+ /**
28
+ * 构建标准命令列表(匹配后直接回复,不进入 AI)
29
+ */
30
+ export function buildCommandList(account: ResolvedQQBotAccount, opts: CommandBuildOptions): SlashCommand[] {
31
+ const commands: SlashCommand[] = [];
32
+
33
+ // help 需要访问完整命令列表,延迟绑定
34
+ const help = botHelp(account, () => commands);
35
+ commands.push(
36
+ help,
37
+ botPing(),
38
+ botVersion(account),
39
+ botMe(),
40
+ botUpgrade(account),
41
+ botLogs(opts.getRuntime()),
42
+ botStreaming(account, opts.getRuntime),
43
+ botClearStorage(account),
44
+ botApprove(opts.getRuntime),
45
+ botGroupAlways(account, opts.getRuntime),
46
+ botGroupInfo(account),
47
+ botPairing(opts.getRuntime),
48
+ );
49
+
50
+ return commands;
51
+ }