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

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.
@@ -263,8 +263,10 @@ async function ensureImageServer(log, publicBaseUrl) {
263
263
  // 区分 gateway restart(进程重启)和 health-monitor 断线重连
264
264
  let isFirstReadyGlobal = true;
265
265
  const STARTUP_MARKER_FILE = path.join(getQQBotDataDir("data"), "startup-marker.json");
266
- const STARTUP_GREETING_TEXT = `Haha,我的'灵魂'已上线,随时等你吩咐。`;
267
266
  const STARTUP_GREETING_RETRY_COOLDOWN_MS = 10 * 60 * 1000;
267
+ function getStartupGreetingText(version) {
268
+ return `🎉 QQBot 插件已更新至 v${version},在线等候你的吩咐。`;
269
+ }
268
270
  function readStartupMarker() {
269
271
  try {
270
272
  if (fs.existsSync(STARTUP_MARKER_FILE)) {
@@ -303,7 +305,7 @@ function getStartupGreetingPlan() {
303
305
  return { shouldSend: false, version: currentVersion, reason: "cooldown" };
304
306
  }
305
307
  }
306
- return { shouldSend: true, greeting: STARTUP_GREETING_TEXT, version: currentVersion };
308
+ return { shouldSend: true, greeting: getStartupGreetingText(currentVersion), version: currentVersion };
307
309
  }
308
310
  function markStartupGreetingSent(version) {
309
311
  writeStartupMarker({
@@ -14,7 +14,7 @@ import { createRequire } from "node:module";
14
14
  import { execFileSync, execFile } from "node:child_process";
15
15
  import path from "node:path";
16
16
  import fs from "node:fs";
17
- import { getUpdateInfo } from "./update-checker.js";
17
+ import { getUpdateInfo, checkVersionExists } from "./update-checker.js";
18
18
  import { getHomeDir, getQQBotDataDir, isWindows } from "./utils/platform.js";
19
19
  import { fileURLToPath } from "node:url";
20
20
  const require = createRequire(import.meta.url);
@@ -258,25 +258,32 @@ function fireHotUpgrade(targetVersion) {
258
258
  return { ok: true };
259
259
  }
260
260
  /**
261
- * /bot-upgrade — 统一升级入口:能热更就热更,失败则返回升级指引
261
+ * /bot-upgrade — 统一升级入口
262
262
  *
263
- * 支持参数:
264
- * /bot-upgrade 检查更新后自动热更
265
- * /bot-upgrade 1.6.4升级到指定版本
266
- * /bot-upgrade --force 强制升级(即使当前已是最新版)
263
+ * 产品流程:
264
+ * /bot-upgrade 展示版本信息+确认按钮(不直接升级)
265
+ * /bot-upgrade --latest确认升级到最新版本
266
+ * /bot-upgrade --version X 升级到指定版本
267
+ * /bot-upgrade --force — 强制升级(即使当前已是最新版)
267
268
  */
269
+ let _upgrading = false; // 升级锁
268
270
  registerCommand({
269
271
  name: "bot-upgrade",
270
272
  description: "检查更新并自动热更(失败则返回升级指引)",
271
- handler: (ctx) => {
273
+ handler: async (ctx) => {
272
274
  // 升级相关指令仅在私聊中可用
273
275
  if (ctx.type !== "c2c") {
274
276
  return `💡 请在私聊中使用此指令`;
275
277
  }
278
+ // 升级锁:防止重复触发
279
+ if (_upgrading) {
280
+ return `⏳ 正在升级中,请稍候...`;
281
+ }
276
282
  const url = ctx.accountConfig?.upgradeUrl || DEFAULT_UPGRADE_URL;
277
283
  const args = ctx.args.trim();
278
284
  const info = getUpdateInfo();
279
285
  let isForce = false;
286
+ let isLatest = false;
280
287
  let versionArg;
281
288
  const tokens = args ? args.split(/\s+/).filter(Boolean) : [];
282
289
  for (let i = 0; i < tokens.length; i += 1) {
@@ -285,6 +292,10 @@ registerCommand({
285
292
  isForce = true;
286
293
  continue;
287
294
  }
295
+ if (t === "--latest") {
296
+ isLatest = true;
297
+ continue;
298
+ }
288
299
  if (t === "--version") {
289
300
  const next = tokens[i + 1];
290
301
  if (!next || next.startsWith("--")) {
@@ -307,21 +318,65 @@ registerCommand({
307
318
  continue;
308
319
  }
309
320
  }
310
- if (!versionArg && !isForce) {
321
+ const GITHUB_URL = "https://github.com/tencent-connect/openclaw-qqbot/";
322
+ // ── 无参数(也没有 --latest / --version / --force):只展示信息+确认按钮 ──
323
+ if (!versionArg && !isLatest && !isForce) {
311
324
  if (info.checkedAt === 0) {
312
325
  return `⏳ 版本检查中,请稍后再试`;
313
326
  }
314
327
  if (info.error) {
315
- return `⚠️ 版本检查失败\n⬆️升级指引:[点击查看](${url})`;
328
+ return `❌ 当前我的主机网络访问 GitHub 异常,查看手动升级指引:[点击查看](${url})`;
316
329
  }
317
330
  if (!info.hasUpdate) {
318
- return `✅ 当前版本 v${PLUGIN_VERSION} 已是最新,无需升级\n\n> 💡 使用 /bot-upgrade --force 可强制重新安装`;
331
+ const lines = [
332
+ `🌟 GitHub:[tencent-connect/openclaw-qqbot](${GITHUB_URL})`,
333
+ `✅ 当前使用的已经是最新版本:v${PLUGIN_VERSION}`,
334
+ ];
335
+ return lines.join("\n");
336
+ }
337
+ // 有新版本:展示信息 + 确认按钮
338
+ const lines = [
339
+ `🌟 GitHub:[tencent-connect/openclaw-qqbot](${GITHUB_URL})`,
340
+ `📌 当前使用版本:v${PLUGIN_VERSION}`,
341
+ `🆕 最新可用版本:v${info.latest}`,
342
+ `📖 手动升级指引:[点击查看](${url})`,
343
+ `--`,
344
+ `**✅ 当前支持自动升级:**`,
345
+ `请确认机 OpenClaw 主机网络环境连接 GitHub 是否正常,插件更新会重启一次 OpenClaw 主机的 GateWay 服务,过程中会有服务不可用的状态。`,
346
+ `‼️点击确认 <qqbot-cmd-enter text="/bot-upgrade --latest" />`,
347
+ ];
348
+ return lines.join("\n");
349
+ }
350
+ // ── --version 指定版本:先校验版本号是否存在 ──
351
+ if (versionArg) {
352
+ const exists = await checkVersionExists(versionArg);
353
+ if (!exists) {
354
+ return `❌ 当前不存在 ${versionArg} 版本号,请检查 version 设置`;
355
+ }
356
+ // 检查是否就是当前版本
357
+ if (versionArg === PLUGIN_VERSION && !isForce) {
358
+ return `😀 当前已是 v${PLUGIN_VERSION} 版本,无需升级`;
359
+ }
360
+ }
361
+ // ── --latest:检查是否需要升级 ──
362
+ if (isLatest && !versionArg) {
363
+ if (info.checkedAt === 0) {
364
+ return `⏳ 版本检查中,请稍后再试`;
365
+ }
366
+ if (info.error) {
367
+ return `❌ 当前我的主机网络访问 GitHub 异常,查看手动升级指引:[点击查看](${url})`;
368
+ }
369
+ if (!info.hasUpdate && !isForce) {
370
+ return `😀 当前已是 v${PLUGIN_VERSION} 版本,无需升级`;
319
371
  }
320
372
  }
321
373
  const targetVersion = versionArg || info.latest || undefined;
374
+ // 加锁
375
+ _upgrading = true;
322
376
  // 异步执行升级
323
377
  const startResult = fireHotUpgrade(targetVersion);
324
378
  if (!startResult.ok) {
379
+ _upgrading = false;
325
380
  if (startResult.reason === "no-script") {
326
381
  return `❌ 未找到本地升级脚本\n⬆️升级指引:[点击查看](${url})`;
327
382
  }
@@ -20,3 +20,8 @@ export declare function triggerUpdateCheck(log?: {
20
20
  debug?: (msg: string) => void;
21
21
  }): void;
22
22
  export declare function getUpdateInfo(): UpdateInfo;
23
+ /**
24
+ * 检查指定版本是否存在于 npm registry
25
+ * 用于 /bot-upgrade --version 的前置校验
26
+ */
27
+ export declare function checkVersionExists(version: string): Promise<boolean>;
@@ -101,6 +101,24 @@ export function triggerUpdateCheck(log) {
101
101
  export function getUpdateInfo() {
102
102
  return { ..._lastInfo };
103
103
  }
104
+ /**
105
+ * 检查指定版本是否存在于 npm registry
106
+ * 用于 /bot-upgrade --version 的前置校验
107
+ */
108
+ export async function checkVersionExists(version) {
109
+ for (const baseUrl of REGISTRIES) {
110
+ try {
111
+ const url = `${baseUrl}/${version}`;
112
+ const json = await fetchJson(url, 10_000);
113
+ if (json && json.version === version)
114
+ return true;
115
+ }
116
+ catch {
117
+ // try next registry
118
+ }
119
+ }
120
+ return false;
121
+ }
104
122
  function compareVersions(a, b) {
105
123
  const parse = (v) => {
106
124
  const clean = v.replace(/^v/, "");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tencent-connect/openclaw-qqbot",
3
- "version": "1.6.4-alpha.6",
3
+ "version": "1.6.4-alpha.7",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/gateway.ts CHANGED
@@ -352,9 +352,12 @@ async function ensureImageServer(log?: GatewayContext["log"], publicBaseUrl?: st
352
352
  let isFirstReadyGlobal = true;
353
353
 
354
354
  const STARTUP_MARKER_FILE = path.join(getQQBotDataDir("data"), "startup-marker.json");
355
- const STARTUP_GREETING_TEXT = `Haha,我的'灵魂'已上线,随时等你吩咐。`;
356
355
  const STARTUP_GREETING_RETRY_COOLDOWN_MS = 10 * 60 * 1000;
357
356
 
357
+ function getStartupGreetingText(version: string): string {
358
+ return `🎉 QQBot 插件已更新至 v${version},在线等候你的吩咐。`;
359
+ }
360
+
358
361
  type StartupMarkerData = {
359
362
  version?: string;
360
363
  startedAt?: string;
@@ -405,7 +408,7 @@ function getStartupGreetingPlan(): { shouldSend: boolean; greeting?: string; ver
405
408
  }
406
409
  }
407
410
 
408
- return { shouldSend: true, greeting: STARTUP_GREETING_TEXT, version: currentVersion };
411
+ return { shouldSend: true, greeting: getStartupGreetingText(currentVersion), version: currentVersion };
409
412
  }
410
413
 
411
414
  function markStartupGreetingSent(version: string): void {
@@ -16,7 +16,7 @@ import { createRequire } from "node:module";
16
16
  import { execFileSync, execFile } from "node:child_process";
17
17
  import path from "node:path";
18
18
  import fs from "node:fs";
19
- import { getUpdateInfo } from "./update-checker.js";
19
+ import { getUpdateInfo, checkVersionExists } from "./update-checker.js";
20
20
  import { getHomeDir, getQQBotDataDir, isWindows } from "./utils/platform.js";
21
21
  import { fileURLToPath } from "node:url";
22
22
  const require = createRequire(import.meta.url);
@@ -346,27 +346,36 @@ function fireHotUpgrade(targetVersion?: string): HotUpgradeStartResult {
346
346
  }
347
347
 
348
348
  /**
349
- * /bot-upgrade — 统一升级入口:能热更就热更,失败则返回升级指引
349
+ * /bot-upgrade — 统一升级入口
350
350
  *
351
- * 支持参数:
352
- * /bot-upgrade 检查更新后自动热更
353
- * /bot-upgrade 1.6.4升级到指定版本
354
- * /bot-upgrade --force 强制升级(即使当前已是最新版)
351
+ * 产品流程:
352
+ * /bot-upgrade 展示版本信息+确认按钮(不直接升级)
353
+ * /bot-upgrade --latest确认升级到最新版本
354
+ * /bot-upgrade --version X 升级到指定版本
355
+ * /bot-upgrade --force — 强制升级(即使当前已是最新版)
355
356
  */
357
+ let _upgrading = false; // 升级锁
358
+
356
359
  registerCommand({
357
360
  name: "bot-upgrade",
358
361
  description: "检查更新并自动热更(失败则返回升级指引)",
359
- handler: (ctx) => {
362
+ handler: async (ctx) => {
360
363
  // 升级相关指令仅在私聊中可用
361
364
  if (ctx.type !== "c2c") {
362
365
  return `💡 请在私聊中使用此指令`;
363
366
  }
364
367
 
368
+ // 升级锁:防止重复触发
369
+ if (_upgrading) {
370
+ return `⏳ 正在升级中,请稍候...`;
371
+ }
372
+
365
373
  const url = ctx.accountConfig?.upgradeUrl || DEFAULT_UPGRADE_URL;
366
374
  const args = ctx.args.trim();
367
375
  const info = getUpdateInfo();
368
376
 
369
377
  let isForce = false;
378
+ let isLatest = false;
370
379
  let versionArg: string | undefined;
371
380
  const tokens = args ? args.split(/\s+/).filter(Boolean) : [];
372
381
  for (let i = 0; i < tokens.length; i += 1) {
@@ -375,6 +384,10 @@ registerCommand({
375
384
  isForce = true;
376
385
  continue;
377
386
  }
387
+ if (t === "--latest") {
388
+ isLatest = true;
389
+ continue;
390
+ }
378
391
  if (t === "--version") {
379
392
  const next = tokens[i + 1];
380
393
  if (!next || next.startsWith("--")) {
@@ -398,23 +411,73 @@ registerCommand({
398
411
  }
399
412
  }
400
413
 
401
- if (!versionArg && !isForce) {
414
+ const GITHUB_URL = "https://github.com/tencent-connect/openclaw-qqbot/";
415
+
416
+ // ── 无参数(也没有 --latest / --version / --force):只展示信息+确认按钮 ──
417
+ if (!versionArg && !isLatest && !isForce) {
402
418
  if (info.checkedAt === 0) {
403
419
  return `⏳ 版本检查中,请稍后再试`;
404
420
  }
405
421
  if (info.error) {
406
- return `⚠️ 版本检查失败\n⬆️升级指引:[点击查看](${url})`;
422
+ return `❌ 当前我的主机网络访问 GitHub 异常,查看手动升级指引:[点击查看](${url})`;
407
423
  }
408
424
  if (!info.hasUpdate) {
409
- return `✅ 当前版本 v${PLUGIN_VERSION} 已是最新,无需升级\n\n> 💡 使用 /bot-upgrade --force 可强制重新安装`;
425
+ const lines = [
426
+ `🌟 GitHub:[tencent-connect/openclaw-qqbot](${GITHUB_URL})`,
427
+ `✅ 当前使用的已经是最新版本:v${PLUGIN_VERSION}`,
428
+ ];
429
+ return lines.join("\n");
430
+ }
431
+
432
+ // 有新版本:展示信息 + 确认按钮
433
+ const lines = [
434
+ `🌟 GitHub:[tencent-connect/openclaw-qqbot](${GITHUB_URL})`,
435
+ `📌 当前使用版本:v${PLUGIN_VERSION}`,
436
+ `🆕 最新可用版本:v${info.latest}`,
437
+ `📖 手动升级指引:[点击查看](${url})`,
438
+ `--`,
439
+ `**✅ 当前支持自动升级:**`,
440
+ `请确认机 OpenClaw 主机网络环境连接 GitHub 是否正常,插件更新会重启一次 OpenClaw 主机的 GateWay 服务,过程中会有服务不可用的状态。`,
441
+ `‼️点击确认 <qqbot-cmd-enter text="/bot-upgrade --latest" />`,
442
+ ];
443
+ return lines.join("\n");
444
+ }
445
+
446
+ // ── --version 指定版本:先校验版本号是否存在 ──
447
+ if (versionArg) {
448
+ const exists = await checkVersionExists(versionArg);
449
+ if (!exists) {
450
+ return `❌ 当前不存在 ${versionArg} 版本号,请检查 version 设置`;
451
+ }
452
+
453
+ // 检查是否就是当前版本
454
+ if (versionArg === PLUGIN_VERSION && !isForce) {
455
+ return `😀 当前已是 v${PLUGIN_VERSION} 版本,无需升级`;
456
+ }
457
+ }
458
+
459
+ // ── --latest:检查是否需要升级 ──
460
+ if (isLatest && !versionArg) {
461
+ if (info.checkedAt === 0) {
462
+ return `⏳ 版本检查中,请稍后再试`;
463
+ }
464
+ if (info.error) {
465
+ return `❌ 当前我的主机网络访问 GitHub 异常,查看手动升级指引:[点击查看](${url})`;
466
+ }
467
+ if (!info.hasUpdate && !isForce) {
468
+ return `😀 当前已是 v${PLUGIN_VERSION} 版本,无需升级`;
410
469
  }
411
470
  }
412
471
 
413
472
  const targetVersion = versionArg || info.latest || undefined;
414
473
 
474
+ // 加锁
475
+ _upgrading = true;
476
+
415
477
  // 异步执行升级
416
478
  const startResult = fireHotUpgrade(targetVersion);
417
479
  if (!startResult.ok) {
480
+ _upgrading = false;
418
481
  if (startResult.reason === "no-script") {
419
482
  return `❌ 未找到本地升级脚本\n⬆️升级指引:[点击查看](${url})`;
420
483
  }
@@ -117,6 +117,23 @@ export function getUpdateInfo(): UpdateInfo {
117
117
  return { ..._lastInfo };
118
118
  }
119
119
 
120
+ /**
121
+ * 检查指定版本是否存在于 npm registry
122
+ * 用于 /bot-upgrade --version 的前置校验
123
+ */
124
+ export async function checkVersionExists(version: string): Promise<boolean> {
125
+ for (const baseUrl of REGISTRIES) {
126
+ try {
127
+ const url = `${baseUrl}/${version}`;
128
+ const json = await fetchJson(url, 10_000);
129
+ if (json && json.version === version) return true;
130
+ } catch {
131
+ // try next registry
132
+ }
133
+ }
134
+ return false;
135
+ }
136
+
120
137
  function compareVersions(a: string, b: string): number {
121
138
  const parse = (v: string) => {
122
139
  const clean = v.replace(/^v/, "");