@tencent-connect/openclaw-qqbot 1.6.2-alpha.3 → 1.6.3
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/README.md +10 -10
- package/README.zh.md +10 -10
- package/dist/src/gateway.js +1 -1
- package/dist/src/slash-commands.js +12 -12
- package/dist/src/types.d.ts +1 -1
- package/dist/src/update-checker.d.ts +4 -2
- package/dist/src/update-checker.js +64 -28
- package/package.json +1 -1
- package/scripts/upgrade-via-npm.sh +12 -1
- package/src/gateway.ts +1 -1
- package/src/slash-commands.ts +12 -12
- package/src/types.ts +1 -1
- package/src/update-checker.ts +63 -35
package/README.md
CHANGED
|
@@ -149,9 +149,9 @@ AI can send videos directly. Supports local files and URLs.
|
|
|
149
149
|
|
|
150
150
|
The plugin provides built-in slash commands that are intercepted before reaching the AI queue, giving instant responses for diagnostics and management.
|
|
151
151
|
|
|
152
|
-
#### `/
|
|
152
|
+
#### `/bot-ping` — Latency Test
|
|
153
153
|
|
|
154
|
-
> **You**: `/
|
|
154
|
+
> **You**: `/bot-ping`
|
|
155
155
|
>
|
|
156
156
|
> **QQBot**: ✅ pong!⏱ Latency: 602ms (network: 602ms, plugin: 0ms)
|
|
157
157
|
|
|
@@ -159,9 +159,9 @@ Measures end-to-end latency from QQ server push to plugin response, broken down
|
|
|
159
159
|
|
|
160
160
|
<img width="360" src="docs/images/slash-ping.jpg" alt="Ping Demo" />
|
|
161
161
|
|
|
162
|
-
#### `/
|
|
162
|
+
#### `/bot-version` — Version Info
|
|
163
163
|
|
|
164
|
-
> **You**: `/
|
|
164
|
+
> **You**: `/bot-version`
|
|
165
165
|
>
|
|
166
166
|
> **QQBot**: 🦞 Framework: OpenClaw 2026.3.13 (61d171a) / 🤖 Plugin: v1.6.2 / 🌟 GitHub repo
|
|
167
167
|
|
|
@@ -169,17 +169,17 @@ Shows framework version, plugin version, and a direct link to the official repos
|
|
|
169
169
|
|
|
170
170
|
<img width="360" src="docs/images/slash-version.jpg" alt="Version Demo" />
|
|
171
171
|
|
|
172
|
-
#### `/
|
|
172
|
+
#### `/bot-help` — Command List
|
|
173
173
|
|
|
174
|
-
> **You**: `/
|
|
174
|
+
> **You**: `/bot-help`
|
|
175
175
|
>
|
|
176
176
|
> **QQBot**: Lists all available slash commands with clickable shortcuts.
|
|
177
177
|
|
|
178
178
|
<img width="360" src="docs/images/slash-help.jpg" alt="Help Demo" />
|
|
179
179
|
|
|
180
|
-
#### `/
|
|
180
|
+
#### `/bot-upgrade` — Upgrade Guide
|
|
181
181
|
|
|
182
|
-
> **You**: `/
|
|
182
|
+
> **You**: `/bot-upgrade`
|
|
183
183
|
>
|
|
184
184
|
> **QQBot**: 📌 Current version / ✅ Up to date / ⬆️ Upgrade guide / 🌟 GitHub repo
|
|
185
185
|
|
|
@@ -187,9 +187,9 @@ Shows current version, update status, upgrade guide link, and official repositor
|
|
|
187
187
|
|
|
188
188
|
<img width="360" src="docs/images/slash-upgrade.jpg" alt="Upgrade Demo" />
|
|
189
189
|
|
|
190
|
-
#### `/
|
|
190
|
+
#### `/bot-logs` — Log Export
|
|
191
191
|
|
|
192
|
-
> **You**: `/
|
|
192
|
+
> **You**: `/bot-logs`
|
|
193
193
|
>
|
|
194
194
|
> **QQBot**: 📋 Logs packaged (~2000 lines), sending file... *(sends a .txt file)*
|
|
195
195
|
|
package/README.zh.md
CHANGED
|
@@ -144,9 +144,9 @@ AI 可直接发送视频,支持本地文件和公网 URL。
|
|
|
144
144
|
|
|
145
145
|
插件内置一组斜杠指令,在消息进入 AI 队列前拦截处理,即时响应,用于诊断和管理。
|
|
146
146
|
|
|
147
|
-
#### `/
|
|
147
|
+
#### `/bot-ping` — 延迟测试
|
|
148
148
|
|
|
149
|
-
> **你**:`/
|
|
149
|
+
> **你**:`/bot-ping`
|
|
150
150
|
>
|
|
151
151
|
> **QQBot**:✅ pong!⏱ 延迟: 602ms(网络传输: 602ms,插件处理: 0ms)
|
|
152
152
|
|
|
@@ -154,9 +154,9 @@ AI 可直接发送视频,支持本地文件和公网 URL。
|
|
|
154
154
|
|
|
155
155
|
<img width="360" src="docs/images/slash-ping.jpg" alt="Ping 演示" />
|
|
156
156
|
|
|
157
|
-
#### `/
|
|
157
|
+
#### `/bot-version` — 版本信息
|
|
158
158
|
|
|
159
|
-
> **你**:`/
|
|
159
|
+
> **你**:`/bot-version`
|
|
160
160
|
>
|
|
161
161
|
> **QQBot**:🦞框架版本:OpenClaw 2026.3.13 (61d171a) / 🤖QQBot 插件版本:v1.6.2 / 🌟官方 GitHub 仓库
|
|
162
162
|
|
|
@@ -164,17 +164,17 @@ AI 可直接发送视频,支持本地文件和公网 URL。
|
|
|
164
164
|
|
|
165
165
|
<img width="360" src="docs/images/slash-version.jpg" alt="Version 演示" />
|
|
166
166
|
|
|
167
|
-
#### `/
|
|
167
|
+
#### `/bot-help` — 指令列表
|
|
168
168
|
|
|
169
|
-
> **你**:`/
|
|
169
|
+
> **你**:`/bot-help`
|
|
170
170
|
>
|
|
171
171
|
> **QQBot**:列出所有可用的斜杠指令及说明,指令可点击快速输入。
|
|
172
172
|
|
|
173
173
|
<img width="360" src="docs/images/slash-help.jpg" alt="Help 演示" />
|
|
174
174
|
|
|
175
|
-
#### `/
|
|
175
|
+
#### `/bot-upgrade` — 升级指引
|
|
176
176
|
|
|
177
|
-
> **你**:`/
|
|
177
|
+
> **你**:`/bot-upgrade`
|
|
178
178
|
>
|
|
179
179
|
> **QQBot**:📌当前版本 / ✅当前已是最新版本 / ⬆️升级指引 / 🌟官方 GitHub 仓库
|
|
180
180
|
|
|
@@ -182,9 +182,9 @@ AI 可直接发送视频,支持本地文件和公网 URL。
|
|
|
182
182
|
|
|
183
183
|
<img width="360" src="docs/images/slash-upgrade.jpg" alt="Upgrade 演示" />
|
|
184
184
|
|
|
185
|
-
#### `/
|
|
185
|
+
#### `/bot-logs` — 日志导出
|
|
186
186
|
|
|
187
|
-
> **你**:`/
|
|
187
|
+
> **你**:`/bot-logs`
|
|
188
188
|
>
|
|
189
189
|
> **QQBot**:📋 日志已打包(约 2000 行),正在发送文件… *(发送 .txt 文件)*
|
|
190
190
|
|
package/dist/src/gateway.js
CHANGED
|
@@ -315,7 +315,7 @@ export async function startGateway(ctx) {
|
|
|
315
315
|
log?.info(`[qqbot:${account.accountId}] ${w}`);
|
|
316
316
|
}
|
|
317
317
|
}
|
|
318
|
-
// 后台版本检查(供 /
|
|
318
|
+
// 后台版本检查(供 /bot-version、/bot-upgrade 指令被动查询)
|
|
319
319
|
triggerUpdateCheck(log);
|
|
320
320
|
// 初始化 API 配置(markdown 支持)
|
|
321
321
|
initApiConfig({
|
|
@@ -58,10 +58,10 @@ function registerCommand(cmd) {
|
|
|
58
58
|
}
|
|
59
59
|
// ============ 内置指令 ============
|
|
60
60
|
/**
|
|
61
|
-
* /
|
|
61
|
+
* /bot-ping — 测试当前 openclaw 与 QQ 连接的网络延迟
|
|
62
62
|
*/
|
|
63
63
|
registerCommand({
|
|
64
|
-
name: "
|
|
64
|
+
name: "bot-ping",
|
|
65
65
|
description: "测试当前 openclaw 与 QQ 连接的网络延迟",
|
|
66
66
|
handler: (ctx) => {
|
|
67
67
|
const now = Date.now();
|
|
@@ -83,10 +83,10 @@ registerCommand({
|
|
|
83
83
|
},
|
|
84
84
|
});
|
|
85
85
|
/**
|
|
86
|
-
* /
|
|
86
|
+
* /bot-version — 查看插件版本号
|
|
87
87
|
*/
|
|
88
88
|
registerCommand({
|
|
89
|
-
name: "
|
|
89
|
+
name: "bot-version",
|
|
90
90
|
description: "查看插件版本号",
|
|
91
91
|
handler: () => {
|
|
92
92
|
const frameworkVersion = getFrameworkVersion();
|
|
@@ -102,17 +102,17 @@ registerCommand({
|
|
|
102
102
|
lines.push(`⚠️ 版本检查失败`);
|
|
103
103
|
}
|
|
104
104
|
else if (info.hasUpdate && info.latest) {
|
|
105
|
-
lines.push(`🆕最新可用版本:v${info.latest},点击 <qqbot-cmd-input text="/
|
|
105
|
+
lines.push(`🆕最新可用版本:v${info.latest},点击 <qqbot-cmd-input text="/bot-upgrade" show="/bot-upgrade"/> 查看升级指引`);
|
|
106
106
|
}
|
|
107
107
|
lines.push(`🌟官方 GitHub 仓库:[点击前往](https://github.com/tencent-connect/openclaw-qqbot/)`);
|
|
108
108
|
return lines.join("\n");
|
|
109
109
|
},
|
|
110
110
|
});
|
|
111
111
|
/**
|
|
112
|
-
* /
|
|
112
|
+
* /bot-help — 查看所有指令以及用途
|
|
113
113
|
*/
|
|
114
114
|
registerCommand({
|
|
115
|
-
name: "
|
|
115
|
+
name: "bot-help",
|
|
116
116
|
description: "查看所有指令以及用途",
|
|
117
117
|
handler: () => {
|
|
118
118
|
const lines = [`### QQBot插件内置调试指令`, ``];
|
|
@@ -125,10 +125,10 @@ registerCommand({
|
|
|
125
125
|
});
|
|
126
126
|
const DEFAULT_UPGRADE_URL = "https://doc.weixin.qq.com/doc/w3_AKEAGQaeACgCNHrh1CbHzTAKtT2gB?scode=AJEAIQdfAAozxFEnLZAKEAGQaeACg";
|
|
127
127
|
/**
|
|
128
|
-
* /
|
|
128
|
+
* /bot-upgrade — 查看版本更新状态 + 升级指引
|
|
129
129
|
*/
|
|
130
130
|
registerCommand({
|
|
131
|
-
name: "
|
|
131
|
+
name: "bot-upgrade",
|
|
132
132
|
description: "查看版本更新与升级指引",
|
|
133
133
|
handler: (ctx) => {
|
|
134
134
|
const url = ctx.accountConfig?.upgradeUrl || DEFAULT_UPGRADE_URL;
|
|
@@ -153,10 +153,10 @@ registerCommand({
|
|
|
153
153
|
},
|
|
154
154
|
});
|
|
155
155
|
/**
|
|
156
|
-
* /
|
|
156
|
+
* /bot-logs — 导出本地日志文件
|
|
157
157
|
*/
|
|
158
158
|
registerCommand({
|
|
159
|
-
name: "
|
|
159
|
+
name: "bot-logs",
|
|
160
160
|
description: "导出本地日志文件",
|
|
161
161
|
handler: () => {
|
|
162
162
|
const homeDir = process.env.HOME || "~";
|
|
@@ -190,7 +190,7 @@ registerCommand({
|
|
|
190
190
|
fs.mkdirSync(tmpDir, { recursive: true });
|
|
191
191
|
}
|
|
192
192
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
193
|
-
const tmpFile = path.join(tmpDir, `
|
|
193
|
+
const tmpFile = path.join(tmpDir, `bot-logs-${timestamp}.txt`);
|
|
194
194
|
fs.writeFileSync(tmpFile, lines.join("\n"), "utf8");
|
|
195
195
|
const totalLines = lines.filter(l => !l.startsWith("=")).length;
|
|
196
196
|
return {
|
package/dist/src/types.d.ts
CHANGED
|
@@ -58,7 +58,7 @@ export interface QQBotAccountConfig {
|
|
|
58
58
|
*/
|
|
59
59
|
urlDirectUpload?: boolean;
|
|
60
60
|
/**
|
|
61
|
-
* /
|
|
61
|
+
* /bot-upgrade 指令返回的升级指引网址
|
|
62
62
|
* 默认: https://doc.weixin.qq.com/doc/w3_AKEAGQaeACgCNHrh1CbHzTAKtT2gB?scode=AJEAIQdfAAozxFEnLZAKEAGQaeACg
|
|
63
63
|
*/
|
|
64
64
|
upgradeUrl?: string;
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
* 后台版本检查器
|
|
3
3
|
*
|
|
4
4
|
* - triggerUpdateCheck(): gateway 启动时调用,后台检查 npm registry 是否有新版本
|
|
5
|
-
* - getUpdateInfo(): 返回上次检查结果(供 /
|
|
6
|
-
*
|
|
5
|
+
* - getUpdateInfo(): 返回上次检查结果(供 /bot-version、/bot-help 指令使用)
|
|
6
|
+
*
|
|
7
|
+
* 使用 HTTPS 直接请求 npm registry API(不依赖 npm CLI),
|
|
8
|
+
* 支持多 registry fallback:npmjs.org → npmmirror.com,解决国内网络问题。
|
|
7
9
|
*/
|
|
8
10
|
export interface UpdateInfo {
|
|
9
11
|
current: string;
|
|
@@ -2,13 +2,20 @@
|
|
|
2
2
|
* 后台版本检查器
|
|
3
3
|
*
|
|
4
4
|
* - triggerUpdateCheck(): gateway 启动时调用,后台检查 npm registry 是否有新版本
|
|
5
|
-
* - getUpdateInfo(): 返回上次检查结果(供 /
|
|
6
|
-
*
|
|
5
|
+
* - getUpdateInfo(): 返回上次检查结果(供 /bot-version、/bot-help 指令使用)
|
|
6
|
+
*
|
|
7
|
+
* 使用 HTTPS 直接请求 npm registry API(不依赖 npm CLI),
|
|
8
|
+
* 支持多 registry fallback:npmjs.org → npmmirror.com,解决国内网络问题。
|
|
7
9
|
*/
|
|
8
10
|
import { createRequire } from "node:module";
|
|
9
|
-
import
|
|
11
|
+
import https from "node:https";
|
|
10
12
|
const require = createRequire(import.meta.url);
|
|
11
13
|
const PKG_NAME = "@tencent-connect/openclaw-qqbot";
|
|
14
|
+
const ENCODED_PKG = encodeURIComponent(PKG_NAME);
|
|
15
|
+
const REGISTRIES = [
|
|
16
|
+
`https://registry.npmjs.org/${ENCODED_PKG}`,
|
|
17
|
+
`https://registry.npmmirror.com/${ENCODED_PKG}`,
|
|
18
|
+
];
|
|
12
19
|
let CURRENT_VERSION = "unknown";
|
|
13
20
|
try {
|
|
14
21
|
const pkg = require("../package.json");
|
|
@@ -24,6 +31,43 @@ let _lastInfo = {
|
|
|
24
31
|
checkedAt: 0,
|
|
25
32
|
};
|
|
26
33
|
let _checking = false;
|
|
34
|
+
function fetchJson(url, timeoutMs) {
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
const req = https.get(url, { timeout: timeoutMs, headers: { Accept: "application/json" } }, (res) => {
|
|
37
|
+
if (res.statusCode !== 200) {
|
|
38
|
+
res.resume();
|
|
39
|
+
reject(new Error(`HTTP ${res.statusCode} from ${url}`));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
let data = "";
|
|
43
|
+
res.on("data", (chunk) => { data += chunk; });
|
|
44
|
+
res.on("end", () => {
|
|
45
|
+
try {
|
|
46
|
+
resolve(JSON.parse(data));
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
reject(e);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
req.on("error", reject);
|
|
54
|
+
req.on("timeout", () => { req.destroy(); reject(new Error(`timeout fetching ${url}`)); });
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async function fetchDistTags(log) {
|
|
58
|
+
for (const url of REGISTRIES) {
|
|
59
|
+
try {
|
|
60
|
+
const json = await fetchJson(url, 10_000);
|
|
61
|
+
const tags = json["dist-tags"];
|
|
62
|
+
if (tags && typeof tags === "object")
|
|
63
|
+
return tags;
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
log?.debug?.(`[qqbot:update-checker] ${url} failed: ${e.message}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
throw new Error("all registries failed");
|
|
70
|
+
}
|
|
27
71
|
export function triggerUpdateCheck(log) {
|
|
28
72
|
if (_checking)
|
|
29
73
|
return;
|
|
@@ -33,33 +77,25 @@ export function triggerUpdateCheck(log) {
|
|
|
33
77
|
}
|
|
34
78
|
_checking = true;
|
|
35
79
|
log?.debug?.(`[qqbot:update-checker] checking (current: ${CURRENT_VERSION})...`);
|
|
36
|
-
|
|
37
|
-
execFile("npm", ["view", PKG_NAME, "dist-tags", "--json"], { timeout: 15_000, env: { ...process.env, PATH: process.env.PATH } }, (err, stdout, _stderr) => {
|
|
38
|
-
_checking = false;
|
|
80
|
+
fetchDistTags(log).then((tags) => {
|
|
39
81
|
const now = Date.now();
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
? (tags.alpha || tags.latest || null)
|
|
51
|
-
: (tags.latest || null);
|
|
52
|
-
const hasUpdate = typeof compareTarget === "string"
|
|
53
|
-
&& compareTarget !== CURRENT_VERSION
|
|
54
|
-
&& compareVersions(compareTarget, CURRENT_VERSION) > 0;
|
|
55
|
-
_lastInfo = { current: CURRENT_VERSION, latest: compareTarget, hasUpdate, checkedAt: now };
|
|
56
|
-
if (hasUpdate) {
|
|
57
|
-
log?.info?.(`[qqbot:update-checker] new version available: ${compareTarget} (current: ${CURRENT_VERSION})`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
catch (parseErr) {
|
|
61
|
-
_lastInfo = { current: CURRENT_VERSION, latest: null, hasUpdate: false, checkedAt: now, error: String(parseErr) };
|
|
82
|
+
const currentIsPrerelease = CURRENT_VERSION.includes("-");
|
|
83
|
+
const compareTarget = currentIsPrerelease
|
|
84
|
+
? (tags.alpha || tags.latest || null)
|
|
85
|
+
: (tags.latest || null);
|
|
86
|
+
const hasUpdate = typeof compareTarget === "string"
|
|
87
|
+
&& compareTarget !== CURRENT_VERSION
|
|
88
|
+
&& compareVersions(compareTarget, CURRENT_VERSION) > 0;
|
|
89
|
+
_lastInfo = { current: CURRENT_VERSION, latest: compareTarget, hasUpdate, checkedAt: now };
|
|
90
|
+
if (hasUpdate) {
|
|
91
|
+
log?.info?.(`[qqbot:update-checker] new version available: ${compareTarget} (current: ${CURRENT_VERSION})`);
|
|
62
92
|
}
|
|
93
|
+
}).catch((err) => {
|
|
94
|
+
const now = Date.now();
|
|
95
|
+
log?.debug?.(`[qqbot:update-checker] check failed: ${err.message}`);
|
|
96
|
+
_lastInfo = { current: CURRENT_VERSION, latest: null, hasUpdate: false, checkedAt: now, error: err.message };
|
|
97
|
+
}).finally(() => {
|
|
98
|
+
_checking = false;
|
|
63
99
|
});
|
|
64
100
|
}
|
|
65
101
|
export function getUpdateInfo() {
|
package/package.json
CHANGED
|
@@ -115,7 +115,18 @@ EXTRACT_DIR=$(mktemp -d)
|
|
|
115
115
|
trap "rm -rf '$TMPDIR_PACK' '$EXTRACT_DIR'" EXIT
|
|
116
116
|
|
|
117
117
|
cd "$TMPDIR_PACK"
|
|
118
|
-
|
|
118
|
+
# 多 registry fallback:npmjs.org → npmmirror(国内镜像)→ 默认 registry
|
|
119
|
+
PACK_OK=false
|
|
120
|
+
for _registry in "https://registry.npmjs.org/" "https://registry.npmmirror.com/" ""; do
|
|
121
|
+
if [ -n "$_registry" ]; then
|
|
122
|
+
echo " 尝试 registry: $_registry"
|
|
123
|
+
npm pack "$INSTALL_SRC" --registry "$_registry" --quiet 2>&1 && PACK_OK=true && break
|
|
124
|
+
else
|
|
125
|
+
echo " 尝试默认 registry..."
|
|
126
|
+
npm pack "$INSTALL_SRC" --quiet 2>&1 && PACK_OK=true && break
|
|
127
|
+
fi
|
|
128
|
+
done
|
|
129
|
+
$PACK_OK || { echo "❌ npm pack 失败(所有 registry 均不可用)"; exit 1; }
|
|
119
130
|
TGZ_FILE=$(ls -1 *.tgz 2>/dev/null | head -1)
|
|
120
131
|
[ -z "$TGZ_FILE" ] && echo "❌ 未找到下载的 tgz 文件" && exit 1
|
|
121
132
|
echo " 已下载: $TGZ_FILE"
|
package/src/gateway.ts
CHANGED
|
@@ -411,7 +411,7 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
|
|
411
411
|
}
|
|
412
412
|
}
|
|
413
413
|
|
|
414
|
-
// 后台版本检查(供 /
|
|
414
|
+
// 后台版本检查(供 /bot-version、/bot-upgrade 指令被动查询)
|
|
415
415
|
triggerUpdateCheck(log);
|
|
416
416
|
|
|
417
417
|
// 初始化 API 配置(markdown 支持)
|
package/src/slash-commands.ts
CHANGED
|
@@ -127,10 +127,10 @@ function registerCommand(cmd: SlashCommand): void {
|
|
|
127
127
|
// ============ 内置指令 ============
|
|
128
128
|
|
|
129
129
|
/**
|
|
130
|
-
* /
|
|
130
|
+
* /bot-ping — 测试当前 openclaw 与 QQ 连接的网络延迟
|
|
131
131
|
*/
|
|
132
132
|
registerCommand({
|
|
133
|
-
name: "
|
|
133
|
+
name: "bot-ping",
|
|
134
134
|
description: "测试当前 openclaw 与 QQ 连接的网络延迟",
|
|
135
135
|
handler: (ctx) => {
|
|
136
136
|
const now = Date.now();
|
|
@@ -153,10 +153,10 @@ registerCommand({
|
|
|
153
153
|
});
|
|
154
154
|
|
|
155
155
|
/**
|
|
156
|
-
* /
|
|
156
|
+
* /bot-version — 查看插件版本号
|
|
157
157
|
*/
|
|
158
158
|
registerCommand({
|
|
159
|
-
name: "
|
|
159
|
+
name: "bot-version",
|
|
160
160
|
description: "查看插件版本号",
|
|
161
161
|
handler: () => {
|
|
162
162
|
const frameworkVersion = getFrameworkVersion();
|
|
@@ -170,7 +170,7 @@ registerCommand({
|
|
|
170
170
|
} else if (info.error) {
|
|
171
171
|
lines.push(`⚠️ 版本检查失败`);
|
|
172
172
|
} else if (info.hasUpdate && info.latest) {
|
|
173
|
-
lines.push(`🆕最新可用版本:v${info.latest},点击 <qqbot-cmd-input text="/
|
|
173
|
+
lines.push(`🆕最新可用版本:v${info.latest},点击 <qqbot-cmd-input text="/bot-upgrade" show="/bot-upgrade"/> 查看升级指引`);
|
|
174
174
|
}
|
|
175
175
|
lines.push(`🌟官方 GitHub 仓库:[点击前往](https://github.com/tencent-connect/openclaw-qqbot/)`);
|
|
176
176
|
return lines.join("\n");
|
|
@@ -178,10 +178,10 @@ registerCommand({
|
|
|
178
178
|
});
|
|
179
179
|
|
|
180
180
|
/**
|
|
181
|
-
* /
|
|
181
|
+
* /bot-help — 查看所有指令以及用途
|
|
182
182
|
*/
|
|
183
183
|
registerCommand({
|
|
184
|
-
name: "
|
|
184
|
+
name: "bot-help",
|
|
185
185
|
description: "查看所有指令以及用途",
|
|
186
186
|
handler: () => {
|
|
187
187
|
const lines = [`### QQBot插件内置调试指令`, ``];
|
|
@@ -196,10 +196,10 @@ registerCommand({
|
|
|
196
196
|
const DEFAULT_UPGRADE_URL = "https://doc.weixin.qq.com/doc/w3_AKEAGQaeACgCNHrh1CbHzTAKtT2gB?scode=AJEAIQdfAAozxFEnLZAKEAGQaeACg";
|
|
197
197
|
|
|
198
198
|
/**
|
|
199
|
-
* /
|
|
199
|
+
* /bot-upgrade — 查看版本更新状态 + 升级指引
|
|
200
200
|
*/
|
|
201
201
|
registerCommand({
|
|
202
|
-
name: "
|
|
202
|
+
name: "bot-upgrade",
|
|
203
203
|
description: "查看版本更新与升级指引",
|
|
204
204
|
handler: (ctx) => {
|
|
205
205
|
const url = ctx.accountConfig?.upgradeUrl || DEFAULT_UPGRADE_URL;
|
|
@@ -225,10 +225,10 @@ registerCommand({
|
|
|
225
225
|
});
|
|
226
226
|
|
|
227
227
|
/**
|
|
228
|
-
* /
|
|
228
|
+
* /bot-logs — 导出本地日志文件
|
|
229
229
|
*/
|
|
230
230
|
registerCommand({
|
|
231
|
-
name: "
|
|
231
|
+
name: "bot-logs",
|
|
232
232
|
description: "导出本地日志文件",
|
|
233
233
|
handler: () => {
|
|
234
234
|
const homeDir = process.env.HOME || "~";
|
|
@@ -264,7 +264,7 @@ registerCommand({
|
|
|
264
264
|
fs.mkdirSync(tmpDir, { recursive: true });
|
|
265
265
|
}
|
|
266
266
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
267
|
-
const tmpFile = path.join(tmpDir, `
|
|
267
|
+
const tmpFile = path.join(tmpDir, `bot-logs-${timestamp}.txt`);
|
|
268
268
|
fs.writeFileSync(tmpFile, lines.join("\n"), "utf8");
|
|
269
269
|
|
|
270
270
|
const totalLines = lines.filter(l => !l.startsWith("=")).length;
|
package/src/types.ts
CHANGED
|
@@ -60,7 +60,7 @@ export interface QQBotAccountConfig {
|
|
|
60
60
|
*/
|
|
61
61
|
urlDirectUpload?: boolean;
|
|
62
62
|
/**
|
|
63
|
-
* /
|
|
63
|
+
* /bot-upgrade 指令返回的升级指引网址
|
|
64
64
|
* 默认: https://doc.weixin.qq.com/doc/w3_AKEAGQaeACgCNHrh1CbHzTAKtT2gB?scode=AJEAIQdfAAozxFEnLZAKEAGQaeACg
|
|
65
65
|
*/
|
|
66
66
|
upgradeUrl?: string;
|
package/src/update-checker.ts
CHANGED
|
@@ -2,16 +2,24 @@
|
|
|
2
2
|
* 后台版本检查器
|
|
3
3
|
*
|
|
4
4
|
* - triggerUpdateCheck(): gateway 启动时调用,后台检查 npm registry 是否有新版本
|
|
5
|
-
* - getUpdateInfo(): 返回上次检查结果(供 /
|
|
6
|
-
*
|
|
5
|
+
* - getUpdateInfo(): 返回上次检查结果(供 /bot-version、/bot-help 指令使用)
|
|
6
|
+
*
|
|
7
|
+
* 使用 HTTPS 直接请求 npm registry API(不依赖 npm CLI),
|
|
8
|
+
* 支持多 registry fallback:npmjs.org → npmmirror.com,解决国内网络问题。
|
|
7
9
|
*/
|
|
8
10
|
|
|
9
11
|
import { createRequire } from "node:module";
|
|
10
|
-
import
|
|
12
|
+
import https from "node:https";
|
|
11
13
|
|
|
12
14
|
const require = createRequire(import.meta.url);
|
|
13
15
|
|
|
14
16
|
const PKG_NAME = "@tencent-connect/openclaw-qqbot";
|
|
17
|
+
const ENCODED_PKG = encodeURIComponent(PKG_NAME);
|
|
18
|
+
|
|
19
|
+
const REGISTRIES = [
|
|
20
|
+
`https://registry.npmjs.org/${ENCODED_PKG}`,
|
|
21
|
+
`https://registry.npmmirror.com/${ENCODED_PKG}`,
|
|
22
|
+
];
|
|
15
23
|
|
|
16
24
|
let CURRENT_VERSION = "unknown";
|
|
17
25
|
try {
|
|
@@ -38,6 +46,38 @@ let _lastInfo: UpdateInfo = {
|
|
|
38
46
|
|
|
39
47
|
let _checking = false;
|
|
40
48
|
|
|
49
|
+
function fetchJson(url: string, timeoutMs: number): Promise<any> {
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
const req = https.get(url, { timeout: timeoutMs, headers: { Accept: "application/json" } }, (res) => {
|
|
52
|
+
if (res.statusCode !== 200) {
|
|
53
|
+
res.resume();
|
|
54
|
+
reject(new Error(`HTTP ${res.statusCode} from ${url}`));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
let data = "";
|
|
58
|
+
res.on("data", (chunk: string) => { data += chunk; });
|
|
59
|
+
res.on("end", () => {
|
|
60
|
+
try { resolve(JSON.parse(data)); } catch (e) { reject(e); }
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
req.on("error", reject);
|
|
64
|
+
req.on("timeout", () => { req.destroy(); reject(new Error(`timeout fetching ${url}`)); });
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function fetchDistTags(log?: { debug?: (msg: string) => void }): Promise<Record<string, string>> {
|
|
69
|
+
for (const url of REGISTRIES) {
|
|
70
|
+
try {
|
|
71
|
+
const json = await fetchJson(url, 10_000);
|
|
72
|
+
const tags = json["dist-tags"];
|
|
73
|
+
if (tags && typeof tags === "object") return tags;
|
|
74
|
+
} catch (e: any) {
|
|
75
|
+
log?.debug?.(`[qqbot:update-checker] ${url} failed: ${e.message}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
throw new Error("all registries failed");
|
|
79
|
+
}
|
|
80
|
+
|
|
41
81
|
export function triggerUpdateCheck(log?: {
|
|
42
82
|
info: (msg: string) => void;
|
|
43
83
|
error: (msg: string) => void;
|
|
@@ -51,38 +91,26 @@ export function triggerUpdateCheck(log?: {
|
|
|
51
91
|
_checking = true;
|
|
52
92
|
log?.debug?.(`[qqbot:update-checker] checking (current: ${CURRENT_VERSION})...`);
|
|
53
93
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const hasUpdate = typeof compareTarget === "string"
|
|
75
|
-
&& compareTarget !== CURRENT_VERSION
|
|
76
|
-
&& compareVersions(compareTarget, CURRENT_VERSION) > 0;
|
|
77
|
-
_lastInfo = { current: CURRENT_VERSION, latest: compareTarget, hasUpdate, checkedAt: now };
|
|
78
|
-
if (hasUpdate) {
|
|
79
|
-
log?.info?.(`[qqbot:update-checker] new version available: ${compareTarget} (current: ${CURRENT_VERSION})`);
|
|
80
|
-
}
|
|
81
|
-
} catch (parseErr) {
|
|
82
|
-
_lastInfo = { current: CURRENT_VERSION, latest: null, hasUpdate: false, checkedAt: now, error: String(parseErr) };
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
);
|
|
94
|
+
fetchDistTags(log).then((tags) => {
|
|
95
|
+
const now = Date.now();
|
|
96
|
+
const currentIsPrerelease = CURRENT_VERSION.includes("-");
|
|
97
|
+
const compareTarget = currentIsPrerelease
|
|
98
|
+
? (tags.alpha || tags.latest || null)
|
|
99
|
+
: (tags.latest || null);
|
|
100
|
+
const hasUpdate = typeof compareTarget === "string"
|
|
101
|
+
&& compareTarget !== CURRENT_VERSION
|
|
102
|
+
&& compareVersions(compareTarget, CURRENT_VERSION) > 0;
|
|
103
|
+
_lastInfo = { current: CURRENT_VERSION, latest: compareTarget, hasUpdate, checkedAt: now };
|
|
104
|
+
if (hasUpdate) {
|
|
105
|
+
log?.info?.(`[qqbot:update-checker] new version available: ${compareTarget} (current: ${CURRENT_VERSION})`);
|
|
106
|
+
}
|
|
107
|
+
}).catch((err) => {
|
|
108
|
+
const now = Date.now();
|
|
109
|
+
log?.debug?.(`[qqbot:update-checker] check failed: ${err.message}`);
|
|
110
|
+
_lastInfo = { current: CURRENT_VERSION, latest: null, hasUpdate: false, checkedAt: now, error: err.message };
|
|
111
|
+
}).finally(() => {
|
|
112
|
+
_checking = false;
|
|
113
|
+
});
|
|
86
114
|
}
|
|
87
115
|
|
|
88
116
|
export function getUpdateInfo(): UpdateInfo {
|