Files
GSM/docs/discord-bot.md
Alexander Zielonka 20ba93b26f Add multi-guild Discord OAuth support
- Users can now login via Bacanaks OR Piccadilly Discord server
- Highest role from all servers is used (superadmin > moderator > user)
- Lazy initialization fixes env loading timing issue
- Updated documentation with implementation details and troubleshooting

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 00:30:15 +01:00

11 KiB
Raw Permalink Blame History

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

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:

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:

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:

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)

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:

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

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:
    grep GUILD /opt/gameserver-monitor/backend/.env
    
  3. Mit --update-env neustarten:
    pm2 restart gameserver-backend --update-env
    
  4. Logs prüfen:
    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:
    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

pm2 logs gameserver-backend --lines 50

Suche nach [DiscordBot] Log-Einträgen.

Bot aus Datenbank entfernen

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):

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:

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.