v0.5.0 – Clownfischserver HomeIP Auto-Update: myfritz Automatik für SSH (5-Min Cron)
Features: - install.sh: Frage in Step 4 ob myfritz-Adresse für Auto-SSH-Freigabe genutzt werden soll - clownfischserver-homeip-updater.sh: DNS-Auflösung + UFW-Update (alle 5 Min via Cron) - .env-Variable: CLOWNFISCHSERVER_HOME_IP_DOMAIN für myfritz-Adresse - State-File tracking zur Vermeidung unnötiger UFW-Updates - Logging: /var/log/clownfischserver-homeip.log - Dokumentation: scripts/README-HOMEIP.md + CLOWNFISCHSERVER.md update Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
+944
@@ -0,0 +1,944 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# 🐠 CLOWNFISCHSERVER
|
||||
# Version: see banner
|
||||
# Author: Mehlzoerwer-Claude (https://github.com/mehlzoerwer-claude)
|
||||
# License: GPL-3.0 – Keep it open. Always.
|
||||
# Repo: https://github.com/mehlzoerwer-claude/clownfischserver
|
||||
# =============================================================================
|
||||
|
||||
# GPL-3.0 License - Keep it open. Always.
|
||||
# https://github.com/mehlzoerwer-claude/clownfischserver
|
||||
|
||||
# --- Colors ---
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
# --- Defaults ---
|
||||
INSTALL_DIR="/opt/clownfischserver"
|
||||
SNAPSHOT_DIR="/opt/clownfisch-snapshots"
|
||||
WORKSPACE_DIR="/opt/clownfisch-workspace"
|
||||
LOG_FILE="/var/log/clownfischserver-install.log"
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
MGMT_USER=""
|
||||
PKG_MANAGER=""
|
||||
DISTRO=""
|
||||
OLLAMA_MODEL=""
|
||||
SNAPSHOT_METHOD="tar"
|
||||
OLLAMA_NUM_CTX=4096
|
||||
|
||||
# HELPERS
|
||||
|
||||
log() { echo "$(date '+%Y-%m-%d %H:%M:%S') $*" >> "$LOG_FILE"; }
|
||||
ok() { echo -e " ${GREEN}✓${NC} $1"; log "OK: $1"; }
|
||||
info() { echo -e " ${CYAN}→${NC} $1"; log "INFO: $1"; }
|
||||
warn() { echo -e " ${YELLOW}⚠${NC} $1"; log "WARN: $1"; }
|
||||
fail() {
|
||||
echo -e "\n ${RED}${BOLD}✗ FEHLER: $1${NC}"
|
||||
echo -e " ${YELLOW}Mehr Details: tail -50 $LOG_FILE${NC}\n"
|
||||
log "ERROR: $1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
step() {
|
||||
echo -e "\n${BOLD}${BLUE}[$1/$TOTAL_STEPS] $2${NC}"
|
||||
log "=== STEP $1: $2 ==="
|
||||
}
|
||||
|
||||
ask() {
|
||||
echo -e "\n ${YELLOW}?${NC} ${BOLD}$1${NC}"
|
||||
if [[ -n "$2" ]]; then
|
||||
echo -e " ${CYAN} $2${NC}"
|
||||
fi
|
||||
read -r -p " → " REPLY
|
||||
echo "$REPLY"
|
||||
}
|
||||
|
||||
ask_yn() {
|
||||
echo -e "\n ${YELLOW}?${NC} ${BOLD}$1${NC} ${CYAN}[j/n]${NC}"
|
||||
read -r -p " → " REPLY
|
||||
[[ "$REPLY" =~ ^[jJyY] ]]
|
||||
}
|
||||
|
||||
pause() {
|
||||
echo -e "\n ${CYAN}$1${NC}"
|
||||
read -r -p " Enter zum Fortfahren..." _
|
||||
}
|
||||
|
||||
TOTAL_STEPS=8
|
||||
|
||||
# PROGRESS + SPINNER
|
||||
|
||||
spinner() {
|
||||
local pid=$1
|
||||
local msg=$2
|
||||
local spin='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'
|
||||
local i=0
|
||||
tput civis 2>/dev/null
|
||||
while kill -0 "$pid" 2>/dev/null; do
|
||||
local c="${spin:$((i % ${#spin})):1}"
|
||||
printf "\r \033[0;36m%s\033[0m %s..." "$c" "$msg"
|
||||
sleep 0.1
|
||||
((i++))
|
||||
done
|
||||
printf "\r\033[K"
|
||||
tput cnorm 2>/dev/null
|
||||
}
|
||||
|
||||
run_with_spinner() {
|
||||
local msg=$1
|
||||
shift
|
||||
("$@" >> "$LOG_FILE" 2>&1) &
|
||||
local pid=$!
|
||||
spinner "$pid" "$msg"
|
||||
wait "$pid"
|
||||
return $?
|
||||
}
|
||||
|
||||
print_banner() {
|
||||
clear
|
||||
echo -e "${CYAN}${BOLD}"
|
||||
echo " ╔══════════════════════════════════════════════════════╗"
|
||||
echo " ║ 🐠 CLOWNFISCHSERVER INSTALLER ║"
|
||||
echo " ║ v0.4.13 | GPL-3.0 ║"
|
||||
echo " ╚══════════════════════════════════════════════════════╝"
|
||||
echo -e "${NC}"
|
||||
echo -e " Willkommen! Dieses Script richtet deinen"
|
||||
echo -e " Clownfischserver ein – Schritt für Schritt."
|
||||
echo -e " Du wirst durch alles geführt.\n"
|
||||
echo -e " ${YELLOW}Log-Datei: $LOG_FILE${NC}\n"
|
||||
}
|
||||
|
||||
# STEP 1: CHECKS
|
||||
|
||||
step_checks() {
|
||||
step 1 "Systemcheck"
|
||||
|
||||
# Root check
|
||||
[[ $EUID -ne 0 ]] && fail "Bitte als root ausführen:\n sudo bash install.sh"
|
||||
ok "Root-Rechte vorhanden"
|
||||
|
||||
# Distro detection
|
||||
if command -v apt &>/dev/null; then
|
||||
PKG_MANAGER="apt"
|
||||
DISTRO=$(grep -oP '(?<=^ID=).+' /etc/os-release | tr -d '"')
|
||||
DISTRO_VERSION=$(grep -oP '(?<=^VERSION_ID=).+' /etc/os-release | tr -d '"')
|
||||
ok "Distribution: $DISTRO $DISTRO_VERSION (apt)"
|
||||
elif command -v dnf &>/dev/null; then
|
||||
PKG_MANAGER="dnf"
|
||||
DISTRO="fedora"
|
||||
ok "Distribution: Fedora/RHEL (dnf)"
|
||||
elif command -v pacman &>/dev/null; then
|
||||
PKG_MANAGER="pacman"
|
||||
DISTRO="arch"
|
||||
ok "Distribution: Arch Linux (pacman)"
|
||||
else
|
||||
fail "Unbekannte Distribution – apt/dnf/pacman nicht gefunden."
|
||||
fi
|
||||
|
||||
# RAM check
|
||||
RAM_MB=$(awk '/MemTotal/ {printf "%d", $2/1024}' /proc/meminfo)
|
||||
info "RAM: ${RAM_MB}MB verfügbar"
|
||||
if [[ "$RAM_MB" -lt 2000 ]]; then
|
||||
warn "Weniger als 2GB RAM – Ollama könnte Probleme haben."
|
||||
ask_yn "Trotzdem fortfahren?" || fail "Installation abgebrochen."
|
||||
else
|
||||
ok "RAM ausreichend"
|
||||
fi
|
||||
|
||||
# Disk check
|
||||
DISK_FREE=$(df -BG / | awk 'NR==2 {print $4}' | tr -d 'G')
|
||||
info "Freier Speicher: ${DISK_FREE}GB"
|
||||
[[ "$DISK_FREE" -lt 5 ]] && warn "Weniger als 5GB frei – Ollama-Modell braucht ~2GB."
|
||||
ok "Systemcheck abgeschlossen"
|
||||
}
|
||||
|
||||
# STEP 2: PAKETE INSTALLIEREN
|
||||
|
||||
step_packages() {
|
||||
step 2 "Pakete installieren"
|
||||
|
||||
info "Paketliste wird aktualisiert..."
|
||||
|
||||
case "$PKG_MANAGER" in
|
||||
apt)
|
||||
run_with_spinner "Paketliste aktualisieren" apt-get update -qq || fail "apt update fehlgeschlagen"
|
||||
run_with_spinner "Pakete installieren" apt-get install -y -qq curl wget git ufw python3 python3-pip python3-venv sudo htop net-tools lsof rsync openssl knockd || warn "Einige Pakete konnten nicht installiert werden – siehe Log"
|
||||
;;
|
||||
dnf)
|
||||
run_with_spinner "Pakete aktualisieren" dnf update -y -q
|
||||
run_with_spinner "Pakete installieren" dnf install -y -q curl wget git python3 python3-pip sudo htop net-tools rsync openssl
|
||||
warn "knockd und ufw nicht verfügbar auf Fedora – firewalld wird genutzt"
|
||||
;;
|
||||
pacman)
|
||||
run_with_spinner "System aktualisieren" pacman -Syu --noconfirm
|
||||
run_with_spinner "Pakete installieren" pacman -S --noconfirm curl wget git python python-pip sudo htop net-tools rsync openssl
|
||||
;;
|
||||
esac
|
||||
|
||||
ok "Pakete installiert"
|
||||
}
|
||||
|
||||
# STEP 3: BENUTZER EINRICHTEN
|
||||
|
||||
step_user() {
|
||||
step 3 "Benutzer einrichten"
|
||||
|
||||
echo ""
|
||||
echo -e " ${BOLD}Welchen User soll der Clownfischserver nutzen?${NC}"
|
||||
echo -e " ${CYAN} Dieser User verwaltet den Server und bekommt sudo-Rechte.${NC}"
|
||||
echo -e " ${CYAN} Du kannst einen neuen anlegen oder einen vorhandenen nutzen.${NC}"
|
||||
echo ""
|
||||
|
||||
# List existing non-system users
|
||||
EXISTING_USERS=$(awk -F: '$3>=1000 && $3<65534 {print $1}' /etc/passwd | tr '\n' ' ')
|
||||
if [[ -n "$EXISTING_USERS" ]]; then
|
||||
info "Vorhandene Benutzer: $EXISTING_USERS"
|
||||
fi
|
||||
|
||||
echo -e " ${YELLOW}?${NC} ${BOLD}Username (Enter = 'clownfish'):${NC}"
|
||||
echo -e " ${CYAN} Neu oder vorhandenen Namen eingeben${NC}"
|
||||
read -r -p " → " MGMT_USER
|
||||
[[ -z "$MGMT_USER" ]] && MGMT_USER="clownfish"
|
||||
|
||||
if id "$MGMT_USER" &>/dev/null; then
|
||||
warn "User '$MGMT_USER' existiert bereits – wird wiederverwendet."
|
||||
else
|
||||
useradd -m -s /bin/bash "$MGMT_USER" || fail "User konnte nicht angelegt werden"
|
||||
ok "User '$MGMT_USER' angelegt"
|
||||
fi
|
||||
|
||||
# Passwordless sudo
|
||||
echo "$MGMT_USER ALL=(ALL) NOPASSWD:ALL" > "/etc/sudoers.d/clownfisch-$MGMT_USER"
|
||||
chmod 0440 "/etc/sudoers.d/clownfisch-$MGMT_USER"
|
||||
ok "Sudo-Rechte gesetzt (passwordless)"
|
||||
|
||||
# SSH dir
|
||||
mkdir -p "/home/$MGMT_USER/.ssh"
|
||||
chmod 700 "/home/$MGMT_USER/.ssh"
|
||||
chown -R "$MGMT_USER:$MGMT_USER" "/home/$MGMT_USER"
|
||||
ok "SSH-Verzeichnis angelegt"
|
||||
|
||||
# Always set a password as backup
|
||||
TEMP_PASS=$(openssl rand -base64 12)
|
||||
echo "$MGMT_USER:$TEMP_PASS" | chpasswd
|
||||
echo ""
|
||||
echo -e " ${BOLD}${YELLOW}╔════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e " ${BOLD}${YELLOW}║ SSH Passwort für '$MGMT_USER': ║${NC}"
|
||||
echo -e " ${BOLD}${YELLOW}║ ║${NC}"
|
||||
echo -e " ${BOLD}${YELLOW}║ ${TEMP_PASS} ║${NC}"
|
||||
echo -e " ${BOLD}${YELLOW}║ ║${NC}"
|
||||
echo -e " ${BOLD}${YELLOW}║ Notiere es jetzt! SSH bleibt offen bis du ║${NC}"
|
||||
echo -e " ${BOLD}${YELLOW}║ im Telegram-Bot /ssh close sendest. ║${NC}"
|
||||
echo -e " ${BOLD}${YELLOW}╚════════════════════════════════════════════════════╝${NC}"
|
||||
pause "Passwort notiert?"
|
||||
}
|
||||
|
||||
# STEP 4: SSH + FIREWALL
|
||||
|
||||
step_ssh() {
|
||||
step 4 "SSH + Firewall"
|
||||
|
||||
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
|
||||
ok "sshd_config gesichert"
|
||||
|
||||
# SSH – nur gezielt aendern, bestehende Config behalten!
|
||||
info "Passe sshd_config an (bestehende Einstellungen bleiben erhalten)..."
|
||||
|
||||
_sshd_set() {
|
||||
local key="$1" val="$2"
|
||||
if grep -qE "^#?\s*${key}" /etc/ssh/sshd_config; then
|
||||
sed -i "s|^#\?\s*${key}.*|${key} ${val}|" /etc/ssh/sshd_config
|
||||
else
|
||||
echo "${key} ${val}" >> /etc/ssh/sshd_config
|
||||
fi
|
||||
}
|
||||
|
||||
_sshd_set "PasswordAuthentication" "yes"
|
||||
_sshd_set "PubkeyAuthentication" "yes"
|
||||
_sshd_set "X11Forwarding" "no"
|
||||
_sshd_set "PermitRootLogin" "yes"
|
||||
ok "sshd_config angepasst (nur relevante Zeilen geaendert)"
|
||||
|
||||
systemctl restart sshd >> "$LOG_FILE" 2>&1
|
||||
ok "SSH läuft – Passwort + Key erlaubt"
|
||||
info "SSH wird erst gesperrt wenn du im Bot /ssh close sendest"
|
||||
|
||||
# Home IP Auto-Allow via myfritz
|
||||
CLOWNFISCHSERVER_HOME_IP_DOMAIN=""
|
||||
echo ""
|
||||
echo -e " ${BOLD}${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e " ${BOLD} myfritz Home-IP Automatik${NC}"
|
||||
echo -e " ${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e " SSH von Zuhause immer offen – ohne Port-Knocking."
|
||||
echo -e " ${CYAN} Clownfischserver aktualisiert deine Home-IP alle 5 Min automatisch.${NC}"
|
||||
echo ""
|
||||
|
||||
if ask_yn "myfritz Home-IP permanent erlauben?"; then
|
||||
echo ""
|
||||
echo -e " ${YELLOW}?${NC} ${BOLD}Deine myfritz Adresse:${NC}"
|
||||
echo -e " ${CYAN} Format: vorname.myfritz.net${NC}"
|
||||
read -r -p " → " CLOWNFISCHSERVER_HOME_IP_DOMAIN
|
||||
|
||||
if [[ -n "$CLOWNFISCHSERVER_HOME_IP_DOMAIN" ]]; then
|
||||
ok "myfritz Adresse gespeichert: $CLOWNFISCHSERVER_HOME_IP_DOMAIN"
|
||||
info "⏱ Clownfischserver-HomeIP Updater wird alle 5 Min gestartet"
|
||||
else
|
||||
warn "Leere Adresse – myfritz Automatik deaktiviert"
|
||||
CLOWNFISCHSERVER_HOME_IP_DOMAIN=""
|
||||
fi
|
||||
else
|
||||
info "myfritz Automatik deaktiviert"
|
||||
fi
|
||||
|
||||
# Firewall
|
||||
case "$PKG_MANAGER" in
|
||||
apt)
|
||||
ufw default deny incoming >> "$LOG_FILE" 2>&1
|
||||
ufw default allow outgoing >> "$LOG_FILE" 2>&1
|
||||
ufw allow 22/tcp >> "$LOG_FILE" 2>&1
|
||||
echo "y" | ufw enable >> "$LOG_FILE" 2>&1
|
||||
ok "ufw Firewall aktiv – Port 22 offen"
|
||||
|
||||
# knockd
|
||||
IFACE=$(ip route | grep default | awk '{print $5}' | head -1)
|
||||
NOTIFY_CMD="$INSTALL_DIR/venv/bin/python3 $INSTALL_DIR/bot/knock_notify.py"
|
||||
|
||||
# knockd – nur clownfisch-Sektionen schreiben, bestehende Config behalten
|
||||
if [ -f /etc/knockd.conf ]; then
|
||||
# Entferne nur unsere alten Sektionen falls vorhanden
|
||||
sed -i '/^\[openSSH\]/,/^$/d' /etc/knockd.conf 2>/dev/null || true
|
||||
sed -i '/^\[closeSSH\]/,/^$/d' /etc/knockd.conf 2>/dev/null || true
|
||||
# Interface in [options] setzen falls vorhanden
|
||||
if grep -q "^\[options\]" /etc/knockd.conf; then
|
||||
grep -q "Interface" /etc/knockd.conf || sed -i "/^\[options\]/a\ Interface = $IFACE" /etc/knockd.conf
|
||||
else
|
||||
echo -e "[options]
|
||||
UseSyslog
|
||||
Interface = $IFACE
|
||||
" >> /etc/knockd.conf
|
||||
fi
|
||||
else
|
||||
# Neue Config anlegen
|
||||
cat > /etc/knockd.conf << KNOCKEOF
|
||||
[options]
|
||||
UseSyslog
|
||||
Interface = $IFACE
|
||||
KNOCKEOF
|
||||
fi
|
||||
|
||||
# Unsere SSH-Sektionen hinzufuegen
|
||||
cat >> /etc/knockd.conf << KNOCKEOF
|
||||
|
||||
[openSSH]
|
||||
sequence = 7000,8000,9000
|
||||
seq_timeout = 10
|
||||
command = /sbin/iptables -I INPUT -s %IP% -p tcp --dport 22 -j ACCEPT && $NOTIFY_CMD open %IP%
|
||||
tcpflags = syn
|
||||
|
||||
[closeSSH]
|
||||
sequence = 9000,8000,7000
|
||||
seq_timeout = 10
|
||||
command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT && $NOTIFY_CMD close %IP%
|
||||
tcpflags = syn
|
||||
KNOCKEOF
|
||||
[ -f /etc/default/knockd ] && sed -i 's/START_KNOCKD=0/START_KNOCKD=1/' /etc/default/knockd
|
||||
systemctl enable knockd >> "$LOG_FILE" 2>&1
|
||||
systemctl restart knockd >> "$LOG_FILE" 2>&1 && ok "Port-Knocking aktiv (7000→8000→9000)" || warn "knockd konnte nicht starten"
|
||||
;;
|
||||
dnf)
|
||||
systemctl enable firewalld >> "$LOG_FILE" 2>&1
|
||||
systemctl start firewalld >> "$LOG_FILE" 2>&1
|
||||
firewall-cmd --permanent --add-service=ssh >> "$LOG_FILE" 2>&1
|
||||
firewall-cmd --reload >> "$LOG_FILE" 2>&1
|
||||
ok "firewalld aktiv – SSH offen"
|
||||
;;
|
||||
pacman)
|
||||
warn "Bitte Firewall manuell einrichten (iptables/nftables)"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# HELPER: Pull an Ollama model with progress bar
|
||||
|
||||
_pull_model() {
|
||||
local model_name="$1"
|
||||
echo ""
|
||||
echo -e " ${CYAN}Lade Modell: ${BOLD}$model_name${NC}"
|
||||
echo -e " ${CYAN}Das kann je nach Internetgeschwindigkeit 2-10 Minuten dauern.${NC}"
|
||||
echo ""
|
||||
|
||||
tput civis 2>/dev/null
|
||||
ollama pull "$model_name" 2>&1 | while IFS= read -r line; do
|
||||
if [[ "$line" =~ ([0-9]+)% ]]; then
|
||||
pct="${BASH_REMATCH[1]}"
|
||||
filled=$(( pct * 30 / 100 ))
|
||||
empty=$(( 30 - filled ))
|
||||
bar=""
|
||||
for ((i=0; i<filled; i++)); do bar+="="; done
|
||||
for ((i=0; i<empty; i++)); do bar+="."; done
|
||||
printf "\r [%-30s] %3s%% " "$bar" "$pct"
|
||||
elif [[ "$line" == *"pulling manifest"* ]]; then
|
||||
printf "\r Manifest wird geladen... "
|
||||
elif [[ "$line" == *"verifying"* ]]; then
|
||||
printf "\r Verifiziere Modell... "
|
||||
elif [[ "$line" == *"writing"* ]]; then
|
||||
printf "\r Schreibe Modell... "
|
||||
elif [[ "$line" == *"success"* ]]; then
|
||||
printf "\r Download abgeschlossen! \n"
|
||||
fi
|
||||
done
|
||||
tput cnorm 2>/dev/null
|
||||
echo ""
|
||||
|
||||
ollama list 2>/dev/null | grep -q "${model_name%%:*}" || fail "Modell konnte nicht geladen werden: $model_name"
|
||||
ok "Modell geladen: $model_name"
|
||||
}
|
||||
|
||||
# STEP 5: OLLAMA
|
||||
|
||||
step_ollama() {
|
||||
step 5 "Ollama (KI-Engine)"
|
||||
|
||||
echo ""
|
||||
info "Ollama ist die KI die deinen Bot intelligent macht."
|
||||
info "Es laeuft komplett lokal auf deinem Server – keine Cloud."
|
||||
|
||||
if command -v ollama &>/dev/null; then
|
||||
warn "Ollama bereits installiert"
|
||||
else
|
||||
run_with_spinner "Ollama wird heruntergeladen und installiert" \
|
||||
bash -c "curl -fsSL https://ollama.com/install.sh | sh" || fail "Ollama Installation fehlgeschlagen"
|
||||
ok "Ollama installiert"
|
||||
fi
|
||||
|
||||
run_with_spinner "Ollama Service starten" systemctl enable ollama
|
||||
run_with_spinner "Ollama hochfahren" bash -c "systemctl start ollama && sleep 5"
|
||||
|
||||
# ─────────────────────────────────────────────────────────
|
||||
# SCHRITT 1: Coder-Modell (Aider + /shell Befehlsgenerierung)
|
||||
# ─────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo -e " ${BOLD}${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e " ${BOLD} [1/2] Coder-Modell${NC}"
|
||||
echo -e " ${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e " Dieses Modell generiert Code (Aider) und Shell-Befehle (/shell)."
|
||||
echo -e " Es muss gut programmieren koennen – Deutsch ist hier zweitrangig."
|
||||
echo ""
|
||||
|
||||
# RAM-basierte Empfehlung fuer Coder
|
||||
if [[ "$RAM_MB" -ge 16000 ]]; then
|
||||
CODER_DEFAULT="qwen2.5-coder:7b"
|
||||
CODER_HINT="16GB+ RAM erkannt – bestes Code-Modell fuer CPU"
|
||||
else
|
||||
CODER_DEFAULT="qwen2.5-coder:3b"
|
||||
CODER_HINT="Kompakt und schnell auf CPU"
|
||||
fi
|
||||
|
||||
echo -e " ${CYAN} Dein RAM: ${BOLD}${RAM_MB}MB${NC}"
|
||||
echo -e " ${CYAN} Empfehlung: ${BOLD}${CODER_DEFAULT}${NC} ${CYAN}(${CODER_HINT})${NC}"
|
||||
echo ""
|
||||
echo -e " ${CYAN} [1] qwen2.5-coder:3b – schnell, ~2GB RAM (unter 16GB)${NC}"
|
||||
echo -e " ${CYAN} [2] qwen2.5-coder:7b – stark, ~4.5GB RAM (16GB+ empfohlen)${NC}"
|
||||
echo -e " ${CYAN} [3] eigenes Modell eingeben${NC}"
|
||||
echo ""
|
||||
echo -e " ${YELLOW}?${NC} ${BOLD}Auswahl [1/2/3]:${NC} (Enter = Empfehlung)"
|
||||
read -r -p " → " CODER_CHOICE
|
||||
case "$CODER_CHOICE" in
|
||||
1) OLLAMA_MODEL="qwen2.5-coder:3b" ;;
|
||||
2) OLLAMA_MODEL="qwen2.5-coder:7b" ;;
|
||||
3) echo -e " ${YELLOW}?${NC} ${BOLD}Modell-Name:${NC} ${CYAN}z.B. codellama:7b${NC}"
|
||||
read -r -p " → " OLLAMA_MODEL ;;
|
||||
*) OLLAMA_MODEL="$CODER_DEFAULT" ;;
|
||||
esac
|
||||
|
||||
_pull_model "$OLLAMA_MODEL"
|
||||
ok "Coder-Modell: $OLLAMA_MODEL"
|
||||
|
||||
# ─────────────────────────────────────────────────────────
|
||||
# SCHRITT 2: Chat-Modell (Deutsch, schnelle Antworten)
|
||||
# ─────────────────────────────────────────────────────────
|
||||
OLLAMA_MODEL_FAST=""
|
||||
|
||||
echo ""
|
||||
echo -e " ${BOLD}${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e " ${BOLD} [2/2] Chat-Modell (optional)${NC}"
|
||||
echo -e " ${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e " Ein separates Chat-Modell antwortet schneller auf Nachrichten"
|
||||
echo -e " und spricht besser Deutsch. Das Coder-Modell bleibt fuer Code."
|
||||
echo ""
|
||||
echo -e " ${CYAN} Coder-Modell: ${BOLD}$OLLAMA_MODEL${NC} ${CYAN}(bereits installiert)${NC}"
|
||||
echo ""
|
||||
|
||||
if ask_yn "Separates Chat-Modell installieren? (empfohlen)"; then
|
||||
echo ""
|
||||
|
||||
# RAM-basierte Empfehlung fuer Chat
|
||||
if [[ "$RAM_MB" -ge 24000 ]]; then
|
||||
CHAT_DEFAULT="mistral:7b"
|
||||
CHAT_HINT="24GB+ RAM – bestes Deutsch (franzoesische KI, exzellent in EU-Sprachen)"
|
||||
echo -e " ${CYAN} [1] gemma3:1b – 800MB RAM, schnell, brauchbares Deutsch${NC}"
|
||||
echo -e " ${CYAN} [2] gemma3:4b – 3GB RAM, sehr gutes Deutsch (Google multilingual)${NC}"
|
||||
echo -e " ${BOLD} [3] mistral:7b – 4.5GB RAM, exzellentes Deutsch ← empfohlen${NC}"
|
||||
echo -e " ${CYAN} [4] eigenes Modell eingeben${NC}"
|
||||
elif [[ "$RAM_MB" -ge 12000 ]]; then
|
||||
CHAT_DEFAULT="gemma3:4b"
|
||||
CHAT_HINT="12-20GB RAM – sehr gutes Deutsch bei wenig RAM (Google multilingual)"
|
||||
echo -e " ${CYAN} [1] gemma3:1b – 800MB RAM, schnell, brauchbares Deutsch${NC}"
|
||||
echo -e " ${BOLD} [2] gemma3:4b – 3GB RAM, sehr gutes Deutsch ← empfohlen${NC}"
|
||||
echo -e " ${CYAN} [3] mistral:7b – 4.5GB RAM, exzellentes Deutsch (viel RAM noetig)${NC}"
|
||||
echo -e " ${CYAN} [4] eigenes Modell eingeben${NC}"
|
||||
else
|
||||
CHAT_DEFAULT="gemma3:1b"
|
||||
CHAT_HINT="<12GB RAM – klein und schnell, spart RAM fuer System"
|
||||
echo -e " ${BOLD} [1] gemma3:1b – 800MB RAM, schnell, brauchbar ← empfohlen${NC}"
|
||||
echo -e " ${CYAN} [2] gemma3:4b – 3GB RAM, sehr gutes Deutsch (knapp bei <12GB)${NC}"
|
||||
echo -e " ${CYAN} [3] mistral:7b – 4.5GB RAM, exzellentes Deutsch (zu gross!)${NC}"
|
||||
echo -e " ${CYAN} [4] eigenes Modell eingeben${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e " ${CYAN} Empfehlung: ${BOLD}${CHAT_DEFAULT}${NC} ${CYAN}(${CHAT_HINT})${NC}"
|
||||
echo ""
|
||||
echo -e " ${YELLOW}?${NC} ${BOLD}Auswahl [1/2/3/4]:${NC} (Enter = Empfehlung)"
|
||||
read -r -p " → " CHAT_CHOICE
|
||||
case "$CHAT_CHOICE" in
|
||||
1) OLLAMA_MODEL_FAST="gemma3:1b" ;;
|
||||
2) OLLAMA_MODEL_FAST="gemma3:4b" ;;
|
||||
3) OLLAMA_MODEL_FAST="mistral:7b" ;;
|
||||
4) echo -e " ${YELLOW}?${NC} ${BOLD}Modell-Name:${NC} ${CYAN}z.B. llama3.2:3b${NC}"
|
||||
read -r -p " → " OLLAMA_MODEL_FAST ;;
|
||||
*) OLLAMA_MODEL_FAST="$CHAT_DEFAULT" ;;
|
||||
esac
|
||||
|
||||
_pull_model "$OLLAMA_MODEL_FAST"
|
||||
ok "Chat-Modell: $OLLAMA_MODEL_FAST"
|
||||
echo ""
|
||||
ok "Dual-Model aktiv:"
|
||||
info " Coder: $OLLAMA_MODEL (Code, /shell, Erklaerungen)"
|
||||
info " Chat: $OLLAMA_MODEL_FAST (Nachrichten, Deutsch)"
|
||||
else
|
||||
info "Single-Model: $OLLAMA_MODEL fuer alles"
|
||||
fi
|
||||
|
||||
# ─────────────────────────────────────────────────────────
|
||||
# Context-Window (wie viel Text das Modell auf einmal sieht)
|
||||
# ─────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo -e " ${BOLD}${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e " ${BOLD} Context-Window (KI-Gedaechtnis)${NC}"
|
||||
echo -e " ${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e " Je groesser, desto mehr Code/Text kann die KI auf einmal sehen."
|
||||
echo -e " Groesseres Fenster = mehr RAM-Verbrauch + etwas langsamer."
|
||||
echo ""
|
||||
|
||||
if [[ "$RAM_MB" -ge 24000 ]]; then
|
||||
CTX_DEFAULT=32768
|
||||
echo -e " ${CYAN} [1] 4096 – minimal, schnell${NC}"
|
||||
echo -e " ${CYAN} [2] 8192 – gut fuer /shell${NC}"
|
||||
echo -e " ${CYAN} [3] 16384 – ideal fuer /code${NC}"
|
||||
echo -e " ${BOLD} [4] 32768 – maximale Code-Qualitaet ← empfohlen${NC}"
|
||||
elif [[ "$RAM_MB" -ge 16000 ]]; then
|
||||
CTX_DEFAULT=16384
|
||||
echo -e " ${CYAN} [1] 4096 – minimal, schnell${NC}"
|
||||
echo -e " ${CYAN} [2] 8192 – gut fuer /shell${NC}"
|
||||
echo -e " ${BOLD} [3] 16384 – ideal fuer /code ← empfohlen${NC}"
|
||||
echo -e " ${CYAN} [4] 32768 – maximale Qualitaet (braucht viel RAM)${NC}"
|
||||
elif [[ "$RAM_MB" -ge 12000 ]]; then
|
||||
CTX_DEFAULT=8192
|
||||
echo -e " ${CYAN} [1] 4096 – minimal${NC}"
|
||||
echo -e " ${BOLD} [2] 8192 – guter Kompromiss ← empfohlen${NC}"
|
||||
echo -e " ${CYAN} [3] 16384 – groessere Scripts${NC}"
|
||||
echo -e " ${CYAN} [4] 32768 – nur wenn genuegend RAM${NC}"
|
||||
else
|
||||
CTX_DEFAULT=4096
|
||||
echo -e " ${BOLD} [1] 4096 – passend fuer wenig RAM ← empfohlen${NC}"
|
||||
echo -e " ${CYAN} [2] 8192 – wenn es reicht${NC}"
|
||||
echo -e " ${CYAN} [3] 16384 – wahrscheinlich zu viel${NC}"
|
||||
echo -e " ${CYAN} [4] 32768 – nicht empfohlen${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e " ${YELLOW}?${NC} ${BOLD}Auswahl [1/2/3/4]:${NC} (Enter = Empfehlung)"
|
||||
read -r -p " → " CTX_CHOICE
|
||||
case "$CTX_CHOICE" in
|
||||
1) OLLAMA_NUM_CTX=4096 ;;
|
||||
2) OLLAMA_NUM_CTX=8192 ;;
|
||||
3) OLLAMA_NUM_CTX=16384 ;;
|
||||
4) OLLAMA_NUM_CTX=32768 ;;
|
||||
*) OLLAMA_NUM_CTX=$CTX_DEFAULT ;;
|
||||
esac
|
||||
ok "Context-Window: $OLLAMA_NUM_CTX Token"
|
||||
}
|
||||
|
||||
# STEP 6: PYTHON + PAKETE
|
||||
|
||||
step_python() {
|
||||
step 6 "Python-Umgebung"
|
||||
|
||||
mkdir -p "$INSTALL_DIR" "$SNAPSHOT_DIR" "$WORKSPACE_DIR"
|
||||
|
||||
run_with_spinner "Python Umgebung erstellen" python3 -m venv "$INSTALL_DIR/venv" || fail "venv konnte nicht erstellt werden"
|
||||
ok "Python venv erstellt"
|
||||
|
||||
run_with_spinner "pip aktualisieren" "$INSTALL_DIR/venv/bin/pip" install --upgrade pip
|
||||
|
||||
run_with_spinner "Telegram-Bot Paket installieren" "$INSTALL_DIR/venv/bin/pip" install "python-telegram-bot==21.0" requests python-dotenv || fail "Basis-Pakete fehlgeschlagen"
|
||||
ok "Telegram + Basis-Pakete installiert"
|
||||
|
||||
# Aider bekommt ein eigenes venv – seine Dependencies kollidieren mit dem Bot
|
||||
info "Installiere aider-chat in separatem venv (2-5 Min)..."
|
||||
AIDER_VENV="$INSTALL_DIR/aider-venv"
|
||||
|
||||
if python3 -m venv "$AIDER_VENV" >> "$LOG_FILE" 2>&1; then
|
||||
"$AIDER_VENV/bin/pip" install --upgrade pip setuptools wheel >> "$LOG_FILE" 2>&1
|
||||
if "$AIDER_VENV/bin/pip" install aider-chat >> "$LOG_FILE" 2>&1; then
|
||||
ok "aider-chat installiert ($AIDER_VENV)"
|
||||
elif "$AIDER_VENV/bin/pip" install --no-build-isolation aider-chat >> "$LOG_FILE" 2>&1; then
|
||||
ok "aider-chat installiert (no-build-isolation)"
|
||||
else
|
||||
warn "aider-chat fehlgeschlagen – spaeter manuell:"
|
||||
info " $AIDER_VENV/bin/pip install aider-chat"
|
||||
info "Der Bot funktioniert ohne Aider – /code ist dann nicht verfuegbar."
|
||||
fi
|
||||
else
|
||||
warn "Aider venv konnte nicht erstellt werden"
|
||||
fi
|
||||
}
|
||||
|
||||
# STEP 7: TELEGRAM
|
||||
|
||||
step_telegram() {
|
||||
step 7 "Telegram Bot einrichten"
|
||||
|
||||
echo ""
|
||||
echo -e " ${BOLD}${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e " ${BOLD} Was ist ein Telegram Bot?${NC}"
|
||||
echo -e " ${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e " Ein Bot ist ein automatisches Telegram-Konto das auf"
|
||||
echo -e " Nachrichten reagiert. Du schreibst dem Bot – der Bot"
|
||||
echo -e " fuehrt Befehle auf deinem Server aus."
|
||||
echo -e " ${BOLD}Jeder braucht seinen eigenen Bot – das geht in 2 Minuten.${NC}"
|
||||
echo ""
|
||||
echo -e " ${BOLD}${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e " ${BOLD} Schritt 1: Bot erstellen${NC}"
|
||||
echo -e " ${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo ""
|
||||
echo -e " 1. Oeffne Telegram auf deinem Handy oder PC"
|
||||
echo -e " 2. Suche nach: ${BOLD}@BotFather${NC}"
|
||||
echo -e " 3. Sende: ${BOLD}/newbot${NC}"
|
||||
echo -e " 4. Vergib einen Anzeigenamen (z.B. 'Mein Server Bot')"
|
||||
echo -e " 5. Vergib einen Username (muss auf 'bot' enden)"
|
||||
echo -e " 6. Du bekommst einen ${BOLD}Token${NC} – sieht so aus:"
|
||||
echo -e " ${CYAN}1234567890:ABCdefGHIjklMNOpqrSTUvwxYZ${NC}"
|
||||
echo ""
|
||||
pause "Bot erstellt? Dann weiter..."
|
||||
|
||||
echo -e " ${YELLOW}?${NC} ${BOLD}Dein Bot-Token:${NC}"
|
||||
echo -e " ${CYAN} Den kompletten Token von @BotFather einfuegen${NC}"
|
||||
read -r -p " -> " BOT_TOKEN
|
||||
[[ -z "$BOT_TOKEN" ]] && fail "Kein Token eingegeben."
|
||||
ok "Token gespeichert"
|
||||
|
||||
echo ""
|
||||
echo -e " ${BOLD}${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e " ${BOLD} Schritt 2: Deine Chat-ID herausfinden${NC}"
|
||||
echo -e " ${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo ""
|
||||
echo -e " 1. Suche in Telegram nach: ${BOLD}@userinfobot${NC}"
|
||||
echo -e " 2. Sende irgendetwas (z.B. 'hi')"
|
||||
echo -e " 3. Der Bot antwortet mit deiner ${BOLD}Id${NC} – das ist deine Chat-ID"
|
||||
echo ""
|
||||
pause "Chat-ID gefunden? Dann weiter..."
|
||||
|
||||
echo -e " ${YELLOW}?${NC} ${BOLD}Deine Chat-ID:${NC}"
|
||||
echo -e " ${CYAN} Nur die Zahl, keine anderen Zeichen${NC}"
|
||||
read -r -p " -> " CHAT_ID
|
||||
[[ -z "$CHAT_ID" ]] && fail "Keine Chat-ID eingegeben."
|
||||
ok "Chat-ID gespeichert"
|
||||
|
||||
# ─────────────────────────────────────────────────────────
|
||||
# OpenRouter Fallback (optional, kostenlos)
|
||||
# ─────────────────────────────────────────────────────────
|
||||
OPENROUTER_API_KEY=""
|
||||
|
||||
echo ""
|
||||
echo -e " ${BOLD}${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e " ${BOLD} OpenRouter Fallback (optional)${NC}"
|
||||
echo -e " ${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e " Wenn dein Ollama offline ist, kann der Bot auf Cloud-Modelle ausweichen."
|
||||
echo -e " OpenRouter Free Tier: 50 Requests/Tag kostenlos (keine Kreditkarte!)"
|
||||
echo ""
|
||||
|
||||
if ask_yn "OpenRouter als Fallback aktivieren?"; then
|
||||
echo ""
|
||||
echo -e " ${CYAN}Registrier dich kostenlos: https://openrouter.ai${NC}"
|
||||
echo -e " ${CYAN}Dann kopier deinen API Key von der Dashboard${NC}"
|
||||
echo ""
|
||||
echo -e " ${YELLOW}?${NC} ${BOLD}OpenRouter API Key:${NC}"
|
||||
echo -e " ${CYAN} Format: sk-or-v1-...${NC}"
|
||||
read -r -p " → " OPENROUTER_API_KEY
|
||||
|
||||
if [[ -z "$OPENROUTER_API_KEY" ]]; then
|
||||
warn "Leerer Key – OpenRouter deaktiviert"
|
||||
OPENROUTER_API_KEY=""
|
||||
else
|
||||
ok "OpenRouter aktiviert"
|
||||
fi
|
||||
else
|
||||
info "OpenRouter deaktiviert – Bot nutzt nur lokal Ollama"
|
||||
fi
|
||||
|
||||
mkdir -p "$INSTALL_DIR/config"
|
||||
cat > "$INSTALL_DIR/config/.env" << ENVEOF
|
||||
TELEGRAM_BOT_TOKEN=$BOT_TOKEN
|
||||
TELEGRAM_CHAT_ID=$CHAT_ID
|
||||
OLLAMA_BASE_URL=http://localhost:11434
|
||||
OLLAMA_MODEL=$OLLAMA_MODEL
|
||||
OLLAMA_MODEL_FAST=$OLLAMA_MODEL_FAST
|
||||
OLLAMA_NUM_CTX=$OLLAMA_NUM_CTX
|
||||
OPENROUTER_API_KEY=$OPENROUTER_API_KEY
|
||||
SNAPSHOT_DIR=$SNAPSHOT_DIR
|
||||
SNAPSHOT_METHOD=$SNAPSHOT_METHOD
|
||||
INSTALL_DIR=$INSTALL_DIR
|
||||
MGMT_USER=$MGMT_USER
|
||||
AIDER_WORKDIR=$WORKSPACE_DIR
|
||||
CLOWNFISCHSERVER_HOME_IP_DOMAIN=$CLOWNFISCHSERVER_HOME_IP_DOMAIN
|
||||
ENVEOF
|
||||
|
||||
chmod 600 "$INSTALL_DIR/config/.env"
|
||||
ok "Konfiguration gespeichert"
|
||||
}
|
||||
|
||||
# STEP 8: BOT STARTEN
|
||||
|
||||
step_service() {
|
||||
step 8 "Bot installieren + starten"
|
||||
|
||||
mkdir -p "$INSTALL_DIR/bot"
|
||||
cp -r "$SCRIPT_DIR/bot/"* "$INSTALL_DIR/bot/" || fail "Bot-Dateien nicht gefunden – ist der bot/ Ordner dabei?"
|
||||
ok "Bot-Dateien kopiert"
|
||||
|
||||
chown -R "$MGMT_USER:$MGMT_USER" "$INSTALL_DIR" "$SNAPSHOT_DIR" "$WORKSPACE_DIR"
|
||||
chmod -R u+w "$INSTALL_DIR/bot"
|
||||
ok "Verzeichnis-Rechte gesetzt"
|
||||
|
||||
cat > /etc/systemd/system/clownfisch.service << SERVICEEOF
|
||||
[Unit]
|
||||
Description=🐠 Clownfischserver Telegram Bot
|
||||
Documentation=https://github.com/mehlzoerwer-claude/clownfischserver
|
||||
After=network-online.target ollama.service
|
||||
Wants=network-online.target ollama.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=$MGMT_USER
|
||||
WorkingDirectory=$INSTALL_DIR
|
||||
EnvironmentFile=$INSTALL_DIR/config/.env
|
||||
ExecStart=$INSTALL_DIR/venv/bin/python3 $INSTALL_DIR/bot/bot.py
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=clownfisch
|
||||
# Allow sudo and iptables for SSH management
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_SYS_ADMIN
|
||||
SecureBits=keep-caps
|
||||
PrivateTmp=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
SERVICEEOF
|
||||
|
||||
run_with_spinner "Systemd neu laden" systemctl daemon-reload
|
||||
run_with_spinner "Bot-Service aktivieren" systemctl enable clownfisch
|
||||
run_with_spinner "Bot starten" systemctl start clownfisch
|
||||
sleep 3
|
||||
|
||||
if systemctl is-active --quiet clownfisch; then
|
||||
ok "Bot laeuft!"
|
||||
else
|
||||
warn "Bot gestartet – check Logs: journalctl -u clownfisch -n 20 --no-pager"
|
||||
fi
|
||||
|
||||
# Boot-Notification Service
|
||||
cat > /etc/systemd/system/clownfisch-boot.service << BOOTEOF
|
||||
[Unit]
|
||||
Description=🐠 Clownfischserver Boot Notification
|
||||
Documentation=https://github.com/mehlzoerwer-claude/clownfischserver
|
||||
After=network-online.target clownfisch.service ollama.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStartPre=/bin/sleep 120
|
||||
ExecStart=$INSTALL_DIR/venv/bin/python3 $INSTALL_DIR/bot/boot_notify.py
|
||||
EnvironmentFile=$INSTALL_DIR/config/.env
|
||||
WorkingDirectory=$INSTALL_DIR
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=clownfisch-boot
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
BOOTEOF
|
||||
|
||||
run_with_spinner "Boot-Notification aktivieren" bash -c "systemctl daemon-reload && systemctl enable clownfisch-boot"
|
||||
ok "Boot-Notification: Telegram-Meldung 2 Min nach Serverstart"
|
||||
|
||||
# Clownfischserver HomeIP Updater (wenn aktiviert)
|
||||
if [[ -n "$CLOWNFISCHSERVER_HOME_IP_DOMAIN" ]]; then
|
||||
mkdir -p "$INSTALL_DIR/scripts"
|
||||
cp "$SCRIPT_DIR/scripts/clownfischserver-homeip-updater.sh" "$INSTALL_DIR/scripts/" 2>/dev/null || \
|
||||
warn "homeip-updater.sh nicht gefunden – manuell nachträglich einrichten"
|
||||
|
||||
if [ -f "$INSTALL_DIR/scripts/clownfischserver-homeip-updater.sh" ]; then
|
||||
chmod +x "$INSTALL_DIR/scripts/clownfischserver-homeip-updater.sh"
|
||||
chown root:root "$INSTALL_DIR/scripts/clownfischserver-homeip-updater.sh"
|
||||
|
||||
# Cron-Job für root (da ufw sudo braucht)
|
||||
CRON_JOB="*/5 * * * * $INSTALL_DIR/scripts/clownfischserver-homeip-updater.sh >> /var/log/clownfischserver-homeip.log 2>&1"
|
||||
|
||||
# Bestehende clownfischserver Cron-Jobs entfernen
|
||||
sudo crontab -l 2>/dev/null | grep -v clownfischserver-homeip-updater | sudo crontab - 2>/dev/null || true
|
||||
|
||||
# Neuen Job hinzufügen
|
||||
(sudo crontab -l 2>/dev/null; echo "$CRON_JOB") | sudo crontab - >> "$LOG_FILE" 2>&1
|
||||
|
||||
ok "Cron-Job aktiviert: alle 5 Min HomeIP prüfen"
|
||||
info "Log: /var/log/clownfischserver-homeip.log"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# ZUSAMMENFASSUNG
|
||||
|
||||
print_summary() {
|
||||
SERVER_IP=$(curl -s ifconfig.me 2>/dev/null || hostname -I | awk '{print $1}')
|
||||
echo ""
|
||||
echo -e "${GREEN}${BOLD}"
|
||||
echo " +======================================================+"
|
||||
echo " | CLOWNFISCHSERVER BEREIT! v0.4.13 |"
|
||||
echo " +======================================================+"
|
||||
echo -e "${NC}"
|
||||
echo -e " ${BOLD}Server:${NC} $SERVER_IP"
|
||||
echo -e " ${BOLD}SSH User:${NC} $MGMT_USER (Passwort aktiv!)"
|
||||
echo -e " ${BOLD}KI-Modell:${NC} $OLLAMA_MODEL"
|
||||
echo -e " ${BOLD}Snapshots:${NC} $SNAPSHOT_DIR ($SNAPSHOT_METHOD)"
|
||||
|
||||
if [[ -n "$CLOWNFISCHSERVER_HOME_IP_DOMAIN" ]]; then
|
||||
echo -e " ${BOLD}${GREEN}HomeIP Auto:${NC} $CLOWNFISCHSERVER_HOME_IP_DOMAIN (alle 5 Min aktualisiert)"
|
||||
else
|
||||
echo -e " ${BOLD}HomeIP Auto:${NC} nicht aktiviert"
|
||||
fi
|
||||
|
||||
echo -e " ${BOLD}Logs:${NC} journalctl -u clownfisch -f"
|
||||
echo -e " ${BOLD}Install-Log:${NC} $LOG_FILE"
|
||||
echo ""
|
||||
echo -e " ${CYAN}${BOLD}Erste Schritte:${NC}"
|
||||
echo -e " ${CYAN} 1. Schreibe deinem Bot: /start${NC}"
|
||||
echo -e " ${CYAN} 2. Teste mit: /status${NC}"
|
||||
echo -e " ${CYAN} 3. Wenn alles laeuft: /ssh close${NC}"
|
||||
echo ""
|
||||
echo -e " ${CYAN}${BOLD}Notfall-Befehle im Bot:${NC}"
|
||||
echo -e " ${CYAN} /run <befehl> -> direkte Shell ohne KI${NC}"
|
||||
echo -e " ${CYAN} /ssh open -> SSH oeffnen${NC}"
|
||||
echo -e " ${CYAN} /ssh open 1.2.3.4 -> SSH nur fuer deine IP${NC}"
|
||||
echo -e " ${CYAN} /ssh close -> SSH sperren${NC}"
|
||||
echo ""
|
||||
|
||||
if [[ -n "$CLOWNFISCHSERVER_HOME_IP_DOMAIN" ]]; then
|
||||
echo -e " ${GREEN}${BOLD}🐠 Clownfischserver-HomeIP Status:${NC}"
|
||||
echo -e " ${GREEN} Cron-Job aktiv: /var/log/clownfischserver-homeip.log${NC}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo -e " ${YELLOW}Install-Log (letzte 10 Zeilen):${NC}"
|
||||
tail -10 "$LOG_FILE"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# MAIN
|
||||
|
||||
update_bot() {
|
||||
echo -e "
|
||||
${BOLD}${CYAN}🐠 Clownfischserver Update-Modus${NC}
|
||||
"
|
||||
log "=== Update gestartet ==="
|
||||
|
||||
# Read MGMT_USER from .env if not set
|
||||
if [[ -z "$MGMT_USER" ]] && [[ -f "$INSTALL_DIR/config/.env" ]]; then
|
||||
MGMT_USER=$(grep "^MGMT_USER=" "$INSTALL_DIR/config/.env" | cut -d= -f2)
|
||||
fi
|
||||
[[ -z "$MGMT_USER" ]] && MGMT_USER="clownfish"
|
||||
|
||||
# Copy new bot files
|
||||
cp -r "$SCRIPT_DIR/bot/"* "$INSTALL_DIR/bot/"
|
||||
chown -R "$MGMT_USER:$MGMT_USER" "$INSTALL_DIR/bot"
|
||||
ok "Bot-Dateien aktualisiert"
|
||||
|
||||
# Update systemd services – patch hardcoded user with actual MGMT_USER
|
||||
for svc in clownfisch.service clownfisch-boot.service; do
|
||||
if [ -f "$SCRIPT_DIR/systemd/$svc" ]; then
|
||||
sed "s/^User=.*/User=$MGMT_USER/;s/^Group=.*/Group=$MGMT_USER/" \
|
||||
"$SCRIPT_DIR/systemd/$svc" > "/etc/systemd/system/$svc"
|
||||
[[ "$svc" == "clownfisch-boot.service" ]] && systemctl enable clownfisch-boot >> "$LOG_FILE" 2>&1
|
||||
fi
|
||||
done
|
||||
systemctl daemon-reload >> "$LOG_FILE" 2>&1
|
||||
ok "Systemd Services aktualisiert (User=$MGMT_USER)"
|
||||
|
||||
# Restart
|
||||
systemctl restart clownfisch >> "$LOG_FILE" 2>&1
|
||||
sleep 3
|
||||
if systemctl is-active --quiet clownfisch; then
|
||||
ok "Bot neu gestartet – Update abgeschlossen!"
|
||||
else
|
||||
warn "Bot-Status unklar: journalctl -u clownfisch -n 20 --no-pager"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e " ${CYAN}Logs: journalctl -u clownfisch -f${NC}"
|
||||
echo ""
|
||||
log "=== Update abgeschlossen ==="
|
||||
}
|
||||
|
||||
main() {
|
||||
print_banner
|
||||
touch "$LOG_FILE"
|
||||
|
||||
# Update mode: service already running
|
||||
if systemctl is-active --quiet clownfisch 2>/dev/null; then
|
||||
echo -e " ${YELLOW}⚠${NC} Clownfischserver läuft bereits!"
|
||||
echo ""
|
||||
echo -e " ${CYAN}[1]${NC} Update (Bot-Dateien + Restart)"
|
||||
echo -e " ${CYAN}[2]${NC} Neuinstallation (alles neu)"
|
||||
echo -e " ${CYAN}[3]${NC} Abbrechen"
|
||||
echo ""
|
||||
read -r -p " → " CHOICE
|
||||
case "$CHOICE" in
|
||||
1) update_bot; exit 0 ;;
|
||||
2) echo "" ;;
|
||||
*) echo "Abgebrochen."; exit 0 ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
log "=== Clownfischserver v0.4.13 Installation gestartet ==="
|
||||
step_checks
|
||||
step_packages
|
||||
step_user
|
||||
step_ssh
|
||||
step_ollama
|
||||
step_python
|
||||
step_telegram
|
||||
step_service
|
||||
print_summary
|
||||
log "=== Installation abgeschlossen ==="
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user