mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-02 04:52:50 +08:00
换过 Node 大版本之后安装就废了,而且安装脚本还会报告成功。原生模块只能在 编译它的那个 Node ABI 上加载(Node 20 = 115、22 = 127、24 = 137),而 better-sqlite3 的 .node 放在与 ABI 无关的固定路径下,旧的 download-binaries 只检查「文件存在且大于 500KB」,于是给 Node 24 编译的 1.9MB 文件在 Node 22 下原样保留,跳过重新下载,机器人启动时死在 NODE_MODULE_VERSION 上。 (@discordjs/opus 的目录名里带 ABI,反而歪打正着没这个问题。) download-binaries.mjs 现在不看文件大小,而是在子进程里真的把每个包 load 一遍 ——子进程是必须的,Windows 上父进程加载过的 .node 会一直被映射,系统随后拒绝 删除或覆盖它。注意 better-sqlite3 的 addon 是在 Database 构造函数里惰性加载的, 所以光 require 这个包探测不出问题,得真的开一个内存库。 失败就按当前 ABI 重新安装,整个替换过程是先把旧文件挪到 node_modules/ .tsmusicbot-backup、下载解压到暂存目录、原子 rename 就位、再探测一次,任何 一步失败都把原文件还原回去——删掉不匹配的二进制却下载不下来,比原来的版本 更糟。备份特意放在包的 build/ 之外,因为源码编译回退会调 node-gyp 把 build/ 清空。被中断(比如下载到一半 Ctrl+C)遗留的备份,下一次运行会自动认领回来。 其他一并修掉的问题: - 版本号原本硬编码 12.8.0,实际锁的是 12.11.1,一旦真的触发下载就会 404; 改为从 node_modules 里读。 - 三个模块原本用 Promise.all 并发。源码编译走的是 execSync,会把事件循环整个 卡住几分钟,而 download() 的 120 秒超时是挂在同一个循环上的 socket 静默计时 器——循环一恢复,还在传输中的连接就会被判超时。这不是小概率竞态:npmmirror 上没有 ABI 137 的 opus,也没有 ABI 115 的 better-sqlite3,所以在 Node 24 和 Node 20 上必定有一个模块在 100ms 内 404 并开始编译,而 ffmpeg 的 80MB 下载 正在进行。ffmpeg 是可选模块,于是它被误杀后只记一条 WARN,脚本照样 exit 0, setup 打印「Setup Complete」,用户装完却没有 ffmpeg,放什么都放不出来。 改成严格串行执行。 - 必需模块(opus / better-sqlite3)失败才返回非零;ffmpeg 有系统 ffmpeg 兜底, 只警告。setup.bat 里原本形同虚设的 FAILED 标志接上了,必需模块失败会中止安装, 不再是「装完才发现」。 - 4b 步骤原本把全部输出重定向进 setup.log,用户盯着不动的窗口以为卡死;现在 进度走 stderr 实时显示,完整记录仍进日志。 新增 scripts/check-native.mjs:启动前预检,直接说清楚哪个模块对不上、分别是哪 个 ABI、怎么修,而不是抛一串 NODE_MODULE_VERSION 堆栈。scripts\start.bat、根目 录 start.bat(现在改为委托给前者,并且会先切到项目目录)和 npm start 的 prestart 都会跑它。Docker 运行镜像也补上这个文件,否则容器里执行 npm start 会因为找不到 脚本而失败。 Node 版本要求改为按依赖的真实下限判断(@honeybbq/teamspeak-client 要 >=20.19、 @sansenjian/qq-music-api 要 >=20.17/22.9,21 和 23 被 better-sqlite3 与 vitest 排除),package.json 补上对应的 engines;比 20/22 LTS 更新的大版本不阻止,只提 示可能要源码编译。README 相应更新,并补一条 NODE_MODULE_VERSION 的常见问题。 注意:批处理里新增的行全部保持纯 ASCII —— cmd.exe 在括号块里遇到多字节 UTF-8 会算错文件偏移,开始吃掉后续行的 echo 前缀,中文提示一律交给 Node 脚本输出。 Closes #140 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
181 lines
5.9 KiB
Bash
Executable File
181 lines
5.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
#
|
|
# TSMusicBot Setup Script (Linux/macOS)
|
|
# - Auto-detect China network, switch to npmmirror
|
|
# - Download native binaries from CDN (避开 GitHub)
|
|
# - One-click setup, same as setup.bat for Windows
|
|
#
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
LOG_FILE="$PROJECT_DIR/setup.log"
|
|
|
|
echo "============================================"
|
|
echo " TSMusicBot - First-Time Setup (Linux)"
|
|
echo "============================================"
|
|
echo ""
|
|
echo "Log file: $LOG_FILE"
|
|
echo ""
|
|
|
|
# ---- Check Node.js ----
|
|
if ! command -v node &>/dev/null; then
|
|
echo "[ERROR] Node.js not found. Please install Node.js 20+ from https://nodejs.org"
|
|
echo " or https://nodejs.cn/ (China mirror)."
|
|
exit 1
|
|
fi
|
|
echo "[OK] Node.js $(node -v)"
|
|
|
|
# Newest Node major this project is regularly tested against. Anything above
|
|
# still works, it just may have no prebuilt addons and fall back to a source build.
|
|
TESTED_NODE_MAJOR=22
|
|
NODE_MAJOR="$(node -p 'process.versions.node.split(".")[0]')"
|
|
|
|
# The floor is not just a major version, so let node decide: @honeybbq/teamspeak-client
|
|
# needs >=20.19, @sansenjian/qq-music-api needs >=20.17 / >=22.9, and the odd majors
|
|
# (21 / 23) are excluded by better-sqlite3 and vitest. Keep in sync with package.json "engines".
|
|
if ! node -e 'const v=process.versions.node.split(".").map(Number); process.exit((v[0]===20&&v[1]>=19)||(v[0]===22&&v[1]>=12)||v[0]>=24?0:1)'; then
|
|
echo "[ERROR] Node.js $(node -v) is not supported. Use Node 20.19+ LTS or Node 22.12+ LTS."
|
|
echo " https://nodejs.org/ | https://nodejs.cn/"
|
|
exit 1
|
|
fi
|
|
if [ "$NODE_MAJOR" -gt "$TESTED_NODE_MAJOR" ]; then
|
|
echo "[WARN] Node $(node -v) is newer than the tested LTS line (Node 20 / Node 22)."
|
|
echo " 新版 Node 可能没有现成的 opus / better-sqlite3 预编译包,"
|
|
echo " 安装时会自动改用源码编译,需要 C/C++ 构建工具,速度较慢。"
|
|
echo " This is only a warning - setup builds the binaries for $(node -v) either way."
|
|
fi
|
|
|
|
if ! command -v npm &>/dev/null; then
|
|
echo "[ERROR] npm not found."
|
|
exit 1
|
|
fi
|
|
echo "[OK] npm v$(npm -v)"
|
|
echo ""
|
|
|
|
# ---- Detect China network ----
|
|
USE_MIRROR=0
|
|
MIRROR_REGISTRY="https://registry.npmjs.org"
|
|
CDN_MIRROR=""
|
|
|
|
echo "Testing connection to npm registry..."
|
|
if ping -c 1 -W 4 registry.npmjs.org &>/dev/null; then
|
|
echo "[OK] npm registry reachable."
|
|
else
|
|
echo "[WARN] Cannot reach npm registry, using China mirror."
|
|
USE_MIRROR=1
|
|
fi
|
|
|
|
if [ "$USE_MIRROR" = "1" ]; then
|
|
echo "[INFO] Using China mirror (npmmirror.com)"
|
|
MIRROR_REGISTRY="https://registry.npmmirror.com"
|
|
CDN_MIRROR="https://cdn.npmmirror.com/binaries"
|
|
export npm_config_registry="$MIRROR_REGISTRY"
|
|
fi
|
|
echo ""
|
|
|
|
# ---- Check build tools (needed for native module fallback) ----
|
|
if ! command -v gcc &>/dev/null && ! command -v clang &>/dev/null; then
|
|
echo "[INFO] No C compiler found. If CDN binaries are unavailable,"
|
|
echo " native modules may fail. Install build tools:"
|
|
echo " sudo apt install build-essential (Ubuntu/Debian)"
|
|
echo " sudo yum groupinstall 'Development Tools' (CentOS/RHEL)"
|
|
echo ""
|
|
fi
|
|
|
|
# ---- Step 1: Install dependencies (skip GitHub binaries) ----
|
|
echo "---- 1/5: Installing Node.js dependencies ----"
|
|
echo ""
|
|
|
|
cd "$PROJECT_DIR"
|
|
npm install --registry="$MIRROR_REGISTRY" --ignore-scripts 2>&1 | tee -a "$LOG_FILE"
|
|
echo "[OK] Dependencies installed."
|
|
echo ""
|
|
|
|
# ---- Step 2: Verify / download / repair native binaries (ABI aware) ----
|
|
echo "---- 2/5: Checking native binaries ----"
|
|
echo ""
|
|
|
|
# The old `if node ... | tee ...` only printed a [WARN] and carried on, so a
|
|
# broken native module still produced a "Setup Complete!" banner. It also read
|
|
# the *pipeline's* status: `set -o pipefail` above happens to surface node's
|
|
# failure, but a failing `tee` (unwritable log) was indistinguishable from a
|
|
# failing node. PIPESTATUS[0] is exactly node's own exit code, nothing else.
|
|
set +e
|
|
node scripts/download-binaries.mjs $CDN_MIRROR 2>&1 | tee -a "$LOG_FILE"
|
|
BIN_STATUS=${PIPESTATUS[0]}
|
|
set -e
|
|
|
|
if [ "$BIN_STATUS" -ne 0 ]; then
|
|
echo ""
|
|
echo "[ERROR] A required native module (@discordjs/opus / better-sqlite3) is unusable."
|
|
echo " 必需的原生模块不可用,安装中止。原因见上面的 [binary] 输出。"
|
|
echo " Log: $LOG_FILE"
|
|
exit 1
|
|
fi
|
|
# ffmpeg-static failures are only a WARN inside the script above (a system
|
|
# ffmpeg on PATH is a supported fallback), so reaching here means we are good.
|
|
echo "[OK] Native binaries ready for $(node -v)."
|
|
echo ""
|
|
|
|
# ---- Step 3: Install web panel dependencies ----
|
|
echo "---- 3/5: Installing web panel dependencies ----"
|
|
echo ""
|
|
|
|
if [ -f "web/package.json" ]; then
|
|
cd "$PROJECT_DIR/web"
|
|
npm install --registry="$MIRROR_REGISTRY" 2>&1 | tee -a "$LOG_FILE"
|
|
cd "$PROJECT_DIR"
|
|
echo "[OK] Web panel dependencies installed."
|
|
else
|
|
echo "[SKIP] web/package.json not found."
|
|
fi
|
|
echo ""
|
|
|
|
# ---- Step 4: Build project ----
|
|
echo "---- 4/5: Building project ----"
|
|
echo ""
|
|
|
|
npm run build 2>&1 | tee -a "$LOG_FILE"
|
|
echo "[OK] Build succeeded."
|
|
echo ""
|
|
|
|
# ---- Step 5: Verify ----
|
|
echo "---- 5/5: Verifying build ----"
|
|
echo ""
|
|
|
|
BUILD_OK=1
|
|
if [ ! -d "dist" ]; then
|
|
echo "[ERROR] dist/ directory missing."
|
|
BUILD_OK=0
|
|
fi
|
|
if [ -d "web" ] && [ ! -d "web/dist" ]; then
|
|
echo "[ERROR] web/dist/ directory missing."
|
|
BUILD_OK=0
|
|
fi
|
|
|
|
if [ "$BUILD_OK" = "0" ]; then
|
|
echo "Build completed but expected output is missing."
|
|
exit 1
|
|
fi
|
|
echo "[OK] Build outputs verified."
|
|
echo ""
|
|
|
|
if [ ! -f "config.json" ]; then
|
|
echo "[INFO] config.json will be auto-generated on first launch."
|
|
fi
|
|
echo ""
|
|
|
|
echo "============================================"
|
|
echo " Setup Complete!"
|
|
echo "============================================"
|
|
echo ""
|
|
echo "Next steps:"
|
|
echo " 1. Run: npm start"
|
|
echo " 2. Open: http://localhost:3000"
|
|
echo ""
|
|
echo "Setup log: $LOG_FILE"
|
|
echo ""
|
|
|