mirror of
https://mirror.skon.top/github.com/sliverp/qqbot
synced 2026-04-20 21:00:16 +08:00
refactor: 整理脚本目录结构,兼容原仓库插件清理
- 脚本统一移入 scripts/ 目录(pull-latest.sh, upgrade-and-run.sh, set-markdown.sh) - 删除不可用的 npm 版 scripts/pull-latest.sh,保留 git 版 - upgrade.sh / qqbot-cli.js 清理逻辑兼容原仓库 @sliverp/qqbot 和本仓库多种插件 ID - 扩展目录查找兼容 extensions/qqbot 和 extensions/openclaw-qq - 新增 moltbot 支持 - LICENSE 添加原作者 sliverp 版权声明 - 更新 README/docs 中脚本路径引用 - package.json files 字段增加 scripts 目录
This commit is contained in:
1
LICENSE
1
LICENSE
@@ -1,5 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 sliverp
|
||||
Copyright (c) 2026 Tencent Connect
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
||||
12
README.md
12
README.md
@@ -358,14 +358,14 @@ STT supports two-level configuration with priority fallback:
|
||||
### Via upgrade-and-run.sh (One-Click)
|
||||
|
||||
```bash
|
||||
bash ./upgrade-and-run.sh
|
||||
bash ./scripts/upgrade-and-run.sh
|
||||
```
|
||||
|
||||
When no `--appid` / `--secret` is provided, the script reads existing config from `~/.openclaw/openclaw.json` automatically.
|
||||
|
||||
```bash
|
||||
# First-time or override credentials
|
||||
bash ./upgrade-and-run.sh --appid YOUR_APPID --secret YOUR_SECRET
|
||||
bash ./scripts/upgrade-and-run.sh --appid YOUR_APPID --secret YOUR_SECRET
|
||||
```
|
||||
|
||||
<details>
|
||||
@@ -385,16 +385,16 @@ Environment variables `QQBOT_APPID`, `QQBOT_SECRET`, `QQBOT_TOKEN` (AppID:Secret
|
||||
### Via pull-latest.sh (Git Source)
|
||||
|
||||
```bash
|
||||
bash ./pull-latest.sh
|
||||
bash ./scripts/pull-latest.sh
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Options</summary>
|
||||
|
||||
```bash
|
||||
bash ./pull-latest.sh --branch main # specify branch (default: main)
|
||||
bash ./pull-latest.sh --force # skip prompts, force update
|
||||
bash ./pull-latest.sh --repo <git-url> # use a different repo
|
||||
bash ./scripts/pull-latest.sh --branch main # specify branch (default: main)
|
||||
bash ./scripts/pull-latest.sh --force # skip prompts, force update
|
||||
bash ./scripts/pull-latest.sh --repo <git-url> # use a different repo
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
12
README.zh.md
12
README.zh.md
@@ -354,14 +354,14 @@ STT 支持两级配置,按优先级查找:
|
||||
### 通过 upgrade-and-run.sh 一键升级
|
||||
|
||||
```bash
|
||||
bash ./upgrade-and-run.sh
|
||||
bash ./scripts/upgrade-and-run.sh
|
||||
```
|
||||
|
||||
不传 `--appid` / `--secret` 参数时,脚本会自动读取 `~/.openclaw/openclaw.json` 中已有的配置。
|
||||
|
||||
```bash
|
||||
# 首次配置或需要覆盖时
|
||||
bash ./upgrade-and-run.sh --appid YOUR_APPID --secret YOUR_SECRET
|
||||
bash ./scripts/upgrade-and-run.sh --appid YOUR_APPID --secret YOUR_SECRET
|
||||
```
|
||||
|
||||
<details>
|
||||
@@ -381,16 +381,16 @@ bash ./upgrade-and-run.sh --appid YOUR_APPID --secret YOUR_SECRET
|
||||
### 通过 pull-latest.sh(Git 源码更新)
|
||||
|
||||
```bash
|
||||
bash ./pull-latest.sh
|
||||
bash ./scripts/pull-latest.sh
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>选项</summary>
|
||||
|
||||
```bash
|
||||
bash ./pull-latest.sh --branch main # 指定分支(默认 main)
|
||||
bash ./pull-latest.sh --force # 跳过交互,强制更新
|
||||
bash ./pull-latest.sh --repo <git-url> # 使用其他仓库地址
|
||||
bash ./scripts/pull-latest.sh --branch main # 指定分支(默认 main)
|
||||
bash ./scripts/pull-latest.sh --force # 跳过交互,强制更新
|
||||
bash ./scripts/pull-latest.sh --repo <git-url> # 使用其他仓库地址
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
@@ -35,18 +35,22 @@ function detectInstallation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 需要清理的所有可能的插件 ID / 包名(原仓库 + 本仓库 + 框架推断名)
|
||||
const PLUGIN_IDS = ['qqbot', 'openclaw-qq', '@sliverp/qqbot', '@tencent-connect/openclaw-qq'];
|
||||
// 可能的扩展目录名
|
||||
const EXTENSION_DIR_NAMES = ['qqbot', 'openclaw-qq'];
|
||||
|
||||
// 清理旧版本插件,返回旧的 qqbot 配置
|
||||
function cleanupInstallation(appName) {
|
||||
const home = homedir();
|
||||
const appDir = join(home, `.${appName}`);
|
||||
const configFile = join(appDir, `${appName}.json`);
|
||||
const extensionDir = join(appDir, 'extensions', 'qqbot');
|
||||
|
||||
let oldQqbotConfig = null;
|
||||
|
||||
console.log(`\n>>> 处理 ${appName} 安装...`);
|
||||
|
||||
// 1. 先读取旧的 qqbot 配置
|
||||
// 1. 先读取旧的 qqbot 配置(尝试所有可能的 channel key)
|
||||
if (existsSync(configFile)) {
|
||||
try {
|
||||
const config = JSON.parse(readFileSync(configFile, 'utf8'));
|
||||
@@ -59,36 +63,39 @@ function cleanupInstallation(appName) {
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 删除旧的扩展目录
|
||||
if (existsSync(extensionDir)) {
|
||||
console.log(`删除旧版本插件: ${extensionDir}`);
|
||||
rmSync(extensionDir, { recursive: true, force: true });
|
||||
} else {
|
||||
console.log('未找到旧版本插件目录,跳过删除');
|
||||
// 2. 删除所有可能的旧扩展目录
|
||||
for (const dirName of EXTENSION_DIR_NAMES) {
|
||||
const extensionDir = join(appDir, 'extensions', dirName);
|
||||
if (existsSync(extensionDir)) {
|
||||
console.log(`删除旧版本插件: ${extensionDir}`);
|
||||
rmSync(extensionDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 清理配置文件中的 qqbot 相关字段
|
||||
// 3. 清理配置文件中所有可能的插件 ID 相关字段
|
||||
if (existsSync(configFile)) {
|
||||
console.log('清理配置文件中的 qqbot 字段...');
|
||||
console.log('清理配置文件中的插件字段...');
|
||||
try {
|
||||
const config = JSON.parse(readFileSync(configFile, 'utf8'));
|
||||
|
||||
// 删除 channels.qqbot
|
||||
if (config.channels?.qqbot) {
|
||||
delete config.channels.qqbot;
|
||||
console.log(' - 已删除 channels.qqbot');
|
||||
}
|
||||
for (const id of PLUGIN_IDS) {
|
||||
// 删除 channels.<id>
|
||||
if (config.channels?.[id]) {
|
||||
delete config.channels[id];
|
||||
console.log(` - 已删除 channels.${id}`);
|
||||
}
|
||||
|
||||
// 删除 plugins.entries.qqbot
|
||||
if (config.plugins?.entries?.qqbot) {
|
||||
delete config.plugins.entries.qqbot;
|
||||
console.log(' - 已删除 plugins.entries.qqbot');
|
||||
}
|
||||
// 删除 plugins.entries.<id>
|
||||
if (config.plugins?.entries?.[id]) {
|
||||
delete config.plugins.entries[id];
|
||||
console.log(` - 已删除 plugins.entries.${id}`);
|
||||
}
|
||||
|
||||
// 删除 plugins.installs.qqbot
|
||||
if (config.plugins?.installs?.qqbot) {
|
||||
delete config.plugins.installs.qqbot;
|
||||
console.log(' - 已删除 plugins.installs.qqbot');
|
||||
// 删除 plugins.installs.<id>
|
||||
if (config.plugins?.installs?.[id]) {
|
||||
delete config.plugins.installs[id];
|
||||
console.log(` - 已删除 plugins.installs.${id}`);
|
||||
}
|
||||
}
|
||||
|
||||
writeFileSync(configFile, JSON.stringify(config, null, 2));
|
||||
|
||||
@@ -138,16 +138,16 @@ openclaw config
|
||||
### 一键升级并启动
|
||||
```bash
|
||||
# 基本用法
|
||||
./upgrade-and-run.sh
|
||||
./scripts/upgrade-and-run.sh
|
||||
|
||||
# 指定 AppID 和 Secret
|
||||
./upgrade-and-run.sh --appid 123456789 --secret your_secret
|
||||
./scripts/upgrade-and-run.sh --appid 123456789 --secret your_secret
|
||||
|
||||
# 同时启用 Markdown
|
||||
./upgrade-and-run.sh --appid 123456789 --secret your_secret --markdown yes
|
||||
./scripts/upgrade-and-run.sh --appid 123456789 --secret your_secret --markdown yes
|
||||
|
||||
# 查看帮助
|
||||
./upgrade-and-run.sh --help
|
||||
./scripts/upgrade-and-run.sh --help
|
||||
```
|
||||
|
||||
**环境变量方式:**
|
||||
@@ -155,22 +155,22 @@ openclaw config
|
||||
export QQBOT_APPID="123456789"
|
||||
export QQBOT_SECRET="your_secret"
|
||||
export QQBOT_MARKDOWN="no"
|
||||
./upgrade-and-run.sh
|
||||
./scripts/upgrade-and-run.sh
|
||||
```
|
||||
|
||||
### Markdown 设置脚本
|
||||
```bash
|
||||
# 启用 Markdown
|
||||
./set-markdown.sh enable
|
||||
./scripts/set-markdown.sh enable
|
||||
|
||||
# 禁用 Markdown
|
||||
./set-markdown.sh disable
|
||||
./scripts/set-markdown.sh disable
|
||||
|
||||
# 查看当前状态
|
||||
./set-markdown.sh status
|
||||
./scripts/set-markdown.sh status
|
||||
|
||||
# 交互式选择
|
||||
./set-markdown.sh
|
||||
./scripts/set-markdown.sh
|
||||
```
|
||||
|
||||
### 升级脚本(清理旧版本)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"bin",
|
||||
"src",
|
||||
"skills",
|
||||
"scripts",
|
||||
"index.ts",
|
||||
"tsconfig.json",
|
||||
"openclaw.plugin.json",
|
||||
|
||||
310
pull-latest.sh
310
pull-latest.sh
@@ -1,310 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# QQBot 拉取最新源码并更新
|
||||
# 从 GitHub 拉取最新代码,安装依赖并重启网关
|
||||
# 兼容 clawdbot / openclaw / moltbot,macOS 开箱即用
|
||||
#
|
||||
# 用法:
|
||||
# pull-latest.sh # 拉取最新代码并更新
|
||||
# pull-latest.sh --branch main # 指定分支(默认 main)
|
||||
# pull-latest.sh --force # 跳过交互,强制更新
|
||||
# pull-latest.sh --repo <git-url> # 指定仓库地址
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
##############################################################################
|
||||
# 常量 & 参数
|
||||
##############################################################################
|
||||
readonly DEFAULT_REPO="https://github.com/tencent-connect/openclaw-qq.git"
|
||||
readonly GATEWAY_PORT=18789
|
||||
readonly SUPPORTED_CLIS=(openclaw clawdbot moltbot)
|
||||
|
||||
FORCE=false
|
||||
BRANCH="main"
|
||||
REPO_URL=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-f|--force) FORCE=true; shift ;;
|
||||
-b|--branch) BRANCH="$2"; shift 2 ;;
|
||||
--repo) REPO_URL="$2"; shift 2 ;;
|
||||
-h|--help)
|
||||
echo "QQBot 拉取最新源码并更新"
|
||||
echo ""
|
||||
echo "用法:"
|
||||
echo " pull-latest.sh # 拉取最新代码并更新"
|
||||
echo " pull-latest.sh --branch main # 指定分支(默认 main)"
|
||||
echo " pull-latest.sh --force # 跳过交互,强制更新"
|
||||
echo " pull-latest.sh --repo <git-url> # 指定仓库地址"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "未知选项: $1 (使用 --help 查看帮助)"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
REPO_URL="${REPO_URL:-$DEFAULT_REPO}"
|
||||
|
||||
##############################################################################
|
||||
# 工具函数
|
||||
##############################################################################
|
||||
json_get() {
|
||||
local file="$1" expr="$2"
|
||||
node -e "process.stdout.write(String((function(){$expr})(JSON.parse(require('fs').readFileSync('$file','utf8')))||''))" 2>/dev/null || true
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# 环境检查
|
||||
##############################################################################
|
||||
printf "%b\n" "\033[32m=========================================\033[0m"
|
||||
printf "%b\n" "\033[32m QQBot 拉取最新源码并更新\033[0m"
|
||||
printf "%b\n" "\033[32m=========================================\033[0m"
|
||||
echo ""
|
||||
|
||||
# 检查必要命令
|
||||
for dep in node npm git; do
|
||||
if ! command -v "$dep" &>/dev/null; then
|
||||
printf "%b\n" "\033[31m❌ 未检测到 $dep,请先安装\033[0m"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
printf "%b\n" "\033[34m系统信息:\033[0m"
|
||||
echo " macOS $(sw_vers -productVersion 2>/dev/null || echo '未知')"
|
||||
echo " Node $(node -v)"
|
||||
echo " npm $(npm -v)"
|
||||
echo " Git $(git --version 2>/dev/null | awk '{print $3}')"
|
||||
echo " 仓库 $REPO_URL"
|
||||
echo " 分支 $BRANCH"
|
||||
|
||||
# 检测 CLI
|
||||
CMD=""
|
||||
for name in "${SUPPORTED_CLIS[@]}"; do
|
||||
if command -v "$name" &>/dev/null; then
|
||||
CMD="$name"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ -z "$CMD" ]; then
|
||||
printf "%b\n" "\033[31m❌ 未找到 openclaw / clawdbot / moltbot 命令,请先安装其中之一\033[0m"
|
||||
exit 1
|
||||
fi
|
||||
echo " CLI $CMD ($($CMD --version 2>/dev/null || echo '未知版本'))"
|
||||
|
||||
APP_HOME="$HOME/.$CMD"
|
||||
APP_CONFIG="$APP_HOME/$CMD.json"
|
||||
|
||||
##############################################################################
|
||||
# 定位插件目录
|
||||
##############################################################################
|
||||
PROJ_DIR=""
|
||||
FRESH_INSTALL=false
|
||||
|
||||
for app in "${SUPPORTED_CLIS[@]}"; do
|
||||
ext_dir="$HOME/.$app/extensions/qqbot"
|
||||
if [ -d "$ext_dir" ] && [ -f "$ext_dir/package.json" ]; then
|
||||
PROJ_DIR="$ext_dir"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$PROJ_DIR" ]; then
|
||||
PROJ_DIR="$APP_HOME/extensions/qqbot"
|
||||
FRESH_INSTALL=true
|
||||
echo " 插件 未安装(首次安装)"
|
||||
else
|
||||
echo " 插件 $PROJ_DIR"
|
||||
fi
|
||||
|
||||
##############################################################################
|
||||
# 第一步:获取当前版本
|
||||
##############################################################################
|
||||
echo ""
|
||||
printf "%b\n" "\033[34m1. 获取当前版本...\033[0m"
|
||||
|
||||
LOCAL_VER=""
|
||||
LOCAL_COMMIT=""
|
||||
if [ "$FRESH_INSTALL" = true ]; then
|
||||
echo " 首次安装,无本地版本"
|
||||
else
|
||||
[ -f "$PROJ_DIR/package.json" ] && LOCAL_VER=$(json_get "$PROJ_DIR/package.json" "c => c.version")
|
||||
[ -d "$PROJ_DIR/.git" ] && LOCAL_COMMIT=$(cd "$PROJ_DIR" && git rev-parse --short HEAD 2>/dev/null || echo "")
|
||||
echo " 当前版本: ${LOCAL_VER:-未知}${LOCAL_COMMIT:+ (${LOCAL_COMMIT})}"
|
||||
fi
|
||||
|
||||
##############################################################################
|
||||
# 第二步:备份通道配置
|
||||
##############################################################################
|
||||
echo ""
|
||||
printf "%b\n" "\033[34m2. 备份通道配置...\033[0m"
|
||||
|
||||
SAVED_CHANNELS_JSON=""
|
||||
for app in "${SUPPORTED_CLIS[@]}"; do
|
||||
cfg="$HOME/.$app/$app.json"
|
||||
[ -f "$cfg" ] || continue
|
||||
SAVED_CHANNELS_JSON=$(node -e "
|
||||
const cfg = JSON.parse(require('fs').readFileSync('$cfg', 'utf8'));
|
||||
const ch = cfg.channels && cfg.channels.qqbot;
|
||||
if (ch) process.stdout.write(JSON.stringify(ch));
|
||||
" 2>/dev/null || true)
|
||||
[ -n "$SAVED_CHANNELS_JSON" ] && break
|
||||
done
|
||||
|
||||
if [ -n "$SAVED_CHANNELS_JSON" ]; then
|
||||
echo " ✅ 已备份 qqbot 通道配置"
|
||||
else
|
||||
echo " ℹ️ 未找到已有通道配置"
|
||||
fi
|
||||
|
||||
##############################################################################
|
||||
# 第三步:拉取最新代码
|
||||
##############################################################################
|
||||
echo ""
|
||||
printf "%b\n" "\033[34m3. 拉取最新代码...\033[0m"
|
||||
|
||||
TMP_DIR="${TMPDIR:-/tmp}/qqbot-update-$$"
|
||||
cleanup() { rm -rf "$TMP_DIR" 2>/dev/null; }
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
if [ -d "$PROJ_DIR/.git" ] && [ "$FRESH_INSTALL" = false ]; then
|
||||
cd "$PROJ_DIR"
|
||||
|
||||
# 有本地修改直接重置,插件目录不需要保留用户改动
|
||||
if ! git diff --quiet 2>/dev/null || ! git diff --cached --quiet 2>/dev/null; then
|
||||
echo " 检测到本地修改,自动重置..."
|
||||
git checkout -- . 2>/dev/null
|
||||
git clean -fd 2>/dev/null
|
||||
fi
|
||||
|
||||
echo " 切换到分支 $BRANCH..."
|
||||
git fetch --all --prune 2>&1 | tail -3
|
||||
git checkout "$BRANCH" 2>/dev/null || git checkout -b "$BRANCH" "origin/$BRANCH" 2>/dev/null || true
|
||||
git reset --hard "origin/$BRANCH" 2>/dev/null
|
||||
|
||||
REMOTE_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "")
|
||||
NEW_VER=$(json_get "$PROJ_DIR/package.json" "c => c.version")
|
||||
|
||||
if [ -n "$LOCAL_COMMIT" ] && [ "$LOCAL_COMMIT" = "$REMOTE_COMMIT" ]; then
|
||||
echo " ✅ 已是最新 ($LOCAL_VER, commit: $LOCAL_COMMIT),继续检查依赖..."
|
||||
else
|
||||
echo " 更新: ${LOCAL_COMMIT:-???} → ${REMOTE_COMMIT}"
|
||||
git --no-pager log --oneline "${LOCAL_COMMIT}..HEAD" 2>/dev/null | head -10 || true
|
||||
fi
|
||||
else
|
||||
rm -rf "$TMP_DIR"
|
||||
echo " 克隆仓库..."
|
||||
if ! git clone --branch "$BRANCH" --depth 1 "$REPO_URL" "$TMP_DIR" 2>&1 | tail -3; then
|
||||
printf "%b\n" "\033[31m❌ Git clone 失败\033[0m"
|
||||
echo ""
|
||||
echo "请排查:"
|
||||
echo " 1. 检查网络: curl -I https://github.com"
|
||||
echo " 2. 检查仓库地址: $REPO_URL"
|
||||
echo " 3. 如果是私有仓库,确认已配置 SSH key 或 token"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$PROJ_DIR"
|
||||
rsync -a --delete --exclude 'node_modules' "$TMP_DIR/" "$PROJ_DIR/"
|
||||
|
||||
cd "$PROJ_DIR"
|
||||
REMOTE_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "")
|
||||
NEW_VER=$(json_get "$PROJ_DIR/package.json" "c => c.version")
|
||||
echo " 已克隆到版本: ${NEW_VER:-未知} (${REMOTE_COMMIT})"
|
||||
cleanup
|
||||
fi
|
||||
|
||||
NEW_VER="${NEW_VER:-未知}"
|
||||
printf "%b\n" "\033[32m ✅ 代码已更新到 $NEW_VER\033[0m"
|
||||
|
||||
##############################################################################
|
||||
# 第四步:安装依赖
|
||||
##############################################################################
|
||||
echo ""
|
||||
printf "%b\n" "\033[34m4. 安装依赖...\033[0m"
|
||||
|
||||
cd "$PROJ_DIR"
|
||||
if ! npm install --omit=dev 2>&1 | tail -5; then
|
||||
printf "%b\n" "\033[31m❌ npm 依赖安装失败\033[0m"
|
||||
echo ""
|
||||
echo "请排查:"
|
||||
echo " 1. 手动重试: cd $PROJ_DIR && npm install --omit=dev"
|
||||
echo " 2. 清理后重试: rm -rf $PROJ_DIR/node_modules && npm install --omit=dev"
|
||||
echo " 3. 切换镜像: npm config set registry https://registry.npmmirror.com/"
|
||||
exit 1
|
||||
fi
|
||||
echo " ✅ 依赖安装完成"
|
||||
|
||||
##############################################################################
|
||||
# 第五步:恢复配置 → 重启网关
|
||||
##############################################################################
|
||||
echo ""
|
||||
printf "%b\n" "\033[34m5. 恢复配置并重启网关...\033[0m"
|
||||
|
||||
# 恢复通道配置
|
||||
if [ -n "$SAVED_CHANNELS_JSON" ]; then
|
||||
if node -e "
|
||||
const fs = require('fs');
|
||||
const cfg = JSON.parse(fs.readFileSync('$APP_CONFIG', 'utf8'));
|
||||
cfg.channels = cfg.channels || {};
|
||||
cfg.channels.qqbot = $SAVED_CHANNELS_JSON;
|
||||
fs.writeFileSync('$APP_CONFIG', JSON.stringify(cfg, null, 4) + '\n');
|
||||
" 2>/dev/null; then
|
||||
echo " ✅ 通道配置已恢复"
|
||||
else
|
||||
printf "%b\n" "\033[33m ⚠️ 通道配置写入失败,请手动检查: $APP_CONFIG\033[0m"
|
||||
fi
|
||||
elif [ "$FRESH_INSTALL" = true ]; then
|
||||
echo ""
|
||||
printf "%b\n" "\033[33m ⚠️ 首次安装,请配置 QQ Bot 凭据:\033[0m"
|
||||
echo " $CMD channels add --channel qqbot --token 'YOUR_APPID:YOUR_SECRET'"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# 停止旧 gateway
|
||||
echo " 停止旧网关..."
|
||||
$CMD gateway stop 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
# 强制杀占用端口的进程
|
||||
PORT_PID=$(lsof -ti:"$GATEWAY_PORT" 2>/dev/null || true)
|
||||
if [ -n "$PORT_PID" ]; then
|
||||
printf "%b\n" "\033[33m ⚠️ 端口 $GATEWAY_PORT 仍被占用 (PID: $PORT_PID),强制终止...\033[0m"
|
||||
kill -9 $PORT_PID 2>/dev/null || true
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
# 卸载 launchd 服务(防止自动拉起旧进程)
|
||||
for svc in ai.openclaw.gateway ai.clawdbot.gateway ai.moltbot.gateway; do
|
||||
launchctl bootout "gui/$(id -u)/$svc" 2>/dev/null || true
|
||||
done
|
||||
|
||||
# 启动网关
|
||||
echo " 启动网关..."
|
||||
if $CMD gateway 2>&1; then
|
||||
printf "%b\n" "\033[32m ✅ 网关已启动\033[0m"
|
||||
else
|
||||
echo ""
|
||||
printf "%b\n" "\033[33m ⚠️ 网关启动失败(不影响已安装的插件)\033[0m"
|
||||
echo ""
|
||||
echo " 请手动启动:"
|
||||
echo " 1. 安装服务: $CMD gateway install"
|
||||
echo " 2. 启动网关: $CMD gateway"
|
||||
echo " 3. 查看日志: $CMD logs --follow"
|
||||
fi
|
||||
|
||||
##############################################################################
|
||||
# 完成
|
||||
##############################################################################
|
||||
echo ""
|
||||
printf "%b\n" "\033[32m=========================================\033[0m"
|
||||
printf "%b\n" "\033[32m ✅ QQBot 已更新到 ${NEW_VER}${REMOTE_COMMIT:+ (${REMOTE_COMMIT})}\033[0m"
|
||||
[ -n "$LOCAL_VER" ] && printf "%b\n" "\033[32m (从 ${LOCAL_VER}${LOCAL_COMMIT:+ (${LOCAL_COMMIT})} 升级)\033[0m"
|
||||
printf "%b\n" "\033[32m=========================================\033[0m"
|
||||
echo ""
|
||||
echo "常用命令:"
|
||||
echo " $CMD logs --follow # 跟踪日志"
|
||||
echo " $CMD gateway restart # 重启服务"
|
||||
echo " $CMD plugins list # 查看插件列表"
|
||||
echo " cd $PROJ_DIR && git log # 查看更新历史"
|
||||
echo "========================================="
|
||||
@@ -1,336 +1,316 @@
|
||||
#!/bin/bash
|
||||
|
||||
# QQBot 拉取最新 npm 包并更新脚本
|
||||
# 从 npm 下载 @tencent-connect/openclaw-qq@latest,解压覆盖本地源码,重新安装插件并重启
|
||||
# QQBot 拉取最新源码并更新
|
||||
# 从 GitHub 拉取最新代码,安装依赖并重启网关
|
||||
# 兼容 clawdbot / openclaw / moltbot,macOS 开箱即用
|
||||
#
|
||||
# 用法:
|
||||
# ./scripts/pull-latest.sh # 更新到最新版
|
||||
# ./scripts/pull-latest.sh @tencent-connect/openclaw-qq@1.5.2 # 更新到指定版本
|
||||
# ./scripts/pull-latest.sh --force # 跳过交互,强制重新安装
|
||||
# ./scripts/pull-latest.sh --force @tencent-connect/openclaw-qq@1.5.3
|
||||
# pull-latest.sh # 拉取最新代码并更新
|
||||
# pull-latest.sh --branch main # 指定分支(默认 main)
|
||||
# pull-latest.sh --force # 跳过交互,强制更新
|
||||
# pull-latest.sh --repo <git-url> # 指定仓库地址
|
||||
|
||||
set -e
|
||||
set -euo pipefail
|
||||
|
||||
##############################################################################
|
||||
# 常量 & 参数
|
||||
##############################################################################
|
||||
readonly DEFAULT_REPO="https://github.com/tencent-connect/openclaw-qq.git"
|
||||
readonly GATEWAY_PORT=18789
|
||||
readonly SUPPORTED_CLIS=(openclaw clawdbot moltbot)
|
||||
|
||||
# ============================================================
|
||||
# 参数解析
|
||||
# ============================================================
|
||||
FORCE=false
|
||||
PKG_NAME="@tencent-connect/openclaw-qq"
|
||||
PKG_SPEC=""
|
||||
BRANCH="main"
|
||||
REPO_URL=""
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
-f|--force) FORCE=true ;;
|
||||
*) PKG_SPEC="$arg" ;;
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-f|--force) FORCE=true; shift ;;
|
||||
-b|--branch) BRANCH="$2"; shift 2 ;;
|
||||
--repo) REPO_URL="$2"; shift 2 ;;
|
||||
-h|--help)
|
||||
echo "QQBot 拉取最新源码并更新"
|
||||
echo ""
|
||||
echo "用法:"
|
||||
echo " pull-latest.sh # 拉取最新代码并更新"
|
||||
echo " pull-latest.sh --branch main # 指定分支(默认 main)"
|
||||
echo " pull-latest.sh --force # 跳过交互,强制更新"
|
||||
echo " pull-latest.sh --repo <git-url> # 指定仓库地址"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "未知选项: $1 (使用 --help 查看帮助)"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
PKG_SPEC="${PKG_SPEC:-${PKG_NAME}@latest}"
|
||||
REPO_URL="${REPO_URL:-$DEFAULT_REPO}"
|
||||
|
||||
# ============================================================
|
||||
# 定位项目目录(兼容从任意位置执行)
|
||||
# ============================================================
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# 如果脚本在 scripts/ 子目录里,往上一级就是项目根目录
|
||||
if [ "$(basename "$SCRIPT_DIR")" = "scripts" ]; then
|
||||
PROJ_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
else
|
||||
PROJ_DIR="$SCRIPT_DIR"
|
||||
fi
|
||||
cd "$PROJ_DIR"
|
||||
|
||||
# ============================================================
|
||||
# 前置依赖检查
|
||||
# ============================================================
|
||||
check_cmd() {
|
||||
if ! command -v "$1" &>/dev/null; then
|
||||
echo "❌ 缺少必要命令: $1"
|
||||
echo " $2"
|
||||
exit 1
|
||||
fi
|
||||
##############################################################################
|
||||
# 工具函数
|
||||
##############################################################################
|
||||
json_get() {
|
||||
local file="$1" expr="$2"
|
||||
node -e "process.stdout.write(String((function(){$expr})(JSON.parse(require('fs').readFileSync('$file','utf8')))||''))" 2>/dev/null || true
|
||||
}
|
||||
|
||||
check_cmd node "请先安装 Node.js: https://nodejs.org/ 或 brew install node"
|
||||
check_cmd npm "npm 通常随 Node.js 一起安装"
|
||||
check_cmd tar "macOS 自带 tar,如果缺失请检查系统完整性"
|
||||
|
||||
echo "========================================="
|
||||
echo " QQBot 拉取最新版本并更新"
|
||||
echo "========================================="
|
||||
##############################################################################
|
||||
# 环境检查
|
||||
##############################################################################
|
||||
printf "%b\n" "\033[32m=========================================\033[0m"
|
||||
printf "%b\n" "\033[32m QQBot 拉取最新源码并更新\033[0m"
|
||||
printf "%b\n" "\033[32m=========================================\033[0m"
|
||||
echo ""
|
||||
echo "系统信息:"
|
||||
echo " macOS $(sw_vers -productVersion 2>/dev/null || echo '未知')"
|
||||
echo " Node $(node -v 2>/dev/null)"
|
||||
echo " npm $(npm -v 2>/dev/null)"
|
||||
|
||||
# ============================================================
|
||||
# 0. 检测 openclaw / clawdbot / moltbot
|
||||
# ============================================================
|
||||
# 检查必要命令
|
||||
for dep in node npm git; do
|
||||
if ! command -v "$dep" &>/dev/null; then
|
||||
printf "%b\n" "\033[31m❌ 未检测到 $dep,请先安装\033[0m"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
printf "%b\n" "\033[34m系统信息:\033[0m"
|
||||
echo " macOS $(sw_vers -productVersion 2>/dev/null || echo '未知')"
|
||||
echo " Node $(node -v)"
|
||||
echo " npm $(npm -v)"
|
||||
echo " Git $(git --version 2>/dev/null | awk '{print $3}')"
|
||||
echo " 仓库 $REPO_URL"
|
||||
echo " 分支 $BRANCH"
|
||||
|
||||
# 检测 CLI
|
||||
CMD=""
|
||||
for name in openclaw clawdbot moltbot; do
|
||||
for name in "${SUPPORTED_CLIS[@]}"; do
|
||||
if command -v "$name" &>/dev/null; then
|
||||
CMD="$name"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ -z "$CMD" ]; then
|
||||
echo ""
|
||||
echo "❌ 未找到 openclaw / clawdbot / moltbot 命令"
|
||||
echo " 请先安装其中之一,参考: https://docs.openclaw.ai"
|
||||
printf "%b\n" "\033[31m❌ 未找到 openclaw / clawdbot / moltbot 命令,请先安装其中之一\033[0m"
|
||||
exit 1
|
||||
fi
|
||||
echo " CLI $CMD ($($CMD --version 2>/dev/null || echo '未知版本'))"
|
||||
echo " CLI $CMD ($($CMD --version 2>/dev/null || echo '未知版本'))"
|
||||
|
||||
# ============================================================
|
||||
# 1. 获取当前本地版本
|
||||
# ============================================================
|
||||
LOCAL_VER=""
|
||||
if [ -f "$PROJ_DIR/package.json" ]; then
|
||||
LOCAL_VER=$(node -e "process.stdout.write(JSON.parse(require('fs').readFileSync('$PROJ_DIR/package.json','utf8')).version||'')" 2>/dev/null || true)
|
||||
fi
|
||||
echo ""
|
||||
echo "[1/5] 当前本地版本: ${LOCAL_VER:-未知}"
|
||||
APP_HOME="$HOME/.$CMD"
|
||||
APP_CONFIG="$APP_HOME/$CMD.json"
|
||||
|
||||
# ============================================================
|
||||
# 2. 查询 npm 远程版本
|
||||
# ============================================================
|
||||
echo ""
|
||||
echo "[2/5] 查询 npm 版本..."
|
||||
##############################################################################
|
||||
# 定位插件目录
|
||||
##############################################################################
|
||||
PROJ_DIR=""
|
||||
FRESH_INSTALL=false
|
||||
|
||||
# 如果指定了具体版本号,直接从 PKG_SPEC 提取;否则查询 latest
|
||||
if echo "$PKG_SPEC" | grep -qE '@[0-9]+\.[0-9]+'; then
|
||||
REMOTE_VER=$(echo "$PKG_SPEC" | sed 's/.*@//')
|
||||
else
|
||||
REMOTE_VER=$(npm view "$PKG_NAME" version 2>/dev/null || echo "")
|
||||
fi
|
||||
|
||||
if [ -z "$REMOTE_VER" ]; then
|
||||
echo "❌ 无法查询 $PKG_NAME 的版本,请检查网络"
|
||||
echo " 当前 npm 源: $(npm config get registry 2>/dev/null)"
|
||||
echo ""
|
||||
echo " 可尝试切换镜像源:"
|
||||
echo " npm config set registry https://registry.npmmirror.com/"
|
||||
exit 1
|
||||
fi
|
||||
echo "目标版本: ${REMOTE_VER}"
|
||||
|
||||
if [ "$LOCAL_VER" = "$REMOTE_VER" ]; then
|
||||
echo ""
|
||||
echo "✅ 本地版本已是最新 ($LOCAL_VER)"
|
||||
if [ "$FORCE" = true ]; then
|
||||
echo "已指定 --force,继续重新安装..."
|
||||
else
|
||||
printf "是否强制重新安装? (y/N): "
|
||||
read -r force_choice </dev/tty 2>/dev/null || force_choice="N"
|
||||
case "$force_choice" in
|
||||
[Yy]* ) echo "强制重新安装..." ;;
|
||||
* ) echo "跳过更新。"; exit 0 ;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# 3. 备份通道配置
|
||||
# ============================================================
|
||||
echo ""
|
||||
echo "[3/5] 备份已有通道配置..."
|
||||
|
||||
SAVED_QQBOT_TOKEN=""
|
||||
SAVED_MARKDOWN=""
|
||||
|
||||
for APP_NAME in openclaw clawdbot moltbot; do
|
||||
CONFIG_FILE="$HOME/.$APP_NAME/$APP_NAME.json"
|
||||
[ -f "$CONFIG_FILE" ] || continue
|
||||
|
||||
if [ -z "$SAVED_QQBOT_TOKEN" ]; then
|
||||
SAVED_QQBOT_TOKEN=$(node -e "
|
||||
const cfg = JSON.parse(require('fs').readFileSync('$CONFIG_FILE', 'utf8'));
|
||||
const ch = cfg.channels && cfg.channels.qqbot;
|
||||
if (!ch) process.exit(0);
|
||||
if (ch.token) { process.stdout.write(ch.token); process.exit(0); }
|
||||
if (ch.appId && ch.clientSecret) { process.stdout.write(ch.appId + ':' + ch.clientSecret); process.exit(0); }
|
||||
" 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
if [ -z "$SAVED_MARKDOWN" ]; then
|
||||
SAVED_MARKDOWN=$(node -e "
|
||||
const cfg = JSON.parse(require('fs').readFileSync('$CONFIG_FILE', 'utf8'));
|
||||
const v = (cfg.channels && cfg.channels.qqbot && cfg.channels.qqbot.markdownSupport);
|
||||
if (v !== undefined && v !== null) process.stdout.write(String(v));
|
||||
" 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
[ -n "$SAVED_QQBOT_TOKEN" ] && [ -n "$SAVED_MARKDOWN" ] && break
|
||||
for app in "${SUPPORTED_CLIS[@]}"; do
|
||||
for plugin_dir in qqbot openclaw-qq; do
|
||||
ext_dir="$HOME/.$app/extensions/$plugin_dir"
|
||||
if [ -d "$ext_dir" ] && [ -f "$ext_dir/package.json" ]; then
|
||||
PROJ_DIR="$ext_dir"
|
||||
break 2
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
if [ -n "$SAVED_QQBOT_TOKEN" ]; then
|
||||
echo "已备份 qqbot 通道 token: ${SAVED_QQBOT_TOKEN:0:10}..."
|
||||
if [ -z "$PROJ_DIR" ]; then
|
||||
PROJ_DIR="$APP_HOME/extensions/qqbot"
|
||||
FRESH_INSTALL=true
|
||||
echo " 插件 未安装(首次安装)"
|
||||
else
|
||||
echo "未找到已有通道配置(首次安装或已清理)"
|
||||
echo " 插件 $PROJ_DIR"
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# 4. 下载并解压最新包
|
||||
# ============================================================
|
||||
##############################################################################
|
||||
# 第一步:获取当前版本
|
||||
##############################################################################
|
||||
echo ""
|
||||
echo "[4/5] 下载 $PKG_SPEC 并更新本地文件..."
|
||||
printf "%b\n" "\033[34m1. 获取当前版本...\033[0m"
|
||||
|
||||
TMP_DIR="$PROJ_DIR/.qqbot-update-tmp"
|
||||
LOCAL_VER=""
|
||||
LOCAL_COMMIT=""
|
||||
if [ "$FRESH_INSTALL" = true ]; then
|
||||
echo " 首次安装,无本地版本"
|
||||
else
|
||||
[ -f "$PROJ_DIR/package.json" ] && LOCAL_VER=$(json_get "$PROJ_DIR/package.json" "c => c.version")
|
||||
[ -d "$PROJ_DIR/.git" ] && LOCAL_COMMIT=$(cd "$PROJ_DIR" && git rev-parse --short HEAD 2>/dev/null || echo "")
|
||||
echo " 当前版本: ${LOCAL_VER:-未知}${LOCAL_COMMIT:+ (${LOCAL_COMMIT})}"
|
||||
fi
|
||||
|
||||
# 清理函数:删除临时文件夹
|
||||
cleanup() {
|
||||
if [ -d "$TMP_DIR" ]; then
|
||||
echo "清理临时文件夹: $TMP_DIR"
|
||||
rm -rf "$TMP_DIR"
|
||||
fi
|
||||
}
|
||||
# 正常退出、中断、终止时都清理
|
||||
##############################################################################
|
||||
# 第二步:备份通道配置
|
||||
##############################################################################
|
||||
echo ""
|
||||
printf "%b\n" "\033[34m2. 备份通道配置...\033[0m"
|
||||
|
||||
SAVED_CHANNELS_JSON=""
|
||||
for app in "${SUPPORTED_CLIS[@]}"; do
|
||||
cfg="$HOME/.$app/$app.json"
|
||||
[ -f "$cfg" ] || continue
|
||||
SAVED_CHANNELS_JSON=$(node -e "
|
||||
const cfg = JSON.parse(require('fs').readFileSync('$cfg', 'utf8'));
|
||||
// 尝试所有可能的 channel key(原仓库 + 本仓库)
|
||||
const keys = ['qqbot', 'openclaw-qq'];
|
||||
for (const key of keys) {
|
||||
const ch = cfg.channels && cfg.channels[key];
|
||||
if (ch) { process.stdout.write(JSON.stringify(ch)); process.exit(0); }
|
||||
}
|
||||
" 2>/dev/null || true)
|
||||
[ -n "$SAVED_CHANNELS_JSON" ] && break
|
||||
done
|
||||
|
||||
if [ -n "$SAVED_CHANNELS_JSON" ]; then
|
||||
echo " ✅ 已备份 qqbot 通道配置"
|
||||
else
|
||||
echo " ℹ️ 未找到已有通道配置"
|
||||
fi
|
||||
|
||||
##############################################################################
|
||||
# 第三步:拉取最新代码
|
||||
##############################################################################
|
||||
echo ""
|
||||
printf "%b\n" "\033[34m3. 拉取最新代码...\033[0m"
|
||||
|
||||
TMP_DIR="${TMPDIR:-/tmp}/qqbot-update-$$"
|
||||
cleanup() { rm -rf "$TMP_DIR" 2>/dev/null; }
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
# 如果上次意外残留,先清理
|
||||
[ -d "$TMP_DIR" ] && rm -rf "$TMP_DIR"
|
||||
mkdir -p "$TMP_DIR"
|
||||
if [ -d "$PROJ_DIR/.git" ] && [ "$FRESH_INSTALL" = false ]; then
|
||||
cd "$PROJ_DIR"
|
||||
|
||||
echo "下载中..."
|
||||
TARBALL=$(cd "$TMP_DIR" && npm pack "$PKG_SPEC" 2>/dev/null)
|
||||
if [ -z "$TARBALL" ] || [ ! -f "$TMP_DIR/$TARBALL" ]; then
|
||||
echo "❌ 下载失败"
|
||||
echo " 请检查网络连接,或尝试:"
|
||||
echo " npm pack $PKG_SPEC"
|
||||
exit 1
|
||||
fi
|
||||
echo "已下载: $TARBALL"
|
||||
|
||||
echo "解压中..."
|
||||
tar xzf "$TMP_DIR/$TARBALL" -C "$TMP_DIR"
|
||||
|
||||
PACK_DIR="$TMP_DIR/package"
|
||||
if [ ! -d "$PACK_DIR" ]; then
|
||||
echo "❌ 解压后未找到 package 目录"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NEW_VER=$(node -e "process.stdout.write(JSON.parse(require('fs').readFileSync('$PACK_DIR/package.json','utf8')).version||'')" 2>/dev/null || echo "$REMOTE_VER")
|
||||
echo "将更新到版本: $NEW_VER"
|
||||
|
||||
# 同步文件(不用 rsync,用 tar + cp 保证 macOS 原生兼容)
|
||||
echo "同步文件到本地..."
|
||||
|
||||
# 把包中所有文件复制过来,跳过不该覆盖的目录
|
||||
(
|
||||
cd "$PACK_DIR"
|
||||
find . -type f | while IFS= read -r f; do
|
||||
case "$f" in
|
||||
./.DS_Store|./.git/*|./node_modules/*) continue ;;
|
||||
esac
|
||||
dir=$(dirname "$f")
|
||||
mkdir -p "$PROJ_DIR/$dir"
|
||||
cp -f "$f" "$PROJ_DIR/$f"
|
||||
done
|
||||
)
|
||||
|
||||
echo "✅ 文件已更新到 $NEW_VER"
|
||||
|
||||
# 删除临时文件夹(不等到 EXIT,立即清理)
|
||||
echo "删除临时文件夹..."
|
||||
rm -rf "$TMP_DIR"
|
||||
|
||||
echo "安装依赖..."
|
||||
cd "$PROJ_DIR"
|
||||
npm install --omit=dev 2>&1 | tail -5
|
||||
|
||||
# ============================================================
|
||||
# 5. 卸载旧插件、安装新插件、恢复配置、重启
|
||||
# ============================================================
|
||||
echo ""
|
||||
echo "[5/5] 重新安装插件并重启..."
|
||||
|
||||
# 清理旧版本(配置 + 扩展目录)
|
||||
if [ -f "$PROJ_DIR/scripts/upgrade.sh" ]; then
|
||||
echo "清理旧版本插件..."
|
||||
bash "$PROJ_DIR/scripts/upgrade.sh"
|
||||
fi
|
||||
|
||||
# 强制删除已有扩展目录,防止 "plugin already exists" 错误
|
||||
for APP_NAME in openclaw clawdbot moltbot; do
|
||||
EXT_DIR="$HOME/.$APP_NAME/extensions/qqbot"
|
||||
if [ -d "$EXT_DIR" ]; then
|
||||
echo "删除已有扩展目录: $EXT_DIR"
|
||||
rm -rf "$EXT_DIR"
|
||||
# 有本地修改直接重置,插件目录不需要保留用户改动
|
||||
if ! git diff --quiet 2>/dev/null || ! git diff --cached --quiet 2>/dev/null; then
|
||||
echo " 检测到本地修改,自动重置..."
|
||||
git checkout -- . 2>/dev/null
|
||||
git clean -fd 2>/dev/null
|
||||
fi
|
||||
done
|
||||
|
||||
# 安装插件
|
||||
echo " 切换到分支 $BRANCH..."
|
||||
git fetch --all --prune 2>&1 | tail -3
|
||||
git checkout "$BRANCH" 2>/dev/null || git checkout -b "$BRANCH" "origin/$BRANCH" 2>/dev/null || true
|
||||
git reset --hard "origin/$BRANCH" 2>/dev/null
|
||||
|
||||
REMOTE_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "")
|
||||
NEW_VER=$(json_get "$PROJ_DIR/package.json" "c => c.version")
|
||||
|
||||
if [ -n "$LOCAL_COMMIT" ] && [ "$LOCAL_COMMIT" = "$REMOTE_COMMIT" ]; then
|
||||
echo " ✅ 已是最新 ($LOCAL_VER, commit: $LOCAL_COMMIT),继续检查依赖..."
|
||||
else
|
||||
echo " 更新: ${LOCAL_COMMIT:-???} → ${REMOTE_COMMIT}"
|
||||
git --no-pager log --oneline "${LOCAL_COMMIT}..HEAD" 2>/dev/null | head -10 || true
|
||||
fi
|
||||
else
|
||||
rm -rf "$TMP_DIR"
|
||||
echo " 克隆仓库..."
|
||||
if ! git clone --branch "$BRANCH" --depth 1 "$REPO_URL" "$TMP_DIR" 2>&1 | tail -3; then
|
||||
printf "%b\n" "\033[31m❌ Git clone 失败\033[0m"
|
||||
echo ""
|
||||
echo "请排查:"
|
||||
echo " 1. 检查网络: curl -I https://github.com"
|
||||
echo " 2. 检查仓库地址: $REPO_URL"
|
||||
echo " 3. 如果是私有仓库,确认已配置 SSH key 或 token"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$PROJ_DIR"
|
||||
rsync -a --delete --exclude 'node_modules' "$TMP_DIR/" "$PROJ_DIR/"
|
||||
|
||||
cd "$PROJ_DIR"
|
||||
REMOTE_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "")
|
||||
NEW_VER=$(json_get "$PROJ_DIR/package.json" "c => c.version")
|
||||
echo " 已克隆到版本: ${NEW_VER:-未知} (${REMOTE_COMMIT})"
|
||||
cleanup
|
||||
fi
|
||||
|
||||
NEW_VER="${NEW_VER:-未知}"
|
||||
printf "%b\n" "\033[32m ✅ 代码已更新到 $NEW_VER\033[0m"
|
||||
|
||||
##############################################################################
|
||||
# 第四步:安装依赖
|
||||
##############################################################################
|
||||
echo ""
|
||||
echo "安装新版本插件..."
|
||||
if ! $CMD plugins install . 2>&1; then
|
||||
echo "❌ 插件安装失败,请检查上方错误信息"
|
||||
printf "%b\n" "\033[34m4. 安装依赖...\033[0m"
|
||||
|
||||
cd "$PROJ_DIR"
|
||||
if ! npm install --omit=dev 2>&1 | tail -5; then
|
||||
printf "%b\n" "\033[31m❌ npm 依赖安装失败\033[0m"
|
||||
echo ""
|
||||
echo "请排查:"
|
||||
echo " 1. 手动重试: cd $PROJ_DIR && npm install --omit=dev"
|
||||
echo " 2. 清理后重试: rm -rf $PROJ_DIR/node_modules && npm install --omit=dev"
|
||||
echo " 3. 切换镜像: npm config set registry https://registry.npmmirror.com/"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ 插件安装成功"
|
||||
echo " ✅ 依赖安装完成"
|
||||
|
||||
##############################################################################
|
||||
# 第五步:恢复配置 → 重启网关
|
||||
##############################################################################
|
||||
echo ""
|
||||
printf "%b\n" "\033[34m5. 恢复配置并重启网关...\033[0m"
|
||||
|
||||
# 恢复通道配置
|
||||
if [ -n "$SAVED_QQBOT_TOKEN" ]; then
|
||||
echo ""
|
||||
echo "恢复 qqbot 通道配置..."
|
||||
$CMD channels add --channel qqbot --token "$SAVED_QQBOT_TOKEN" 2>&1 || true
|
||||
fi
|
||||
|
||||
# 恢复 markdown 配置
|
||||
if [ "$SAVED_MARKDOWN" = "true" ]; then
|
||||
echo "恢复 Markdown 配置 (已启用)..."
|
||||
APP_CONFIG="$HOME/.$CMD/$CMD.json"
|
||||
if [ -f "$APP_CONFIG" ]; then
|
||||
node -e "
|
||||
var fs = require('fs');
|
||||
var cfg = JSON.parse(fs.readFileSync('$APP_CONFIG', 'utf-8'));
|
||||
if (!cfg.channels) cfg.channels = {};
|
||||
if (!cfg.channels.qqbot) cfg.channels.qqbot = {};
|
||||
cfg.channels.qqbot.markdownSupport = true;
|
||||
fs.writeFileSync('$APP_CONFIG', JSON.stringify(cfg, null, 4) + '\n');
|
||||
" 2>/dev/null && echo "✅ Markdown 配置已恢复" || echo "⚠️ Markdown 配置恢复失败"
|
||||
if [ -n "$SAVED_CHANNELS_JSON" ]; then
|
||||
if node -e "
|
||||
const fs = require('fs');
|
||||
const cfg = JSON.parse(fs.readFileSync('$APP_CONFIG', 'utf8'));
|
||||
cfg.channels = cfg.channels || {};
|
||||
cfg.channels.qqbot = $SAVED_CHANNELS_JSON;
|
||||
fs.writeFileSync('$APP_CONFIG', JSON.stringify(cfg, null, 4) + '\n');
|
||||
" 2>/dev/null; then
|
||||
echo " ✅ 通道配置已恢复"
|
||||
else
|
||||
printf "%b\n" "\033[33m ⚠️ 通道配置写入失败,请手动检查: $APP_CONFIG\033[0m"
|
||||
fi
|
||||
elif [ "$FRESH_INSTALL" = true ]; then
|
||||
echo ""
|
||||
printf "%b\n" "\033[33m ⚠️ 首次安装,请配置 QQ Bot 凭据:\033[0m"
|
||||
echo " $CMD channels add --channel qqbot --token 'YOUR_APPID:YOUR_SECRET'"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# 重启网关(先确保旧进程停掉,避免无限重启循环)
|
||||
echo ""
|
||||
echo "重启网关服务..."
|
||||
|
||||
# 先尝试正常停止
|
||||
# 停止旧 gateway
|
||||
echo " 停止旧网关..."
|
||||
$CMD gateway stop 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
# 如果端口还被占用,强制杀进程
|
||||
PORT_PID=$(lsof -ti:18789 2>/dev/null || true)
|
||||
# 强制杀占用端口的进程
|
||||
PORT_PID=$(lsof -ti:"$GATEWAY_PORT" 2>/dev/null || true)
|
||||
if [ -n "$PORT_PID" ]; then
|
||||
echo "端口 18789 仍被占用 (pid: $PORT_PID),强制终止..."
|
||||
printf "%b\n" "\033[33m ⚠️ 端口 $GATEWAY_PORT 仍被占用 (PID: $PORT_PID),强制终止...\033[0m"
|
||||
kill -9 $PORT_PID 2>/dev/null || true
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
# 卸载 launchd 服务(防止自动拉起旧进程)
|
||||
for SVC_NAME in ai.openclaw.gateway ai.clawdbot.gateway ai.moltbot.gateway; do
|
||||
launchctl bootout "gui/$(id -u)/$SVC_NAME" 2>/dev/null || true
|
||||
for svc in ai.openclaw.gateway ai.clawdbot.gateway ai.moltbot.gateway; do
|
||||
launchctl bootout "gui/$(id -u)/$svc" 2>/dev/null || true
|
||||
done
|
||||
|
||||
# 启动新的 gateway
|
||||
# 启动网关
|
||||
echo " 启动网关..."
|
||||
if $CMD gateway 2>&1; then
|
||||
echo ""
|
||||
echo "✅ 网关已启动"
|
||||
echo "查看日志: $CMD gateway log"
|
||||
printf "%b\n" "\033[32m ✅ 网关已启动\033[0m"
|
||||
else
|
||||
echo ""
|
||||
echo "⚠️ 网关启动失败,尝试手动启动:"
|
||||
echo " $CMD gateway install && $CMD gateway"
|
||||
printf "%b\n" "\033[33m ⚠️ 网关启动失败(不影响已安装的插件)\033[0m"
|
||||
echo ""
|
||||
echo " 请手动启动:"
|
||||
echo " 1. 安装服务: $CMD gateway install"
|
||||
echo " 2. 启动网关: $CMD gateway"
|
||||
echo " 3. 查看日志: $CMD logs --follow"
|
||||
fi
|
||||
|
||||
##############################################################################
|
||||
# 完成
|
||||
##############################################################################
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo " ✅ QQBot 已从 ${LOCAL_VER:-未知} 更新到 ${NEW_VER}"
|
||||
echo "========================================="
|
||||
printf "%b\n" "\033[32m=========================================\033[0m"
|
||||
printf "%b\n" "\033[32m ✅ QQBot 已更新到 ${NEW_VER}${REMOTE_COMMIT:+ (${REMOTE_COMMIT})}\033[0m"
|
||||
[ -n "$LOCAL_VER" ] && printf "%b\n" "\033[32m (从 ${LOCAL_VER}${LOCAL_COMMIT:+ (${LOCAL_COMMIT})} 升级)\033[0m"
|
||||
printf "%b\n" "\033[32m=========================================\033[0m"
|
||||
echo ""
|
||||
echo "常用命令:"
|
||||
echo " $CMD gateway log # 查看日志"
|
||||
echo " $CMD logs --follow # 跟踪日志"
|
||||
echo " $CMD gateway restart # 重启服务"
|
||||
echo " $CMD plugins list # 查看插件列表"
|
||||
echo " cd $PROJ_DIR && git log # 查看更新历史"
|
||||
echo "========================================="
|
||||
|
||||
@@ -13,7 +13,13 @@
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
cd "$SCRIPT_DIR"
|
||||
# 如果脚本在 scripts/ 子目录里,往上一级就是项目根目录
|
||||
if [ "$(basename "$SCRIPT_DIR")" = "scripts" ]; then
|
||||
PROJ_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
else
|
||||
PROJ_DIR="$SCRIPT_DIR"
|
||||
fi
|
||||
cd "$PROJ_DIR"
|
||||
|
||||
# 解析命令行参数
|
||||
APPID=""
|
||||
@@ -75,17 +81,19 @@ echo "========================================="
|
||||
echo ""
|
||||
echo "[1/6] 备份已有配置..."
|
||||
SAVED_QQBOT_TOKEN=""
|
||||
for APP_NAME in openclaw clawdbot; do
|
||||
for APP_NAME in openclaw clawdbot moltbot; do
|
||||
CONFIG_FILE="$HOME/.$APP_NAME/$APP_NAME.json"
|
||||
if [ -f "$CONFIG_FILE" ]; then
|
||||
SAVED_QQBOT_TOKEN=$(node -e "
|
||||
const cfg = JSON.parse(require('fs').readFileSync('$CONFIG_FILE', 'utf8'));
|
||||
const ch = cfg.channels && cfg.channels.qqbot;
|
||||
if (!ch) process.exit(0);
|
||||
// token 字段(openclaw channels add 写入)
|
||||
if (ch.token) { process.stdout.write(ch.token); process.exit(0); }
|
||||
// appId + clientSecret 字段(openclaw 实际存储格式)
|
||||
if (ch.appId && ch.clientSecret) { process.stdout.write(ch.appId + ':' + ch.clientSecret); process.exit(0); }
|
||||
// 尝试所有可能的 channel key(原仓库 + 本仓库)
|
||||
const keys = ['qqbot', 'openclaw-qq'];
|
||||
for (const key of keys) {
|
||||
const ch = cfg.channels && cfg.channels[key];
|
||||
if (!ch) continue;
|
||||
if (ch.token) { process.stdout.write(ch.token); process.exit(0); }
|
||||
if (ch.appId && ch.clientSecret) { process.stdout.write(ch.appId + ':' + ch.clientSecret); process.exit(0); }
|
||||
}
|
||||
" 2>/dev/null || true)
|
||||
if [ -n "$SAVED_QQBOT_TOKEN" ]; then
|
||||
echo "已备份 qqbot 通道 token: ${SAVED_QQBOT_TOKEN:0:10}..."
|
||||
@@ -97,8 +105,8 @@ done
|
||||
# 2. 移除老版本
|
||||
echo ""
|
||||
echo "[2/6] 移除老版本..."
|
||||
if [ -f "./scripts/upgrade.sh" ]; then
|
||||
bash ./scripts/upgrade.sh
|
||||
if [ -f "$PROJ_DIR/scripts/upgrade.sh" ]; then
|
||||
bash "$PROJ_DIR/scripts/upgrade.sh"
|
||||
else
|
||||
echo "警告: upgrade.sh 不存在,跳过移除步骤"
|
||||
fi
|
||||
@@ -18,49 +18,55 @@ detect_installation() {
|
||||
fi
|
||||
}
|
||||
|
||||
# 可能的扩展目录名(原仓库 qqbot + 本仓库框架推断名 openclaw-qq)
|
||||
EXTENSION_DIRS=("qqbot" "openclaw-qq")
|
||||
|
||||
# 清理指定目录的函数
|
||||
cleanup_installation() {
|
||||
local APP_NAME="$1"
|
||||
local APP_DIR="$HOME/.$APP_NAME"
|
||||
local CONFIG_FILE="$APP_DIR/$APP_NAME.json"
|
||||
local EXTENSION_DIR="$APP_DIR/extensions/qqbot"
|
||||
|
||||
echo ""
|
||||
echo ">>> 处理 $APP_NAME 安装..."
|
||||
|
||||
# 1. 删除旧的扩展目录
|
||||
if [ -d "$EXTENSION_DIR" ]; then
|
||||
echo "删除旧版本插件: $EXTENSION_DIR"
|
||||
rm -rf "$EXTENSION_DIR"
|
||||
else
|
||||
echo "未找到旧版本插件目录,跳过删除"
|
||||
fi
|
||||
# 1. 删除所有可能的旧扩展目录
|
||||
for dir_name in "${EXTENSION_DIRS[@]}"; do
|
||||
local ext_dir="$APP_DIR/extensions/$dir_name"
|
||||
if [ -d "$ext_dir" ]; then
|
||||
echo "删除旧版本插件: $ext_dir"
|
||||
rm -rf "$ext_dir"
|
||||
fi
|
||||
done
|
||||
|
||||
# 2. 清理配置文件中的 qqbot 相关字段
|
||||
# 2. 清理配置文件中所有可能的插件 ID 相关字段
|
||||
if [ -f "$CONFIG_FILE" ]; then
|
||||
echo "清理配置文件中的 qqbot 字段..."
|
||||
echo "清理配置文件中的插件字段..."
|
||||
|
||||
# 使用 node 处理 JSON(比 jq 更可靠处理复杂结构)
|
||||
node -e "
|
||||
const fs = require('fs');
|
||||
const config = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf8'));
|
||||
const ids = ['qqbot', 'openclaw-qq', '@sliverp/qqbot', '@tencent-connect/openclaw-qq'];
|
||||
|
||||
// 删除 channels.qqbot
|
||||
if (config.channels && config.channels.qqbot) {
|
||||
delete config.channels.qqbot;
|
||||
console.log(' - 已删除 channels.qqbot');
|
||||
}
|
||||
|
||||
// 删除 plugins.entries.qqbot
|
||||
if (config.plugins && config.plugins.entries && config.plugins.entries.qqbot) {
|
||||
delete config.plugins.entries.qqbot;
|
||||
console.log(' - 已删除 plugins.entries.qqbot');
|
||||
}
|
||||
|
||||
// 删除 plugins.installs.qqbot
|
||||
if (config.plugins && config.plugins.installs && config.plugins.installs.qqbot) {
|
||||
delete config.plugins.installs.qqbot;
|
||||
console.log(' - 已删除 plugins.installs.qqbot');
|
||||
for (const id of ids) {
|
||||
// 删除 channels.<id>
|
||||
if (config.channels && config.channels[id]) {
|
||||
delete config.channels[id];
|
||||
console.log(' - 已删除 channels.' + id);
|
||||
}
|
||||
|
||||
// 删除 plugins.entries.<id>
|
||||
if (config.plugins && config.plugins.entries && config.plugins.entries[id]) {
|
||||
delete config.plugins.entries[id];
|
||||
console.log(' - 已删除 plugins.entries.' + id);
|
||||
}
|
||||
|
||||
// 删除 plugins.installs.<id>
|
||||
if (config.plugins && config.plugins.installs && config.plugins.installs[id]) {
|
||||
delete config.plugins.installs[id];
|
||||
console.log(' - 已删除 plugins.installs.' + id);
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync('$CONFIG_FILE', JSON.stringify(config, null, 2));
|
||||
@@ -86,10 +92,16 @@ if [ -d "$HOME/.openclaw" ]; then
|
||||
FOUND_INSTALLATION="openclaw"
|
||||
fi
|
||||
|
||||
# 检查 moltbot
|
||||
if [ -d "$HOME/.moltbot" ]; then
|
||||
cleanup_installation "moltbot"
|
||||
FOUND_INSTALLATION="moltbot"
|
||||
fi
|
||||
|
||||
# 如果都没找到
|
||||
if [ -z "$FOUND_INSTALLATION" ]; then
|
||||
echo "未找到 clawdbot 或 openclaw 安装目录"
|
||||
echo "请确认已安装 clawdbot 或 openclaw"
|
||||
echo "未找到 clawdbot / openclaw / moltbot 安装目录"
|
||||
echo "请确认已安装其中之一"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -100,7 +112,7 @@ echo ""
|
||||
echo "=== 清理完成 ==="
|
||||
echo ""
|
||||
echo "接下来请执行以下命令重新安装插件:"
|
||||
echo " cd /path/to/qqbot"
|
||||
echo " cd /path/to/openclaw-qq"
|
||||
echo " $CMD plugins install ."
|
||||
echo " $CMD channels add --channel qqbot --token \"AppID:AppSecret\""
|
||||
echo " $CMD gateway restart"
|
||||
|
||||
Reference in New Issue
Block a user