#!/usr/bin/env bash set -euo pipefail BASE_URL="${GUKO_BASE_URL:-https://mjjtop.com/admin/guko/raw/branch/main}" INSTALL_DIR="${GUKO_INSTALL_DIR:-/opt/guko}" COMPOSE_FILE="${INSTALL_DIR}/docker-compose.yml" ENV_FILE="${INSTALL_DIR}/.env" SERVERS_FILE="${INSTALL_DIR}/servers.json" IMAGE_NAME="${GUKO_IMAGE:-guko:local}" DEFAULT_TG_ID="${GUKO_TG_ID:-165067365}" BACKUP_DIR="${GUKO_BACKUP_DIR:-${INSTALL_DIR}/backups}" need_root() { if [ "$(id -u)" -ne 0 ]; then echo "请用 root 运行:sudo bash <(curl -fsSL https://mjjtop.com/guko)" >&2 exit 1 fi } install_docker() { if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then return fi echo "==> 安装 Docker / Compose" curl -fsSL https://get.docker.com | sh systemctl enable --now docker >/dev/null 2>&1 || true } render_compose() { cat > "${COMPOSE_FILE}" < 下载 GUKO 源码" local tmp tmp="$(mktemp -d)" fetch "${BASE_URL}/telegram-bot/Dockerfile" "${tmp}/Dockerfile.check" rm -rf "${INSTALL_DIR}/telegram-bot" "${INSTALL_DIR}/optional" "${INSTALL_DIR}/scripts" \ "${INSTALL_DIR}/auth.py" "${INSTALL_DIR}/guko.py" "${INSTALL_DIR}/jiaoops.py" \ "${INSTALL_DIR}/Makefile" "${INSTALL_DIR}/README.md" "${INSTALL_DIR}/LICENSE" fetch "https://mjjtop.com/admin/guko/archive/main.tar.gz" "${tmp}/guko.tar.gz" tar -xzf "${tmp}/guko.tar.gz" -C "${tmp}" local src src="$(find "${tmp}" -maxdepth 1 -type d \( -name 'guko' -o -name 'guko-*' \) | head -1)" if [ -z "${src}" ] || [ ! -d "${src}" ]; then src="$(find "${tmp}" -maxdepth 1 -mindepth 1 -type d | head -1)" fi cp -a "${src}/auth.py" "${src}/guko.py" "${src}/jiaoops.py" "${src}/telegram-bot" "${INSTALL_DIR}/" [ -d "${src}/optional" ] && cp -a "${src}/optional" "${INSTALL_DIR}/" || true [ -d "${src}/scripts" ] && cp -a "${src}/scripts" "${INSTALL_DIR}/" || true cp -a "${src}/README.md" "${src}/LICENSE" "${INSTALL_DIR}/" 2>/dev/null || true rm -rf "${tmp}" } fetch() { local url="$1" out="$2" curl -fL --retry 3 --connect-timeout 10 "$url" -o "$out" } init_files() { echo "==> 初始化 ${INSTALL_DIR}" mkdir -p "${INSTALL_DIR}" "${INSTALL_DIR}/keys" "${INSTALL_DIR}/media" "${INSTALL_DIR}/results" "${INSTALL_DIR}/tmp" chmod 700 "${INSTALL_DIR}/keys" fetch_source render_compose if [ ! -f "${ENV_FILE}" ]; then fetch "${BASE_URL}/.env.example" "${ENV_FILE}" sed -i \ -e 's/^BOT_TOKEN=.*/BOT_TOKEN=replace-me/' \ -e "s/^ALLOWED_USERS=.*/ALLOWED_USERS=${DEFAULT_TG_ID}/" \ -e "s/^ADMIN_USERS=.*/ADMIN_USERS=${DEFAULT_TG_ID}/" \ "${ENV_FILE}" fi if [ ! -f "${SERVERS_FILE}" ]; then fetch "${BASE_URL}/servers.example.json" "${SERVERS_FILE}" fi } usage() { cat </dev/null || true chmod 600 "${out}" echo "✅ 备份完成:${out}" } doctor() { echo "== GUKO doctor ==" echo "install_dir=${INSTALL_DIR}" echo "compose=${COMPOSE_FILE}" command -v docker >/dev/null 2>&1 && docker --version || echo "docker=missing" docker compose version 2>/dev/null || echo "docker compose=missing" [ -f "${ENV_FILE}" ] && echo ".env=ok" || echo ".env=missing" [ -f "${SERVERS_FILE}" ] && echo "servers.json=ok" || echo "servers.json=missing" if [ -f "${ENV_FILE}" ]; then grep -E '^(BOT_TOKEN|ALLOWED_USERS|ADMIN_USERS)=' "${ENV_FILE}" | sed -E 's/(BOT_TOKEN=).*/\1***/' fi if [ -f "${COMPOSE_FILE}" ] && command -v docker >/dev/null 2>&1; then compose ps || true fi } cmd="${1:-install}" case "$cmd" in install|init) need_root install_docker init_files cat <&2; exit 1; } compose build --pull compose up -d compose ps ;; down|stop) need_root compose down ;; restart) need_root compose restart compose ps ;; update) need_root install_docker init_files compose build --pull compose up -d compose ps ;; pull) need_root init_files ;; backup) need_root backup_data ;; doctor) need_root doctor ;; repo) echo "https://mjjtop.com/guko-repo" ;; logs) need_root compose logs -f --tail=200 ;; status|ps) need_root compose ps ;; help|-h|--help) usage ;; *) usage exit 2 ;; esac