jbergner 4864574c56
All checks were successful
release-tag / release-image (push) Successful in 2m9s
Charts-Update
2026-05-31 16:04:59 +02:00
2026-05-30 15:33:35 +02:00
2026-05-30 12:05:35 +02:00
2026-05-30 15:33:35 +02:00
2026-05-31 08:34:07 +02:00
2026-05-30 15:33:35 +02:00
2026-05-29 09:16:05 +02:00
fix
2026-05-29 09:22:10 +02:00
2026-05-31 16:04:59 +02:00
2026-05-31 16:04:59 +02:00
2026-05-31 08:34:07 +02:00
2026-05-31 08:34:07 +02:00
2026-05-31 08:34:07 +02:00
2026-05-31 16:04:59 +02:00
2026-05-31 16:04:59 +02:00

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

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

Zeigt den Auftrag. Team/Admin sieht die interne Ansicht inklusive Lagerprüfung, normale Nutzer nur eigene Aufträge ohne Lagerdaten.

/auftrag_liste status:<optional> limit:<optional>

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:<optional> limit:<optional>

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

Einreicher, Annehmer oder Team/Admin können abschließen.

/auftrag_abbrechen id:<id> grund:<optional>

Einreicher oder Team/Admin können abbrechen.

/auftrag_nachricht id:<id> text:<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. Lagerorte werden separat verwaltet und bei den ort-Feldern per Autocomplete vorgeschlagen. Dadurch entstehen keine Tippfehler wie Orison, orison und Orison als getrennte Lagerorte.

/lagerort_add

Team/Admin kann interne Lagerorte anlegen. Diese Orte sind danach bei /lager_add, /lager_remove und /lagerort_remove als Auswahl verfügbar.

/lagerort_add name:Baijini Point
/lagerort_add name:Orison

/lagerort_remove

Team/Admin kann Lagerorte löschen, aber nur wenn dort kein aktiver Bestand mehr liegt.

/lagerort_remove ort:Baijini Point

Wenn noch Bestand vorhanden ist, lehnt der Bot das Löschen ab.

/lager_add

Fügt Bestand hinzu oder setzt ihn absolut.

/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 ein Pflichtfeld und muss vorher mit /lagerort_add angelegt worden sein. Discord schlägt vorhandene Lagerorte beim Tippen automatisch vor. Der Ort 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.:

Lager geändert durch b1tk1ll3r: P4-AR Q725 Stück @ Baijini Point: 0 → 2

/lager_remove

Reduziert Bestand. Der Bot verhindert negative Bestände.

/lager_remove ware:Gold qualitaet:900 menge:10 einheit:SCU ort:Orison

Auch hier ist ort ein Pflichtfeld und wird aus den angelegten Lagerorten vorgeschlagen. Dadurch werden Bestände an verschiedenen Orten sauber getrennt geführt.

/lager_liste

Zeigt interne Lagerbestände ephemeral an.

/lager_liste suche:Gold limit:20

/lager_check

Prüft intern, ob eine Menge verfügbar ist.

/lager_check ware:Gold qualitaet:900 menge:64 einheit:SCU
/lager_check ware:P4-AR qualitaet:725 menge:2 einheit:cSCU

Statusmodell

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
  • storage_locations: verwaltete Lagerorte für Autocomplete und Validierung
  • 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:

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

Trading-Modul

Das Trading-Modul ersetzt eine Excel-Erfassung für monatliche Trading-/Hauling-Runs.

Neue Konfiguration

DISCORD_TRADING_ROLE_IDS=456789012345678901
DISCORD_TRADING_REPORT_CHANNEL_ID=TradingReportChannelIDOptional
TRADING_SHIPS=Caterpillar,C2 Hercules,Freelancer MAX,Hull A,Hull C,Taurus
TRADING_COMMODITIES=Gold,Beryl,Diamond,Laranite,Agricium,Quantanium,Processed Food,Medical Supplies
TRADING_SHARE_TRADE=10
TRADING_SHARE_HAULING=5
TRADING_SHARE_EVENT=0
TRADING_SHARE_INTERN=0

Die Prozentwerte können als 10 oder 0.10 angegeben werden. Beide Varianten bedeuten 10%.

Neue Commands

/trading_add
/trading_liste
/trading_delete
/trading_report
/trading_abrechnung

/trading_add darf von Mitgliedern mit DISCORD_TRADING_ROLE_IDS sowie Team/Admin genutzt werden. Schiff und Ware werden aus den Umgebungsvariablen per Autocomplete vorgeschlagen.

Gewinnberechnung:

Gewinn = (Verkaufswert - Einkaufswert - Kosten) × Wiederholungen
Orga-Anteil = max(0, Gewinn × Prozentwert des Zwecks)
Transportierte SCU = SCU × Wiederholungen
Kosten gesamt = Kosten × Wiederholungen

/trading_report monat:<1-12> jahr:<YYYY> darf nur von Admins genutzt werden und postet eine hübsche Embed-Auswertung in DISCORD_TRADING_REPORT_CHANNEL_ID.

/trading_delete id:<id> ist ein Admin-Kommando. Einträge werden soft-deleted, damit sie nicht mehr in Listen/Reports auftauchen, aber nachvollziehbar bleiben.

Trading-Abrechnung per DM

Admins können am Monatsende jedem Mitglied mit positivem Orga-Anteil automatisch eine private Abrechnung senden:

/trading_abrechnung monat:<1-12> jahr:<YYYY>

Der Bot fasst pro Nutzer zusammen:

  • Anzahl Runs
  • Gewinn
  • transportierte SCU
  • zu überweisender Orga-Anteil
  • Empfänger aus TRADING_PAYOUT_RECIPIENT

Neue Konfiguration:

TRADING_PAYOUT_RECIPIENT=Jan / Orga-Kasse

Mitglieder ohne Orga-Anteil werden übersprungen. Falls ein Mitglied keine DMs zulässt, meldet der Bot diese fehlgeschlagene Zustellung in der Command-Antwort und schreibt Details ins Log.

Internes Web-Dashboard

Optional kann ein kleines Web-Dashboard gestartet werden. Es zeigt:

  • Trading-Monatswerte
  • Abrechnung pro Nutzer
  • aktive Aufträge
  • archivierte Aufträge
  • Lagerbestände

Aktivierung in .env:

DASHBOARD_ADDR=127.0.0.1:8080
DASHBOARD_TOKEN=ChangeMePlease

Aufruf:

http://127.0.0.1:8080/?token=ChangeMePlease

Für Docker sollte DASHBOARD_ADDR=0.0.0.0:8080 gesetzt werden. Das Dashboard ist als internes Admin-/Team-Werkzeug gedacht. Stelle es nicht ungeschützt öffentlich ins Internet. Nutze mindestens DASHBOARD_TOKEN; für produktiven Betrieb idealerweise zusätzlich Reverse Proxy mit HTTPS und Zugriffsbeschränkung.

Webinterface: Rich Presence & Session-Einladung

Im Dashboard gibt es jetzt den Bereich Rich Presence & Session-Einladung. Dort können Admins über das Webinterface konfigurieren:

  • Bot-Status: online, idle, dnd, invisible
  • Activity-Typ: spielt, schaut, hört, tritt an, custom
  • Name, State, Details
  • Start- und Endzeit
  • Large-/Small-Image-Asset-Keys und Texte
  • Discord-Zielchannel-ID
  • Post-Channel-ID für einen Session-Invite
  • Button-Label, Post-Titel und Post-Text

Wichtig: Discord erlaubt Bot-Activities offiziell nur eingeschränkt. Die Discord-Dokumentation sagt, dass Bot-User nur name, state, type und url setzen können. Deshalb speichert das Dashboard zwar Details, Assets und Timestamps und sendet sie mit, aber Discord-Clients können diese Felder ignorieren. Für den eigentlichen Einladungszweck erzeugt der Button Session-Invite posten zuverlässig einen Discord-Post mit Link-Button zum konfigurierten Channel.

Der Channel-Link wird aus DISCORD_GUILD_ID und der im Webinterface eingetragenen Zielchannel-ID erzeugt:

https://discord.com/channels/<GuildID>/<ChannelID>

Der Bot benötigt im Invite-Post-Channel:

View Channel
Send Messages
Embed Links
Description
No description provided
Readme 233 KiB
Languages
Go 99.7%
Dockerfile 0.3%