@cxyhhhhh/openclaw-qqbot 1.6.7-alpha.1

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 (218) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +470 -0
  3. package/README.zh.md +465 -0
  4. package/bin/qqbot-cli.js +243 -0
  5. package/dist/index.d.ts +17 -0
  6. package/dist/index.js +26 -0
  7. package/dist/src/admin-resolver.d.ts +33 -0
  8. package/dist/src/admin-resolver.js +157 -0
  9. package/dist/src/api.d.ts +264 -0
  10. package/dist/src/api.js +777 -0
  11. package/dist/src/channel.d.ts +29 -0
  12. package/dist/src/channel.js +452 -0
  13. package/dist/src/config.d.ts +56 -0
  14. package/dist/src/config.js +278 -0
  15. package/dist/src/credential-backup.d.ts +31 -0
  16. package/dist/src/credential-backup.js +66 -0
  17. package/dist/src/deliver-debounce.d.ts +74 -0
  18. package/dist/src/deliver-debounce.js +174 -0
  19. package/dist/src/gateway.d.ts +18 -0
  20. package/dist/src/gateway.js +2021 -0
  21. package/dist/src/group-history.d.ts +136 -0
  22. package/dist/src/group-history.js +226 -0
  23. package/dist/src/image-server.d.ts +87 -0
  24. package/dist/src/image-server.js +570 -0
  25. package/dist/src/inbound-attachments.d.ts +60 -0
  26. package/dist/src/inbound-attachments.js +248 -0
  27. package/dist/src/known-users.d.ts +100 -0
  28. package/dist/src/known-users.js +263 -0
  29. package/dist/src/message-gating.d.ts +53 -0
  30. package/dist/src/message-gating.js +107 -0
  31. package/dist/src/message-queue.d.ts +86 -0
  32. package/dist/src/message-queue.js +257 -0
  33. package/dist/src/onboarding.d.ts +10 -0
  34. package/dist/src/onboarding.js +203 -0
  35. package/dist/src/outbound-deliver.d.ts +48 -0
  36. package/dist/src/outbound-deliver.js +392 -0
  37. package/dist/src/outbound.d.ts +205 -0
  38. package/dist/src/outbound.js +926 -0
  39. package/dist/src/proactive.d.ts +170 -0
  40. package/dist/src/proactive.js +399 -0
  41. package/dist/src/ref-index-store.d.ts +70 -0
  42. package/dist/src/ref-index-store.js +250 -0
  43. package/dist/src/reply-dispatcher.d.ts +35 -0
  44. package/dist/src/reply-dispatcher.js +311 -0
  45. package/dist/src/request-context.d.ts +18 -0
  46. package/dist/src/request-context.js +30 -0
  47. package/dist/src/runtime.d.ts +3 -0
  48. package/dist/src/runtime.js +10 -0
  49. package/dist/src/session-store.d.ts +52 -0
  50. package/dist/src/session-store.js +254 -0
  51. package/dist/src/slash-commands.d.ts +77 -0
  52. package/dist/src/slash-commands.js +1461 -0
  53. package/dist/src/startup-greeting.d.ts +30 -0
  54. package/dist/src/startup-greeting.js +97 -0
  55. package/dist/src/streaming.d.ts +250 -0
  56. package/dist/src/streaming.js +914 -0
  57. package/dist/src/stt.d.ts +21 -0
  58. package/dist/src/stt.js +70 -0
  59. package/dist/src/tools/channel.d.ts +16 -0
  60. package/dist/src/tools/channel.js +234 -0
  61. package/dist/src/tools/remind.d.ts +2 -0
  62. package/dist/src/tools/remind.js +248 -0
  63. package/dist/src/types.d.ts +364 -0
  64. package/dist/src/types.js +17 -0
  65. package/dist/src/typing-keepalive.d.ts +27 -0
  66. package/dist/src/typing-keepalive.js +64 -0
  67. package/dist/src/update-checker.d.ts +34 -0
  68. package/dist/src/update-checker.js +160 -0
  69. package/dist/src/utils/audio-convert.d.ts +98 -0
  70. package/dist/src/utils/audio-convert.js +755 -0
  71. package/dist/src/utils/chunked-upload.d.ts +59 -0
  72. package/dist/src/utils/chunked-upload.js +289 -0
  73. package/dist/src/utils/file-utils.d.ts +61 -0
  74. package/dist/src/utils/file-utils.js +172 -0
  75. package/dist/src/utils/image-size.d.ts +51 -0
  76. package/dist/src/utils/image-size.js +234 -0
  77. package/dist/src/utils/media-send.d.ts +148 -0
  78. package/dist/src/utils/media-send.js +456 -0
  79. package/dist/src/utils/media-tags.d.ts +14 -0
  80. package/dist/src/utils/media-tags.js +164 -0
  81. package/dist/src/utils/payload.d.ts +112 -0
  82. package/dist/src/utils/payload.js +186 -0
  83. package/dist/src/utils/pkg-version.d.ts +5 -0
  84. package/dist/src/utils/pkg-version.js +51 -0
  85. package/dist/src/utils/platform.d.ts +137 -0
  86. package/dist/src/utils/platform.js +390 -0
  87. package/dist/src/utils/ssrf-guard.d.ts +25 -0
  88. package/dist/src/utils/ssrf-guard.js +91 -0
  89. package/dist/src/utils/text-parsing.d.ts +32 -0
  90. package/dist/src/utils/text-parsing.js +69 -0
  91. package/dist/src/utils/upload-cache.d.ts +34 -0
  92. package/dist/src/utils/upload-cache.js +93 -0
  93. package/index.ts +31 -0
  94. package/node_modules/@eshaz/web-worker/LICENSE +201 -0
  95. package/node_modules/@eshaz/web-worker/README.md +134 -0
  96. package/node_modules/@eshaz/web-worker/browser.js +17 -0
  97. package/node_modules/@eshaz/web-worker/cjs/browser.js +16 -0
  98. package/node_modules/@eshaz/web-worker/cjs/node.js +219 -0
  99. package/node_modules/@eshaz/web-worker/index.d.ts +4 -0
  100. package/node_modules/@eshaz/web-worker/node.js +223 -0
  101. package/node_modules/@eshaz/web-worker/package.json +54 -0
  102. package/node_modules/@wasm-audio-decoders/common/index.js +5 -0
  103. package/node_modules/@wasm-audio-decoders/common/package.json +36 -0
  104. package/node_modules/@wasm-audio-decoders/common/src/WASMAudioDecoderCommon.js +231 -0
  105. package/node_modules/@wasm-audio-decoders/common/src/WASMAudioDecoderWorker.js +129 -0
  106. package/node_modules/@wasm-audio-decoders/common/src/puff/README +67 -0
  107. package/node_modules/@wasm-audio-decoders/common/src/puff/build_puff.js +31 -0
  108. package/node_modules/@wasm-audio-decoders/common/src/puff/puff.c +863 -0
  109. package/node_modules/@wasm-audio-decoders/common/src/puff/puff.h +35 -0
  110. package/node_modules/@wasm-audio-decoders/common/src/utilities.js +3 -0
  111. package/node_modules/@wasm-audio-decoders/common/types.d.ts +7 -0
  112. package/node_modules/mpg123-decoder/README.md +265 -0
  113. package/node_modules/mpg123-decoder/dist/mpg123-decoder.min.js +185 -0
  114. package/node_modules/mpg123-decoder/dist/mpg123-decoder.min.js.map +1 -0
  115. package/node_modules/mpg123-decoder/index.js +8 -0
  116. package/node_modules/mpg123-decoder/package.json +58 -0
  117. package/node_modules/mpg123-decoder/src/EmscriptenWasm.js +464 -0
  118. package/node_modules/mpg123-decoder/src/MPEGDecoder.js +200 -0
  119. package/node_modules/mpg123-decoder/src/MPEGDecoderWebWorker.js +21 -0
  120. package/node_modules/mpg123-decoder/types.d.ts +30 -0
  121. package/node_modules/silk-wasm/LICENSE +21 -0
  122. package/node_modules/silk-wasm/README.md +85 -0
  123. package/node_modules/silk-wasm/lib/index.cjs +16 -0
  124. package/node_modules/silk-wasm/lib/index.d.ts +70 -0
  125. package/node_modules/silk-wasm/lib/index.mjs +16 -0
  126. package/node_modules/silk-wasm/lib/silk.wasm +0 -0
  127. package/node_modules/silk-wasm/lib/utils.d.ts +4 -0
  128. package/node_modules/silk-wasm/package.json +39 -0
  129. package/node_modules/simple-yenc/.github/FUNDING.yml +1 -0
  130. package/node_modules/simple-yenc/.prettierignore +1 -0
  131. package/node_modules/simple-yenc/LICENSE +7 -0
  132. package/node_modules/simple-yenc/README.md +163 -0
  133. package/node_modules/simple-yenc/dist/esm.js +1 -0
  134. package/node_modules/simple-yenc/dist/index.js +1 -0
  135. package/node_modules/simple-yenc/package.json +50 -0
  136. package/node_modules/simple-yenc/rollup.config.js +27 -0
  137. package/node_modules/simple-yenc/src/simple-yenc.js +302 -0
  138. package/node_modules/ws/LICENSE +20 -0
  139. package/node_modules/ws/README.md +548 -0
  140. package/node_modules/ws/browser.js +8 -0
  141. package/node_modules/ws/index.js +13 -0
  142. package/node_modules/ws/lib/buffer-util.js +131 -0
  143. package/node_modules/ws/lib/constants.js +19 -0
  144. package/node_modules/ws/lib/event-target.js +292 -0
  145. package/node_modules/ws/lib/extension.js +203 -0
  146. package/node_modules/ws/lib/limiter.js +55 -0
  147. package/node_modules/ws/lib/permessage-deflate.js +528 -0
  148. package/node_modules/ws/lib/receiver.js +706 -0
  149. package/node_modules/ws/lib/sender.js +602 -0
  150. package/node_modules/ws/lib/stream.js +161 -0
  151. package/node_modules/ws/lib/subprotocol.js +62 -0
  152. package/node_modules/ws/lib/validation.js +152 -0
  153. package/node_modules/ws/lib/websocket-server.js +554 -0
  154. package/node_modules/ws/lib/websocket.js +1393 -0
  155. package/node_modules/ws/package.json +69 -0
  156. package/node_modules/ws/wrapper.mjs +8 -0
  157. package/openclaw.plugin.json +17 -0
  158. package/package.json +67 -0
  159. package/preload.cjs +33 -0
  160. package/scripts/cleanup-legacy-plugins.sh +124 -0
  161. package/scripts/link-sdk-core.cjs +185 -0
  162. package/scripts/postinstall-link-sdk.js +113 -0
  163. package/scripts/proactive-api-server.ts +369 -0
  164. package/scripts/send-proactive.ts +293 -0
  165. package/scripts/set-markdown.sh +156 -0
  166. package/scripts/test-sendmedia.ts +116 -0
  167. package/scripts/upgrade-via-npm.ps1 +451 -0
  168. package/scripts/upgrade-via-npm.sh +528 -0
  169. package/scripts/upgrade-via-source.sh +916 -0
  170. package/skills/qqbot-channel/SKILL.md +263 -0
  171. package/skills/qqbot-channel/references/api_references.md +521 -0
  172. package/skills/qqbot-media/SKILL.md +60 -0
  173. package/skills/qqbot-remind/SKILL.md +149 -0
  174. package/src/admin-resolver.ts +181 -0
  175. package/src/api.ts +1138 -0
  176. package/src/channel.ts +477 -0
  177. package/src/config.ts +347 -0
  178. package/src/credential-backup.ts +72 -0
  179. package/src/deliver-debounce.ts +229 -0
  180. package/src/gateway.ts +2257 -0
  181. package/src/group-history.ts +328 -0
  182. package/src/image-server.ts +675 -0
  183. package/src/inbound-attachments.ts +321 -0
  184. package/src/known-users.ts +353 -0
  185. package/src/message-gating.ts +190 -0
  186. package/src/message-queue.ts +349 -0
  187. package/src/onboarding.ts +274 -0
  188. package/src/openclaw-plugin-sdk.d.ts +587 -0
  189. package/src/outbound-deliver.ts +473 -0
  190. package/src/outbound.ts +1119 -0
  191. package/src/proactive.ts +530 -0
  192. package/src/ref-index-store.ts +335 -0
  193. package/src/reply-dispatcher.ts +334 -0
  194. package/src/request-context.ts +39 -0
  195. package/src/runtime.ts +14 -0
  196. package/src/session-store.ts +303 -0
  197. package/src/slash-commands.ts +1615 -0
  198. package/src/startup-greeting.ts +120 -0
  199. package/src/streaming.ts +1102 -0
  200. package/src/stt.ts +86 -0
  201. package/src/tools/channel.ts +281 -0
  202. package/src/tools/remind.ts +300 -0
  203. package/src/types.ts +386 -0
  204. package/src/typing-keepalive.ts +59 -0
  205. package/src/update-checker.ts +174 -0
  206. package/src/utils/audio-convert.ts +859 -0
  207. package/src/utils/chunked-upload.ts +419 -0
  208. package/src/utils/file-utils.ts +193 -0
  209. package/src/utils/image-size.ts +266 -0
  210. package/src/utils/media-send.ts +585 -0
  211. package/src/utils/media-tags.ts +182 -0
  212. package/src/utils/payload.ts +265 -0
  213. package/src/utils/pkg-version.ts +54 -0
  214. package/src/utils/platform.ts +435 -0
  215. package/src/utils/ssrf-guard.ts +102 -0
  216. package/src/utils/text-parsing.ts +75 -0
  217. package/src/utils/upload-cache.ts +128 -0
  218. package/tsconfig.json +16 -0
@@ -0,0 +1,274 @@
1
+ /**
2
+ * QQBot CLI Onboarding Adapter
3
+ *
4
+ * 提供 openclaw onboard 命令的交互式配置支持
5
+ */
6
+ import type {
7
+ ChannelOnboardingAdapter,
8
+ ChannelOnboardingStatus,
9
+ ChannelOnboardingStatusContext,
10
+ ChannelOnboardingConfigureContext,
11
+ ChannelOnboardingResult,
12
+ OpenClawConfig,
13
+ } from "openclaw/plugin-sdk";
14
+ import { DEFAULT_ACCOUNT_ID, listQQBotAccountIds, resolveQQBotAccount } from "./config.js";
15
+
16
+ // 内部类型(用于类型安全)
17
+ interface QQBotChannelConfig {
18
+ enabled?: boolean;
19
+ appId?: string;
20
+ clientSecret?: string;
21
+ clientSecretFile?: string;
22
+ name?: string;
23
+ imageServerBaseUrl?: string;
24
+ markdownSupport?: boolean;
25
+ allowFrom?: string[];
26
+ accounts?: Record<string, {
27
+ enabled?: boolean;
28
+ appId?: string;
29
+ clientSecret?: string;
30
+ clientSecretFile?: string;
31
+ name?: string;
32
+ imageServerBaseUrl?: string;
33
+ markdownSupport?: boolean;
34
+ allowFrom?: string[];
35
+ }>;
36
+ }
37
+
38
+ // Prompter 类型定义
39
+ interface Prompter {
40
+ note: (message: string, title?: string) => Promise<void>;
41
+ confirm: (opts: { message: string; initialValue?: boolean }) => Promise<boolean>;
42
+ text: (opts: { message: string; placeholder?: string; initialValue?: string; validate?: (value: string) => string | undefined }) => Promise<string>;
43
+ select: <T>(opts: { message: string; options: Array<{ value: T; label: string }>; initialValue?: T }) => Promise<T>;
44
+ }
45
+
46
+ /**
47
+ * 解析默认账户 ID
48
+ */
49
+ function resolveDefaultQQBotAccountId(cfg: OpenClawConfig): string {
50
+ const ids = listQQBotAccountIds(cfg);
51
+ return ids[0] ?? DEFAULT_ACCOUNT_ID;
52
+ }
53
+
54
+ /**
55
+ * QQBot Onboarding Adapter
56
+ */
57
+ export const qqbotOnboardingAdapter: ChannelOnboardingAdapter = {
58
+ channel: "qqbot" as any,
59
+
60
+ getStatus: async (ctx: ChannelOnboardingStatusContext): Promise<ChannelOnboardingStatus> => {
61
+ const cfg = ctx.cfg as OpenClawConfig;
62
+ const configured = listQQBotAccountIds(cfg).some((accountId) => {
63
+ const account = resolveQQBotAccount(cfg, accountId);
64
+ return Boolean(account.appId && account.clientSecret);
65
+ });
66
+
67
+ return {
68
+ channel: "qqbot" as any,
69
+ configured,
70
+ statusLines: [`QQ Bot: ${configured ? "已配置" : "需要 AppID 和 ClientSecret"}`],
71
+ selectionHint: configured ? "已配置" : "支持 QQ 群聊和私聊(流式消息)",
72
+ quickstartScore: configured ? 1 : 20,
73
+ };
74
+ },
75
+
76
+ configure: async (ctx: ChannelOnboardingConfigureContext): Promise<ChannelOnboardingResult> => {
77
+ const cfg = ctx.cfg as OpenClawConfig;
78
+ const prompter = ctx.prompter as Prompter;
79
+ const accountOverrides = ctx.accountOverrides as Record<string, string> | undefined;
80
+ const shouldPromptAccountIds = ctx.shouldPromptAccountIds;
81
+
82
+ const qqbotOverride = accountOverrides?.qqbot?.trim();
83
+ const defaultAccountId = resolveDefaultQQBotAccountId(cfg);
84
+ let accountId = qqbotOverride ?? defaultAccountId;
85
+
86
+ // 是否需要提示选择账户
87
+ if (shouldPromptAccountIds && !qqbotOverride) {
88
+ const existingIds = listQQBotAccountIds(cfg);
89
+ if (existingIds.length > 1) {
90
+ accountId = await prompter.select({
91
+ message: "选择 QQBot 账户",
92
+ options: existingIds.map((id) => ({
93
+ value: id,
94
+ label: id === DEFAULT_ACCOUNT_ID ? "默认账户" : id,
95
+ })),
96
+ initialValue: accountId,
97
+ });
98
+ }
99
+ }
100
+
101
+ let next: OpenClawConfig = cfg;
102
+ const resolvedAccount = resolveQQBotAccount(next, accountId);
103
+ const accountConfigured = Boolean(resolvedAccount.appId && resolvedAccount.clientSecret);
104
+ const allowEnv = accountId === DEFAULT_ACCOUNT_ID;
105
+ const envAppId = typeof process !== "undefined" ? process.env?.QQBOT_APP_ID?.trim() : undefined;
106
+ const envSecret = typeof process !== "undefined" ? process.env?.QQBOT_CLIENT_SECRET?.trim() : undefined;
107
+ const canUseEnv = allowEnv && Boolean(envAppId && envSecret);
108
+ const hasConfigCredentials = Boolean(resolvedAccount.config.appId && resolvedAccount.config.clientSecret);
109
+
110
+ let appId: string | null = null;
111
+ let clientSecret: string | null = null;
112
+
113
+ // 显示帮助
114
+ if (!accountConfigured) {
115
+ await prompter.note(
116
+ [
117
+ "1) 打开 QQ 开放平台: https://q.qq.com/",
118
+ "2) 创建机器人应用,获取 AppID 和 ClientSecret",
119
+ "3) 在「开发设置」中添加沙箱成员(测试阶段)",
120
+ "4) 你也可以设置环境变量 QQBOT_APP_ID 和 QQBOT_CLIENT_SECRET",
121
+ "",
122
+ "文档: https://bot.q.qq.com/wiki/",
123
+ "",
124
+ "此版本支持流式消息发送!",
125
+ ].join("\n"),
126
+ "QQ Bot 配置",
127
+ );
128
+ }
129
+
130
+ // 检测环境变量
131
+ if (canUseEnv && !hasConfigCredentials) {
132
+ const keepEnv = await prompter.confirm({
133
+ message: "检测到环境变量 QQBOT_APP_ID 和 QQBOT_CLIENT_SECRET,是否使用?",
134
+ initialValue: true,
135
+ });
136
+ if (keepEnv) {
137
+ next = {
138
+ ...next,
139
+ channels: {
140
+ ...next.channels,
141
+ qqbot: {
142
+ ...(next.channels?.qqbot as Record<string, unknown> || {}),
143
+ enabled: true,
144
+ allowFrom: resolvedAccount.config?.allowFrom ?? ["*"],
145
+ },
146
+ },
147
+ };
148
+ } else {
149
+ // 手动输入
150
+ appId = String(
151
+ await prompter.text({
152
+ message: "请输入 QQ Bot AppID",
153
+ placeholder: "例如: 102146862",
154
+ initialValue: resolvedAccount.appId || undefined,
155
+ validate: (value: string) => (value?.trim() ? undefined : "AppID 不能为空"),
156
+ }),
157
+ ).trim();
158
+ clientSecret = String(
159
+ await prompter.text({
160
+ message: "请输入 QQ Bot ClientSecret",
161
+ placeholder: "你的 ClientSecret",
162
+ validate: (value: string) => (value?.trim() ? undefined : "ClientSecret 不能为空"),
163
+ }),
164
+ ).trim();
165
+ }
166
+ } else if (hasConfigCredentials) {
167
+ // 已有配置
168
+ const keep = await prompter.confirm({
169
+ message: "QQ Bot 已配置,是否保留当前配置?",
170
+ initialValue: true,
171
+ });
172
+ if (!keep) {
173
+ appId = String(
174
+ await prompter.text({
175
+ message: "请输入 QQ Bot AppID",
176
+ placeholder: "例如: 102146862",
177
+ initialValue: resolvedAccount.appId || undefined,
178
+ validate: (value: string) => (value?.trim() ? undefined : "AppID 不能为空"),
179
+ }),
180
+ ).trim();
181
+ clientSecret = String(
182
+ await prompter.text({
183
+ message: "请输入 QQ Bot ClientSecret",
184
+ placeholder: "你的 ClientSecret",
185
+ validate: (value: string) => (value?.trim() ? undefined : "ClientSecret 不能为空"),
186
+ }),
187
+ ).trim();
188
+ }
189
+ } else {
190
+ // 没有配置,需要输入
191
+ appId = String(
192
+ await prompter.text({
193
+ message: "请输入 QQ Bot AppID",
194
+ placeholder: "例如: 102146862",
195
+ initialValue: resolvedAccount.appId || undefined,
196
+ validate: (value: string) => (value?.trim() ? undefined : "AppID 不能为空"),
197
+ }),
198
+ ).trim();
199
+ clientSecret = String(
200
+ await prompter.text({
201
+ message: "请输入 QQ Bot ClientSecret",
202
+ placeholder: "你的 ClientSecret",
203
+ validate: (value: string) => (value?.trim() ? undefined : "ClientSecret 不能为空"),
204
+ }),
205
+ ).trim();
206
+ }
207
+
208
+ // 默认允许所有人执行命令(用户无感知)
209
+ const allowFrom: string[] = resolvedAccount.config?.allowFrom ?? ["*"];
210
+
211
+ // 应用配置(markdownSupport 默认开启,如需关闭可用 set-markdown.sh)
212
+ if (appId && clientSecret) {
213
+ const existingQQBot = (next.channels?.qqbot as Record<string, unknown>) || {};
214
+ // 保留已有的 markdownSupport 设置,新装默认 true
215
+ const markdownSupport = existingQQBot.markdownSupport ?? true;
216
+
217
+ if (accountId === DEFAULT_ACCOUNT_ID) {
218
+ next = {
219
+ ...next,
220
+ channels: {
221
+ ...next.channels,
222
+ qqbot: {
223
+ ...existingQQBot,
224
+ enabled: true,
225
+ appId,
226
+ clientSecret,
227
+ markdownSupport,
228
+ allowFrom,
229
+ },
230
+ },
231
+ };
232
+ } else {
233
+ const existingAccounts = ((next.channels?.qqbot as QQBotChannelConfig)?.accounts || {});
234
+ const existingAccount = existingAccounts[accountId] || {};
235
+ const acctMarkdown = existingAccount.markdownSupport ?? true;
236
+
237
+ next = {
238
+ ...next,
239
+ channels: {
240
+ ...next.channels,
241
+ qqbot: {
242
+ ...existingQQBot,
243
+ enabled: true,
244
+ accounts: {
245
+ ...existingAccounts,
246
+ [accountId]: {
247
+ ...existingAccount,
248
+ enabled: true,
249
+ appId,
250
+ clientSecret,
251
+ markdownSupport: acctMarkdown,
252
+ allowFrom,
253
+ },
254
+ },
255
+ },
256
+ },
257
+ };
258
+ }
259
+ }
260
+
261
+ return { success: true, cfg: next as any, accountId };
262
+ },
263
+
264
+ disable: (cfg: unknown) => {
265
+ const config = cfg as OpenClawConfig;
266
+ return {
267
+ ...config,
268
+ channels: {
269
+ ...config.channels,
270
+ qqbot: { ...(config.channels?.qqbot as Record<string, unknown> || {}), enabled: false },
271
+ },
272
+ } as any;
273
+ },
274
+ };