#!/usr/bin/env bash # 抖音客服系统 - Linux 一键安装(兼容宝塔 Python 项目 + 原生 CentOS/Ubuntu) # 用法: # ./install.sh # 自动探测可用 Python 并安装 # ./install.sh --diagnose # 仅列出本机 Python 探测结果 # AUTO_BUILD_PYTHON=1 ./install.sh # 无可用 Python 时自动编译 3.11 # KEFU_PYTHON=/path/to/python3 ./install.sh # 手动指定 set -euo pipefail ROOT="$(cd "$(dirname "$0")" && pwd)" cd "$ROOT" MIN_PY_MAJOR=3 MIN_PY_MINOR=10 DIAGNOSE=0 AUTO_BUILD="${AUTO_BUILD_PYTHON:-0}" for arg in "$@"; do case "$arg" in --diagnose) DIAGNOSE=1 ;; --build-python) AUTO_BUILD=1 ;; esac done echo "========================================" echo " 抖音客服系统 - 依赖安装" echo "========================================" echo # ---------- Python 检测工具 ---------- check_python_ssl() { "$1" -c "import ssl" 2>/dev/null } check_python_version() { "$1" -c "import sys; sys.exit(0 if sys.version_info >= ($MIN_PY_MAJOR, $MIN_PY_MINOR) else 1)" 2>/dev/null } python_brief() { local py="$1" if [ ! -x "$py" ]; then echo "不存在" return fi local ver ssl ver="$("$py" -V 2>&1 | awk '{print $2}')" || ver="?" if check_python_ssl "$py"; then ssl="$("$py" -c "import ssl; print(ssl.OPENSSL_VERSION)" 2>/dev/null)" else ssl="无SSL" fi echo "${ver} | ${ssl}" } try_python() { local py="$1" [ -n "$py" ] && [ -x "$py" ] || return 1 check_python_ssl "$py" || return 1 check_python_version "$py" || return 1 return 0 } # 收集候选 Python(去重,高版本优先) declare -a CANDIDATES=() declare -A SEEN=() add_candidate() { local py="$1" [ -n "$py" ] || return 0 if [ -x "$py" ] && [ -z "${SEEN[$py]:-}" ]; then SEEN[$py]=1 CANDIDATES+=("$py") fi } collect_python_candidates() { CANDIDATES=() SEEN=() # 1) 用户指定 [ -n "${KEFU_PYTHON:-}" ] && add_candidate "$KEFU_PYTHON" # 2) 当前 shell(宝塔 source 项目环境后 which python 会指向项目 Python) add_candidate "$(command -v python 2>/dev/null || true)" add_candidate "$(command -v python3 2>/dev/null || true)" add_candidate "$(command -v python3.11 2>/dev/null || true)" add_candidate "$(command -v python3.10 2>/dev/null || true)" # 3) 虚拟环境 [ -n "${VIRTUAL_ENV:-}" ] && add_candidate "${VIRTUAL_ENV}/bin/python" # 4) 宝塔 Python 项目环境(多版本目录名,动态扫描,不写死单一版本) local base py ver_dir for base in \ /www/server/pyporject_evn/versions \ /home/www/server/pyporject_evn/versions \ /www/server/panel/pyenv/versions \ /www/server/python_project/versions; do [ -d "$base" ] || continue for ver_dir in "$base"/*; do [ -d "$ver_dir" ] || continue for py in "$ver_dir"/bin/python3.* "$ver_dir"/bin/python3 "$ver_dir"/bin/python; do add_candidate "$py" done done done # 5) 源码/包管理器安装路径 for py in \ /usr/local/python3.11/bin/python3.11 \ /usr/local/python3.10/bin/python3.10 \ /usr/local/bin/python3.11 /usr/local/bin/python3.10 \ /usr/bin/python3.11 /usr/bin/python3.10; do add_candidate "$py" done # 6) conda add_candidate "${CONDA_PREFIX:-}/bin/python" for py in /opt/miniconda3/envs/*/bin/python /root/miniconda3/envs/*/bin/python; do [ -e "$py" ] && add_candidate "$py" done } sort_candidates_by_version() { local -a sorted=() local py ver while IFS= read -r line; do sorted+=("$line") done < <( for py in "${CANDIDATES[@]}"; do ver="$("$py" -V 2>&1 | awk '{print $2}' || echo 0.0.0)" echo "$ver $py" done | sort -t. -k1,1nr -k2,2nr -k3,3nr | awk '{print $2}' ) CANDIDATES=("${sorted[@]}") } select_python() { local py collect_python_candidates if [ "${#CANDIDATES[@]}" -eq 0 ]; then return 1 fi sort_candidates_by_version for py in "${CANDIDATES[@]}"; do if try_python "$py"; then echo "$py" return 0 fi done return 1 } print_diagnose() { local py tag echo "[诊断] 本机 Python 探测结果(需要 3.10+ 且带 SSL):" echo collect_python_candidates if [ "${#CANDIDATES[@]}" -eq 0 ]; then echo " (未找到任何 Python 可执行文件)" return fi sort_candidates_by_version for py in "${CANDIDATES[@]}"; do if try_python "$py"; then tag="可用"; else tag="不可用"; fi printf " [%s] %s\n %s\n" "$tag" "$py" "$(python_brief "$py")" done echo if [ -d /www/server/panel ] || [ -d /www/server/pyporject_evn ] || [ -d /home/www/server/pyporject_evn ]; then echo " 环境: 检测到宝塔面板" echo " 提示: 宝塔 Python 项目需先 source 项目环境,或确保 pyporject_evn 下 Python 带 _ssl" else echo " 环境: 原生 Linux" fi } build_python311() { local PYVER="3.11.9" PY_SHORT="3.11" local PREFIX="/usr/local/python${PY_SHORT}" local PY_BIN="${PREFIX}/bin/python${PY_SHORT}" local SRC="/usr/src/Python-${PYVER}" if [ "$(id -u)" -ne 0 ]; then echo "[错误] 自动编译 Python 需要 root 权限" return 1 fi if [ -x "$PY_BIN" ] && try_python "$PY_BIN"; then echo "[OK] 已存在可用 Python: $PY_BIN" echo "$PY_BIN" return 0 fi echo "[编译] 未找到可用 Python,开始编译 ${PYVER} -> ${PREFIX}(约 10 分钟)" if command -v yum &>/dev/null; then yum groupinstall -y "Development Tools" 2>/dev/null || yum install -y gcc make yum install -y openssl openssl-devel libffi-devel zlib-devel bzip2-devel \ readline-devel sqlite-devel xz-devel wget tar elif command -v apt-get &>/dev/null; then apt-get update apt-get install -y build-essential libssl-dev zlib1g-dev libbz2-dev \ libreadline-dev libsqlite3-dev libffi-dev wget tar else echo "[错误] 不支持的包管理器" return 1 fi mkdir -p /usr/src && cd /usr/src if [ ! -f "Python-${PYVER}.tgz" ]; then wget -q "https://www.python.org/ftp/python/${PYVER}/Python-${PYVER}.tgz" \ || wget -q "https://registry.npmmirror.com/-/binary/python/${PYVER}/Python-${PYVER}.tgz" fi rm -rf "$SRC" && tar xzf "Python-${PYVER}.tgz" && cd "$SRC" ./configure --prefix="$PREFIX" --enable-optimizations --with-openssl=/usr make -j"$(nproc 2>/dev/null || echo 2)" make altinstall try_python "$PY_BIN" || return 1 echo "[OK] 编译完成: $PY_BIN" echo "$PY_BIN" } # ---------- 选择 Python ---------- if [ "$DIAGNOSE" -eq 1 ]; then print_diagnose exit 0 fi PYTHON="" if [ -n "${KEFU_PYTHON:-}" ]; then if [ ! -x "$KEFU_PYTHON" ]; then echo "[错误] KEFU_PYTHON 不存在: $KEFU_PYTHON" echo " 运行 ./install.sh --diagnose 查看本机可用 Python" exit 1 fi if ! try_python "$KEFU_PYTHON"; then echo "[错误] KEFU_PYTHON 不可用(需 3.10+ 且带 SSL): $KEFU_PYTHON" echo " 详情: $(python_brief "$KEFU_PYTHON")" echo " 运行 ./install.sh --diagnose 查看全部候选" exit 1 fi PYTHON="$KEFU_PYTHON" else if ! PYTHON="$(select_python)"; then print_diagnose echo "[错误] 未找到可用的 Python 3.10+(需带 SSL 模块)。" echo if [ "$AUTO_BUILD" = "1" ] || [ "$AUTO_BUILD" = "yes" ]; then PYTHON="$(build_python311)" || exit 1 else echo " 宝塔用户常见原因: pyporject_evn 里的 Python 编译时缺少 openssl-devel。" echo echo " 解决方式(任选):" echo " 1) 自动编译 Python 3.11:" echo " AUTO_BUILD_PYTHON=1 ./install.sh" echo " 2) 宝塔面板卸载 Python 3.11 后执行:" echo " yum install -y openssl openssl-devel && 在面板重装 Python 3.11" echo " 3) 手动指定已验证可用的 Python:" echo " KEFU_PYTHON=/path/to/python3.11 ./install.sh" echo " 4) 查看探测详情:" echo " ./install.sh --diagnose" exit 1 fi fi fi echo "[OK] $("$PYTHON" --version) @ $PYTHON" "$PYTHON" -c "import ssl; print('[OK] OpenSSL:', ssl.OPENSSL_VERSION)" # ---------- Node.js ---------- if ! command -v node &>/dev/null; then echo "[警告] 未找到 Node.js,私信签名将无法工作" echo " 宝塔: 软件商店 -> Node 版本管理器 -> 安装 Node 18+" else echo "[OK] Node $(node --version)" fi echo # ---------- 1. Python venv ---------- echo "[1/6] Python 虚拟环境与 pip 包..." cd "$ROOT/backend" VENV_PY=".venv/bin/python" need_new_venv=0 if [ ! -x "$VENV_PY" ]; then need_new_venv=1 elif ! try_python "$VENV_PY"; then echo " 现有 .venv 不符合要求,删除并重建..." rm -rf .venv need_new_venv=1 fi if [ "$need_new_venv" -eq 1 ]; then echo " 创建 .venv <- $PYTHON" "$PYTHON" -m venv .venv fi try_python "$VENV_PY" || { echo "[错误] .venv 创建失败"; exit 1; } chmod +x .venv/bin/python .venv/bin/pip 2>/dev/null || true .venv/bin/python -m pip install -U pip -q .venv/bin/pip install -r requirements.txt echo " Python 依赖完成" echo # ---------- 2. Playwright ---------- echo "[2/6] Playwright Chromium..." export PLAYWRIGHT_BROWSERS_PATH="${PLAYWRIGHT_BROWSERS_PATH:-$ROOT/playwright-browsers}" .venv/bin/python -m playwright install chromium if [ "$(uname -s)" = "Linux" ]; then .venv/bin/python -m playwright install-deps chromium 2>/dev/null \ || echo "[警告] install-deps 失败,可手动以 root 执行" fi echo " 路径: $PLAYWRIGHT_BROWSERS_PATH" echo # ---------- 3. IM Node 依赖 ---------- echo "[3/6] IM 签名依赖..." cd "$ROOT/backend/rpa_engine/douyin_im/static" if [ -f package.json ]; then npm install --no-fund --no-audit echo " jsrsasign 完成" fi echo # ---------- 4. 前端依赖 ---------- echo "[4/6] 前端依赖..." cd "$ROOT/frontend" if [ -f package.json ]; then npm install --no-fund --no-audit echo " 前端 npm 完成" fi echo # ---------- 5. 构建前端 ---------- echo "[5/6] 构建前端..." if [ -f "$ROOT/frontend/package.json" ]; then cd "$ROOT/frontend" && npm run build echo " -> frontend/dist" fi echo # ---------- 6. 校验 ---------- echo "[6/6] 校验..." cd "$ROOT/backend" .venv/bin/python -c "import fastapi, uvicorn, playwright, execjs; print(' 核心包 OK')" [ -f "$ROOT/frontend/dist/index.html" ] && echo " 前端静态 OK" \ || echo "[警告] 未找到 frontend/dist/index.html" echo echo "========================================" echo " 安装完成" echo echo " 启动: ./start_web.sh" echo " API: ./start_backend.sh" echo " 地址: http://服务器IP:8000" echo echo " 宝塔 Python 项目:" echo " 项目路径: $ROOT/backend" echo " 启动命令: bash $ROOT/backend/baota_start.sh" echo " 初始化: bash $ROOT/backend/baota_init.sh" echo " 详见: deploy/baota.md" echo "========================================"