@tencent-connect/openclaw-qqbot 1.6.5-alpha.3 → 1.6.5-alpha.5

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
@@ -10,7 +10,7 @@
10
10
 
11
11
  **Connect your AI assistant to QQ — private chat, group chat, and rich media, all in one plugin.**
12
12
 
13
- ### 🚀 Current Version: `v1.6.4`
13
+ ### 🚀 Current Version: `v1.6.5`
14
14
 
15
15
  [![License](https://img.shields.io/badge/license-MIT-green)](./LICENSE)
16
16
  [![QQ Bot](https://img.shields.io/badge/QQ_Bot-API_v2-red)](https://bot.q.qq.com/wiki/)
package/README.zh.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  **让你的 AI 助手接入 QQ — 私聊、群聊、富媒体,一个插件全搞定。**
11
11
 
12
- ### 🚀 当前版本: `v1.6.4`
12
+ ### 🚀 当前版本: `v1.6.5`
13
13
 
14
14
  [![License](https://img.shields.io/badge/license-MIT-green)](./LICENSE)
15
15
  [![QQ Bot](https://img.shields.io/badge/QQ_Bot-API_v2-red)](https://bot.q.qq.com/wiki/)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tencent-connect/openclaw-qqbot",
3
- "version": "1.6.5-alpha.3",
3
+ "version": "1.6.5-alpha.5",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -22,7 +22,11 @@
22
22
  "id": "openclaw-qqbot",
23
23
  "extensions": [
24
24
  "./dist/index.js"
25
- ]
25
+ ],
26
+ "channel": {
27
+ "id": "qqbot",
28
+ "label": "QQ Bot"
29
+ }
26
30
  },
27
31
  "scripts": {
28
32
  "build": "tsc || true",
@@ -1,22 +1,27 @@
1
1
  #!/bin/bash
2
2
 
3
- # qqbot 通过 npm 包升级(纯文件操作版本)
3
+ # qqbot 通过 openclaw 原生插件指令升级
4
4
  #
5
- # 默认只做文件替换,不修改 openclaw.json 配置文件。
6
- # 但如果提供了 --appid/--secret 参数(首次安装场景),
7
- # 则在文件安装完成后自动写入通道配置。
5
+ # 使用 openclaw plugins install/update 原生命令进行安装和升级,
6
+ # 保留 appid/secret 配置写入、热更新 (--no-restart)、结构化输出等功能。
7
+ #
8
+ # 升级策略:
9
+ # 1. 已安装(plugins.installs 有记录)→ openclaw plugins update
10
+ # 2. 未安装 / update 失败 → 删除旧目录 + openclaw plugins install
8
11
  #
9
12
  # 用法:
10
13
  # upgrade-via-npm.sh # 升级到 latest(默认)
11
14
  # upgrade-via-npm.sh --version <version> # 升级到指定版本
12
15
  # upgrade-via-npm.sh --self-version # 升级到当前仓库 package.json 版本
13
16
  # upgrade-via-npm.sh --appid <appid> --secret <secret> # 首次安装时配置 appid/secret
14
- # upgrade-via-npm.sh --no-restart # 只做文件替换,不重启 gateway(供热更指令使用)
17
+ # upgrade-via-npm.sh --no-restart # 只做文件替换,不重启 gateway(供热更指令使用)
15
18
 
16
19
  set -eo pipefail
17
20
 
18
21
  PKG_NAME="@tencent-connect/openclaw-qqbot"
22
+ PLUGIN_ID="openclaw-qqbot"
19
23
  INSTALL_SRC=""
24
+ TARGET_VERSION=""
20
25
  APPID=""
21
26
  SECRET=""
22
27
  NO_RESTART=false
@@ -56,16 +61,19 @@ while [[ $# -gt 0 ]]; do
56
61
  case "$1" in
57
62
  --tag)
58
63
  [ -z "$2" ] && echo "❌ --tag 需要参数" && exit 1
64
+ TARGET_VERSION="$2"
59
65
  INSTALL_SRC="${PKG_NAME}@$2"
60
66
  shift 2
61
67
  ;;
62
68
  --version)
63
69
  [ -z "$2" ] && echo "❌ --version 需要参数" && exit 1
70
+ TARGET_VERSION="$2"
64
71
  INSTALL_SRC="${PKG_NAME}@$2"
65
72
  shift 2
66
73
  ;;
67
74
  --self-version)
68
75
  [ -z "$LOCAL_VERSION" ] && echo "❌ 无法从 package.json 读取版本" && exit 1
76
+ TARGET_VERSION="$LOCAL_VERSION"
69
77
  INSTALL_SRC="${PKG_NAME}@${LOCAL_VERSION}"
70
78
  shift 1
71
79
  ;;
@@ -100,7 +108,7 @@ if [ -z "$APPID" ] && [ -z "$SECRET" ] && [ -n "$QQBOT_TOKEN" ]; then
100
108
  SECRET="${QQBOT_TOKEN#*:}"
101
109
  fi
102
110
 
103
- # 检测 CLI(仅用于确定 extensions 目录路径)
111
+ # 检测 CLI
104
112
  CMD=""
105
113
  for name in openclaw clawdbot moltbot; do
106
114
  command -v "$name" &>/dev/null && CMD="$name" && break
@@ -110,91 +118,163 @@ done
110
118
  EXTENSIONS_DIR="$HOME/.$CMD/extensions"
111
119
 
112
120
  echo "==========================================="
113
- echo " qqbot npm 升级: $INSTALL_SRC"
121
+ echo " qqbot 升级: $INSTALL_SRC"
114
122
  echo "==========================================="
115
123
  echo ""
116
124
 
117
- # [1/5] 下载并安装新版本到临时目录
118
- echo "[1/5] 下载新版本..."
119
- TMPDIR_PACK=$(mktemp -d)
120
- EXTRACT_DIR=$(mktemp -d)
121
- trap "rm -rf '$TMPDIR_PACK' '$EXTRACT_DIR'" EXIT
122
-
123
- cd "$TMPDIR_PACK"
124
- # registry fallback:npmjs.org → npmmirror(国内镜像)→ 默认 registry
125
- PACK_OK=false
126
- for _registry in "https://registry.npmjs.org/" "https://registry.npmmirror.com/" ""; do
127
- if [ -n "$_registry" ]; then
128
- echo " 尝试 registry: $_registry"
129
- npm pack "$INSTALL_SRC" --registry "$_registry" --quiet 2>&1 && PACK_OK=true && break
130
- else
131
- echo " 尝试默认 registry..."
132
- npm pack "$INSTALL_SRC" --quiet 2>&1 && PACK_OK=true && break
125
+ # 记录升级前的版本
126
+ OLD_VERSION=""
127
+ OLD_PKG="$EXTENSIONS_DIR/$PLUGIN_ID/package.json"
128
+ if [ -f "$OLD_PKG" ]; then
129
+ OLD_VERSION="$(node -e "
130
+ try {
131
+ const v = JSON.parse(require('fs').readFileSync('$OLD_PKG', 'utf8')).version;
132
+ if (v) process.stdout.write(String(v));
133
+ } catch {}
134
+ " 2>/dev/null || true)"
135
+ echo " 当前版本: ${OLD_VERSION:-unknown}"
136
+ fi
137
+
138
+ # [1/4] 通过 openclaw 原生指令安装/升级
139
+ echo ""
140
+ echo "[1/4] 安装/升级插件..."
141
+
142
+ # ── 兼容 openclaw 3.23+ 配置严格校验 ──
143
+ # 3.23+ 在 plugins install/update 时会校验整个配置文件,
144
+ # 如果 channels.qqbot 已存在但 qqbot 插件尚未加载,校验会失败。
145
+ # 解决:install/update 前临时移除 channels.qqbot,成功后恢复。
146
+ CONFIG_FILE="$HOME/.$CMD/$CMD.json"
147
+ QQBOT_CHANNEL_BACKUP=""
148
+ if [ -f "$CONFIG_FILE" ]; then
149
+ QQBOT_CHANNEL_BACKUP="$(node -e "
150
+ try {
151
+ const fs = require('fs');
152
+ const cfg = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf8'));
153
+ if (cfg.channels && cfg.channels.qqbot) {
154
+ process.stdout.write(JSON.stringify(cfg.channels.qqbot));
155
+ delete cfg.channels.qqbot;
156
+ if (Object.keys(cfg.channels).length === 0) delete cfg.channels;
157
+ fs.writeFileSync('$CONFIG_FILE', JSON.stringify(cfg, null, 4) + '\n');
158
+ }
159
+ } catch {}
160
+ " 2>/dev/null || true)"
161
+ if [ -n "$QQBOT_CHANNEL_BACKUP" ]; then
162
+ echo " [兼容] 临时移除 channels.qqbot 以通过配置校验"
133
163
  fi
134
- done
135
- $PACK_OK || { echo "❌ npm pack 失败(所有 registry 均不可用)"; exit 1; }
136
- TGZ_FILE=$(ls -1 *.tgz 2>/dev/null | head -1)
137
- [ -z "$TGZ_FILE" ] && echo "❌ 未找到下载的 tgz 文件" && exit 1
138
- echo " 已下载: $TGZ_FILE"
139
-
140
- tar xzf "$TGZ_FILE" -C "$EXTRACT_DIR"
141
- PACKAGE_DIR="$EXTRACT_DIR/package"
142
- [ ! -d "$PACKAGE_DIR" ] && echo "❌ 解压失败,未找到 package 目录" && exit 1
143
-
144
- # 准备 staging 目录:放在 ~/.openclaw/ 下(extensions 的父目录),
145
- # 同一文件系统保证 mv 原子操作,同时避免 OpenClaw 扫描 extensions/ 时发现它。
146
- STAGING_DIR="$(dirname "$EXTENSIONS_DIR")/.qqbot-upgrade-staging"
147
- rm -rf "$STAGING_DIR"
148
- mkdir -p "$STAGING_DIR"
149
- cp -R "$PACKAGE_DIR/"* "$STAGING_DIR/"
150
-
151
- # 依赖处理:所有 production dependencies 都声明为 bundledDependencies,
152
- # npm pack 时已打包进 tgz,解压后 node_modules/ 已包含全部依赖,无需 npm install。
153
- # 注意:不能执行 npm install,否则会安装 peerDependencies(openclaw 平台及其 400+ 传递依赖),
154
- # 导致插件目录膨胀到 900MB+,而这些依赖在运行时由宿主 openclaw 提供。
155
- if [ -d "$STAGING_DIR/node_modules" ]; then
156
- BUNDLED_COUNT=$(ls -d "$STAGING_DIR/node_modules"/*/ "$STAGING_DIR/node_modules"/@*/*/ 2>/dev/null | wc -l | tr -d ' ')
157
- echo " bundled 依赖已就绪(${BUNDLED_COUNT} 个包)"
164
+ fi
165
+
166
+ # 恢复 channels.qqbot 的函数(install/update 完成后调用)
167
+ restore_qqbot_channel() {
168
+ if [ -n "$QQBOT_CHANNEL_BACKUP" ] && [ -f "$CONFIG_FILE" ]; then
169
+ node -e "
170
+ try {
171
+ const fs = require('fs');
172
+ const cfg = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf8'));
173
+ if (!cfg.channels) cfg.channels = {};
174
+ cfg.channels.qqbot = $QQBOT_CHANNEL_BACKUP;
175
+ fs.writeFileSync('$CONFIG_FILE', JSON.stringify(cfg, null, 4) + '\n');
176
+ } catch {}
177
+ " 2>/dev/null || true
178
+ echo " [兼容] 已恢复 channels.qqbot 配置"
179
+ fi
180
+ }
181
+
182
+ UPGRADE_OK=false
183
+
184
+ # 检测安装状态:同时检查配置记录和磁盘目录
185
+ HAS_INSTALL_RECORD="$(node -e "
186
+ try {
187
+ const fs = require('fs');
188
+ const p = '$HOME/.$CMD/$CMD.json';
189
+ const cfg = JSON.parse(fs.readFileSync(p, 'utf8'));
190
+ const inst = cfg.plugins && cfg.plugins.installs && cfg.plugins.installs['$PLUGIN_ID'];
191
+ if (inst) process.stdout.write('yes');
192
+ } catch {}
193
+ " 2>/dev/null || true)"
194
+ HAS_PLUGIN_DIR=false
195
+ [ -d "$EXTENSIONS_DIR/$PLUGIN_ID" ] && [ -f "$EXTENSIONS_DIR/$PLUGIN_ID/package.json" ] && HAS_PLUGIN_DIR=true
196
+
197
+ # 决策矩阵:
198
+ # 配置有记录 + 目录存在 → update(最佳路径)
199
+ # 配置有记录 + 目录不存在 → 清理残留记录,走 install
200
+ # 配置无记录 + 目录存在 → 删目录,走 install(配置与文件不一致)
201
+ # 配置无记录 + 目录不存在 → 走 install(全新安装)
202
+ #
203
+ # 指定了具体版本(--version/--tag/--self-version)时:
204
+ # update 不支持指定版本,直接走 删除 + install
205
+
206
+ USE_UPDATE=false
207
+
208
+ if [ "$HAS_INSTALL_RECORD" = "yes" ] && [ "$HAS_PLUGIN_DIR" = "true" ] && [ -z "$TARGET_VERSION" ]; then
209
+ # 配置和目录都齐全,且未指定版本 → 走 update
210
+ USE_UPDATE=true
211
+ echo " [检测] 配置记录 ✓ | 插件目录 ✓ | 未指定版本 → 使用 update"
212
+ elif [ "$HAS_INSTALL_RECORD" = "yes" ] && [ "$HAS_PLUGIN_DIR" = "true" ]; then
213
+ echo " [检测] 配置记录 ✓ | 插件目录 ✓ | 指定版本 $TARGET_VERSION → 使用 reinstall"
214
+ elif [ "$HAS_INSTALL_RECORD" = "yes" ]; then
215
+ echo " [检测] 配置记录 ✓ | 插件目录 ✗ → 配置与文件不一致,使用 install"
216
+ elif [ "$HAS_PLUGIN_DIR" = "true" ]; then
217
+ echo " [检测] 配置记录 ✗ | 插件目录 ✓ → 目录残留,清理后 install"
158
218
  else
159
- echo " ⚠️ 未找到 bundled node_modules,尝试安装依赖..."
160
- NPM_TMP_CACHE=$(mktemp -d)
161
- (cd "$STAGING_DIR" && npm install --omit=dev --omit=peer --ignore-scripts --cache="$NPM_TMP_CACHE" --quiet 2>&1) || echo " ⚠️ 依赖安装失败"
162
- rm -rf "$NPM_TMP_CACHE"
219
+ echo " [检测] 配置记录 ✗ | 插件目录 ✗ → 全新安装"
163
220
  fi
164
221
 
165
- # 清理下载临时文件
166
- rm -rf "$TMPDIR_PACK" "$EXTRACT_DIR"
167
- cd "$HOME"
222
+ if [ "$USE_UPDATE" = "true" ]; then
223
+ echo " 尝试 update..."
224
+ if $CMD plugins update "$PLUGIN_ID" 2>&1; then
225
+ UPGRADE_OK=true
226
+ echo " ✅ update 成功"
227
+ else
228
+ echo " ⚠️ update 失败,回退到 reinstall..."
229
+ fi
230
+ fi
231
+
232
+ if [ "$UPGRADE_OK" != "true" ]; then
233
+ # 清理旧目录(包含当前插件和历史遗留名称)
234
+ for dir_name in "$PLUGIN_ID" qqbot openclaw-qq; do
235
+ [ -d "$EXTENSIONS_DIR/$dir_name" ] && rm -rf "$EXTENSIONS_DIR/$dir_name" && echo " 已清理: $EXTENSIONS_DIR/$dir_name"
236
+ done
168
237
 
169
- # ── Preflight 检查:在写入 extensions 之前确保新包完整有效 ──
238
+ echo " 执行 install: $INSTALL_SRC"
239
+ if $CMD plugins install "$INSTALL_SRC" --pin 2>&1; then
240
+ UPGRADE_OK=true
241
+ echo " ✅ install 成功"
242
+ else
243
+ echo "❌ install 失败"
244
+ restore_qqbot_channel
245
+ echo "QQBOT_NEW_VERSION=unknown"
246
+ echo "QQBOT_REPORT=❌ QQBot 安装失败,请检查网络和 npm registry"
247
+ exit 1
248
+ fi
249
+ fi
250
+
251
+ # install/update 完成,恢复 channels.qqbot
252
+ restore_qqbot_channel
253
+
254
+ # [2/4] 验证安装
170
255
  echo ""
171
- echo "[2/5] Preflight 检查..."
256
+ echo "[2/4] 验证安装..."
257
+
258
+ PKG_JSON="$EXTENSIONS_DIR/$PLUGIN_ID/package.json"
259
+ if [ -f "$PKG_JSON" ]; then
260
+ NEW_VERSION="$(node -e "process.stdout.write(JSON.parse(require('fs').readFileSync(process.argv[1],'utf8')).version||'')" "$PKG_JSON" 2>/dev/null || true)"
261
+ fi
262
+
263
+ # Preflight 检查
172
264
  PREFLIGHT_OK=true
265
+ TARGET_DIR="$EXTENSIONS_DIR/$PLUGIN_ID"
173
266
 
174
- # (a) package.json 存在且可解析,且包含 version 字段
175
- STAGING_PKG="$STAGING_DIR/package.json"
176
- if [ ! -f "$STAGING_PKG" ]; then
177
- echo " ❌ 新包缺少 package.json"
267
+ if [ -z "$NEW_VERSION" ]; then
268
+ echo " ❌ 无法读取新版本号"
178
269
  PREFLIGHT_OK=false
179
270
  else
180
- STAGING_VERSION="$(node -e "
181
- try {
182
- const v = JSON.parse(require('fs').readFileSync('$STAGING_PKG', 'utf8')).version;
183
- if (v) process.stdout.write(String(v));
184
- } catch {}
185
- " 2>/dev/null || true)"
186
- if [ -z "$STAGING_VERSION" ]; then
187
- echo " ❌ package.json 无法解析或缺少 version 字段"
188
- PREFLIGHT_OK=false
189
- else
190
- echo " ✅ 版本号: $STAGING_VERSION"
191
- fi
271
+ echo " 版本号: $NEW_VERSION"
192
272
  fi
193
273
 
194
- # (b) 入口文件存在(dist/index.js 或 index.js)
274
+ # 入口文件
195
275
  ENTRY_FILE=""
196
276
  for candidate in "dist/index.js" "index.js"; do
197
- if [ -f "$STAGING_DIR/$candidate" ]; then
277
+ if [ -f "$TARGET_DIR/$candidate" ]; then
198
278
  ENTRY_FILE="$candidate"
199
279
  break
200
280
  fi
@@ -206,23 +286,23 @@ else
206
286
  echo " ✅ 入口文件: $ENTRY_FILE"
207
287
  fi
208
288
 
209
- # (c) 核心目录 dist/src 存在
210
- if [ ! -d "$STAGING_DIR/dist/src" ]; then
211
- echo " ❌ 缺少核心目录 dist/src/"
212
- PREFLIGHT_OK=false
213
- else
214
- CORE_JS_COUNT=$(find "$STAGING_DIR/dist/src" -name "*.js" -type f 2>/dev/null | wc -l | tr -d ' ')
289
+ # 核心目录
290
+ if [ -d "$TARGET_DIR/dist/src" ]; then
291
+ CORE_JS_COUNT=$(find "$TARGET_DIR/dist/src" -name "*.js" -type f 2>/dev/null | wc -l | tr -d ' ')
215
292
  echo " ✅ dist/src/ 包含 ${CORE_JS_COUNT} 个 JS 文件"
216
293
  if [ "$CORE_JS_COUNT" -lt 5 ]; then
217
294
  echo " ❌ JS 文件数量异常偏少(预期 ≥ 5,实际 ${CORE_JS_COUNT})"
218
295
  PREFLIGHT_OK=false
219
296
  fi
297
+ else
298
+ echo " ❌ 缺少核心目录 dist/src/"
299
+ PREFLIGHT_OK=false
220
300
  fi
221
301
 
222
- # (d) 关键模块文件存在
302
+ # 关键模块
223
303
  MISSING_MODULES=""
224
304
  for module in "dist/src/gateway.js" "dist/src/api.js" "dist/src/admin-resolver.js"; do
225
- if [ ! -f "$STAGING_DIR/$module" ]; then
305
+ if [ ! -f "$TARGET_DIR/$module" ]; then
226
306
  MISSING_MODULES="$MISSING_MODULES $module"
227
307
  fi
228
308
  done
@@ -233,11 +313,11 @@ else
233
313
  echo " ✅ 关键模块完整"
234
314
  fi
235
315
 
236
- # (e) bundled node_modules 健康检查
237
- if [ -d "$STAGING_DIR/node_modules" ]; then
316
+ # bundled 依赖
317
+ if [ -d "$TARGET_DIR/node_modules" ]; then
238
318
  BUNDLED_OK=true
239
319
  for dep in "ws" "silk-wasm"; do
240
- if [ ! -d "$STAGING_DIR/node_modules/$dep" ]; then
320
+ if [ ! -d "$TARGET_DIR/node_modules/$dep" ]; then
241
321
  echo " ⚠️ bundled 依赖缺失: $dep"
242
322
  BUNDLED_OK=false
243
323
  fi
@@ -247,104 +327,20 @@ if [ -d "$STAGING_DIR/node_modules" ]; then
247
327
  fi
248
328
  fi
249
329
 
250
- # (f) 如果有旧版本,检查新版本是否合理(不允许降级到 0.x 等异常版本)
251
- if [ -n "$STAGING_VERSION" ]; then
252
- STAGING_MAJOR="$(echo "$STAGING_VERSION" | cut -d. -f1)"
253
- if [ "$STAGING_MAJOR" = "0" ]; then
254
- echo " ⚠️ 新版本主版本号为 0($STAGING_VERSION),可能不是正式发布版"
255
- fi
256
- fi
257
-
258
- # 检查结果
259
330
  if [ "$PREFLIGHT_OK" != "true" ]; then
260
331
  echo ""
261
- echo "❌ Preflight 检查未通过,中止升级(旧版本未受影响)"
262
- rm -rf "$STAGING_DIR"
332
+ echo "❌ 验证未通过"
333
+ echo "QQBOT_NEW_VERSION=unknown"
334
+ echo "QQBOT_REPORT=⚠️ QQBot 升级异常,验证未通过"
263
335
  exit 1
264
336
  fi
265
- echo " ✅ Preflight 检查全部通过"
337
+ echo " ✅ 验证全部通过"
266
338
 
267
- # [3/5] 原子替换:使用 mv -T/rename 确保目录切换尽可能原子
268
- # 策略:先把 staging 放到 extensions/ 同级的临时名,再做单次 mv 替换
339
+ # [3/4] 输出结构化信息(供 TS handler 解析)
269
340
  echo ""
270
- echo "[3/5] 原子替换插件目录..."
271
- mkdir -p "$EXTENSIONS_DIR"
272
- TARGET_DIR="$EXTENSIONS_DIR/openclaw-qqbot"
273
- OLD_DIR="$(dirname "$EXTENSIONS_DIR")/.qqbot-upgrade-old"
274
-
275
- rm -rf "$OLD_DIR"
276
-
277
- # 先把 staging 目录移到 extensions/ 下的临时位置(同文件系统,确保 mv 是 rename 操作)
278
- STAGING_IN_EXT="$EXTENSIONS_DIR/.openclaw-qqbot-new"
279
- rm -rf "$STAGING_IN_EXT"
280
- mv "$STAGING_DIR" "$STAGING_IN_EXT"
281
-
282
- if [ -d "$TARGET_DIR" ]; then
283
- # 使用连续两个 mv 但中间零操作,最小化目录不存在的时间窗口
284
- mv "$TARGET_DIR" "$OLD_DIR" && mv "$STAGING_IN_EXT" "$TARGET_DIR"
285
- else
286
- mv "$STAGING_IN_EXT" "$TARGET_DIR"
287
- fi
288
- rm -rf "$OLD_DIR"
289
-
290
- # 清理可能残留的旧版 staging 目录(extensions 内外都清理)
291
- rm -rf "$EXTENSIONS_DIR/openclaw-qqbot.staging"
292
- rm -rf "$EXTENSIONS_DIR/.qqbot-upgrade-staging"
293
- rm -rf "$EXTENSIONS_DIR/.qqbot-upgrade-old"
294
-
295
- # 同时清理历史遗留的其他目录名
296
- for dir_name in qqbot openclaw-qq; do
297
- [ -d "$EXTENSIONS_DIR/$dir_name" ] && rm -rf "$EXTENSIONS_DIR/$dir_name"
298
- done
299
- echo " 已安装到: $TARGET_DIR"
300
-
301
- # 执行 postinstall 脚本创建 openclaw SDK symlink
302
- # (upgrade-via-npm 是纯文件操作,不走 npm install,所以 postinstall 不会自动触发)
303
- POSTINSTALL_SCRIPT="$TARGET_DIR/scripts/postinstall-link-sdk.js"
304
- if [ -f "$POSTINSTALL_SCRIPT" ]; then
305
- echo " 执行 postinstall: 创建 openclaw SDK symlink..."
306
- POSTINSTALL_OUTPUT="$(cd "$TARGET_DIR" && node "$POSTINSTALL_SCRIPT" 2>&1)" || true
307
- [ -n "$POSTINSTALL_OUTPUT" ] && echo " $POSTINSTALL_OUTPUT"
308
- # 验证 symlink 是否创建成功
309
- if [ -d "$TARGET_DIR/node_modules/openclaw" ]; then
310
- echo " ✅ openclaw SDK symlink 已就绪"
311
- else
312
- echo " ⚠️ openclaw SDK symlink 未创建,插件可能无法加载"
313
- echo " 尝试手动创建 symlink..."
314
- # 手动 fallback:尝试从 CLI 数据目录名推断全局包名
315
- _CLI_DATA_DIR="$(dirname "$EXTENSIONS_DIR")"
316
- _CLI_NAME="$(basename "$_CLI_DATA_DIR" | sed 's/^\.//')"
317
- _GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"
318
- if [ -n "$_GLOBAL_ROOT" ] && [ -n "$_CLI_NAME" ] && [ -d "$_GLOBAL_ROOT/$_CLI_NAME" ]; then
319
- mkdir -p "$TARGET_DIR/node_modules"
320
- ln -sf "$_GLOBAL_ROOT/$_CLI_NAME" "$TARGET_DIR/node_modules/openclaw" 2>/dev/null && \
321
- echo " ✅ 手动 symlink 创建成功: -> $_GLOBAL_ROOT/$_CLI_NAME" || \
322
- echo " ❌ 手动 symlink 创建也失败了"
323
- else
324
- echo " ❌ 无法定位全局 $_CLI_NAME 安装路径(npm root -g: $_GLOBAL_ROOT)"
325
- fi
326
- fi
327
- else
328
- echo " ⚠️ 未找到 postinstall 脚本,跳过 symlink 创建"
329
- fi
330
-
331
- # [4/5] 输出新版本号和升级报告(供调用方解析)
332
- echo ""
333
- echo "[4/5] 验证安装..."
334
- NEW_VERSION="$(node -e "
335
- try {
336
- const fs = require('fs');
337
- const path = require('path');
338
- const p = path.join('$EXTENSIONS_DIR', 'openclaw-qqbot', 'package.json');
339
- if (fs.existsSync(p)) {
340
- const v = JSON.parse(fs.readFileSync(p, 'utf8')).version;
341
- if (v) { process.stdout.write(v); process.exit(0); }
342
- }
343
- } catch {}
344
- " 2>/dev/null || true)"
341
+ echo "[3/4] 升级结果..."
345
342
  echo "QQBOT_NEW_VERSION=${NEW_VERSION:-unknown}"
346
343
 
347
- # 输出结构化升级报告(QQBOT_REPORT=...),供 TS handler 解析后直接回复用户
348
344
  if [ -n "$NEW_VERSION" ] && [ "$NEW_VERSION" != "unknown" ]; then
349
345
  echo "QQBOT_REPORT=✅ QQBot 升级完成: v${NEW_VERSION}"
350
346
  else
@@ -353,13 +349,10 @@ fi
353
349
 
354
350
  echo ""
355
351
  echo "==========================================="
356
- echo " ✅ 文件安装完成"
352
+ echo " ✅ 安装完成"
357
353
  echo "==========================================="
358
354
 
359
- # --no-restart 模式(热更新场景):文件替换完成后立即退出,
360
- # 让调用方尽快触发 gateway restart,避免 openclaw 配置轮询
361
- # 在旧进程中检测到插件变更产生 "plugin not found" warning 刷屏。
362
- # appid/secret 配置在热更新场景下已经存在,无需重新写入。
355
+ # --no-restart 模式(热更新场景):立即退出,让调用方触发 gateway restart
363
356
  if [ "$NO_RESTART" = "true" ]; then
364
357
  echo ""
365
358
  echo "[跳过重启] --no-restart 已指定,脚本立即退出以便调用方触发 gateway restart"
@@ -395,10 +388,9 @@ if [ -n "$APPID" ] && [ -n "$SECRET" ]; then
395
388
 
396
389
  if [ "$CURRENT_TOKEN" = "$DESIRED_TOKEN" ]; then
397
390
  echo " ✅ 当前配置已是目标值,跳过写入"
398
- elif $CMD channels add --channel qqbot --token "$DESIRED_TOKEN" 2>&1; then
399
- echo " ✅ 通道配置写入成功"
400
391
  else
401
- echo " ⚠️ $CMD channels add 失败,尝试直接编辑配置文件..."
392
+ # qqbot 是插件自定义通道,openclaw channels add --channel 不支持,
393
+ # 直接编辑配置文件写入 channels.qqbot
402
394
  CONFIG_FILE="$HOME/.$CMD/$CMD.json"
403
395
  if [ -f "$CONFIG_FILE" ] && node -e "
404
396
  const fs = require('fs');
@@ -409,10 +401,10 @@ if [ -n "$APPID" ] && [ -n "$SECRET" ]; then
409
401
  cfg.channels.qqbot.clientSecret = '$SECRET';
410
402
  fs.writeFileSync('$CONFIG_FILE', JSON.stringify(cfg, null, 4) + '\n');
411
403
  " 2>&1; then
412
- echo " ✅ 通道配置写入成功(直接编辑配置文件)"
404
+ echo " ✅ 通道配置写入成功"
413
405
  else
414
- echo " ❌ 配置写入失败,请手动配置:"
415
- echo " $CMD channels add --channel qqbot --token \"${APPID}:${SECRET}\""
406
+ echo " ❌ 配置写入失败,请手动编辑 $CONFIG_FILE 添加 channels.qqbot:"
407
+ echo " { \"channels\": { \"qqbot\": { \"appId\": \"$APPID\", \"clientSecret\": \"...\" } } }"
416
408
  fi
417
409
  fi
418
410
  elif [ -n "$APPID" ] || [ -n "$SECRET" ]; then
@@ -420,11 +412,10 @@ elif [ -n "$APPID" ] || [ -n "$SECRET" ]; then
420
412
  echo "⚠️ --appid 和 --secret 必须同时提供"
421
413
  fi
422
414
 
423
- # [5/5] 重启 gateway 使新版本生效
415
+ # [4/4] 重启 gateway 使新版本生效
424
416
  echo ""
425
417
 
426
418
  # 手动升级场景:提前写入 startup-marker,阻止重启后 bot 重复推送升级通知
427
- # (控制台已打印同款提示语,无需 bot 再发一次)
428
419
  if [ -n "$NEW_VERSION" ] && [ "$NEW_VERSION" != "unknown" ]; then
429
420
  MARKER_DIR="$HOME/.openclaw/qqbot/data"
430
421
  mkdir -p "$MARKER_DIR"
@@ -436,7 +427,6 @@ fi
436
427
  echo "[重启] 重启 gateway 使新版本生效..."
437
428
  if $CMD gateway restart 2>&1; then
438
429
  echo " ✅ gateway 已重启"
439
- # 打印与 bot 通知同款的更新提示语(手动升级场景无需通过 bot 推送)
440
430
  echo ""
441
431
  if [ -n "$NEW_VERSION" ] && [ "$NEW_VERSION" != "unknown" ]; then
442
432
  echo "🎉 QQBot 插件已更新至 v${NEW_VERSION},在线等候你的吩咐。"
@@ -1,307 +0,0 @@
1
- #!/bin/bash
2
-
3
- # qqbot 测试脚本:自由切换不同的 QQBot npm 包
4
- #
5
- # 支持从任意 npm 包安装指定版本到 openclaw extensions 目录。
6
- # 可用于在不同包之间切换测试,如 @sliverp/qqbot、@tencent-connect/openclaw-qqbot 等。
7
- #
8
- # 用法:
9
- # upgrade-via-alt-pkg.sh --pkg <包名> --version <version> # 指定包+版本
10
- # upgrade-via-alt-pkg.sh --pkg <包名> # 指定包,安装 latest
11
- # upgrade-via-alt-pkg.sh --appid <appid> --secret <secret> # 首次安装时配置
12
- # upgrade-via-alt-pkg.sh --no-restart # 只做文件替换,不重启
13
- #
14
- # 示例:
15
- # bash scripts/upgrade-via-alt-pkg.sh --pkg @sliverp/qqbot --version 1.5.1
16
- # bash scripts/upgrade-via-alt-pkg.sh --pkg @sliverp/qqbot --version 1.5.4
17
- # bash scripts/upgrade-via-alt-pkg.sh --pkg @tencent-connect/openclaw-qqbot --version 1.6.4
18
- # bash scripts/upgrade-via-alt-pkg.sh --pkg @sliverp/qqbot
19
- # bash scripts/upgrade-via-alt-pkg.sh --pkg @sliverp/qqbot --version 1.5.4 --appid 12345 --secret abc123
20
-
21
- set -eo pipefail
22
-
23
- PKG_NAME=""
24
- VERSION=""
25
- INSTALL_SRC=""
26
- APPID=""
27
- SECRET=""
28
- NO_RESTART=false
29
-
30
- print_usage() {
31
- echo "用法:"
32
- echo " upgrade-via-alt-pkg.sh --pkg <包名> --version <版本号>"
33
- echo " upgrade-via-alt-pkg.sh --pkg <包名> # 安装 latest"
34
- echo ""
35
- echo "选项:"
36
- echo " --pkg <name> npm 包名(必填,如 @sliverp/qqbot、@tencent-connect/openclaw-qqbot)"
37
- echo " --version <version> 指定版本号(如 1.5.1, 1.5.4, 1.6.4)"
38
- echo " --appid <appid> QQ机器人 appid(首次安装时必填)"
39
- echo " --secret <secret> QQ机器人 secret(首次安装时必填)"
40
- echo " --no-restart 只做文件替换,不重启 gateway"
41
- echo " -h, --help 显示帮助信息"
42
- echo ""
43
- echo "环境变量:"
44
- echo " QQBOT_APPID QQ机器人 appid"
45
- echo " QQBOT_SECRET QQ机器人 secret"
46
- echo " QQBOT_TOKEN QQ机器人 token (appid:secret)"
47
- echo ""
48
- echo "示例:"
49
- echo " # 从 @sliverp/qqbot 包安装 v1.5.1"
50
- echo " bash scripts/upgrade-via-alt-pkg.sh --pkg @sliverp/qqbot --version 1.5.1"
51
- echo ""
52
- echo " # 从 @sliverp/qqbot 包安装 v1.5.4"
53
- echo " bash scripts/upgrade-via-alt-pkg.sh --pkg @sliverp/qqbot --version 1.5.4"
54
- echo ""
55
- echo " # 从官方包安装 v1.6.4"
56
- echo " bash scripts/upgrade-via-alt-pkg.sh --pkg @tencent-connect/openclaw-qqbot --version 1.6.4"
57
- echo ""
58
- echo " # 切回 @sliverp/qqbot 包的 latest"
59
- echo " bash scripts/upgrade-via-alt-pkg.sh --pkg @sliverp/qqbot"
60
- }
61
-
62
- while [[ $# -gt 0 ]]; do
63
- case "$1" in
64
- --pkg|--package)
65
- [ -z "$2" ] && echo "❌ --pkg 需要参数" && exit 1
66
- PKG_NAME="$2"
67
- shift 2
68
- ;;
69
- --version)
70
- [ -z "$2" ] && echo "❌ --version 需要参数" && exit 1
71
- VERSION="$2"
72
- shift 2
73
- ;;
74
- --tag)
75
- [ -z "$2" ] && echo "❌ --tag 需要参数" && exit 1
76
- VERSION="$2"
77
- shift 2
78
- ;;
79
- --appid)
80
- [ -z "$2" ] && echo "❌ --appid 需要参数" && exit 1
81
- APPID="$2"
82
- shift 2
83
- ;;
84
- --secret)
85
- [ -z "$2" ] && echo "❌ --secret 需要参数" && exit 1
86
- SECRET="$2"
87
- shift 2
88
- ;;
89
- --no-restart)
90
- NO_RESTART=true
91
- shift 1
92
- ;;
93
- -h|--help)
94
- print_usage
95
- exit 0
96
- ;;
97
- *) echo "未知选项: $1"; print_usage; exit 1 ;;
98
- esac
99
- done
100
-
101
- # --pkg 必填
102
- if [ -z "$PKG_NAME" ]; then
103
- echo "❌ 必须指定 --pkg 参数"
104
- echo ""
105
- print_usage
106
- exit 1
107
- fi
108
-
109
- if [ -n "$VERSION" ]; then
110
- INSTALL_SRC="${PKG_NAME}@${VERSION}"
111
- else
112
- INSTALL_SRC="${PKG_NAME}@latest"
113
- fi
114
-
115
- # 环境变量 fallback
116
- APPID="${APPID:-$QQBOT_APPID}"
117
- SECRET="${SECRET:-$QQBOT_SECRET}"
118
- if [ -z "$APPID" ] && [ -z "$SECRET" ] && [ -n "$QQBOT_TOKEN" ]; then
119
- APPID="${QQBOT_TOKEN%%:*}"
120
- SECRET="${QQBOT_TOKEN#*:}"
121
- fi
122
-
123
- # 检测 CLI(仅用于确定 extensions 目录路径)
124
- CMD=""
125
- for name in openclaw clawdbot moltbot; do
126
- command -v "$name" &>/dev/null && CMD="$name" && break
127
- done
128
- [ -z "$CMD" ] && echo "❌ 未找到 openclaw / clawdbot / moltbot" && exit 1
129
-
130
- EXTENSIONS_DIR="$HOME/.$CMD/extensions"
131
-
132
- echo "==========================================="
133
- echo " qqbot 测试升级: $INSTALL_SRC"
134
- echo "==========================================="
135
- echo ""
136
-
137
- # [1/3] 下载并安装新版本到临时目录
138
- echo "[1/3] 下载新版本..."
139
- TMPDIR_PACK=$(mktemp -d)
140
- EXTRACT_DIR=$(mktemp -d)
141
- trap "rm -rf '$TMPDIR_PACK' '$EXTRACT_DIR'" EXIT
142
-
143
- cd "$TMPDIR_PACK"
144
- # 多 registry fallback:npmjs.org → npmmirror(国内镜像)→ 默认 registry
145
- PACK_OK=false
146
- for _registry in "https://registry.npmjs.org/" "https://registry.npmmirror.com/" ""; do
147
- if [ -n "$_registry" ]; then
148
- echo " 尝试 registry: $_registry"
149
- npm pack "$INSTALL_SRC" --registry "$_registry" --quiet 2>&1 && PACK_OK=true && break
150
- else
151
- echo " 尝试默认 registry..."
152
- npm pack "$INSTALL_SRC" --quiet 2>&1 && PACK_OK=true && break
153
- fi
154
- done
155
- $PACK_OK || { echo "❌ npm pack 失败(所有 registry 均不可用)"; exit 1; }
156
- TGZ_FILE=$(ls -1 *.tgz 2>/dev/null | head -1)
157
- [ -z "$TGZ_FILE" ] && echo "❌ 未找到下载的 tgz 文件" && exit 1
158
- echo " 已下载: $TGZ_FILE"
159
-
160
- tar xzf "$TGZ_FILE" -C "$EXTRACT_DIR"
161
- PACKAGE_DIR="$EXTRACT_DIR/package"
162
- [ ! -d "$PACKAGE_DIR" ] && echo "❌ 解压失败,未找到 package 目录" && exit 1
163
-
164
- # 准备 staging 目录
165
- STAGING_DIR="$(dirname "$EXTENSIONS_DIR")/.qqbot-upgrade-staging"
166
- rm -rf "$STAGING_DIR"
167
- mkdir -p "$STAGING_DIR"
168
- cp -R "$PACKAGE_DIR/." "$STAGING_DIR/"
169
-
170
- # 依赖处理
171
- if [ -d "$STAGING_DIR/node_modules" ]; then
172
- BUNDLED_COUNT=$(find "$STAGING_DIR/node_modules" -mindepth 1 -maxdepth 2 -type d | wc -l | tr -d ' ')
173
- echo " bundled 依赖已就绪(${BUNDLED_COUNT} 个包)"
174
- else
175
- echo " ⚠️ 未找到 bundled node_modules,尝试安装依赖..."
176
- NPM_TMP_CACHE=$(mktemp -d)
177
- (cd "$STAGING_DIR" && npm install --omit=dev --omit=peer --ignore-scripts --cache="$NPM_TMP_CACHE" --quiet 2>&1) || echo " ⚠️ 依赖安装失败"
178
- rm -rf "$NPM_TMP_CACHE"
179
- fi
180
-
181
- # 清理下载临时文件
182
- rm -rf "$TMPDIR_PACK" "$EXTRACT_DIR"
183
- cd "$HOME"
184
-
185
- # [2/3] 原子替换插件目录
186
- echo ""
187
- echo "[2/3] 原子替换插件目录..."
188
- TARGET_DIR="$EXTENSIONS_DIR/openclaw-qqbot"
189
- OLD_DIR="$(dirname "$EXTENSIONS_DIR")/.qqbot-upgrade-old"
190
-
191
- rm -rf "$OLD_DIR"
192
-
193
- STAGING_IN_EXT="$EXTENSIONS_DIR/.openclaw-qqbot-new"
194
- rm -rf "$STAGING_IN_EXT"
195
- mv "$STAGING_DIR" "$STAGING_IN_EXT"
196
-
197
- if [ -d "$TARGET_DIR" ]; then
198
- mv "$TARGET_DIR" "$OLD_DIR" && mv "$STAGING_IN_EXT" "$TARGET_DIR"
199
- else
200
- mv "$STAGING_IN_EXT" "$TARGET_DIR"
201
- fi
202
- rm -rf "$OLD_DIR"
203
-
204
- # 清理可能残留的旧版 staging 目录
205
- rm -rf "$EXTENSIONS_DIR/openclaw-qqbot.staging"
206
- rm -rf "$EXTENSIONS_DIR/.qqbot-upgrade-staging"
207
- rm -rf "$EXTENSIONS_DIR/.qqbot-upgrade-old"
208
-
209
- # 清理历史遗留的其他目录名
210
- for dir_name in qqbot openclaw-qq; do
211
- [ -d "$EXTENSIONS_DIR/$dir_name" ] && rm -rf "$EXTENSIONS_DIR/$dir_name"
212
- done
213
- echo " 已安装到: $TARGET_DIR"
214
-
215
- # [3/3] 输出新版本号和升级报告
216
- echo ""
217
- echo "[3/3] 验证安装..."
218
- NEW_VERSION="$(node -e "
219
- try {
220
- const fs = require('fs');
221
- const path = require('path');
222
- const p = path.join('$EXTENSIONS_DIR', 'openclaw-qqbot', 'package.json');
223
- if (fs.existsSync(p)) {
224
- const v = JSON.parse(fs.readFileSync(p, 'utf8')).version;
225
- if (v) { process.stdout.write(v); process.exit(0); }
226
- }
227
- } catch {}
228
- " 2>/dev/null || true)"
229
- echo "QQBOT_NEW_VERSION=${NEW_VERSION:-unknown}"
230
-
231
- if [ -n "$NEW_VERSION" ] && [ "$NEW_VERSION" != "unknown" ]; then
232
- echo "QQBOT_REPORT=✅ QQBot 升级完成 ($PKG_NAME): v${NEW_VERSION}"
233
- else
234
- echo "QQBOT_REPORT=⚠️ QQBot 升级异常,无法确认新版本"
235
- fi
236
-
237
- echo ""
238
- echo "==========================================="
239
- echo " ✅ 文件安装完成"
240
- echo "==========================================="
241
-
242
- # --no-restart 模式
243
- if [ "$NO_RESTART" = "true" ]; then
244
- echo ""
245
- echo "[跳过重启] --no-restart 已指定,脚本立即退出以便调用方触发 gateway restart"
246
- exit 0
247
- fi
248
-
249
- # [4/4] 配置 appid/secret(仅在提供了参数时执行)
250
- if [ -n "$APPID" ] && [ -n "$SECRET" ]; then
251
- echo ""
252
- echo "[配置] 写入 qqbot 通道配置..."
253
- DESIRED_TOKEN="${APPID}:${SECRET}"
254
-
255
- CURRENT_TOKEN=""
256
- for _app in openclaw clawdbot moltbot; do
257
- _cfg="$HOME/.$_app/$_app.json"
258
- if [ -f "$_cfg" ]; then
259
- CURRENT_TOKEN=$(node -e "
260
- const cfg = JSON.parse(require('fs').readFileSync('$_cfg', 'utf8'));
261
- const keys = ['qqbot', 'openclaw-qqbot', 'openclaw-qq'];
262
- for (const key of keys) {
263
- const ch = cfg.channels && cfg.channels[key];
264
- if (!ch) continue;
265
- if (ch.token) { process.stdout.write(ch.token); process.exit(0); }
266
- if (ch.appId && ch.clientSecret) { process.stdout.write(ch.appId + ':' + ch.clientSecret); process.exit(0); }
267
- }
268
- " 2>/dev/null || true)
269
- [ -n "$CURRENT_TOKEN" ] && break
270
- fi
271
- done
272
-
273
- if [ "$CURRENT_TOKEN" = "$DESIRED_TOKEN" ]; then
274
- echo " ✅ 当前配置已是目标值,跳过写入"
275
- elif $CMD channels add --channel qqbot --token "$DESIRED_TOKEN" 2>&1; then
276
- echo " ✅ 通道配置写入成功"
277
- else
278
- echo " ⚠️ $CMD channels add 失败,尝试直接编辑配置文件..."
279
- CONFIG_FILE="$HOME/.$CMD/$CMD.json"
280
- if [ -f "$CONFIG_FILE" ] && node -e "
281
- const fs = require('fs');
282
- const cfg = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf8'));
283
- if (!cfg.channels) cfg.channels = {};
284
- if (!cfg.channels.qqbot) cfg.channels.qqbot = {};
285
- cfg.channels.qqbot.appId = '$APPID';
286
- cfg.channels.qqbot.clientSecret = '$SECRET';
287
- fs.writeFileSync('$CONFIG_FILE', JSON.stringify(cfg, null, 4) + '\n');
288
- " 2>&1; then
289
- echo " ✅ 通道配置写入成功(直接编辑配置文件)"
290
- else
291
- echo " ❌ 配置写入失败,请手动配置:"
292
- echo " $CMD channels add --channel qqbot --token \"${APPID}:${SECRET}\""
293
- fi
294
- fi
295
- elif [ -n "$APPID" ] || [ -n "$SECRET" ]; then
296
- echo ""
297
- echo "⚠️ --appid 和 --secret 必须同时提供"
298
- fi
299
-
300
- # [5/5] 重启 gateway 使新版本生效
301
- echo ""
302
- echo "[重启] 重启 gateway 使新版本生效..."
303
- if $CMD gateway restart 2>&1; then
304
- echo " ✅ gateway 已重启"
305
- else
306
- echo " ⚠️ gateway 重启失败,请手动执行: $CMD gateway restart"
307
- fi