@tencent-connect/openclaw-qqbot 1.6.4-alpha.7 → 1.6.4-alpha.9

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.
@@ -65,6 +65,12 @@ function registerCommand(cmd) {
65
65
  registerCommand({
66
66
  name: "bot-ping",
67
67
  description: "测试当前 openclaw 与 QQ 连接的网络延迟",
68
+ usage: [
69
+ `/bot-ping`,
70
+ ``,
71
+ `测试 OpenClaw 主机与 QQ 服务器之间的网络延迟。`,
72
+ `返回网络传输耗时和插件处理耗时。`,
73
+ ].join("\n"),
68
74
  handler: (ctx) => {
69
75
  const now = Date.now();
70
76
  const eventTime = new Date(ctx.eventTimestamp).getTime();
@@ -90,6 +96,12 @@ registerCommand({
90
96
  registerCommand({
91
97
  name: "bot-version",
92
98
  description: "查看插件版本号",
99
+ usage: [
100
+ `/bot-version`,
101
+ ``,
102
+ `查看当前 QQBot 插件版本和 OpenClaw 框架版本。`,
103
+ `同时检查是否有新版本可用。`,
104
+ ].join("\n"),
93
105
  handler: () => {
94
106
  const frameworkVersion = getFrameworkVersion();
95
107
  const lines = [
@@ -116,6 +128,12 @@ registerCommand({
116
128
  registerCommand({
117
129
  name: "bot-help",
118
130
  description: "查看所有指令以及用途",
131
+ usage: [
132
+ `/bot-help`,
133
+ ``,
134
+ `列出所有可用的 QQBot 插件内置指令及其简要说明。`,
135
+ `使用 /指令名 ? 可查看某条指令的详细用法。`,
136
+ ].join("\n"),
119
137
  handler: () => {
120
138
  const lines = [`### QQBot插件内置调试指令`, ``];
121
139
  for (const [name, cmd] of commands) {
@@ -205,6 +223,31 @@ function findBash() {
205
223
  return null;
206
224
  }
207
225
  }
226
+ /**
227
+ * 将 openclaw.json 中的 qqbot 插件 source 从 "path" 切换为 "npm"。
228
+ * 用于热更新场景:从 npm 拉取新版本后,确保 openclaw 不再从本地源码加载。
229
+ */
230
+ function switchPluginSourceToNpm() {
231
+ try {
232
+ const homeDir = getHomeDir();
233
+ for (const cli of ["openclaw", "clawdbot", "moltbot"]) {
234
+ const cfgPath = path.join(homeDir, `.${cli}`, `${cli}.json`);
235
+ if (!fs.existsSync(cfgPath))
236
+ continue;
237
+ const cfg = JSON.parse(fs.readFileSync(cfgPath, "utf8"));
238
+ const inst = cfg?.plugins?.installs?.["openclaw-qqbot"];
239
+ if (inst && inst.source !== "npm") {
240
+ inst.source = "npm";
241
+ delete inst.sourcePath;
242
+ fs.writeFileSync(cfgPath, JSON.stringify(cfg, null, 4) + "\n");
243
+ }
244
+ break;
245
+ }
246
+ }
247
+ catch {
248
+ // 非关键操作,静默忽略
249
+ }
250
+ }
208
251
  /**
209
252
  * 执行热更新:执行脚本(--no-restart) → 立即触发 gateway restart
210
253
  *
@@ -243,6 +286,11 @@ function fireHotUpgrade(targetVersion) {
243
286
  // 文件替换异常,不执行 restart 以保持现有服务
244
287
  return;
245
288
  }
289
+ // 文件替换成功,在 restart 之前把 source 从 path 切换为 npm,
290
+ // 确保新进程启动时读到的是 npm source,不会被本地源码覆盖。
291
+ // 必须在 restart 之前同步完成,避免 openclaw 轮询检测到配置变更后
292
+ // 先于我们的 restart 触发非预期的 reload。
293
+ switchPluginSourceToNpm();
246
294
  // 文件替换成功,立即触发 gateway restart(不再等后续步骤)
247
295
  execFile(cli, ["gateway", "restart"], { timeout: 30_000 }, (restartErr) => {
248
296
  if (restartErr) {
@@ -270,6 +318,14 @@ let _upgrading = false; // 升级锁
270
318
  registerCommand({
271
319
  name: "bot-upgrade",
272
320
  description: "检查更新并自动热更(失败则返回升级指引)",
321
+ usage: [
322
+ `/bot-upgrade 检查是否有新版本(展示信息+确认按钮)`,
323
+ `/bot-upgrade --latest 确认升级到最新版本`,
324
+ `/bot-upgrade --version X 升级到指定版本(如 1.6.4-alpha.7)`,
325
+ `/bot-upgrade --force 强制重新安装当前版本`,
326
+ ``,
327
+ `⚠️ 仅在私聊中可用。升级过程约 30~60 秒,期间服务短暂不可用。`,
328
+ ].join("\n"),
273
329
  handler: async (ctx) => {
274
330
  // 升级相关指令仅在私聊中可用
275
331
  if (ctx.type !== "c2c") {
@@ -507,6 +563,12 @@ function collectRecentLogFiles(logDirs) {
507
563
  registerCommand({
508
564
  name: "bot-logs",
509
565
  description: "导出本地日志文件",
566
+ usage: [
567
+ `/bot-logs`,
568
+ ``,
569
+ `导出最近的 OpenClaw 日志文件(最多 4 个)。`,
570
+ `每个文件最多保留最后 1000 行,以文件形式返回。`,
571
+ ].join("\n"),
510
572
  handler: () => {
511
573
  const logDirs = collectCandidateLogDirs();
512
574
  const recentFiles = collectRecentLogFiles(logDirs).slice(0, 4);
@@ -577,6 +639,13 @@ export async function matchSlashCommand(ctx) {
577
639
  const cmd = commands.get(cmdName);
578
640
  if (!cmd)
579
641
  return null; // 不是插件级指令,交给框架
642
+ // /指令 ? — 返回用法说明
643
+ if (args === "?") {
644
+ if (cmd.usage) {
645
+ return `📖 /${cmd.name} 用法:\n\n${cmd.usage}`;
646
+ }
647
+ return `/${cmd.name} — ${cmd.description}`;
648
+ }
580
649
  ctx.args = args;
581
650
  const result = await cmd.handler(ctx);
582
651
  return result;
@@ -71,7 +71,7 @@ async function fetchDistTags(log) {
71
71
  export function triggerUpdateCheck(log) {
72
72
  if (_checking)
73
73
  return;
74
- const INTERVAL_MS = 30 * 60 * 1000;
74
+ const INTERVAL_MS = 1 * 60 * 1000;
75
75
  if (_lastInfo.checkedAt > 0 && Date.now() - _lastInfo.checkedAt < INTERVAL_MS) {
76
76
  return;
77
77
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tencent-connect/openclaw-qqbot",
3
- "version": "1.6.4-alpha.7",
3
+ "version": "1.6.4-alpha.9",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -259,6 +259,25 @@ else
259
259
  echo "✅ 插件安装命令执行完成"
260
260
  echo "安装日志已保存到: $INSTALL_LOG"
261
261
 
262
+ # 确保 openclaw.json 中的 source 为 path(从 npm 切回 path)
263
+ for _app in openclaw clawdbot moltbot; do
264
+ _cfg="$HOME/.$_app/$_app.json"
265
+ if [ -f "$_cfg" ]; then
266
+ node -e "
267
+ const fs = require('fs');
268
+ const cfg = JSON.parse(fs.readFileSync('$_cfg', 'utf8'));
269
+ const inst = cfg.plugins && cfg.plugins.installs && cfg.plugins.installs['openclaw-qqbot'];
270
+ if (inst && inst.source !== 'path') {
271
+ inst.source = 'path';
272
+ inst.sourcePath = '$PROJ_DIR';
273
+ fs.writeFileSync('$_cfg', JSON.stringify(cfg, null, 4) + '\n');
274
+ console.log(' 已将 plugins.installs.openclaw-qqbot.source 更新为 path');
275
+ }
276
+ " 2>/dev/null || true
277
+ break
278
+ fi
279
+ done
280
+
262
281
  # 验证插件目录是否真正创建(防止 "安装成功" 但目录缺失的情况)
263
282
  _plugin_dir_ok=0
264
283
  for _candidate_name in openclaw-qqbot qqbot openclaw-qq; do
@@ -116,6 +116,8 @@ interface SlashCommand {
116
116
  name: string;
117
117
  /** 简要描述 */
118
118
  description: string;
119
+ /** 详细用法说明(支持多行),用于 /指令 ? 查询 */
120
+ usage?: string;
119
121
  /** 处理函数 */
120
122
  handler: (ctx: SlashCommandContext) => SlashCommandResult | Promise<SlashCommandResult>;
121
123
  }
@@ -136,6 +138,12 @@ function registerCommand(cmd: SlashCommand): void {
136
138
  registerCommand({
137
139
  name: "bot-ping",
138
140
  description: "测试当前 openclaw 与 QQ 连接的网络延迟",
141
+ usage: [
142
+ `/bot-ping`,
143
+ ``,
144
+ `测试 OpenClaw 主机与 QQ 服务器之间的网络延迟。`,
145
+ `返回网络传输耗时和插件处理耗时。`,
146
+ ].join("\n"),
139
147
  handler: (ctx) => {
140
148
  const now = Date.now();
141
149
  const eventTime = new Date(ctx.eventTimestamp).getTime();
@@ -162,6 +170,12 @@ registerCommand({
162
170
  registerCommand({
163
171
  name: "bot-version",
164
172
  description: "查看插件版本号",
173
+ usage: [
174
+ `/bot-version`,
175
+ ``,
176
+ `查看当前 QQBot 插件版本和 OpenClaw 框架版本。`,
177
+ `同时检查是否有新版本可用。`,
178
+ ].join("\n"),
165
179
  handler: () => {
166
180
  const frameworkVersion = getFrameworkVersion();
167
181
  const lines = [
@@ -187,6 +201,12 @@ registerCommand({
187
201
  registerCommand({
188
202
  name: "bot-help",
189
203
  description: "查看所有指令以及用途",
204
+ usage: [
205
+ `/bot-help`,
206
+ ``,
207
+ `列出所有可用的 QQBot 插件内置指令及其简要说明。`,
208
+ `使用 /指令名 ? 可查看某条指令的详细用法。`,
209
+ ].join("\n"),
190
210
  handler: () => {
191
211
  const lines = [`### QQBot插件内置调试指令`, ``];
192
212
  for (const [name, cmd] of commands) {
@@ -289,6 +309,30 @@ function findBash(): string | null {
289
309
  }
290
310
  }
291
311
 
312
+ /**
313
+ * 将 openclaw.json 中的 qqbot 插件 source 从 "path" 切换为 "npm"。
314
+ * 用于热更新场景:从 npm 拉取新版本后,确保 openclaw 不再从本地源码加载。
315
+ */
316
+ function switchPluginSourceToNpm(): void {
317
+ try {
318
+ const homeDir = getHomeDir();
319
+ for (const cli of ["openclaw", "clawdbot", "moltbot"]) {
320
+ const cfgPath = path.join(homeDir, `.${cli}`, `${cli}.json`);
321
+ if (!fs.existsSync(cfgPath)) continue;
322
+ const cfg = JSON.parse(fs.readFileSync(cfgPath, "utf8"));
323
+ const inst = cfg?.plugins?.installs?.["openclaw-qqbot"];
324
+ if (inst && inst.source !== "npm") {
325
+ inst.source = "npm";
326
+ delete inst.sourcePath;
327
+ fs.writeFileSync(cfgPath, JSON.stringify(cfg, null, 4) + "\n");
328
+ }
329
+ break;
330
+ }
331
+ } catch {
332
+ // 非关键操作,静默忽略
333
+ }
334
+ }
335
+
292
336
  /**
293
337
  * 执行热更新:执行脚本(--no-restart) → 立即触发 gateway restart
294
338
  *
@@ -329,6 +373,12 @@ function fireHotUpgrade(targetVersion?: string): HotUpgradeStartResult {
329
373
  return;
330
374
  }
331
375
 
376
+ // 文件替换成功,在 restart 之前把 source 从 path 切换为 npm,
377
+ // 确保新进程启动时读到的是 npm source,不会被本地源码覆盖。
378
+ // 必须在 restart 之前同步完成,避免 openclaw 轮询检测到配置变更后
379
+ // 先于我们的 restart 触发非预期的 reload。
380
+ switchPluginSourceToNpm();
381
+
332
382
  // 文件替换成功,立即触发 gateway restart(不再等后续步骤)
333
383
  execFile(cli, ["gateway", "restart"], { timeout: 30_000 }, (restartErr) => {
334
384
  if (restartErr) {
@@ -359,6 +409,14 @@ let _upgrading = false; // 升级锁
359
409
  registerCommand({
360
410
  name: "bot-upgrade",
361
411
  description: "检查更新并自动热更(失败则返回升级指引)",
412
+ usage: [
413
+ `/bot-upgrade 检查是否有新版本(展示信息+确认按钮)`,
414
+ `/bot-upgrade --latest 确认升级到最新版本`,
415
+ `/bot-upgrade --version X 升级到指定版本(如 1.6.4-alpha.7)`,
416
+ `/bot-upgrade --force 强制重新安装当前版本`,
417
+ ``,
418
+ `⚠️ 仅在私聊中可用。升级过程约 30~60 秒,期间服务短暂不可用。`,
419
+ ].join("\n"),
362
420
  handler: async (ctx) => {
363
421
  // 升级相关指令仅在私聊中可用
364
422
  if (ctx.type !== "c2c") {
@@ -615,6 +673,12 @@ function collectRecentLogFiles(logDirs: string[]): LogCandidate[] {
615
673
  registerCommand({
616
674
  name: "bot-logs",
617
675
  description: "导出本地日志文件",
676
+ usage: [
677
+ `/bot-logs`,
678
+ ``,
679
+ `导出最近的 OpenClaw 日志文件(最多 4 个)。`,
680
+ `每个文件最多保留最后 1000 行,以文件形式返回。`,
681
+ ].join("\n"),
618
682
  handler: () => {
619
683
  const logDirs = collectCandidateLogDirs();
620
684
  const recentFiles = collectRecentLogFiles(logDirs).slice(0, 4);
@@ -691,6 +755,14 @@ export async function matchSlashCommand(ctx: SlashCommandContext): Promise<Slash
691
755
  const cmd = commands.get(cmdName);
692
756
  if (!cmd) return null; // 不是插件级指令,交给框架
693
757
 
758
+ // /指令 ? — 返回用法说明
759
+ if (args === "?") {
760
+ if (cmd.usage) {
761
+ return `📖 /${cmd.name} 用法:\n\n${cmd.usage}`;
762
+ }
763
+ return `/${cmd.name} — ${cmd.description}`;
764
+ }
765
+
694
766
  ctx.args = args;
695
767
  const result = await cmd.handler(ctx);
696
768
  return result;
@@ -84,7 +84,7 @@ export function triggerUpdateCheck(log?: {
84
84
  debug?: (msg: string) => void;
85
85
  }): void {
86
86
  if (_checking) return;
87
- const INTERVAL_MS = 30 * 60 * 1000;
87
+ const INTERVAL_MS = 1 * 60 * 1000;
88
88
  if (_lastInfo.checkedAt > 0 && Date.now() - _lastInfo.checkedAt < INTERVAL_MS) {
89
89
  return;
90
90
  }