#!/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 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 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 ' "$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" </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="" 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."