# Starauftrag Discord Bot (Go + SQLite) Produktionsnaher Discord-Bot für Lieferaufträge mit SQLite-Persistenz, Slash Commands, internen Team-Buttons, Audit-Log und privater Lagerverwaltung. ## Kernidee - Endnutzer erstellen mit `/auftrag` einen Lieferauftrag. - Der öffentliche Auftragspost enthält nur auftragsbezogene Basisdaten. - Das Team erhält in einem privaten internen Channel denselben Auftrag plus Lagerprüfung. - Annahme/Ablehnung erfolgt ausschließlich im internen Channel per Button. - Lagerdaten werden nie an den Einreicher oder in öffentliche Posts ausgegeben. - Alle relevanten Änderungen werden in `order_events` auditiert. Discord Buttons sind Message Components mit `custom_id`; beim Klick erhält der Bot eine Component Interaction und ordnet sie damit wieder dem Auftrag zu. ## Setup 1. Discord Developer Portal: Bot erstellen, Token kopieren, Bot in deinen Server einladen. 2. Bot-Berechtigungen: - `Send Messages` - `Use Slash Commands` - `Read Message History` - `Embed Links` - `Create Public Threads` oder `Create Private Threads` je nach Channel-Einstellung - `Send Messages in Threads` 3. `.env.example` nach `.env` kopieren oder Umgebungsvariablen setzen. 4. Starten: ```bash go mod tidy go run . ``` Für schnelle Command-Updates in Entwicklung `DISCORD_GUILD_ID` setzen. Ohne Guild-ID werden Slash Commands global registriert, was länger dauern kann. ## Wichtige Umgebungsvariablen ```env DISCORD_TOKEN=... DISCORD_APP_ID=... DISCORD_GUILD_ID=... DISCORD_PUBLIC_ORDER_CHANNEL_ID=... DISCORD_INTERNAL_ORDER_CHANNEL_ID=... DISCORD_AUDIT_CHANNEL_ID=... DISCORD_ADMIN_ROLE_IDS=roleId1,roleId2 DISCORD_TEAM_ROLE_IDS=roleId3,roleId4 DATABASE_PATH=orders.db THREAD_DELETE_AFTER_HOURS=72 ``` `DISCORD_INTERNAL_ORDER_CHANNEL_ID` sollte ein privater Team-Channel sein. Nur dort erscheinen Lagerprüfung und Annahme-/Ablehnen-Buttons. ## User Commands ### `/auftrag` Erstellt einen Auftrag mit: - `ware` - `qualitaet` 0-1000 - `menge` als Zahl - `einheit` `SCU`, `cSCU` oder `Stück` - `frist` im Format `YYYY-MM-DD` - `lieferort` - `budget` in aUEC ### `/auftrag_status id:` Zeigt den Auftrag. Team/Admin sieht die interne Ansicht inklusive Lagerprüfung, normale Nutzer nur eigene Aufträge ohne Lagerdaten. ### `/auftrag_liste status: limit:` Listet nur aktive Aufträge. Standardmäßig werden `offen`, `angenommen`, `in Lieferung` und `geliefert` angezeigt. Abgeschlossene, abgelehnte und abgebrochene Aufträge erscheinen hier nicht mehr. ### `/auftrag_archiv status: limit:` Listet archivierte Aufträge mit den deutschen Statuswerten `abgeschlossen`, `abgelehnt` und `abgebrochen`. Intern werden die stabilen Werte `completed`, `declined` und `cancelled` gespeichert. ### `/auftrag_abschliessen id:` Einreicher, Annehmer oder Team/Admin können abschließen. ### `/auftrag_abbrechen id: grund:` Einreicher oder Team/Admin können abbrechen. ### `/auftrag_nachricht id: text:` Leitet eine Nachricht per DM an Einreicher und Annehmer weiter und schreibt sie zusätzlich in den internen Auftragsthread. ## Team/Admin Commands ### `/auftrag_status_setzen` Team/Admin kann den Status manuell setzen: - `offen` - `angenommen` - `in Lieferung` - `geliefert` - `abgeschlossen` - `abgelehnt` - `abgebrochen` ## Lager Commands Nur Admins können Lagerbestände verändern. Team/Admin kann Lager prüfen und anzeigen. ### `/lager_add` Fügt Bestand hinzu oder setzt ihn absolut. ```text /lager_add ware:Gold qualitaet:900 menge:64 einheit:SCU ort:Orison modus:addieren /lager_add ware:Gold qualitaet:900 menge:100 einheit:SCU ort:Orison modus:setzen ``` `ort` ist der interne Lagerort. Er wird nur in Team-/Admin-Ausgaben, der internen Lagerprüfung und im Auditlog angezeigt. Öffentliche Auftragsposts und Endnutzer-DMs enthalten keine Lagerorte. Das Auditlog zeigt Bestandsänderungen mit altem und neuem Wert, z. B.: ```text Lager geändert durch b1tk1ll3r: P4-AR Q725 Stück @ Baijini Point: 0 → 2 ``` ### `/lager_remove` Reduziert Bestand. Der Bot verhindert negative Bestände. ```text /lager_remove ware:Gold qualitaet:900 menge:10 einheit:SCU ort:Orison ``` Auch hier bezieht sich `ort` auf genau diese Lagerposition. Wird kein Ort angegeben, wird der Bestand ohne Lagerort geführt. ### `/lager_liste` Zeigt interne Lagerbestände ephemeral an. ```text /lager_liste suche:Gold limit:20 ``` ### `/lager_check` Prüft intern, ob eine Menge verfügbar ist. ```text /lager_check ware:Gold qualitaet:900 menge:64 einheit:SCU /lager_check ware:P4-AR qualitaet:725 menge:2 einheit:cSCU ``` ## Statusmodell ```text offen -> angenommen -> in Lieferung -> geliefert -> abgeschlossen \-> abgebrochen offen -> abgelehnt Intern speichert der Bot weiterhin stabile technische Statuswerte wie `open`, `accepted`, `completed` usw. In Discord werden sie deutsch angezeigt. ``` ## Datenbanktabellen - `orders`: Aufträge und Discord-Message-Referenzen - `inventory`: interne Lagerpositionen - `order_events`: Audit-Log für Aufträge und Lageränderungen SQLite wird mit WAL-Modus und `busy_timeout` betrieben, damit kleine produktive Deployments robuster laufen. ## Produktionshinweise - Setze `DISCORD_INTERNAL_ORDER_CHANNEL_ID` auf einen strikt privaten Channel. - Gib Lager-Commands nur einer kleinen Admin-Rolle. - Sichere `orders.db` regelmäßig. - Führe den Bot als systemd-Service oder Container aus. - Aktiviere Monitoring für Logs und Neustarts. - Teste DMs: Manche Nutzer blockieren DMs von Servermitgliedern; der Bot loggt solche Fehler, kann sie aber nicht erzwingen. ## Lagerbestand bei 0 Wenn ein Lagerbestand durch `/lager_remove` oder `/lager_add` mit `modus:setzen` auf `0` fällt, wird der Lagerdatensatz automatisch gelöscht. Das Auditlog zeigt weiterhin die Änderung als `alter Wert → neuer Wert`, z. B. `2 → 0`. Zusätzlich verhindert die Lagerlogik negative Bestände. Wenn mehr entfernt wird als vorhanden ist, antwortet der Bot mit einem Fehler und ändert den Bestand nicht. `/lager_liste` und die interne Lagerprüfung berücksichtigen nur Bestände größer als `0`, sodass alte Nullbestände nicht mehr angezeigt werden. ## Automatische Thread-Bereinigung Interne Auftragsthreads werden nach einer konfigurierbaren Zeit automatisch gelöscht, sobald der Auftrag archiviert ist. Archiviert bedeutet: - `abgeschlossen` - `abgelehnt` - `abgebrochen` Die Zeit steuerst du über: ```env THREAD_DELETE_AFTER_HOURS=72 ``` `72` bedeutet: Der Thread wird frühestens 72 Stunden nach der letzten Statusänderung gelöscht. `0` deaktiviert die automatische Thread-Bereinigung. Für das Löschen benötigt der Bot im internen Channel zusätzlich `Manage Threads`. Wenn diese Berechtigung fehlt, bleibt der Thread bestehen und der Fehler erscheint im Bot-Log. ## Deutsche Statusbegriffe Discord zeigt Statuswerte jetzt deutsch an: | Anzeige | interner Wert | | --- | --- | | offen | `open` | | angenommen | `accepted` | | in Lieferung | `in_delivery` | | geliefert | `delivered` | | abgeschlossen | `completed` | | abgelehnt | `declined` | | abgebrochen | `cancelled` | Die internen Werte bleiben absichtlich englisch, damit bestehende Datenbanken und Integrationen stabil bleiben. ## Einheiten Bei Aufträgen und Lagerbeständen sind jetzt diese Einheiten auswählbar: - `SCU` - `cSCU` - `Stück`