# Discord Bot Der GSM Discord Bot bietet Live-Status-Updates für alle Gameserver direkt in Discord. Der Bot kann auf mehreren Discord-Servern gleichzeitig laufen. ## Features - **Live-Status**: Automatisch aktualisiertes Embed mit Server-Status, Spielerzahlen und Metriken - **Alerts**: Benachrichtigungen wenn Server online/offline gehen oder Spieler joinen/leaven - **Multi-Guild**: Kann auf beliebig vielen Discord-Servern eingesetzt werden - **Auto-Setup**: Erstellt automatisch alle nötigen Channels beim Beitreten ## Bot einladen ### Invite-Link ``` https://discord.com/oauth2/authorize?client_id=1458251194806833306&permissions=34359831568&integration_type=0&scope=bot+applications.commands ``` Der Link ist auch im GSM Dashboard unter den Server-Cards verfügbar. ### Benötigte Permissions | Permission | Verwendung | |------------|------------| | View Channels | Channels sehen | | Manage Channels | Channels erstellen | | Send Messages | Nachrichten senden | | Manage Messages | Eigene Nachrichten bearbeiten | | Embed Links | Rich Embeds für Status | | Read Message History | Alte Nachrichten lesen | | Create Public Threads | Forum-Threads erstellen | ## Automatisch erstellte Channel-Struktur Wenn der Bot einem Server beitritt, erstellt er automatisch folgende Struktur: ``` 🎮 Gameserver (Kategorie) ├── ℹ️│info - Informationen zum GSM System ├── 📊│status - Live-Status aller Gameserver (Auto-Update) ├── 📢│alerts - Server-Events und Spieler-Benachrichtigungen ├── 📰│updates - Ankündigungen zu neuen Gameservern ├── 💬│diskussion - Diskussions-Channel (User können schreiben) └── 💡│requests - Forum für Gameserver-Vorschläge ``` ### Channel-Permissions | Channel | @everyone | Bot | |---------|-----------|-----| | Kategorie | Lesen | ViewChannel + Schreiben | | info | Lesen (kein Schreiben) | ViewChannel + Schreiben | | status | Lesen (kein Schreiben) | ViewChannel + Schreiben | | alerts | Lesen (kein Schreiben) | ViewChannel + Schreiben | | updates | Lesen (kein Schreiben) | ViewChannel + Schreiben | | diskussion | Lesen + Schreiben | ViewChannel + Schreiben | | requests | Lesen + Threads erstellen | ViewChannel + Schreiben | **Wichtig**: Der Bot braucht explizit `ViewChannel` Permission für jeden Channel, auch wenn @everyone den Channel sehen kann. Ohne `ViewChannel` kann der Bot nicht in den Channel schreiben ("Missing Access" Fehler). ## Datenbank ### guild_settings Tabelle Speichert die Channel-IDs für jeden Discord-Server: ```sql CREATE TABLE guild_settings ( guild_id TEXT PRIMARY KEY, category_id TEXT, info_channel_id TEXT, status_channel_id TEXT, status_message_id TEXT, alerts_channel_id TEXT, updates_channel_id TEXT, discussion_channel_id TEXT, requests_channel_id TEXT, requests_info_thread_id TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ); ``` Die Datenbank liegt in `backend/db/users.sqlite`. ### DB-Funktionen In `backend/db/init.js`: ```javascript initGuildSettings() // Tabelle erstellen getGuildSettings(guildId) // Settings für einen Server getAllGuildSettings() // Alle Server-Settings setGuildSettings(guildId, {}) // Settings speichern deleteGuildSettings(guildId) // Settings löschen (bei Bot-Kick) ``` ## Bot-Events ### guildCreate Wird ausgelöst wenn der Bot einem neuen Server beitritt: 1. Erstellt Kategorie und alle Channels 2. Postet Info-Nachricht im `ℹ️│info` Channel 3. Erstellt Info-Thread im `💡│requests` Forum 4. Speichert alle Channel-IDs in der Datenbank 5. Sendet erste Status-Nachricht ### guildDelete Wird ausgelöst wenn der Bot von einem Server entfernt wird: 1. Löscht alle Settings aus der Datenbank ## Status-Updates Der Bot aktualisiert die Status-Nachricht in allen registrierten Guilds alle 30 Sekunden: ```javascript async function updateAllGuildStatus() { const guilds = getAllGuildSettings(); for (const guild of guilds) { await updateGuildStatus(guild); } } ``` Jeder Server bekommt ein eigenes Embed mit: - Server-Name und Status (Online/Offline) - Aktuelle Spielerzahl - CPU und RAM Auslastung - Verbindungsadresse ## Konfiguration ### Umgebungsvariablen (.env) ```env DISCORD_CLIENT_ID=1458251194806833306 DISCORD_CLIENT_SECRET=xxx DISCORD_BOT_TOKEN=xxx # Multi-Server OAuth Login # User muss nur in EINEM der Server Mitglied sein # Rollen werden pro Server geprüft, höchste Berechtigung zählt # Server 1: Bacanaks DISCORD_GUILD_ID_1=729865854329815051 DISCORD_ADMIN_ROLE_ID_1=1024693717434650736 DISCORD_MOD_ROLE_ID_1=1024693170958766141 # Server 2: Piccadilly DISCORD_GUILD_ID_2=730907665802330224 DISCORD_ADMIN_ROLE_ID_2=1458595551514988584 DISCORD_MOD_ROLE_ID_2=1458591909210488914 ``` **Hinweis**: Die Guild-IDs werden nur für den Discord OAuth Login verwendet, nicht für den Bot selbst. ### OAuth Login-Logik ``` 1. User loggt sich via Discord OAuth ein 2. Für jeden konfigurierten Server: - Ist User Mitglied? → Rollen prüfen - Admin-Rolle → superadmin - Mod-Rolle → moderator - Nur Mitglied → user 3. Höchste Berechtigung aus allen Servern wird verwendet 4. Nicht in mindestens einem Server → Login verweigert ``` | User ist in... | Bacanaks Rolle | Piccadilly Rolle | GSM Rolle | |----------------|----------------|------------------|-----------| | Nur Bacanaks | Admin | - | superadmin | | Nur Piccadilly | - | Mod | moderator | | Beide | Mitglied | Admin | superadmin | | Keinem | - | - | ❌ Kein Login | ### Developer Portal Einstellungen 1. Gehe zu https://discord.com/developers/applications 2. Wähle die Bot-Application 3. **Bot** Tab: - "Public Bot" aktivieren (damit andere einladen können) - Privileged Gateway Intents: - Server Members Intent: Optional - Message Content Intent: Nicht benötigt ## Dateien | Datei | Beschreibung | |-------|--------------| | `backend/services/discord.js` | OAuth-Logik, Multi-Guild Membership-Prüfung | | `backend/services/discordBot.js` | Bot-Logik und Event-Handler | | `backend/routes/auth.js` | Auth-Endpoints (Login, Callback, Refresh) | | `backend/db/init.js` | Guild-Settings DB-Funktionen | | `frontend/src/pages/Dashboard.jsx` | Invite-Button im Dashboard | ## Implementierungsdetails ### Multi-Guild OAuth Die OAuth-Implementierung in `discord.js` verwendet **lazy initialization** für die Guild-Konfigurationen: ```javascript let _guildConfigs = null; function getGuildConfigs() { if (_guildConfigs === null) { _guildConfigs = [ { name: 'Bacanaks', guildId: process.env.DISCORD_GUILD_ID_1, ... }, { name: 'Piccadilly', guildId: process.env.DISCORD_GUILD_ID_2, ... } ].filter(config => config.guildId); } return _guildConfigs; } ``` **Wichtig**: Die Konfiguration darf NICHT beim Modul-Import initialisiert werden, da zu diesem Zeitpunkt dotenv die `.env` noch nicht geladen hat. Die lazy initialization stellt sicher, dass die Umgebungsvariablen verfügbar sind. ### Funktionen | Funktion | Beschreibung | |----------|--------------| | `getGuildMemberships(userId)` | Prüft alle konfigurierten Server, gibt Array von Memberships zurück | | `getUserRoleFromMemberships(memberships)` | Bestimmt höchste Rolle aus allen Memberships | | `getGuildMember(userId)` | Legacy-Funktion, gibt ersten Match zurück | | `getUserRole(memberRoles)` | Legacy-Funktion für einzelne Rollen-Liste | ### Rollen-Priorität ```javascript const ROLE_PRIORITY = { superadmin: 3, moderator: 2, user: 1 }; ``` Bei mehreren Memberships wird immer die höchste Rolle verwendet. ### Voraussetzungen - Der Bot muss auf **allen** konfigurierten Discord-Servern sein - Der Bot braucht Zugriff auf die Guild Members API ## Troubleshooting ### Login schlägt fehl mit "nicht Mitglied" 1. **Bot auf allen Servern?** Der Bot muss auf Bacanaks UND Piccadilly eingeladen sein 2. **Env-Variablen prüfen**: ```bash grep GUILD /opt/gameserver-monitor/backend/.env ``` 3. **Mit --update-env neustarten**: ```bash pm2 restart gameserver-backend --update-env ``` 4. **Logs prüfen**: ```bash pm2 logs gameserver-backend --lines 30 | grep -i discord ``` ### Rolle wird nicht erkannt 1. **Rollen-IDs prüfen** - Discord Developer Mode aktivieren, Rechtsklick auf Rolle → ID kopieren 2. **User-Rollen abfragen**: ```bash curl -s -H "Authorization: Bot BOT_TOKEN" \ "https://discord.com/api/v10/guilds/GUILD_ID/members/USER_ID" | jq '.roles' ``` 3. **Konfigurierte IDs vergleichen** mit den tatsächlichen Rollen des Users ### Bot erstellt keine Channels - Prüfen ob Bot "Manage Channels" Permission hat - Prüfen ob Bot-Rolle hoch genug in der Rollen-Hierarchie ist ### Status-Nachricht wird nicht aktualisiert ```bash pm2 logs gameserver-backend --lines 50 ``` Suche nach `[DiscordBot]` Log-Einträgen. ### Bot aus Datenbank entfernen ```bash cd /opt/gameserver-monitor/backend node -e " import Database from 'better-sqlite3'; const db = new Database('./db/users.sqlite'); db.prepare('DELETE FROM guild_settings WHERE guild_id = ?').run('GUILD_ID_HIER'); console.log('Deleted'); " ``` Alternativ mit sqlite3 (falls installiert): ```bash sqlite3 /opt/gameserver-monitor/backend/db/users.sqlite DELETE FROM guild_settings WHERE guild_id = 'xxx'; ``` ### Missing Access Fehler Wenn der Bot "Missing Access" meldet obwohl er eingeladen wurde: 1. **ViewChannel Permission prüfen**: Der Bot braucht explizit `ViewChannel` für jeden Channel 2. Im Discord: Rechtsklick auf Channel → Bearbeiten → Berechtigungen → Bot auswählen → "Kanal ansehen" aktivieren 3. Logs prüfen: `pm2 logs gameserver-backend --lines 50 | grep -i "missing\|access"` ### Status-Nachricht wurde gelöscht Wenn die Status-Nachricht manuell gelöscht wurde, erscheint "Unknown Message" in den Logs. Fix: ```bash cd /opt/gameserver-monitor/backend node -e " import Database from 'better-sqlite3'; const db = new Database('./db/users.sqlite'); // Status Message ID auf NULL setzen, Bot erstellt neue beim nächsten Update db.prepare('UPDATE guild_settings SET status_message_id = NULL WHERE guild_id = ?').run('GUILD_ID_HIER'); console.log('Reset status_message_id'); " ``` ## Login vs. Bot | Feature | Login (OAuth) | Bot | |---------|--------------|-----| | Erfordert Mitgliedschaft | Bacanaks oder Piccadilly | Nein | | Server-Steuerung | Ja (je nach Rolle) | Nein | | Status sehen | Ja | Ja | | Alerts erhalten | Nein | Ja | | Verfügbar für | Mitglieder beider Discord-Server | Alle mit Bot | Der Login zur Webapp erfordert Mitgliedschaft in mindestens einem der konfigurierten Discord-Server (Bacanaks oder Piccadilly). Die höchste Rolle aus beiden Servern bestimmt die GSM-Berechtigung. Der Bot ist davon unabhängig und zeigt nur passive Status-Updates.