| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- #!/bin/sh
- # ===========================================================================
- # Dis2Hook installer — run INSIDE an existing Alpine Linux LXC, never on the
- # Proxmox host itself.
- #
- # One-liner:
- # wget -qO- https://gogs.av2x.dev/av2x/Dis2Hook/raw/master/install.sh | ash
- #
- # Options via environment:
- # D2H_BRANCH=master branch to pull from (auto-falls back to main)
- # D2H_DIR=/opt/dis2hook install directory
- # D2H_PORT=8823 web UI / webhook listen port
- # DISCORD_BOT_TOKEN=... skip the interactive token prompt
- # ===========================================================================
- set -eu
- REPO="${D2H_REPO:-https://gogs.av2x.dev/av2x/Dis2Hook}"
- BRANCH="${D2H_BRANCH:-master}"
- APP_DIR="${D2H_DIR:-/opt/dis2hook}"
- PORT="${D2H_PORT:-8823}"
- SERVICE="dis2hook"
- say() { printf '\033[1;33m[dis2hook]\033[0m %s\n' "$*"; }
- fail() { printf '\033[1;31m[dis2hook] ERROR:\033[0m %s\n' "$*" >&2; exit 1; }
- # --- guards ----------------------------------------------------------------
- # Never install on the Proxmox host: this belongs inside the container.
- if [ -d /etc/pve ] || command -v pveversion >/dev/null 2>&1; then
- fail "This looks like a Proxmox VE host. Dis2Hook must be installed INSIDE an Alpine LXC.
- Enter the container first, e.g.: pct enter <ctid> then re-run this installer."
- fi
- [ -f /etc/alpine-release ] || fail "This installer only supports Alpine Linux (no /etc/alpine-release found)."
- [ "$(id -u)" = "0" ] || fail "Run as root (Alpine LXC default)."
- # --- fetch helper with branch fallback -------------------------------------
- fetch() { # fetch <repo-path> <dest>
- wget -q -O "$2" "$REPO/raw/$BRANCH/$1" || return 1
- [ -s "$2" ]
- }
- say "Checking repository branch '$BRANCH'…"
- TMP="$(mktemp -d)"
- trap 'rm -rf "$TMP"' EXIT
- if ! fetch app.py "$TMP/app.py"; then
- if [ "$BRANCH" = "master" ] && wget -q -O "$TMP/app.py" "$REPO/raw/main/app.py" && [ -s "$TMP/app.py" ]; then
- BRANCH="main"
- say "Branch 'master' not found, using 'main'."
- else
- fail "Could not download app.py from $REPO (branch $BRANCH). Check network/DNS inside the container."
- fi
- fi
- grep -q DIS2HOOK_VERSION "$TMP/app.py" || fail "Downloaded app.py does not look valid. Aborting."
- # --- packages ---------------------------------------------------------------
- say "Installing packages (python3, Flask, requests)…"
- apk add --no-cache python3 py3-flask py3-requests >/dev/null
- # --- pull application files from the repo -----------------------------------
- say "Pulling application files from $REPO ($BRANCH)…"
- mkdir -p "$APP_DIR"
- fetch index.html "$TMP/index.html" || fail "Could not download index.html."
- fetch update.sh "$TMP/update.sh" || fail "Could not download update.sh."
- fetch dis2hook.initd "$TMP/dis2hook.initd" || fail "Could not download dis2hook.initd."
- grep -qi '<html' "$TMP/index.html" || fail "Downloaded index.html does not look valid. Aborting."
- install -m 644 "$TMP/app.py" "$APP_DIR/app.py"
- install -m 644 "$TMP/index.html" "$APP_DIR/index.html"
- install -m 755 "$TMP/update.sh" "$APP_DIR/update.sh"
- install -m 755 "$TMP/dis2hook.initd" "/etc/init.d/$SERVICE"
- printf '%s\n' "$BRANCH" > "$APP_DIR/.branch"
- # --- secrets: token.json is generated locally, never synced with the repo ---
- if [ -f "$APP_DIR/token.json" ]; then
- say "Existing token.json found — keeping it (secrets are never overwritten)."
- ADMIN_KEY="(unchanged — see $APP_DIR/token.json)"
- else
- BOT_TOKEN="${DISCORD_BOT_TOKEN:-}"
- if [ -z "$BOT_TOKEN" ]; then
- say "A Discord bot token is required (Discord Developer Portal → your app → Bot → Token)."
- printf '[dis2hook] Paste bot token (input hidden): '
- stty -echo < /dev/tty 2>/dev/null || true
- read -r BOT_TOKEN < /dev/tty
- stty echo < /dev/tty 2>/dev/null || true
- printf '\n'
- fi
- [ -n "$BOT_TOKEN" ] || fail "No bot token provided. Re-run, or set DISCORD_BOT_TOKEN=… before the one-liner."
- ADMIN_KEY="$(python3 -c 'import secrets; print(secrets.token_hex(16))')"
- umask 077
- python3 - "$APP_DIR/token.json" "$BOT_TOKEN" "$ADMIN_KEY" <<'PYEOF'
- import json, sys
- path, bot, admin = sys.argv[1], sys.argv[2], sys.argv[3]
- with open(path, "w") as f:
- json.dump({"bot_token": bot, "admin_key": admin}, f, indent=2)
- PYEOF
- chmod 600 "$APP_DIR/token.json"
- umask 022
- say "Generated $APP_DIR/token.json (chmod 600)."
- fi
- # --- default config (only if missing) ----------------------------------------
- if [ ! -f "$APP_DIR/config.json" ]; then
- cat > "$APP_DIR/config.json" <<CFGEOF
- {
- "listen_host": "0.0.0.0",
- "listen_port": $PORT,
- "heartbeat": { "enabled": false, "channel_id": "", "interval_minutes": 60 },
- "status": { "enabled": false, "channel_id": "" },
- "sources": []
- }
- CFGEOF
- say "Wrote default config.json (port $PORT)."
- fi
- # --- service ------------------------------------------------------------------
- say "Registering OpenRC service '$SERVICE'…"
- rc-update add "$SERVICE" default >/dev/null 2>&1 || true
- rc-service "$SERVICE" restart >/dev/null 2>&1 || rc-service "$SERVICE" start
- sleep 1
- IP="$(ip -4 -o addr show scope global 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | head -n1)"
- [ -n "$IP" ] || IP="<container-ip>"
- ACTUAL_PORT="$(python3 -c 'import json;print(json.load(open("'"$APP_DIR"'/config.json"))["listen_port"])')"
- printf '\n'
- say "──────────────────────────────────────────────────────"
- say "Dis2Hook is installed and running."
- say " Web UI : http://$IP:$ACTUAL_PORT/"
- say " Admin key : $ADMIN_KEY"
- say " App dir : $APP_DIR"
- say " Logs : tail -f /var/log/dis2hook.log"
- say " Update : $APP_DIR/update.sh"
- say "──────────────────────────────────────────────────────"
- say "Keep the admin key safe — it is stored only in token.json."
|