Neue Seite /status auf dem Hub: Gesamtlage als Banner, darunter die
freigegebenen Dienste mit 90-Tage-Balken, die Game-Server, und die
Stoerungs-Historie der letzten 90 Tage.
Die Adressliste ist von einer kommagetrennten Einstellung in eine Tabelle
gewandert. Jeder Dienst hat jetzt Namen, Gruppe und einen Schalter
"oeffentlich" — sonst stuende auf einer oeffentlichen Seite die nackte URL,
auch von internen Diensten. Die alte Liste wird beim ersten Start uebernommen,
bewusst als nicht oeffentlich: was dort auftaucht, soll eine Entscheidung sein
und nicht durch eine Migration passieren.
/api/status liefert nur freigegebene Dienste und dabei keine Adressen, nur
Namen. Auch die Stoerungs-Historie ist gefiltert — sonst verriete sie, was es
sonst noch gibt. Im Browser gegengeprueft: auf der ganzen Seite steht kein
einziges https://.
Stoerungen werden jetzt aufgezeichnet, nicht nur gemeldet: pro Adresse ein
offener Eintrag, bis es wieder laeuft. Beim Wiederkommen wird er geschlossen,
bevor die DM rausgeht — sonst bliebe er bei einem Sendefehler ewig offen.
Aufbewahrung von 7 auf 90 Tage. Der Balken kann jetzt zwei Aufloesungen:
168 Stunden fuers Panel ("was war letzte Nacht?") und 90 Tage fuer die
Statusseite ("wie zuverlaessig?"). Stuendlich ueber 90 Tage waeren ueber 2000
Striche und unlesbar.
Im Panel sind aus der reinen Anzeige Verwaltung geworden: anlegen, bearbeiten,
entfernen, freigeben — dazu die Stoerungsliste.
Beim Bauen gefunden: die Uebernahme der alten Liste stand mitten in db.js und
rief getSetting(), dessen prepared statement erst 300 Zeilen weiter unten
entsteht. Der Bot starb beim Start mit "Cannot access getSettingStmt before
initialization" — nachgewiesen, dann ans Dateiende verschoben.
Geprueft: 182 SQL-Abfragen gegen das Schema, alle schreibenden Routen bewacht,
Stoerungs-Logik durchgespielt (doppeltes Eroeffnen wird abgelehnt, Schliessen
ohne offene gibt null), und beide Seiten im Browser mit drei Diensten in allen
drei Zustaenden.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Die DM-Alarme waren als einzige Meldung noch reiner Text — direkt neben dem
Raid-Alarm, der ein Embed ist. Jetzt beide im selben Look, mit Ausfalldauer
bzw. Fehlergrund und Antwortzeit als Felder.
Uebersicht im Panel (System, unter den Watchdog-URLs): je Adresse ein
Erreichbarkeits-Balken ueber 7 Tage, gruen/rot/grau wie bei den Game-Servern,
dazu Status, Antwortzeit und seit wann etwas weg ist.
Dafuer schreibt der Waechter jetzt mit — vorher lebte sein Zustand nur im
Speicher und war nach jedem Neustart weg. Neue Tabelle watchdog_history,
gleiche Bauart und gleiche 7-Tage-Aufbewahrung wie player_history, stundenweise
zusammengefasst statt roh ausgeliefert.
Wichtig dabei: aufgezeichnet wird jeder Check, nicht nur die Wechsel. Sonst
saehen ruhige Stunden im Balken aus wie Messluecken.
Der Balken ist aus Server.jsx in components/UptimeTrack.jsx gewandert, damit
oeffentliche Seite und Panel denselben benutzen. Er zeigt die Antwortzeit im
Tooltip nur, wo sie erhoben wird — bei Game-Servern gibt es keine.
Der Endpunkt /api/watchdog liegt hinter der Rechtepruefung, nicht oeffentlich:
in der Adressliste koennen interne Dienste stehen.
Zwei fehlende Importe in api.js gefunden und ergaenzt (moduleEnabled, tuning) —
der erste Aufruf der Route waere sonst mit ReferenceError gestorben.
Geprueft: alle 171 SQL-Abfragen gegen das Schema, die drei neuen inklusive.
Im Browser mit drei Faellen — laeuft, ist weg, noch nie geprueft: Punktfarbe,
Quote, Ausfallstunden und Messluecken stimmen, die ungepruefte Adresse zeigt
korrekt gar keinen Balken. Oeffentliche Server-Seite nach dem Umbau
gegengeprueft.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Server-Monitor: Der Footer behauptete fest "alle 2 min", obwohl das Intervall
seit den einstellbaren Werten aus monitor_interval kommt. Wer im Panel etwas
anderes setzt, bekam eine falsche Angabe. Liest jetzt den echten Wert.
Level-Aufstieg: War als einziges im Community-Bereich reiner Text, waehrend
Geburtstage, Verlosungen und Bewerbungen laengst Embeds sind. Jetzt ueber
brandEmbed mit Avatar, Gesamt-XP und Rest bis zum naechsten Level. Der Text
bleibt die Vorlage aus dem Panel — nur der Rahmen ist neu.
Dazu zeigt das Embed die Belohnungs-Rolle: entweder die gerade vergebene oder
die naechste, auf die man hinspielt. grantRewards() gibt dafuer zurueck, was
wirklich dazukam.
Ist die Vorlage im Panel leer, wirft setDescription('') — der Aufstieg waere
still ausgefallen. Faellt jetzt auf eine schlichte Zeile zurueck.
/rank: Avatar als Thumbnail, Podest-Zeichen fuer die ersten drei, Rest-XP und
naechste Belohnung als eigene Felder. Das Feld erscheint nur, wenn es
Belohnungs-Rollen gibt — sonst stuende dort ein leeres Feld.
Nebenbei zwei veraltete ephemeral: true auf flags: MessageFlags.Ephemeral
umgestellt, wie es der Rest der Befehle schon macht.
Geprueft: alle vier Embeds wirklich gebaut und ausgegeben, discord.js
validiert dabei. Das Intervall im Footer zog den auf 7 gestellten Wert.
Die negative XP-Differenz im ersten Testlauf war ein inkonsistenter
Testdatensatz — mit 4200 XP ist man Level 9, nicht 7; ueber 0..60000 XP
durchgespielt wird die Differenz nie negativ.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Session-Cookie ohne secure-Flag (src/web/auth.js)
Das Anmelde-Cookie war httpOnly, sameSite und signiert — aber ohne secure.
Beim ersten Aufruf ueber http, bevor der Proxy auf https umleitet, geht es
damit im Klartext mit. Auf einem fremden WLAN ist das die Sitzung.
Das Flag laesst sich nicht aus request.protocol ableiten: hinter dem
Reverse-Proxy kommt jede Anfrage als http an, trustProxy ist nicht gesetzt.
Stattdessen aus der konfigurierten Adresse des Hosts, mit derselben
Zuordnung, die der OAuth-Redirect schon benutzt. Lokal ueber
http://localhost bleibt es aus, sonst kaeme man beim Entwickeln nicht rein.
Geprueft gegen sechs Hosts: hub und bot ergeben secure, localhost nicht.
Gilt auch fuer das OAuth-State- und das SSO-Ruecksprung-Cookie.
Unbehandelte Rejection beim Start (src/bot/client.js)
registerCommands() faengt nichts ab, der Aufrufer im ClientReady-Handler
auch nicht. Seit Node 15 beendet eine unbehandelte Rejection den Prozess —
ein Rate-Limit von Discord beim Start haette den Bot also abgeschossen und
mit restart: unless-stopped in eine Neustart-Schleife geschickt. Dabei
stehen die zuvor registrierten Befehle bei Discord weiter, und alles
andere koennte laufen. Jetzt wird der Fehler geloggt und der Bot bleibt an.
Number('') ist 0 (src/bot/anti-raid.js)
Beim Aufheben der Raid-Sperre wird die gemerkte Verifizierungsstufe
zurueckgesetzt. Fehlt die Notiz, ergab Number('') die 0 — und 0 ist eine
gueltige Stufe, naemlich "keine". Der Server waere also auf offen gestellt
worden statt so gelassen, wie er war. Jetzt wird nur zurueckgesetzt, was
auch wirklich als Zahl notiert ist.
Ohne Befund geprueft: alle 168 SQL-Abfragen gegen das echte Schema inklusive
Migrationen, alle 109 Routen auf Rechtepruefung (53 von 54 schreibenden haben
eine, die 54. haengt am timing-safe verglichenen Secret), Farbwerte vor dem
Einbetten in ausgeliefertes CSS, das Escaping der OG-Meta-Tags, Timer-Cleanup
im Frontend und die uebrigen async-Handler.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Der fehlende Befehl zum Raid-Schutz. Drei Unterbefehle:
status ist gerade gesperrt, und wie lange noch
an jetzt sperren — fuer die Welle, die man kommen sieht
aus sofort aufheben — fuer den Fehlalarm um drei Uhr nachts
Rechte: ManageGuild, weil die Sperre genau das anfasst — Verifizierung und
Invites. Antworten sind ephemeral.
Die Sperr-Massnahmen sind in dichtmachen() herausgezogen, damit automatisch
und von Hand denselben Weg nehmen und sich nicht auseinanderentwickeln. Die
Meldung sagt jeweils dazu, wodurch sie ausgeloest wurde und wer es war.
Ist der Raid-Schutz ausgeschaltet, verweist der Befehl aufs Panel statt
stillschweigend nichts zu tun.
Dazu: der Befehl steht auf der oeffentlichen Befehls-Seite, in beiden
Sprachen, und die Zahl auf der Produktseite geht von 16 auf 17.
Geprueft: 17 Befehle laden, keine doppelten Namen, Rechte-Bitmaske 32
(ManageGuild), alle neuen Exporte vorhanden, kein Sprachschluessel fehlt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Neues Modul anti_raid, standardmaessig AUS. Zaehlt Beitritte in einem
Zeitfenster; reisst es, geht der Server dicht: Verifizierung auf Hoch,
Einladungen gesperrt, DM an den Owner und Eintrag ins Protokoll.
Bewusst zurueckhaltend — es wird niemand gekickt oder gebannt. Bei einem
Fehlalarm, etwa nach einem Stream-Shoutout, waere ein Massenkick schlimmer
als der Raid. Beide Massnahmen sind reversibel und treffen niemanden, der
schon drin ist.
Der Sperrzustand liegt in den Einstellungen, nicht nur im Speicher: startet
der Bot mitten in einer Sperre neu, waere der Timer sonst weg und der Server
bliebe fuer immer dicht. Beim Start wird die Restzeit abgesessen oder, wenn
laengst abgelaufen, sofort aufgehoben.
Drei Werte im Panel: Schwelle (8), Fenster (20 s), Sperrdauer (15 min).
disableInvites() gibt es erst ab discord.js 14.4 und nur mit ManageGuild —
beides wird geprueft, statt im Ernstfall einen Fehler zu werfen.
Die Fenster-Logik ist als reine Funktion herausgezogen und mit neun Faellen
geprueft: langsame Beitritte loesen nicht aus, die Welle schon, der Rand des
Zeitfensters zaehlt nicht mehr mit, und nach Ruhe faengt das Fenster von vorn.
Offen: ein Befehl zum sofortigen Aufheben. Bis dahin laeuft die Sperre aus
oder man setzt die Verifizierung in Discord selbst zurueck.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Zwei Stellen, an denen die Marke bisher fehlte.
Willkommens-Karte: Das Wasserzeichen war der Markenname als Text mit 5 %
Deckkraft — jetzt der Katzenkopf. Die Datei wird einmal beim Start gelesen
statt bei jedem Beitritt, und fehlt sie, rendert die Karte wie bisher ohne
Wasserzeichen. Der Schalter heisst entsprechend "Logo im Hintergrund".
Link-Vorschau: og:image war nur gesetzt, wenn ein Devlog einen Screenshot
hatte. Startseite, Roadmap, Funktionen und jedes Devlog ohne Bild kamen in
Discord als graue Textzeile. Jetzt faellt alles ohne eigenes Bild auf
og-default.png zurueck (1200x630, Lockup auf Schwarz mit Akzentstreifen),
und twitter:card ist durchgehend summary_large_image.
Die Laufzeit-Datei liegt unter src/bot/assets/, nicht in docs/ — das
Dockerfile kopiert nur src/ und frontend/dist/.
Geprueft: librsvg zeichnet das eingebettete <image> wirklich (42726 Pixel
unterscheiden sich gegen eine Fassung ohne), das OG-Bild landet im dist.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Der Einzelknopf war der Anfang; wer frisch startet, klickt sich damit
sechzehnmal durch. Der Assistent macht das in einem Durchlauf.
Erst eine Vorschau, bevor irgendetwas passiert: was neu entsteht, was schon
existiert und nur verknüpft wird, welche Kanäle privat werden und in welche
Kategorie alles kommt. Die Kategorie heißt wie die Marke und wird
wiederverwendet, wenn es sie schon gibt.
Der Plan ist nach Einstellung entdoppelt — Devlogs und Wochen-Rückblick
brauchen denselben Kanal, der soll nicht zweimal auftauchen. Rollen kommen
ohne Kategorie, die gibt es dort nicht.
Zwischen zwei Anlagen liegen 350 ms. Discord verträgt ein Dutzend
Kanal-Erstellungen am Stück schlecht, und ein Rate-Limit mitten im Lauf
wäre die unangenehmste Art zu scheitern.
Scheitert ein Schritt, laufen die übrigen weiter und das Ergebnis zeigt pro
Zeile, was passiert ist. Vorher hätte ein einzelner Fehler den Rest
verschluckt.
Im Test: 19 offene Punkte, Vorschau zeigt 16, danach bleiben 2 — die beiden
ohne Vorlage (Gitea-Token und Watchdog-Adressen), die sich nicht anlegen
lassen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Die Status-Seite listet Module, denen ein Kanal oder eine Rolle fehlt.
Bisher hieß das: rüber zu Discord, Kanal anlegen, Rechte setzen, zurück ins
Panel, auswählen, speichern. Jetzt steht daneben ein Knopf, der genau das
in einem Schritt macht — inklusive Eintragen der Einstellung.
Die Vorlagen hängen bei den Modulen (modules.js): Name, Thema und ob der
Kanal privat sein soll. Private Kanäle bekommen @everyone entzogen und
Bot plus alle Rollen mit Moderationsrecht eingetragen — sonst legt man
einen Modmail-Kanal an, den das Team nicht sieht.
Gibt es Kanal oder Rolle schon (bei Rollen unabhängig von Groß- und
Kleinschreibung), werden sie verknüpft statt doppelt angelegt. Beim
Temp-Voice-Hub entsteht ein Sprachkanal statt eines Textkanals.
Fehlen dem Bot die Rechte, steht das im Panel im Klartext („Dem Bot fehlt
das Recht Kanäle verwalten") statt einer Discord-Fehlernummer.
Bewusst nur Anlegen: kein Löschen, kein Umbenennen. Ein Fehlklick beim
Anlegen kostet einen überflüssigen Kanal, einer beim Löschen dessen
Verlauf. Umbenennen wäre zusätzlich eine Falle, weil Discord es auf zwei
Änderungen pro zehn Minuten und Kanal begrenzt.
moduleMissing liefert dafür Objekte statt Beschriftungen, damit das Panel
weiß, was sich anlegen lässt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Beide Befehle nahmen ihre Eingaben als Slash-Optionen entgegen. Das heißt:
eine Fehlerbeschreibung einzeilig in die Befehlszeile tippen, ohne
Umbrüche, ohne Absätze. Entsprechend dürftig fielen die Meldungen aus.
/bug öffnet jetzt ein Fenster mit vier Feldern: Titel, was ist passiert,
was war erwartet, wie kann man es nachstellen. Die letzten beiden sind
freiwillig und erzeugen im Issue nur dann eine Überschrift, wenn sie
ausgefüllt sind. /wunsch fragt die Idee und ein optionales „warum wäre das
gut?" ab — die Rückfrage macht aus einem Einzeiler einen Vorschlag, über
den man abstimmen kann.
Der Screenshot bleibt eine Option am Befehl, weil Discord in Fenstern
keine Dateien erlaubt. Er hängt damit an der Befehls-Interaktion, das
Formular kommt aber als eigene zurück — der Anhang wird deshalb kurz
zwischengeparkt und nach dem Absenden verwendet. Abgelaufene Einträge
räumt der nächste Aufruf mit weg, damit da nichts liegen bleibt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
XP-Beträge, Wartezeiten, Prüf-Intervalle, Obergrenzen und Postzeiten
standen als Konstanten im Code. Wer den Server-Monitor seltener prüfen
lassen wollte oder das Level-System anders austarieren, musste die
Quelldateien anfassen.
src/tuning.js hält sie jetzt an einer Stelle, mit Standard, erlaubtem
Bereich und Erklärung. Der neue Config-Bereich „Werte" zeigt sie nach
Themen gruppiert; neben jedem Feld steht der erlaubte Bereich und der
Standard, angepasste Werte lassen sich einzeln zurücksetzen.
Die Grenzen sind nicht Kosmetik: ein Vertipper beim Prüf-Intervall würde
sonst den Bot in eine Schleife im Sekundentakt schicken. Gespeichert wird
ganz oder gar nicht — ein ungültiger Wert im Formular lässt auch die
gültigen daneben unverändert, statt die Hälfte zu schreiben.
Intervalle laufen nicht mehr über setInterval mit festem Abstand, sondern
über everyTuned: der Wert wird vor jeder Runde neu gelesen. Ein geändertes
Intervall greift damit ab dem nächsten Durchlauf, ohne Neustart. Ein
Fehler in einer Runde beendet die Schleife nicht.
Beim Testen aufgefallen und behoben: Number('') ist 0 und nicht NaN — ein
nie gesetzter Wert wäre dadurch auf sein Minimum gefallen statt auf den
Standard. Das Level-System hätte also 1 XP pro Nachricht vergeben und der
Monitor jede Minute geprüft.
Der Fetch-Wrapper im Frontend hat die Fehlermeldung des Servers verworfen;
jetzt steht im Panel „XP pro Nachricht: 1–500 XP" statt „fehlgeschlagen".
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Die Karte war komplett fest verdrahtet — Farben, der Schriftzug
„WILLKOMMEN", die Zeile „Member #N", das Hintergrund-Raster. Wer etwas
ändern wollte, musste das SVG im Code anfassen.
Jetzt steht das Aussehen unter Config → Support: vier Farben, die beiden
Textzeilen, ein optionales Hintergrundbild per Adresse, der Markenname im
Hintergrund an/aus, und die Karte selbst abschaltbar (dann bleibt das
Text-Embed mit Avatar als Vorschaubild).
Daneben eine Vorschau, die beim Tippen mitrendert — sie geht über
/api/welcome-preview.png und bekommt die Werte als Query, damit man sieht,
was man einstellt, bevor gespeichert wird. Gespeichert wird davon nichts.
Die Akzentfarben greifen auf die Markenfarben zurück, solange nichts
Eigenes gesetzt ist. Wer also die Marke umfärbt, hat die Karte gleich mit.
Beim Hintergrundbild ist Vorsicht eingebaut: nur http(s), Zeitlimit,
Größenbegrenzung, und ein nicht erreichbares Bild lässt die Karte ohne
Bild rendern statt sie ausfallen zu lassen. Über einem Bild liegt ein
Schleier, sonst säuft der Text ab.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Die Seite hieß Setup und listete 15 Bereiche flach untereinander. Wer
nicht wusste, dass Geburtstage unter „Community" stecken und Willkommens-
Texte unter „Support", hat geklickt bis er es fand.
Die Seitenleiste gruppiert jetzt nach Überblick, Auftritt, Inhalte,
Community, Technik und Zugang. Darüber steht ein Suchfeld, das nicht nur
Beschriftungen durchsucht, sondern auch Stichwörter je Bereich — „geburtstag"
führt zu Community, „ticket" zu Support. Enter springt zum ersten Treffer.
Der aktive Bereich steht in der Adresse (/settings#texte). Damit überlebt er
das Neuladen, und man kann jemandem einen Link auf genau die Stelle schicken.
Der Status-Bereich war die leerste Seite im ganzen Panel: sechs Zahlen. Er ist
jetzt der Einstieg und beantwortet die Frage, die man beim Öffnen wirklich hat
— was läuft noch nicht? Eingeschaltete Module, denen ein Kanal oder eine Rolle
fehlt, stehen dort mit einem Knopf, der direkt an die richtige Stelle springt.
Ist alles eingerichtet, sagt die Seite genau das.
Umbenannt in „Config", weil „Setup" nach einmaliger Einrichtung klingt — die
Seite ist aber der Ort, an dem man dauerhaft alles einstellt.
Schmale Bildschirme: die Leiste wird zur umbrechenden Zeile ohne
Gruppen-Überschriften, das Suchfeld nimmt die volle Breite.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bisher standen Begrüßung, Level-Ansage, Ticket-Texte und alle
Bestätigungs-Nachrichten fest im Code. Wer den Ton ändern wollte, musste
die Quelldateien anfassen und neu deployen — für alle außer mir also gar
nicht.
src/templates.js hält jetzt alle nach außen gehenden Texte an einer
Stelle: Standardtext, Platzhalter mit Erklärung und die Zuordnung zum
Modul. Der neue Setup-Tab „Texte" zeigt sie als Karten mit Textfeld;
die Bausteine darunter fügen sich per Klick an der Cursor-Position ein.
Der Standard bleibt im Code, die Datenbank speichert nur echte
Abweichungen. Ein leeres Feld heißt deshalb „wieder Standard" — und
angepasste Vorlagen sind an einer Marke erkennbar. Vorlagen zu
ausgeschalteten Modulen sind als solche gekennzeichnet, damit niemand
einen Text feilt, den gerade nichts sendet.
Unbekannte Platzhalter bleiben stehen statt still zu verschwinden, damit
ein Tippfehler in der Nachricht sichtbar wird.
Die Route heißt /api/texts, nicht /api/templates — letzteres sind seit je
die gespeicherten Composer-Nachrichten.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bisher waren die Schalter über die Oberfläche verstreut: fünf Funktionen
hatten einen Haken, 22 weitere gingen nur über den Umweg „Kanal leeren".
Für jeden außer dem Owner war das nicht auffindbar.
Jetzt steht in src/modules.js ein zentrales Register aller Funktionen mit
Beschreibung, Standard-Zustand und den Einstellungen, die sie brauchen.
Daraus entsteht ein neuer Setup-Tab mit Karten: Schiebeschalter, kurze
Erklärung, Hinweis was noch fehlt ("Fehlt noch: Release-Kanal") und ein
Sprung zu den Einstellungen.
Damit die Schalter keine Attrappen sind, prüfen die Bot-Module jetzt an
18 Stellen zentral, ob sie laufen dürfen — Starboard, Galerie, Modmail,
Willkommen, Protokoll, Auto-Antworten, Sprachkanäle, Erinnerungen,
Events, Twitch/YouTube, Monitor, Wächter, Releases, Rückblick,
Geburtstage, Verlosungen und geplante Beiträge.
Funktionen mit vorhandenem Schalter (Level-System, Backups, Member-Gate …)
nutzen weiterhin dieselbe Einstellung, damit keine zweite Wahrheit
entsteht und bestehende Konfigurationen unverändert weiterlaufen.
Nebenbei gefunden und behoben: Beim Anlegen eines Modmail-Threads wurde
`member.client` benutzt, obwohl es an der Stelle kein `member` gibt — der
erste Modmail-Thread wäre mit einem Fehler abgebrochen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Willkommens-Karten: gerendertes PNG (SVG → sharp) mit Avatar im
Neon-Ring, Willkommens-Schriftzug und Member-Nummer; Fallback aufs
bisherige Embed wenn das Rendering scheitert; Dockerfile installiert
fonts-dejavu-core für die Text-Darstellung
- Geburtstags-System: /geburtstag setzen|entfernen (birthdays-Tabelle),
tägliche Runde ab 09:00 Europe/Berlin (Doppel-Post-Schutz über
last_birthday_run), Gratulations-Embed + Tages-Rolle (wird am
nächsten Morgen wieder abgeräumt); Kanal + Rolle im Community-Tab
- Devlog-Permalinks: /devlogs/:id als eigene Seite (GET /api/devlogs/:id),
Link-Symbol an jeder Karte, Link-kopieren-Button; der Server injiziert
Open-Graph-Tags ins SPA-HTML — Devlog-Links zeigen Titel, Anriss und
Bild, alle anderen Seiten bekommen Default-Tags (auch die Startseite)
- Auto-Publish: maybeCrosspost() veröffentlicht Devlog-, Release-,
Composer- und geplante Posts automatisch in Ankündigungs-Kanälen
- brandEmbed: leere Avatar-URL crasht nicht mehr die Footer-Validierung
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Startseite (/): Hero mit CTAs (Discord-Invite + Devlogs), Live-Zahlen
(Member, Server online, Spieler), Bereichs-Kacheln, neuestes Devlog
als Teaser — kein Redirect mehr auf /devlogs
- Server-Seite v2: Übersichtsleiste (X/Y online, Spieler gesamt),
2-Spalten-Grid, Server-Logo (neues Feld image_url, https-only, auch
als Embed-Thumbnail im Discord-Status), große Spielerzahl, Uptime %
+ Peak aus den 24h-Samples, Adresse als Copy-Button
- Lightbox: rendert jetzt per Portal in <body> (Header/Scanlines
schienen durchs Overlay), Pfeile stehen neben dem Bild statt drauf,
Info-Zeile unterm Bild
- Navbar: Community-Dropdown-Button auf Link-Höhe ausgerichtet
(line-height + align-items)
- Setup Server-Tab: Logo-URL-Feld
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Navbar entschlackt: nur noch Devlog · Roadmap · Server · Community ·
Setup; Galerie/Level/Events/Changelog im Community-Dropdown,
Profil/Commits/Logout im Avatar-Menü; Mobile-Burger mit Drawer
- Öffentliche /server-Seite: Live-Status-Karten mit Spieler-Balken,
Map, Connect-Button und 24h-Spielerzahl-Chart (SVG); Monitor sampelt
jede Abfrage in player_history (7 Tage Retention), GET /api/servers
liefert nur öffentliche Felder (keine Query-URLs/Hosts)
- Lightbox: Galerie- und Devlog-Bilder öffnen im Overlay mit
Pfeiltasten, Buttons, Wisch-Gesten und Zähler statt neuem Tab
- Skeleton-Loader: schimmernde Platzhalter auf Devlogs, Roadmap,
Galerie, Level, Changelog und Server statt "Lade …"
- Team-Audit-Log: audit_log-Tabelle (max 500 Einträge), Logging bei
Settings/Composer/Vorlagen/Rollen-Menüs/Gameservern/Team/API-Keys/
Branding, GET /api/auditlog (Owner) + Liste im Team-Tab
- Giveaway: 🔁 Reroll-Button (Admin-only) + 👥 Teilnehmerliste am
Gewinner-Post
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Game-Server: eigene Tabelle statt CSV-Setting (automatische Migration), eigener
Setup-Tab mit Server-Builder (Name, Typ fivem/http, Query-URL, Anzeige-Adresse);
pro Server ein Live-Embed (gruen/rot, Spieler-Balken, Map, Adresse als Code-Block),
edit in place; Embed wird beim Server-Loeschen mit entfernt
- Team-System: web_admins mit Bereichs-Scopes (content/community/rollen/bewerbungen/
server/settings/devlogs); Owner behaelt Brand/System/API-Keys/Team exklusiv;
32 Routen-Guards auf Scopes umgestellt, sensible PUT-Felder fuer Team gestrippt;
Team-Tab (Owner) + Tab-Filterung im Frontend, /api/me liefert Scopes
- Bot-Karte: Profil-Beschreibung (Ueber mich) via application.edit
- Fix: settings-Tabelle wird vor der Gameserver-Migration angelegt (frische DBs
crashten sonst beim Start)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Schließen-Button erstellt jetzt ein Text-Transcript (chronologisch, inkl.
Attachment-URLs, max 500 Nachrichten) und schickt es dem Ersteller per DM
- Kopie ins Mod-Log (Aufbewahrung fürs Team, mit Hinweis falls DM blockiert)
- Thread wird danach gelöscht statt nur archiviert (Fallback: sperren+archivieren,
falls dem Bot Lösch-Rechte fehlen)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Alpha-Keys: Pool im Community-Tab, Ein-Klick-Verteilung per DM an alle Playtester
(Key bleibt frei, wenn die DM geblockt wird)
- Bewerbungs-Formulare: Builder im neuen Bewerbungen-Tab (bis 5 Fragen),
Button → Discord-Modal → Review-Embed mit ✅/❌, Rolle + DM bei Entscheidung
- Events: GuildScheduledEventCreate → Announce-Embed; öffentliche /events-Seite
aus den Discord-Events (5-min-Cache)
- Server-Stats: activity_daily (Nachrichten/Joins/Leaves) → Balken-Chart auf /level
- Triggers (Auto-Antworten, 30s-Cooldown), /remind (DM-Scheduler),
Twitch-Live (Helix, App-Creds write-only) + YouTube-RSS-Announcements,
Temp-Voice (Join to Create, Cleanup bei Leerstand + Start)
- Brand-Tab: MEE6-Style Bot-Identity-Karte (Avatar-Vorschau mit Status-Dot,
Bot-Name via setUsername, Presence online/idle/dnd, Aktivität)
- Neue Intents: GuildVoiceStates, GuildScheduledEvents; alles smoke-getestet
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Alle Embeds nutzen jetzt brandName/brandColor/brandColor2/brandFooter aus den
Settings (Codemod über 20 Module) — Farbe & Name serverweit per Klick änderbar
- Bot-Status (Presence): Typ (Spielt/Schaut/Hört/Status) + Text, sofort angewendet;
Server-Monitor nutzt die Presence nur noch, wenn kein eigener Status gesetzt ist
- Avatar-/Banner-Upload direkt aufs Bot-Profil (Base64, 8-MB-Limit,
Discord-Rate-Limit sauber gemeldet)
- Env-Verlagerung: GITEA_API_TOKEN (write-only Setting) und DISCORD_GUILD_ID
jetzt auch über den Brand-Tab pflegbar — weniger Redeploys
- Rollen-Menüs: Button-Farbe pro Eintrag wählbar (Grau/Blau/Grün/Rot)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Rollen-Menüs: Builder im Webinterface (Titel, Beschreibung, Kanal, Emoji/Label/Rolle,
Exklusiv-Modus = max. 1 Rolle pro Menü), Bot postet Button-Embeds; Klick = Rolle
nehmen/abgeben (ephemeres Feedback), Update editiert den Discord-Post in place,
Löschen räumt ihn mit auf
- Bewusst Buttons statt klassischer Emoji-Reaktionen: kein Reaction-Spam am Post,
kein Custom-Emoji-Parsing, exklusive Menüs möglich; halbfertige Reaction-Variante
aus abgebrochener Session entfernt und konsolidiert
- Setup-Seite: komplett neu strukturiert in 8 Tabs mit sticky Sidebar
(Status/Feeds/Community/Rollen/Support/Composer/System/API), Feedback als Toast
- Modern-Polish: weiche Radien auf Karten/Inputs/Buttons, ruhigere Input-Flächen
mit Fokus-Glow, Sektions-Hover; JSON-Parser akzeptiert leere Bodies
- API: /api/rolemenus CRUD + publish (Admin), README aktualisiert
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Tickets: /ticket-setup postet 🎫-Button; Klick erstellt privaten Thread
(ein offenes Ticket pro User), Schließen-Button sperrt + archiviert
- Composer auf der Setup-Seite: Nachricht/Embed als Bot in beliebigen Kanal
senden oder per Message-ID bearbeiten (nur eigene Bot-Posts)
- API v1: PATCH /api/v1/message — Skripte können ihre Bot-Posts aktualisieren
- Setting ticket_channel_id; README ergänzt
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- /wunsch: Voting-Embed mit 👍, Reaction-Tracking (add/remove) in SQLite,
Top-20-Rangliste öffentlich auf der Roadmap-Seite
- /giveaway (Admin): Preis/Dauer/Gewinnerzahl, 🎉-Teilnahme-Button (toggle),
Minuten-Scheduler zieht Gewinner, schließt das Embed ab und pingt sie
- Contribution-Heatmap: 52-Wochen-Grid in Brand-Gelb aus dem Commit-Archiv
(/api/heatmap) auf der Roadmap-Seite
- Setting voting_channel_id auf der Setup-Seite
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Modmail: DMs an den Bot landen als Threads im privaten Staff-Kanal,
Thread-Antworten gehen als DM zurück (Zustell-Feedback per Reaktion)
- Willkommens-Embed für neue Member (GuildMemberAdd; Server-Members-Intent nötig)
- Mod-Log: gelöschte/bearbeitete User-Nachrichten in privaten Log-Kanal
- Server-Monitor: FiveM-kompatible Endpoints (/dynamic.json) alle 2 min,
persistentes Status-Embed (edit in place) + Spielerzahl als Bot-Presence
- Neue Intents: DirectMessages, GuildMembers; Partials.Channel
- Setup-Seite: Sektionen // Moderation & Kontakt und // Game-Server
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Playtester: /playtester-setup postet Bewerbungs-Embed mit 🧪-Button;
Toggle vergibt Rolle + DB-Eintrag; Liste + Rollen-Auswahl auf der Setup-Seite
- Starboard: ab konfigurierbarem ⭐-Schwellwert Repost als Embed in den
Best-of-Kanal (Dedupe, kein Selbst-Boarding); neue Intents/Partials für Reaktionen
- Galerie: Bilder aus dem Screenshot-Kanal lokal archiviert (live + /galerie-backfill,
Delete-Sync, Bot reagiert mit ⭐) → öffentliche /galerie-Seite mit Hover-Grid
- Setup-Seite: Sektion // Community (Rolle, Starboard-Kanal + Schwellwert, Screenshot-Kanal)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- /bug (alle Member): erstellt Gitea-Issue inkl. Screenshot-Upload als Asset;
bug_reports-Tabelle für den Rückkanal — Issue geschlossen (issues-Webhook)
→ DM an den Reporter
- Auto-Thread '💬 Devlog <Datum>' unter jedem Devlog-Post (Setting, Default an)
- Watchdog: prüft Setting watchdog_urls alle 2 min, DM an Admin nach 2 Fails
in Folge + Entwarnung mit Downtime-Dauer
- Setup-Seite: Bug-Repo, Threads-Toggle, Watchdog-URLs; Env GITEA_API_TOKEN/GITEA_URL
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Scheduler (5-Minuten-Check, dedupe über last_weekly_recap-Setting)
- Embed: Commits pro Tag als Balken-Grafik, Commit-/Devlog-Zahlen, Top-Projekte
- Setting weekly_recap_enabled + 'Rückblick jetzt testen'-Button auf der Setup-Seite
- TZ Europe/Berlin im Compose (Scheduler + Datumsformate)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Setting devlog_ping_role_id (Rollen-Dropdown auf der Setup-Seite, leer = aus)
- Devlog-Post erwähnt die Rolle (allowedMentions gescoped) und trägt einen
🔔-Button, über den Member die Rolle selbst an-/abmelden (ephemere Bestätigung)
- API liefert vergebbare Rollen (ohne @everyone/managed), PUT validiert
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Gitea-Release-Webhook: Releases werden archiviert (releases-Tabelle) und neue
'published'-Releases als oranges 🚀-Embed mit Buttons angekündigt
- Release-Kanal auf der Setup-Seite wählbar (leer = Feature aus) + Test-Button
- /changelog: öffentliche Seite mit Tag-Chips, Pre-Release-Badge, Release-Notes
- /api/releases öffentlich; Updates/Redeliveries posten nicht doppelt
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Settings-Tabelle in SQLite; Env-Variablen nur noch Fallback, Änderungen greifen sofort
- /settings (Admin): Devlog-/Commit-Kanal als Dropdown aus allen sichtbaren Textkanälen,
je mit Test-senden-Button
- Commit-Feed-Regeln: an/aus, Branch-Filter, ignorierte Repos (archiviert wird immer,
gefiltert wird nur das Posten; ignorierte Repos komplett übersprungen)
- Status-Panel: Bot-Tag, Uptime, Devlog-/Commit-Zahlen, DB-Größe
- API: GET/PUT /api/settings, POST /api/settings/test/:target (alles Admin-only)
- COMMIT_CHANNEL_ID/DEVLOG_CHANNEL_ID in config optional
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- MessageDelete-Listener (mit Partials): in Discord gelöschte Devlogs verschwinden
automatisch aus dem Archiv, inkl. lokaler Bilder
- DELETE /api/devlogs/:id (nur Admin) + ✕-Button auf den Karten (nur für Admin sichtbar)
- Anlass: Test-Post mit Commit-Liste von vor der Prosa-Sperre stand noch im Archiv
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Bild-Attachments werden beim Archivieren lokal gespeichert (Discord-CDN-URLs
laufen ab) und unter /devlog-assets/ ausgeliefert; DB-Migration: images-Spalte
- Frontend: Bildergalerie im Grid (responsive nach Anzahl), Embed-Titel als
Projekt-Chip, Commit-Fußzeile abgesetzt, Monats-Trenner in der Timeline
- Backfill lädt Bilder alter Devlogs nach (Dedupe verhindert Doppel-Downloads)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- devlog.py kann per Webhook ODER Bot-Token posten — beides wird jetzt archiviert
(nur User-Chatter und der EcoBot selbst bleiben ausgeschlossen)
- /devlog-backfill meldet jetzt Webhook/Bot/User-Zählung zur Diagnose
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Webhook-Nachrichten im Devlog-Kanal werden automatisch in SQLite archiviert
(devlog.py im EcoGame-Repo bleibt unverändert)
- /devlog-backfill (Admin): scannt die komplette Kanal-Historie in 100er-Blöcken
- Dedupe über Discord-Message-ID, Embeds werden mit Titel+Beschreibung erfasst
- Neue Intents: GuildMessages + MessageContent, neue Env-Var DEVLOG_CHANNEL_ID
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Fastify-Webserver mit /health und /webhooks/gitea (HMAC-SHA256-Signaturprüfung, timing-safe)
- Push-Commits werden in SQLite gespeichert (Dedupe per SHA) und als Embed gepostet
- Dockerfile auf node:22-slim (glibc-Prebuilds für better-sqlite3), Port 3080 published
- README: Anleitung für Cloudflare-DNS, Nginx Proxy Manager (bot.d4rkst3r.de) und Gitea-Webhook
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- discord.js v14 Bot mit Slash-Command-Registry (Guild oder global)
- Konfiguration über .env mit Validierung (config.js)
- Dockerfile + docker-compose.yml für Portainer-Deployment
- README mit Schritt-für-Schritt-Anleitung (Discord Developer Portal)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>