@tencent-connect/openclaw-qqbot 1.6.2-alpha.3 → 1.6.3-alpha.channel
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 +11 -11
- package/README.zh.md +11 -11
- package/dist/src/gateway.js +16 -14
- package/dist/src/slash-commands.js +251 -21
- package/dist/src/tools/channel.d.ts +16 -0
- package/dist/src/tools/channel.js +234 -0
- package/dist/src/types.d.ts +7 -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/test-sendmedia.ts +116 -0
- package/scripts/upgrade-via-npm.sh +29 -7
- package/src/gateway.ts +15 -15
- package/src/slash-commands.ts +273 -21
- package/src/types.ts +7 -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,27 +159,27 @@ 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
|
-
> **QQBot**: 🦞 Framework: OpenClaw 2026.3.13 (61d171a) / 🤖 Plugin: v1.6.
|
|
166
|
+
> **QQBot**: 🦞 Framework: OpenClaw 2026.3.13 (61d171a) / 🤖 Plugin: v1.6.3 / 🌟 GitHub repo
|
|
167
167
|
|
|
168
168
|
Shows framework version, plugin version, and a direct link to the official repository.
|
|
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,27 +154,27 @@ 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
|
-
> **QQBot**:🦞框架版本:OpenClaw 2026.3.13 (61d171a) / 🤖QQBot 插件版本:v1.6.
|
|
161
|
+
> **QQBot**:🦞框架版本:OpenClaw 2026.3.13 (61d171a) / 🤖QQBot 插件版本:v1.6.3 / 🌟官方 GitHub 仓库
|
|
162
162
|
|
|
163
163
|
一目了然查看框架版本、插件版本,并可直接跳转官方仓库。
|
|
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({
|
|
@@ -438,21 +438,23 @@ export async function startGateway(ctx) {
|
|
|
438
438
|
const greeting = getStartupGreeting();
|
|
439
439
|
if (!greeting) {
|
|
440
440
|
log?.info(`[qqbot:${account.accountId}] Skipping startup greeting (debounced, trigger=${trigger})`);
|
|
441
|
-
return;
|
|
442
441
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
442
|
+
else {
|
|
443
|
+
const adminId = resolveAdminOpenId();
|
|
444
|
+
if (!adminId) {
|
|
445
|
+
log?.info(`[qqbot:${account.accountId}] Skipping startup greeting (no admin or known user)`);
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
log?.info(`[qqbot:${account.accountId}] Sending startup greeting to admin (trigger=${trigger}): "${greeting}"`);
|
|
449
|
+
const token = await getAccessToken(account.appId, account.clientSecret);
|
|
450
|
+
const GREETING_TIMEOUT_MS = 10_000;
|
|
451
|
+
await Promise.race([
|
|
452
|
+
sendProactiveC2CMessage(token, adminId, greeting),
|
|
453
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Startup greeting send timeout (10s)")), GREETING_TIMEOUT_MS)),
|
|
454
|
+
]);
|
|
455
|
+
log?.info(`[qqbot:${account.accountId}] Sent startup greeting to admin: ${adminId}`);
|
|
456
|
+
}
|
|
447
457
|
}
|
|
448
|
-
log?.info(`[qqbot:${account.accountId}] Sending startup greeting to admin (trigger=${trigger}): "${greeting}"`);
|
|
449
|
-
const token = await getAccessToken(account.appId, account.clientSecret);
|
|
450
|
-
const GREETING_TIMEOUT_MS = 10_000;
|
|
451
|
-
await Promise.race([
|
|
452
|
-
sendProactiveC2CMessage(token, adminId, greeting),
|
|
453
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error("Startup greeting send timeout (10s)")), GREETING_TIMEOUT_MS)),
|
|
454
|
-
]);
|
|
455
|
-
log?.info(`[qqbot:${account.accountId}] Sent startup greeting to admin: ${adminId}`);
|
|
456
458
|
}
|
|
457
459
|
catch (err) {
|
|
458
460
|
log?.error(`[qqbot:${account.accountId}] Failed to send startup greeting: ${err}`);
|
|
@@ -11,10 +11,12 @@
|
|
|
11
11
|
* 从而计算「开平→插件」和「插件处理」两段耗时
|
|
12
12
|
*/
|
|
13
13
|
import { createRequire } from "node:module";
|
|
14
|
-
import { execFileSync } from "node:child_process";
|
|
14
|
+
import { execFileSync, execFile } from "node:child_process";
|
|
15
15
|
import path from "node:path";
|
|
16
16
|
import fs from "node:fs";
|
|
17
17
|
import { getUpdateInfo } from "./update-checker.js";
|
|
18
|
+
import { getHomeDir, isWindows } from "./utils/platform.js";
|
|
19
|
+
import { fileURLToPath } from "node:url";
|
|
18
20
|
const require = createRequire(import.meta.url);
|
|
19
21
|
// 读取 package.json 中的版本号
|
|
20
22
|
let PLUGIN_VERSION = "unknown";
|
|
@@ -58,10 +60,10 @@ function registerCommand(cmd) {
|
|
|
58
60
|
}
|
|
59
61
|
// ============ 内置指令 ============
|
|
60
62
|
/**
|
|
61
|
-
* /
|
|
63
|
+
* /bot-ping — 测试当前 openclaw 与 QQ 连接的网络延迟
|
|
62
64
|
*/
|
|
63
65
|
registerCommand({
|
|
64
|
-
name: "
|
|
66
|
+
name: "bot-ping",
|
|
65
67
|
description: "测试当前 openclaw 与 QQ 连接的网络延迟",
|
|
66
68
|
handler: (ctx) => {
|
|
67
69
|
const now = Date.now();
|
|
@@ -83,10 +85,10 @@ registerCommand({
|
|
|
83
85
|
},
|
|
84
86
|
});
|
|
85
87
|
/**
|
|
86
|
-
* /
|
|
88
|
+
* /bot-version — 查看插件版本号
|
|
87
89
|
*/
|
|
88
90
|
registerCommand({
|
|
89
|
-
name: "
|
|
91
|
+
name: "bot-version",
|
|
90
92
|
description: "查看插件版本号",
|
|
91
93
|
handler: () => {
|
|
92
94
|
const frameworkVersion = getFrameworkVersion();
|
|
@@ -102,17 +104,17 @@ registerCommand({
|
|
|
102
104
|
lines.push(`⚠️ 版本检查失败`);
|
|
103
105
|
}
|
|
104
106
|
else if (info.hasUpdate && info.latest) {
|
|
105
|
-
lines.push(`🆕最新可用版本:v${info.latest},点击 <qqbot-cmd-input text="/
|
|
107
|
+
lines.push(`🆕最新可用版本:v${info.latest},点击 <qqbot-cmd-input text="/bot-upgrade" show="/bot-upgrade"/> 查看升级指引`);
|
|
106
108
|
}
|
|
107
109
|
lines.push(`🌟官方 GitHub 仓库:[点击前往](https://github.com/tencent-connect/openclaw-qqbot/)`);
|
|
108
110
|
return lines.join("\n");
|
|
109
111
|
},
|
|
110
112
|
});
|
|
111
113
|
/**
|
|
112
|
-
* /
|
|
114
|
+
* /bot-help — 查看所有指令以及用途
|
|
113
115
|
*/
|
|
114
116
|
registerCommand({
|
|
115
|
-
name: "
|
|
117
|
+
name: "bot-help",
|
|
116
118
|
description: "查看所有指令以及用途",
|
|
117
119
|
handler: () => {
|
|
118
120
|
const lines = [`### QQBot插件内置调试指令`, ``];
|
|
@@ -124,54 +126,282 @@ registerCommand({
|
|
|
124
126
|
},
|
|
125
127
|
});
|
|
126
128
|
const DEFAULT_UPGRADE_URL = "https://doc.weixin.qq.com/doc/w3_AKEAGQaeACgCNHrh1CbHzTAKtT2gB?scode=AJEAIQdfAAozxFEnLZAKEAGQaeACg";
|
|
129
|
+
// ============ 热更新 ============
|
|
127
130
|
/**
|
|
128
|
-
* /
|
|
131
|
+
* 找到 CLI 命令名(openclaw / clawdbot / moltbot)
|
|
132
|
+
*/
|
|
133
|
+
function findCli() {
|
|
134
|
+
const whichCmd = isWindows() ? "where" : "which";
|
|
135
|
+
for (const cli of ["openclaw", "clawdbot", "moltbot"]) {
|
|
136
|
+
try {
|
|
137
|
+
execFileSync(whichCmd, [cli], { timeout: 3000, encoding: "utf8", stdio: "pipe" });
|
|
138
|
+
return cli;
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* 找到升级脚本路径
|
|
148
|
+
*/
|
|
149
|
+
function getUpgradeScriptPath() {
|
|
150
|
+
const currentFile = fileURLToPath(import.meta.url);
|
|
151
|
+
const scriptDir = path.resolve(path.dirname(currentFile), "..", "..", "scripts");
|
|
152
|
+
const scriptPath = path.join(scriptDir, "upgrade-via-npm.sh");
|
|
153
|
+
return fs.existsSync(scriptPath) ? scriptPath : null;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* 在 Windows 上查找可用的 bash(Git Bash / WSL 等)
|
|
157
|
+
*/
|
|
158
|
+
function findBash() {
|
|
159
|
+
if (!isWindows())
|
|
160
|
+
return "bash";
|
|
161
|
+
// Git Bash 常见路径
|
|
162
|
+
const candidates = [
|
|
163
|
+
path.join(process.env.ProgramFiles || "C:\\Program Files", "Git", "bin", "bash.exe"),
|
|
164
|
+
path.join(process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)", "Git", "bin", "bash.exe"),
|
|
165
|
+
path.join(process.env.LOCALAPPDATA || "", "Programs", "Git", "bin", "bash.exe"),
|
|
166
|
+
];
|
|
167
|
+
for (const p of candidates) {
|
|
168
|
+
if (p && fs.existsSync(p))
|
|
169
|
+
return p;
|
|
170
|
+
}
|
|
171
|
+
// 尝试 PATH 中的 bash
|
|
172
|
+
try {
|
|
173
|
+
execFileSync("where", ["bash"], { timeout: 3000, encoding: "utf8", stdio: "pipe" });
|
|
174
|
+
return "bash";
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* 执行热更新:执行脚本(--no-restart) → 触发 gateway restart
|
|
182
|
+
*
|
|
183
|
+
* fire-and-forget 操作:
|
|
184
|
+
* - 异步执行升级脚本(--no-restart,只做文件替换)
|
|
185
|
+
* - 脚本完成后触发 gateway restart(当前进程会被杀掉)
|
|
186
|
+
* - 新进程启动时 getStartupGreeting() 检测到版本变更,自动通知管理员
|
|
187
|
+
*
|
|
188
|
+
* @returns true 表示已启动升级流程,false 表示无法执行(如 Windows 无 bash)
|
|
189
|
+
*/
|
|
190
|
+
function fireHotUpgrade(targetVersion) {
|
|
191
|
+
const scriptPath = getUpgradeScriptPath();
|
|
192
|
+
if (!scriptPath)
|
|
193
|
+
return false;
|
|
194
|
+
const cli = findCli();
|
|
195
|
+
if (!cli)
|
|
196
|
+
return false;
|
|
197
|
+
const bash = findBash();
|
|
198
|
+
if (!bash)
|
|
199
|
+
return false;
|
|
200
|
+
const args = ["--no-restart"];
|
|
201
|
+
if (targetVersion) {
|
|
202
|
+
args.push("--version", targetVersion);
|
|
203
|
+
}
|
|
204
|
+
// 异步执行升级脚本
|
|
205
|
+
execFile(bash, [scriptPath, ...args], {
|
|
206
|
+
timeout: 120_000,
|
|
207
|
+
env: { ...process.env },
|
|
208
|
+
...(isWindows() ? { windowsHide: true } : {}),
|
|
209
|
+
}, (error, _stdout, _stderr) => {
|
|
210
|
+
if (error) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
// 文件替换成功,触发 gateway restart
|
|
214
|
+
execFile(cli, ["gateway", "restart"], { timeout: 30_000 }, () => { });
|
|
215
|
+
});
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* /bot-upgrade — 查看版本更新状态 + 升级指引(根据 upgradeMode 决定行为)
|
|
129
220
|
*/
|
|
130
221
|
registerCommand({
|
|
131
|
-
name: "
|
|
222
|
+
name: "bot-upgrade",
|
|
132
223
|
description: "查看版本更新与升级指引",
|
|
133
224
|
handler: (ctx) => {
|
|
225
|
+
// 升级相关指令仅在私聊中可用
|
|
226
|
+
if (ctx.type !== "c2c") {
|
|
227
|
+
return `💡 请在私聊中使用此指令`;
|
|
228
|
+
}
|
|
229
|
+
const upgradeMode = ctx.accountConfig?.upgradeMode || "doc";
|
|
134
230
|
const url = ctx.accountConfig?.upgradeUrl || DEFAULT_UPGRADE_URL;
|
|
135
231
|
const info = getUpdateInfo();
|
|
136
232
|
const lines = [];
|
|
137
233
|
lines.push(`📌当前版本:v${PLUGIN_VERSION}`);
|
|
138
234
|
if (info.checkedAt === 0) {
|
|
139
235
|
lines.push(`⏳ 版本检查中,请稍后再试`);
|
|
236
|
+
return lines.join("\n");
|
|
140
237
|
}
|
|
141
238
|
else if (info.error) {
|
|
142
239
|
lines.push(`⚠️ 版本检查失败`);
|
|
240
|
+
lines.push(`⬆️升级指引:[点击查看](${url})`);
|
|
241
|
+
return lines.join("\n");
|
|
143
242
|
}
|
|
144
243
|
else if (info.hasUpdate && info.latest) {
|
|
145
244
|
lines.push(`🆕最新可用版本:v${info.latest}`);
|
|
146
245
|
}
|
|
147
246
|
else {
|
|
148
247
|
lines.push(`✅ 当前已是最新版本`);
|
|
248
|
+
return lines.join("\n");
|
|
249
|
+
}
|
|
250
|
+
// 有新版本:根据 upgradeMode 决定行为
|
|
251
|
+
if (upgradeMode === "hot-reload") {
|
|
252
|
+
const started = fireHotUpgrade(info.latest);
|
|
253
|
+
if (!started) {
|
|
254
|
+
lines.push(`⚠️ 当前环境不支持热更新(需要 bash 环境)`);
|
|
255
|
+
lines.push(`⬆️升级指引:[点击查看](${url})`);
|
|
256
|
+
return lines.join("\n");
|
|
257
|
+
}
|
|
258
|
+
lines.push(``);
|
|
259
|
+
lines.push(`🔄 正在执行热更新到 v${info.latest}...`);
|
|
260
|
+
lines.push(`⏳ 升级过程约需 30~60 秒,完成后会自动通知您`);
|
|
261
|
+
return lines.join("\n");
|
|
149
262
|
}
|
|
263
|
+
// doc 模式:展示升级文档
|
|
150
264
|
lines.push(`⬆️升级指引:[点击查看](${url})`);
|
|
151
265
|
lines.push(`🌟官方 GitHub 仓库:[点击前往](https://github.com/tencent-connect/openclaw-qqbot/)`);
|
|
266
|
+
lines.push(``, `> 💡 提示:管理员可通过 <qqbot-cmd-input text="/bot-hot-upgrade" show="/bot-hot-upgrade"/> 直接执行热更新`);
|
|
152
267
|
return lines.join("\n");
|
|
153
268
|
},
|
|
154
269
|
});
|
|
155
270
|
/**
|
|
156
|
-
* /
|
|
271
|
+
* /bot-hot-upgrade — 直接执行热更新(无论 upgradeMode 配置如何)
|
|
272
|
+
*
|
|
273
|
+
* 支持参数:
|
|
274
|
+
* /bot-hot-upgrade — 升级到 latest
|
|
275
|
+
* /bot-hot-upgrade 1.6.4 — 升级到指定版本
|
|
276
|
+
* /bot-hot-upgrade --force — 强制升级(即使当前已是最新版)
|
|
157
277
|
*/
|
|
158
278
|
registerCommand({
|
|
159
|
-
name: "
|
|
279
|
+
name: "bot-hot-upgrade",
|
|
280
|
+
description: "直接执行热更新升级",
|
|
281
|
+
handler: (ctx) => {
|
|
282
|
+
// 升级相关指令仅在私聊中可用
|
|
283
|
+
if (ctx.type !== "c2c") {
|
|
284
|
+
return `💡 请在私聊中使用此指令`;
|
|
285
|
+
}
|
|
286
|
+
// 前置检查
|
|
287
|
+
const scriptPath = getUpgradeScriptPath();
|
|
288
|
+
if (!scriptPath) {
|
|
289
|
+
return "❌ 升级脚本不存在,请检查安装是否完整";
|
|
290
|
+
}
|
|
291
|
+
const cli = findCli();
|
|
292
|
+
if (!cli) {
|
|
293
|
+
return "❌ 未找到 openclaw / clawdbot / moltbot CLI";
|
|
294
|
+
}
|
|
295
|
+
const args = ctx.args.trim();
|
|
296
|
+
const info = getUpdateInfo();
|
|
297
|
+
// 解析参数
|
|
298
|
+
const isForce = args.includes("--force");
|
|
299
|
+
const versionArg = args.replace("--force", "").trim() || undefined;
|
|
300
|
+
// 如果没有指定版本,先检查是否有更新
|
|
301
|
+
if (!versionArg && !isForce) {
|
|
302
|
+
if (info.checkedAt === 0) {
|
|
303
|
+
return `⏳ 版本检查中,请稍后再试`;
|
|
304
|
+
}
|
|
305
|
+
if (!info.hasUpdate && !info.error) {
|
|
306
|
+
return `✅ 当前版本 v${PLUGIN_VERSION} 已是最新,无需升级\n\n> 💡 使用 /bot-hot-upgrade --force 可强制重新安装`;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
const targetVersion = versionArg || info.latest || undefined;
|
|
310
|
+
// 异步执行升级
|
|
311
|
+
const started = fireHotUpgrade(targetVersion);
|
|
312
|
+
if (!started) {
|
|
313
|
+
return `❌ 当前环境不支持热更新(需要 bash 环境)\n\n> Windows 用户请安装 Git for Windows 后重试,或手动执行升级脚本`;
|
|
314
|
+
}
|
|
315
|
+
const lines = [
|
|
316
|
+
`🔄 开始热更新...`,
|
|
317
|
+
`📌 当前版本:v${PLUGIN_VERSION}`,
|
|
318
|
+
];
|
|
319
|
+
if (targetVersion) {
|
|
320
|
+
lines.push(`🎯 目标版本:v${targetVersion}`);
|
|
321
|
+
}
|
|
322
|
+
lines.push(``);
|
|
323
|
+
lines.push(`⏳ 升级过程约需 30~60 秒,完成后会自动通知您`);
|
|
324
|
+
return lines.join("\n");
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
/**
|
|
328
|
+
* /bot-logs — 导出本地日志文件
|
|
329
|
+
*
|
|
330
|
+
* 日志路径检测策略(兼容特殊安装路径和 --profile/--dev 模式):
|
|
331
|
+
* 1. OPENCLAW_STATE_DIR 环境变量指定的目录
|
|
332
|
+
* 2. 扫描 home 目录下所有 .openclaw-xxx/logs/ 目录,取最近修改的 gateway.log
|
|
333
|
+
*/
|
|
334
|
+
registerCommand({
|
|
335
|
+
name: "bot-logs",
|
|
160
336
|
description: "导出本地日志文件",
|
|
161
337
|
handler: () => {
|
|
162
|
-
const homeDir =
|
|
163
|
-
|
|
164
|
-
const
|
|
165
|
-
|
|
338
|
+
const homeDir = getHomeDir();
|
|
339
|
+
// 收集所有可能的日志目录
|
|
340
|
+
const logDirs = [];
|
|
341
|
+
// 优先:环境变量指定的状态目录
|
|
342
|
+
const stateDir = process.env.OPENCLAW_STATE_DIR;
|
|
343
|
+
if (stateDir) {
|
|
344
|
+
logDirs.push(path.join(stateDir, "logs"));
|
|
345
|
+
}
|
|
346
|
+
// 扫描搜索根目录列表(兼容 Windows APPDATA 路径)
|
|
347
|
+
const searchRoots = new Set([homeDir]);
|
|
348
|
+
const appData = process.env.APPDATA; // Windows: C:\Users\xxx\AppData\Roaming
|
|
349
|
+
if (appData)
|
|
350
|
+
searchRoots.add(appData);
|
|
351
|
+
const localAppData = process.env.LOCALAPPDATA; // Windows: C:\Users\xxx\AppData\Local
|
|
352
|
+
if (localAppData)
|
|
353
|
+
searchRoots.add(localAppData);
|
|
354
|
+
for (const root of searchRoots) {
|
|
355
|
+
try {
|
|
356
|
+
const entries = fs.readdirSync(root, { withFileTypes: true });
|
|
357
|
+
for (const entry of entries) {
|
|
358
|
+
if (entry.isDirectory() && (entry.name.startsWith(".openclaw") || entry.name.startsWith("openclaw"))) {
|
|
359
|
+
const candidate = path.join(root, entry.name, "logs");
|
|
360
|
+
if (!logDirs.includes(candidate)) {
|
|
361
|
+
logDirs.push(candidate);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
catch {
|
|
367
|
+
// 无权限或不存在,跳过
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
// 兜底:默认路径
|
|
371
|
+
const defaultLogDir = path.join(homeDir, ".openclaw", "logs");
|
|
372
|
+
if (!logDirs.includes(defaultLogDir)) {
|
|
373
|
+
logDirs.push(defaultLogDir);
|
|
374
|
+
}
|
|
375
|
+
// 从所有候选目录中找到存在且最近修改的 gateway.log
|
|
376
|
+
let bestLogDir = null;
|
|
377
|
+
let bestMtime = 0;
|
|
378
|
+
for (const logDir of logDirs) {
|
|
379
|
+
const gatewayLog = path.join(logDir, "gateway.log");
|
|
380
|
+
try {
|
|
381
|
+
const stat = fs.statSync(gatewayLog);
|
|
382
|
+
if (stat.mtimeMs > bestMtime) {
|
|
383
|
+
bestMtime = stat.mtimeMs;
|
|
384
|
+
bestLogDir = logDir;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
catch {
|
|
388
|
+
// 不存在或无权限,跳过
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (!bestLogDir) {
|
|
392
|
+
const searched = logDirs.map(d => ` - ${d}`).join("\n");
|
|
393
|
+
return `⚠️ 未找到日志文件\n\n已搜索以下路径:\n${searched}`;
|
|
394
|
+
}
|
|
395
|
+
const gatewayLog = path.join(bestLogDir, "gateway.log");
|
|
396
|
+
const errLog = path.join(bestLogDir, "gateway.err.log");
|
|
166
397
|
const lines = [];
|
|
167
|
-
// 读取 gateway.log 最后 2000 行
|
|
168
398
|
for (const logFile of [gatewayLog, errLog]) {
|
|
169
399
|
if (!fs.existsSync(logFile))
|
|
170
400
|
continue;
|
|
171
401
|
try {
|
|
172
402
|
const content = fs.readFileSync(logFile, "utf8");
|
|
173
403
|
const allLines = content.split("\n");
|
|
174
|
-
const tail = allLines.slice(-1000);
|
|
404
|
+
const tail = allLines.slice(-1000);
|
|
175
405
|
if (tail.length > 0) {
|
|
176
406
|
lines.push(`\n========== ${path.basename(logFile)} (last ${tail.length} lines) ==========\n`);
|
|
177
407
|
lines.push(...tail);
|
|
@@ -182,7 +412,7 @@ registerCommand({
|
|
|
182
412
|
}
|
|
183
413
|
}
|
|
184
414
|
if (lines.length === 0) {
|
|
185
|
-
return
|
|
415
|
+
return `⚠️ 日志文件为空(路径:${bestLogDir})`;
|
|
186
416
|
}
|
|
187
417
|
// 写入临时文件
|
|
188
418
|
const tmpDir = path.join(homeDir, ".openclaw", "qqbot", "downloads");
|
|
@@ -190,11 +420,11 @@ registerCommand({
|
|
|
190
420
|
fs.mkdirSync(tmpDir, { recursive: true });
|
|
191
421
|
}
|
|
192
422
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
193
|
-
const tmpFile = path.join(tmpDir, `
|
|
423
|
+
const tmpFile = path.join(tmpDir, `bot-logs-${timestamp}.txt`);
|
|
194
424
|
fs.writeFileSync(tmpFile, lines.join("\n"), "utf8");
|
|
195
425
|
const totalLines = lines.filter(l => !l.startsWith("=")).length;
|
|
196
426
|
return {
|
|
197
|
-
text: `📋 日志已打包(约 ${totalLines}
|
|
427
|
+
text: `📋 日志已打包(约 ${totalLines} 行),正在发送文件...\n📂 来源:${bestLogDir}`,
|
|
198
428
|
filePath: tmpFile,
|
|
199
429
|
};
|
|
200
430
|
},
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
+
/**
|
|
3
|
+
* 注册 QQ 频道 API 代理工具。
|
|
4
|
+
*
|
|
5
|
+
* 该工具作为 QQ 开放平台频道 API 的 HTTP 代理,自动处理 Token 鉴权。
|
|
6
|
+
* AI 通过 skill 文档了解各接口的路径、方法和参数,构造请求后由此工具代理发送。
|
|
7
|
+
*
|
|
8
|
+
* 支持的能力:
|
|
9
|
+
* - 频道管理(Guild)
|
|
10
|
+
* - 子频道管理(Channel)
|
|
11
|
+
* - 成员管理(Member)
|
|
12
|
+
* - 公告管理(Announces)
|
|
13
|
+
* - 论坛管理(Forum Thread)
|
|
14
|
+
* - 日程管理(Schedule)
|
|
15
|
+
*/
|
|
16
|
+
export declare function registerChannelTool(api: OpenClawPluginApi): void;
|