@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 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
- #### `/qqbot-ping` — Latency Test
152
+ #### `/bot-ping` — Latency Test
153
153
 
154
- > **You**: `/qqbot-ping`
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
- #### `/qqbot-version` — Version Info
162
+ #### `/bot-version` — Version Info
163
163
 
164
- > **You**: `/qqbot-version`
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
- #### `/qqbot-help` — Command List
172
+ #### `/bot-help` — Command List
173
173
 
174
- > **You**: `/qqbot-help`
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
- #### `/qqbot-upgrade` — Upgrade Guide
180
+ #### `/bot-upgrade` — Upgrade Guide
181
181
 
182
- > **You**: `/qqbot-upgrade`
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
- #### `/qqbot-logs` — Log Export
190
+ #### `/bot-logs` — Log Export
191
191
 
192
- > **You**: `/qqbot-logs`
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
- #### `/qqbot-ping` — 延迟测试
147
+ #### `/bot-ping` — 延迟测试
148
148
 
149
- > **你**:`/qqbot-ping`
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
- #### `/qqbot-version` — 版本信息
157
+ #### `/bot-version` — 版本信息
158
158
 
159
- > **你**:`/qqbot-version`
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
- #### `/qqbot-help` — 指令列表
167
+ #### `/bot-help` — 指令列表
168
168
 
169
- > **你**:`/qqbot-help`
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
- #### `/qqbot-upgrade` — 升级指引
175
+ #### `/bot-upgrade` — 升级指引
176
176
 
177
- > **你**:`/qqbot-upgrade`
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
- #### `/qqbot-logs` — 日志导出
185
+ #### `/bot-logs` — 日志导出
186
186
 
187
- > **你**:`/qqbot-logs`
187
+ > **你**:`/bot-logs`
188
188
  >
189
189
  > **QQBot**:📋 日志已打包(约 2000 行),正在发送文件… *(发送 .txt 文件)*
190
190
 
@@ -315,7 +315,7 @@ export async function startGateway(ctx) {
315
315
  log?.info(`[qqbot:${account.accountId}] ${w}`);
316
316
  }
317
317
  }
318
- // 后台版本检查(供 /qqbot-version、/qqbot-upgrade 指令被动查询)
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
- * /qqbot-ping — 测试当前 openclaw 与 QQ 连接的网络延迟
61
+ * /bot-ping — 测试当前 openclaw 与 QQ 连接的网络延迟
62
62
  */
63
63
  registerCommand({
64
- name: "qqbot-ping",
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
- * /qqbot-version — 查看插件版本号
86
+ * /bot-version — 查看插件版本号
87
87
  */
88
88
  registerCommand({
89
- name: "qqbot-version",
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="/qqbot-upgrade" show="/qqbot-upgrade"/> 查看升级指引`);
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
- * /qqbot-help — 查看所有指令以及用途
112
+ * /bot-help — 查看所有指令以及用途
113
113
  */
114
114
  registerCommand({
115
- name: "qqbot-help",
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
- * /qqbot-upgrade — 查看版本更新状态 + 升级指引
128
+ * /bot-upgrade — 查看版本更新状态 + 升级指引
129
129
  */
130
130
  registerCommand({
131
- name: "qqbot-upgrade",
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
- * /qqbot-logs — 导出本地日志文件
156
+ * /bot-logs — 导出本地日志文件
157
157
  */
158
158
  registerCommand({
159
- name: "qqbot-logs",
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, `qqbot-logs-${timestamp}.txt`);
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 {
@@ -58,7 +58,7 @@ export interface QQBotAccountConfig {
58
58
  */
59
59
  urlDirectUpload?: boolean;
60
60
  /**
61
- * /qqbot-upgrade 指令返回的升级指引网址
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(): 返回上次检查结果(供 /qqbot-version、/qqbot-help 指令使用)
6
- * - formatUpdateNotice(): 格式化更新提示文本
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(): 返回上次检查结果(供 /qqbot-version、/qqbot-help 指令使用)
6
- * - formatUpdateNotice(): 格式化更新提示文本
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 { execFile } from "node:child_process";
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
- // 获取 dist-tags,同时比较 latest 和 alpha 通道
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
- if (err) {
41
- log?.debug?.(`[qqbot:update-checker] check failed: ${err.message}`);
42
- _lastInfo = { current: CURRENT_VERSION, latest: null, hasUpdate: false, checkedAt: now, error: err.message };
43
- return;
44
- }
45
- try {
46
- const tags = JSON.parse(stdout.trim());
47
- // 当前是 prerelease alpha 通道比;正式版 latest 通道比
48
- const currentIsPrerelease = CURRENT_VERSION.includes("-");
49
- const compareTarget = currentIsPrerelease
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tencent-connect/openclaw-qqbot",
3
- "version": "1.6.2-alpha.3",
3
+ "version": "1.6.3",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -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
- npm pack "$INSTALL_SRC" --quiet 2>&1 || { echo "❌ npm pack 失败"; exit 1; }
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
- // 后台版本检查(供 /qqbot-version、/qqbot-upgrade 指令被动查询)
414
+ // 后台版本检查(供 /bot-version、/bot-upgrade 指令被动查询)
415
415
  triggerUpdateCheck(log);
416
416
 
417
417
  // 初始化 API 配置(markdown 支持)
@@ -127,10 +127,10 @@ function registerCommand(cmd: SlashCommand): void {
127
127
  // ============ 内置指令 ============
128
128
 
129
129
  /**
130
- * /qqbot-ping — 测试当前 openclaw 与 QQ 连接的网络延迟
130
+ * /bot-ping — 测试当前 openclaw 与 QQ 连接的网络延迟
131
131
  */
132
132
  registerCommand({
133
- name: "qqbot-ping",
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
- * /qqbot-version — 查看插件版本号
156
+ * /bot-version — 查看插件版本号
157
157
  */
158
158
  registerCommand({
159
- name: "qqbot-version",
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="/qqbot-upgrade" show="/qqbot-upgrade"/> 查看升级指引`);
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
- * /qqbot-help — 查看所有指令以及用途
181
+ * /bot-help — 查看所有指令以及用途
182
182
  */
183
183
  registerCommand({
184
- name: "qqbot-help",
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
- * /qqbot-upgrade — 查看版本更新状态 + 升级指引
199
+ * /bot-upgrade — 查看版本更新状态 + 升级指引
200
200
  */
201
201
  registerCommand({
202
- name: "qqbot-upgrade",
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
- * /qqbot-logs — 导出本地日志文件
228
+ * /bot-logs — 导出本地日志文件
229
229
  */
230
230
  registerCommand({
231
- name: "qqbot-logs",
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, `qqbot-logs-${timestamp}.txt`);
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
- * /qqbot-upgrade 指令返回的升级指引网址
63
+ * /bot-upgrade 指令返回的升级指引网址
64
64
  * 默认: https://doc.weixin.qq.com/doc/w3_AKEAGQaeACgCNHrh1CbHzTAKtT2gB?scode=AJEAIQdfAAozxFEnLZAKEAGQaeACg
65
65
  */
66
66
  upgradeUrl?: string;
@@ -2,16 +2,24 @@
2
2
  * 后台版本检查器
3
3
  *
4
4
  * - triggerUpdateCheck(): gateway 启动时调用,后台检查 npm registry 是否有新版本
5
- * - getUpdateInfo(): 返回上次检查结果(供 /qqbot-version、/qqbot-help 指令使用)
6
- * - formatUpdateNotice(): 格式化更新提示文本
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 { execFile } from "node:child_process";
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
- // 获取 dist-tags,同时比较 latest 和 alpha 通道
55
- execFile(
56
- "npm",
57
- ["view", PKG_NAME, "dist-tags", "--json"],
58
- { timeout: 15_000, env: { ...process.env, PATH: process.env.PATH } },
59
- (err, stdout, _stderr) => {
60
- _checking = false;
61
- const now = Date.now();
62
- if (err) {
63
- log?.debug?.(`[qqbot:update-checker] check failed: ${err.message}`);
64
- _lastInfo = { current: CURRENT_VERSION, latest: null, hasUpdate: false, checkedAt: now, error: err.message };
65
- return;
66
- }
67
- try {
68
- const tags = JSON.parse(stdout.trim());
69
- // 当前是 prerelease → 和 alpha 通道比;正式版 → 和 latest 通道比
70
- const currentIsPrerelease = CURRENT_VERSION.includes("-");
71
- const compareTarget = currentIsPrerelease
72
- ? (tags.alpha || tags.latest || null)
73
- : (tags.latest || null);
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 {