@tencent-connect/openclaw-qqbot 1.6.4-alpha.6 → 1.6.4-alpha.8
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/dist/src/gateway.js +4 -2
- package/dist/src/slash-commands.js +104 -10
- package/dist/src/update-checker.d.ts +5 -0
- package/dist/src/update-checker.js +18 -0
- package/package.json +1 -1
- package/src/gateway.ts +5 -2
- package/src/slash-commands.ts +115 -10
- package/src/update-checker.ts +17 -0
package/dist/src/gateway.js
CHANGED
|
@@ -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:
|
|
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);
|
|
@@ -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) {
|
|
@@ -258,25 +276,40 @@ function fireHotUpgrade(targetVersion) {
|
|
|
258
276
|
return { ok: true };
|
|
259
277
|
}
|
|
260
278
|
/**
|
|
261
|
-
* /bot-upgrade —
|
|
279
|
+
* /bot-upgrade — 统一升级入口
|
|
262
280
|
*
|
|
263
|
-
*
|
|
264
|
-
* /bot-upgrade
|
|
265
|
-
* /bot-upgrade
|
|
266
|
-
* /bot-upgrade --
|
|
281
|
+
* 产品流程:
|
|
282
|
+
* /bot-upgrade — 展示版本信息+确认按钮(不直接升级)
|
|
283
|
+
* /bot-upgrade --latest — 确认升级到最新版本
|
|
284
|
+
* /bot-upgrade --version X — 升级到指定版本
|
|
285
|
+
* /bot-upgrade --force — 强制升级(即使当前已是最新版)
|
|
267
286
|
*/
|
|
287
|
+
let _upgrading = false; // 升级锁
|
|
268
288
|
registerCommand({
|
|
269
289
|
name: "bot-upgrade",
|
|
270
290
|
description: "检查更新并自动热更(失败则返回升级指引)",
|
|
271
|
-
|
|
291
|
+
usage: [
|
|
292
|
+
`/bot-upgrade 检查是否有新版本(展示信息+确认按钮)`,
|
|
293
|
+
`/bot-upgrade --latest 确认升级到最新版本`,
|
|
294
|
+
`/bot-upgrade --version X 升级到指定版本(如 1.6.4-alpha.7)`,
|
|
295
|
+
`/bot-upgrade --force 强制重新安装当前版本`,
|
|
296
|
+
``,
|
|
297
|
+
`⚠️ 仅在私聊中可用。升级过程约 30~60 秒,期间服务短暂不可用。`,
|
|
298
|
+
].join("\n"),
|
|
299
|
+
handler: async (ctx) => {
|
|
272
300
|
// 升级相关指令仅在私聊中可用
|
|
273
301
|
if (ctx.type !== "c2c") {
|
|
274
302
|
return `💡 请在私聊中使用此指令`;
|
|
275
303
|
}
|
|
304
|
+
// 升级锁:防止重复触发
|
|
305
|
+
if (_upgrading) {
|
|
306
|
+
return `⏳ 正在升级中,请稍候...`;
|
|
307
|
+
}
|
|
276
308
|
const url = ctx.accountConfig?.upgradeUrl || DEFAULT_UPGRADE_URL;
|
|
277
309
|
const args = ctx.args.trim();
|
|
278
310
|
const info = getUpdateInfo();
|
|
279
311
|
let isForce = false;
|
|
312
|
+
let isLatest = false;
|
|
280
313
|
let versionArg;
|
|
281
314
|
const tokens = args ? args.split(/\s+/).filter(Boolean) : [];
|
|
282
315
|
for (let i = 0; i < tokens.length; i += 1) {
|
|
@@ -285,6 +318,10 @@ registerCommand({
|
|
|
285
318
|
isForce = true;
|
|
286
319
|
continue;
|
|
287
320
|
}
|
|
321
|
+
if (t === "--latest") {
|
|
322
|
+
isLatest = true;
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
288
325
|
if (t === "--version") {
|
|
289
326
|
const next = tokens[i + 1];
|
|
290
327
|
if (!next || next.startsWith("--")) {
|
|
@@ -307,21 +344,65 @@ registerCommand({
|
|
|
307
344
|
continue;
|
|
308
345
|
}
|
|
309
346
|
}
|
|
310
|
-
|
|
347
|
+
const GITHUB_URL = "https://github.com/tencent-connect/openclaw-qqbot/";
|
|
348
|
+
// ── 无参数(也没有 --latest / --version / --force):只展示信息+确认按钮 ──
|
|
349
|
+
if (!versionArg && !isLatest && !isForce) {
|
|
311
350
|
if (info.checkedAt === 0) {
|
|
312
351
|
return `⏳ 版本检查中,请稍后再试`;
|
|
313
352
|
}
|
|
314
353
|
if (info.error) {
|
|
315
|
-
return
|
|
354
|
+
return `❌ 当前我的主机网络访问 GitHub 异常,查看手动升级指引:[点击查看](${url})`;
|
|
316
355
|
}
|
|
317
356
|
if (!info.hasUpdate) {
|
|
318
|
-
|
|
357
|
+
const lines = [
|
|
358
|
+
`🌟 GitHub:[tencent-connect/openclaw-qqbot](${GITHUB_URL})`,
|
|
359
|
+
`✅ 当前使用的已经是最新版本:v${PLUGIN_VERSION}`,
|
|
360
|
+
];
|
|
361
|
+
return lines.join("\n");
|
|
362
|
+
}
|
|
363
|
+
// 有新版本:展示信息 + 确认按钮
|
|
364
|
+
const lines = [
|
|
365
|
+
`🌟 GitHub:[tencent-connect/openclaw-qqbot](${GITHUB_URL})`,
|
|
366
|
+
`📌 当前使用版本:v${PLUGIN_VERSION}`,
|
|
367
|
+
`🆕 最新可用版本:v${info.latest}`,
|
|
368
|
+
`📖 手动升级指引:[点击查看](${url})`,
|
|
369
|
+
`--`,
|
|
370
|
+
`**✅ 当前支持自动升级:**`,
|
|
371
|
+
`请确认机 OpenClaw 主机网络环境连接 GitHub 是否正常,插件更新会重启一次 OpenClaw 主机的 GateWay 服务,过程中会有服务不可用的状态。`,
|
|
372
|
+
`‼️点击确认 <qqbot-cmd-enter text="/bot-upgrade --latest" />`,
|
|
373
|
+
];
|
|
374
|
+
return lines.join("\n");
|
|
375
|
+
}
|
|
376
|
+
// ── --version 指定版本:先校验版本号是否存在 ──
|
|
377
|
+
if (versionArg) {
|
|
378
|
+
const exists = await checkVersionExists(versionArg);
|
|
379
|
+
if (!exists) {
|
|
380
|
+
return `❌ 当前不存在 ${versionArg} 版本号,请检查 version 设置`;
|
|
381
|
+
}
|
|
382
|
+
// 检查是否就是当前版本
|
|
383
|
+
if (versionArg === PLUGIN_VERSION && !isForce) {
|
|
384
|
+
return `😀 当前已是 v${PLUGIN_VERSION} 版本,无需升级`;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
// ── --latest:检查是否需要升级 ──
|
|
388
|
+
if (isLatest && !versionArg) {
|
|
389
|
+
if (info.checkedAt === 0) {
|
|
390
|
+
return `⏳ 版本检查中,请稍后再试`;
|
|
391
|
+
}
|
|
392
|
+
if (info.error) {
|
|
393
|
+
return `❌ 当前我的主机网络访问 GitHub 异常,查看手动升级指引:[点击查看](${url})`;
|
|
394
|
+
}
|
|
395
|
+
if (!info.hasUpdate && !isForce) {
|
|
396
|
+
return `😀 当前已是 v${PLUGIN_VERSION} 版本,无需升级`;
|
|
319
397
|
}
|
|
320
398
|
}
|
|
321
399
|
const targetVersion = versionArg || info.latest || undefined;
|
|
400
|
+
// 加锁
|
|
401
|
+
_upgrading = true;
|
|
322
402
|
// 异步执行升级
|
|
323
403
|
const startResult = fireHotUpgrade(targetVersion);
|
|
324
404
|
if (!startResult.ok) {
|
|
405
|
+
_upgrading = false;
|
|
325
406
|
if (startResult.reason === "no-script") {
|
|
326
407
|
return `❌ 未找到本地升级脚本\n⬆️升级指引:[点击查看](${url})`;
|
|
327
408
|
}
|
|
@@ -452,6 +533,12 @@ function collectRecentLogFiles(logDirs) {
|
|
|
452
533
|
registerCommand({
|
|
453
534
|
name: "bot-logs",
|
|
454
535
|
description: "导出本地日志文件",
|
|
536
|
+
usage: [
|
|
537
|
+
`/bot-logs`,
|
|
538
|
+
``,
|
|
539
|
+
`导出最近的 OpenClaw 日志文件(最多 4 个)。`,
|
|
540
|
+
`每个文件最多保留最后 1000 行,以文件形式返回。`,
|
|
541
|
+
].join("\n"),
|
|
455
542
|
handler: () => {
|
|
456
543
|
const logDirs = collectCandidateLogDirs();
|
|
457
544
|
const recentFiles = collectRecentLogFiles(logDirs).slice(0, 4);
|
|
@@ -522,6 +609,13 @@ export async function matchSlashCommand(ctx) {
|
|
|
522
609
|
const cmd = commands.get(cmdName);
|
|
523
610
|
if (!cmd)
|
|
524
611
|
return null; // 不是插件级指令,交给框架
|
|
612
|
+
// /指令 ? — 返回用法说明
|
|
613
|
+
if (args === "?") {
|
|
614
|
+
if (cmd.usage) {
|
|
615
|
+
return `📖 /${cmd.name} 用法:\n\n${cmd.usage}`;
|
|
616
|
+
}
|
|
617
|
+
return `/${cmd.name} — ${cmd.description}`;
|
|
618
|
+
}
|
|
525
619
|
ctx.args = args;
|
|
526
620
|
const result = await cmd.handler(ctx);
|
|
527
621
|
return result;
|
|
@@ -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
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:
|
|
411
|
+
return { shouldSend: true, greeting: getStartupGreetingText(currentVersion), version: currentVersion };
|
|
409
412
|
}
|
|
410
413
|
|
|
411
414
|
function markStartupGreetingSent(version: string): void {
|
package/src/slash-commands.ts
CHANGED
|
@@ -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);
|
|
@@ -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) {
|
|
@@ -346,27 +366,44 @@ function fireHotUpgrade(targetVersion?: string): HotUpgradeStartResult {
|
|
|
346
366
|
}
|
|
347
367
|
|
|
348
368
|
/**
|
|
349
|
-
* /bot-upgrade —
|
|
369
|
+
* /bot-upgrade — 统一升级入口
|
|
350
370
|
*
|
|
351
|
-
*
|
|
352
|
-
* /bot-upgrade
|
|
353
|
-
* /bot-upgrade
|
|
354
|
-
* /bot-upgrade --
|
|
371
|
+
* 产品流程:
|
|
372
|
+
* /bot-upgrade — 展示版本信息+确认按钮(不直接升级)
|
|
373
|
+
* /bot-upgrade --latest — 确认升级到最新版本
|
|
374
|
+
* /bot-upgrade --version X — 升级到指定版本
|
|
375
|
+
* /bot-upgrade --force — 强制升级(即使当前已是最新版)
|
|
355
376
|
*/
|
|
377
|
+
let _upgrading = false; // 升级锁
|
|
378
|
+
|
|
356
379
|
registerCommand({
|
|
357
380
|
name: "bot-upgrade",
|
|
358
381
|
description: "检查更新并自动热更(失败则返回升级指引)",
|
|
359
|
-
|
|
382
|
+
usage: [
|
|
383
|
+
`/bot-upgrade 检查是否有新版本(展示信息+确认按钮)`,
|
|
384
|
+
`/bot-upgrade --latest 确认升级到最新版本`,
|
|
385
|
+
`/bot-upgrade --version X 升级到指定版本(如 1.6.4-alpha.7)`,
|
|
386
|
+
`/bot-upgrade --force 强制重新安装当前版本`,
|
|
387
|
+
``,
|
|
388
|
+
`⚠️ 仅在私聊中可用。升级过程约 30~60 秒,期间服务短暂不可用。`,
|
|
389
|
+
].join("\n"),
|
|
390
|
+
handler: async (ctx) => {
|
|
360
391
|
// 升级相关指令仅在私聊中可用
|
|
361
392
|
if (ctx.type !== "c2c") {
|
|
362
393
|
return `💡 请在私聊中使用此指令`;
|
|
363
394
|
}
|
|
364
395
|
|
|
396
|
+
// 升级锁:防止重复触发
|
|
397
|
+
if (_upgrading) {
|
|
398
|
+
return `⏳ 正在升级中,请稍候...`;
|
|
399
|
+
}
|
|
400
|
+
|
|
365
401
|
const url = ctx.accountConfig?.upgradeUrl || DEFAULT_UPGRADE_URL;
|
|
366
402
|
const args = ctx.args.trim();
|
|
367
403
|
const info = getUpdateInfo();
|
|
368
404
|
|
|
369
405
|
let isForce = false;
|
|
406
|
+
let isLatest = false;
|
|
370
407
|
let versionArg: string | undefined;
|
|
371
408
|
const tokens = args ? args.split(/\s+/).filter(Boolean) : [];
|
|
372
409
|
for (let i = 0; i < tokens.length; i += 1) {
|
|
@@ -375,6 +412,10 @@ registerCommand({
|
|
|
375
412
|
isForce = true;
|
|
376
413
|
continue;
|
|
377
414
|
}
|
|
415
|
+
if (t === "--latest") {
|
|
416
|
+
isLatest = true;
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
378
419
|
if (t === "--version") {
|
|
379
420
|
const next = tokens[i + 1];
|
|
380
421
|
if (!next || next.startsWith("--")) {
|
|
@@ -398,23 +439,73 @@ registerCommand({
|
|
|
398
439
|
}
|
|
399
440
|
}
|
|
400
441
|
|
|
401
|
-
|
|
442
|
+
const GITHUB_URL = "https://github.com/tencent-connect/openclaw-qqbot/";
|
|
443
|
+
|
|
444
|
+
// ── 无参数(也没有 --latest / --version / --force):只展示信息+确认按钮 ──
|
|
445
|
+
if (!versionArg && !isLatest && !isForce) {
|
|
402
446
|
if (info.checkedAt === 0) {
|
|
403
447
|
return `⏳ 版本检查中,请稍后再试`;
|
|
404
448
|
}
|
|
405
449
|
if (info.error) {
|
|
406
|
-
return
|
|
450
|
+
return `❌ 当前我的主机网络访问 GitHub 异常,查看手动升级指引:[点击查看](${url})`;
|
|
407
451
|
}
|
|
408
452
|
if (!info.hasUpdate) {
|
|
409
|
-
|
|
453
|
+
const lines = [
|
|
454
|
+
`🌟 GitHub:[tencent-connect/openclaw-qqbot](${GITHUB_URL})`,
|
|
455
|
+
`✅ 当前使用的已经是最新版本:v${PLUGIN_VERSION}`,
|
|
456
|
+
];
|
|
457
|
+
return lines.join("\n");
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// 有新版本:展示信息 + 确认按钮
|
|
461
|
+
const lines = [
|
|
462
|
+
`🌟 GitHub:[tencent-connect/openclaw-qqbot](${GITHUB_URL})`,
|
|
463
|
+
`📌 当前使用版本:v${PLUGIN_VERSION}`,
|
|
464
|
+
`🆕 最新可用版本:v${info.latest}`,
|
|
465
|
+
`📖 手动升级指引:[点击查看](${url})`,
|
|
466
|
+
`--`,
|
|
467
|
+
`**✅ 当前支持自动升级:**`,
|
|
468
|
+
`请确认机 OpenClaw 主机网络环境连接 GitHub 是否正常,插件更新会重启一次 OpenClaw 主机的 GateWay 服务,过程中会有服务不可用的状态。`,
|
|
469
|
+
`‼️点击确认 <qqbot-cmd-enter text="/bot-upgrade --latest" />`,
|
|
470
|
+
];
|
|
471
|
+
return lines.join("\n");
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// ── --version 指定版本:先校验版本号是否存在 ──
|
|
475
|
+
if (versionArg) {
|
|
476
|
+
const exists = await checkVersionExists(versionArg);
|
|
477
|
+
if (!exists) {
|
|
478
|
+
return `❌ 当前不存在 ${versionArg} 版本号,请检查 version 设置`;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// 检查是否就是当前版本
|
|
482
|
+
if (versionArg === PLUGIN_VERSION && !isForce) {
|
|
483
|
+
return `😀 当前已是 v${PLUGIN_VERSION} 版本,无需升级`;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// ── --latest:检查是否需要升级 ──
|
|
488
|
+
if (isLatest && !versionArg) {
|
|
489
|
+
if (info.checkedAt === 0) {
|
|
490
|
+
return `⏳ 版本检查中,请稍后再试`;
|
|
491
|
+
}
|
|
492
|
+
if (info.error) {
|
|
493
|
+
return `❌ 当前我的主机网络访问 GitHub 异常,查看手动升级指引:[点击查看](${url})`;
|
|
494
|
+
}
|
|
495
|
+
if (!info.hasUpdate && !isForce) {
|
|
496
|
+
return `😀 当前已是 v${PLUGIN_VERSION} 版本,无需升级`;
|
|
410
497
|
}
|
|
411
498
|
}
|
|
412
499
|
|
|
413
500
|
const targetVersion = versionArg || info.latest || undefined;
|
|
414
501
|
|
|
502
|
+
// 加锁
|
|
503
|
+
_upgrading = true;
|
|
504
|
+
|
|
415
505
|
// 异步执行升级
|
|
416
506
|
const startResult = fireHotUpgrade(targetVersion);
|
|
417
507
|
if (!startResult.ok) {
|
|
508
|
+
_upgrading = false;
|
|
418
509
|
if (startResult.reason === "no-script") {
|
|
419
510
|
return `❌ 未找到本地升级脚本\n⬆️升级指引:[点击查看](${url})`;
|
|
420
511
|
}
|
|
@@ -552,6 +643,12 @@ function collectRecentLogFiles(logDirs: string[]): LogCandidate[] {
|
|
|
552
643
|
registerCommand({
|
|
553
644
|
name: "bot-logs",
|
|
554
645
|
description: "导出本地日志文件",
|
|
646
|
+
usage: [
|
|
647
|
+
`/bot-logs`,
|
|
648
|
+
``,
|
|
649
|
+
`导出最近的 OpenClaw 日志文件(最多 4 个)。`,
|
|
650
|
+
`每个文件最多保留最后 1000 行,以文件形式返回。`,
|
|
651
|
+
].join("\n"),
|
|
555
652
|
handler: () => {
|
|
556
653
|
const logDirs = collectCandidateLogDirs();
|
|
557
654
|
const recentFiles = collectRecentLogFiles(logDirs).slice(0, 4);
|
|
@@ -628,6 +725,14 @@ export async function matchSlashCommand(ctx: SlashCommandContext): Promise<Slash
|
|
|
628
725
|
const cmd = commands.get(cmdName);
|
|
629
726
|
if (!cmd) return null; // 不是插件级指令,交给框架
|
|
630
727
|
|
|
728
|
+
// /指令 ? — 返回用法说明
|
|
729
|
+
if (args === "?") {
|
|
730
|
+
if (cmd.usage) {
|
|
731
|
+
return `📖 /${cmd.name} 用法:\n\n${cmd.usage}`;
|
|
732
|
+
}
|
|
733
|
+
return `/${cmd.name} — ${cmd.description}`;
|
|
734
|
+
}
|
|
735
|
+
|
|
631
736
|
ctx.args = args;
|
|
632
737
|
const result = await cmd.handler(ctx);
|
|
633
738
|
return result;
|
package/src/update-checker.ts
CHANGED
|
@@ -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/, "");
|