Vorbild windrose.support: Klick auf eine Idee, und man sieht sie ganz — Stand,
Begruendung, wer sie eingereicht hat, und darunter, was dazu geschrieben wurde.
Der Unterschied bleibt, wo geschrieben wird. Die Beitraege sind aus dem
Discord-Thread gespiegelt, nicht hier getippt. Deshalb fuehrt der einzige
Knopf am Ende in den Thread und nicht in ein Formular. Ein zweiter Ort fuer
dieselbe Unterhaltung waere genau das, was ich beim Konzept als das Falsche
bezeichnet habe.
Bisher wurde nur mitgezaehlt. Jetzt landen die Beitraege in einer eigenen
Tabelle — mit Bearbeiten und Loeschen, sonst stuende auf der Webseite fuer
immer, was in Discord laengst zurueckgenommen wurde. Reine Bild-Posts ohne Text
bleiben draussen, die haetten hier nichts zu sagen.
Der Kommentar-Zaehler wird jetzt aus der Tabelle gezaehlt statt in einer Spalte
mitgefuehrt. Eine Wahrheit statt zwei, die auseinanderlaufen koennen — dieselbe
Ueberlegung wie beim Zuspruch.
Nebenbei geprueft, weil es haette schiefgehen koennen: /api/wishes/suche und
/api/wishes/:id liegen auf derselben Ebene. Der Router bevorzugt die feste
Route vor der mit Platzhalter — nachgestellt mit find-my-way, nicht aus dem
Gedaechtnis behauptet.
Geprueft: im Browser die Seite mit drei Beitraegen und die ohne (kein Thread,
keine Begruendung, Hinweis statt Liste), der Weg von der Roadmap dorthin ohne
Neuladen, Migration erneut durchgespielt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ueberall, wo ein Emoji nach Discord geht — Rollen-Menues, Ticket-Anliegen,
Wunsch-Bereiche, Portal-Kacheln — stand ein leeres Textfeld, in das man mit
Win+. hineintippen musste. Jetzt sitzt daneben ein Knopf mit einer Tafel:
sieben Gruppen, deutsche Suchbegriffe ("auto", "geld", "warnung"), ein Klick
schreibt ins Feld. Das Feld bleibt daneben stehen, denn Server-Emojis
(<:name:123>) kennt nur Discord.
Bewusst eine eigene Liste statt einer Bibliothek: die vollstaendige
Unicode-Tabelle waere ein halbes Megabyte fuer eine Handvoll Symbole, und die
Suchbegriffe duerfen deutsch sein.
## Forum-Tags
Ist der Voting-Kanal ein Forum, wird jeder Wunsch jetzt ein Forums-Beitrag
statt Nachricht-plus-Thread — und bekommt Tags fuer Bereich und Stand. Damit
laesst sich auch in Discord filtern, wofuer Tags ja da sind. Beim Wechsel des
Stands zieht das Tag mit: der alte fliegt raus, der neue kommt rein, der
Bereich bleibt stehen.
Zugeordnet wird ueber den Namen. Wer im Forum ein Tag "Fahrzeuge" anlegt,
bekommt es automatisch gesetzt; wer keines anlegt, verliert nichts. Bewusst
keine ID-Zuordnung im Panel — das waere eine zweite Liste, die man pflegen
muss und die beim ersten Umbenennen auseinanderlaeuft.
Dabei aufgefallen und mitgefixt: im Forum liegt die Startnachricht im Beitrag
selbst und nicht im Kanal. Der Status-Aktualisierer hat sie vorher im Kanal
gesucht und nicht gefunden — die Begruendung waere im Forum also nie im Post
gelandet. Und der Titel wird jetzt nur um den Stand ergaenzt statt
ueberschrieben, damit "🚗 Fahrzeuge" stehen bleibt.
Geprueft: tagsFuer gegen 20 Faelle — kein Forum, Medien-Kanal, fehlende Tags,
Gross/Kleinschreibung, Deckel bei fuenf (mehr nimmt Discord nicht), und alle
fuenf Staende einzeln. Im Browser die Emoji-Tafel an allen vier Stellen:
Suche, Escape, Klick daneben, Leeren, Uebernehmen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nach dem Vorbild von windrose.support: Bereiche zum Einsortieren, Sortierung
nach Top / Bewegung / Neu, und eine Zahl daneben, wie viel darueber geredet
wird.
Der Unterschied zu so einem Dienst ist, wo geredet wird. Zu jedem Wunsch macht
der Bot jetzt einen Thread unter dem Post auf. Der Kommentar-Zaehler auf der
Roadmap zaehlt die Beitraege darin und verlinkt hinein — es gibt also einen
Zaehler wie bei einem Ideen-Board, aber ohne ein zweites Kommentarsystem neben
Discord zu stellen. Genau das hatte ich beim letzten Mal als das Falsche
bezeichnet, und dabei bleibt es.
Eingereicht wird jetzt auch ueber einen Aufruf-Post: /wunsch-setup postet ihn,
und mit Bereichen wird daraus ein Auswahlmenue — erst wohin, dann das
Formular. Ohne Bereiche bleibt es beim einen Knopf. Derselbe Aufbau wie bei den
Ticket-Anliegen, bis hin zur Emoji-Pruefung, die ich mir dort schon geschrieben
hatte.
Das Anlegen selbst ist nach bot/wishes.js gewandert. /wunsch, der Knopf und die
Webseite gehen jetzt denselben Weg — vorher hatte die Webseite ihr eigenes
Embed zusammengebaut, das dem aus dem Befehl nur aehnlich sah und keinen Thread
bekam.
"Bewegung" sind die Stimmen der letzten sieben Tage. Die Rangliste allein
zementiert alte Wuensche: was einmal oben steht, bleibt oben, egal ob noch
jemand hinschaut. Sortiert wird auf dem Server, weil die Zeitstempel der
Stimmen im Browser gar nicht ankommen.
Bewusst nicht uebernommen: Gegenstimmen. Windrose hat sie, aber bei einer
Community dieser Groesse laden sie zum Nachtreten ein und schrecken vom
Einreichen ab — und eine Zahl, die aus zwei Richtungen kommt, sagt am Ende
weniger als eine, die nur zaehlt, wer etwas will.
Geprueft: Migration erneut durchgespielt (das Schema hat drei Spalten
dazubekommen), Routen, SQL gegen das Schema, und im Browser alle drei
Filterreihen einzeln und kombiniert, der Sortierwechsel, die Thread-Links und
die Bereichs-Pflege im Panel.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Voting hatten wir. Was fehlte, war alles danach: ein Wunsch sammelte zwoelf
Stimmen und dann passierte sichtbar nie wieder etwas damit. Genau das nennt der
Artikel, der die Idee angestossen hat, "letting the board become a graveyard" —
und das war der Zustand.
Jeder Wunsch hat jetzt einen Stand: wird geprueft, geplant, in Arbeit,
umgesetzt, nicht geplant — mit Begruendung. Die steht oeffentlich unter dem
Wunsch auf der Roadmap und wird in den Discord-Post zurueckgeschrieben, wo
abgestimmt wurde. Ein abgelehnter Wunsch mit einem Satz Begruendung ist mehr
wert als einer, der ewig oben schwebt.
Springt ein Wunsch auf "umgesetzt", bekommt jeder eine Direktnachricht, der
dafuer gestimmt hat. Das ist der Punkt, an dem unser Aufbau einem fertigen
Voting-Dienst ueberlegen ist: der Draht zu jedem Einzelnen ist ohnehin offen.
## Zwei Sachen, die kaputt waren
Doppelt abstimmen ging. Der Zuspruch wurde von zwei Wegen hochgezaehlt — 👍 in
Discord und der Web-Knopf — und nur der Web-Weg merkte sich, wer geklickt hat.
Dieselbe Person zaehlte zweimal. Jetzt wird nicht mehr hochgezaehlt, sondern
gezaehlt: eine Zeile je Person, egal woher der Klick kam. Der zusammengesetzte
Schluessel schliesst den Fall aus, statt ihn nachtraeglich zu korrigieren.
Die Discord-Nachrichten-ID war der Primaerschluessel. Damit konnte ein Wunsch
nur existieren, solange seine Nachricht existiert, das Team konnte keinen von
Hand eintragen, und zwei Doppler liessen sich nicht zusammenfuehren. Wuensche
haben jetzt eine eigene ID; die Nachrichten-ID ist nur noch ein Verweis.
Die Migration erhaelt den Zuspruch: was vom alten Punktestand nicht auf
gespeicherte Web-Stimmen zurueckgeht, waren Reaktionen ohne Namen — die Zahl
bleibt als Sockel stehen, weil rueckwirkend niemand mehr feststellen kann, wer
das war. Durchgespielt gegen eine Datenbank im alten Aufbau: Punktestand
erhalten, verwaiste Stimmen fallen raus, ein zweiter Start migriert nicht
nochmal, und der Fall, in dem der alte Zaehler hinter den echten Stimmen
zurueckhing, zaehlt jetzt richtig.
## Dazu
Doppler zusammenfuehren: die Stimmen wandern zum Original, wer fuer beide
gestimmt hat, zaehlt dort weiterhin einmal. Beim Tippen im Wunsch-Feld zeigt die
Seite, was es schon gibt — ein Klick darauf stimmt mit, statt einen zweiten
gleichen Wunsch anzulegen. Und die Liste laesst sich nach Stand filtern.
Was ich bewusst nicht gebaut habe: Kommentare auf der Webseite. Jeder Wunsch
ist schon eine Discord-Nachricht — die Diskussion gehoert in den Thread
darunter und nicht in ein zweites System.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Knopf unter dem Playtester-Aufruf hat jeden aufgenommen, der ihn gedrueckt
hat — ohne Frage, ohne Pruefung. Fuer ein Programm, dessen Teilnehmer Zugang
und Alpha-Keys bekommen, ist das die falsche Tuer.
Statt einen zweiten Pruef-Ablauf danebenzustellen, laeuft die Aufnahme jetzt
ueber die Bewerbungs-Formulare, die es laengst gibt: Modal ausfuellen,
Review-Embed im Staff-Kanal, ✅ oder ❌, Rolle und Antwort-DM bei Zusage. Das
ist dieselbe Maschinerie, nur ein anderer Aufhaenger — und damit auch nur eine
Stelle, an der spaeter etwas kaputtgehen kann.
Ein Formular laesst sich als Playtester-Formular markieren ("Annahme traegt als
Playtester ein"). Wer dort angenommen wird, landet zusaetzlich in der
Playtester-Liste, damit die Schluessel-Verteilung ihn kennt. Nur eines kann es
sein — sonst wuesste /playtester-setup nicht, welches es posten soll.
/playtester-setup postet jetzt den Knopf dieses Formulars. Fehlt das Formular,
fehlen Fragen oder fehlt der Review-Kanal, sagt der Befehl das, statt einen
Aufruf zu posten, der ins Leere fuehrt.
Der alte Knopf in bereits geposteten Nachrichten nimmt niemanden mehr auf: er
verweist auf den Aufruf. Austreten bleibt Selbstbedienung — dafuer braucht es
keine Freigabe.
Im Panel steht im Playtester-Bereich, ueber welches Formular die Aufnahme
laeuft, ob es schon gepostet ist, und ein Weg dorthin. Ohne markiertes Formular
steht dort, dass gerade niemand hereinkommt — das ist sonst der Grund, warum
sich tagelang niemand bewirbt.
Geprueft: Routen, SQL gegen das Schema, und im Browser beide Zustaende des
Hinweises sowie der neue Schalter — beim Wechsel zwischen zwei Formularen
folgt er dem richtigen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Playtester und Alpha-Keys standen als zwei Kaesten mitten im Community-Bereich
zwischen Geburtstagen und Starboard. Die Liste war reine Anzeige: Namen und
Datum, sonst nichts. Wer einem Einzelnen einen Schluessel geben wollte, konnte
nur "an alle verteilen" druecken, und wer jemanden wieder rausnehmen wollte,
musste an die Datenbank.
Jetzt ist es ein eigener Bereich mit drei Zahlen oben und einer Liste, in der
neben jedem Namen sein Schluessel steht. Daneben, je nach Zustand, "Schluessel
geben" oder "Zurueckziehen", und ein ✕ zum Entfernen — die Rolle geht dabei
mit, denn nur aus der Liste zu streichen und die Rolle stehen zu lassen waere
eine Halbwahrheit.
Der Vorrat ist jetzt sichtbar, nicht nur seine Groesse: einen vertippten
Schluessel findet man sonst nie wieder. Nachlegen und "wer bekommt einen?"
laufen ueber Dialoge, damit der Bereich nicht zumuellt — dafuer gibt es jetzt
eine kleine Dialog-Komponente, gebaut wie die Lightbox (Portal, Escape,
Hintergrund festhalten).
Zurueckziehen legt den Schluessel zurueck in den Vorrat. Die Person hat ihn per
DM natuerlich weiterhin — das ist eine Buchhaltung, keine Sperre. Loeschen geht
absichtlich nur bei freien Schluesseln.
## Module dorthin, wo man sie sucht
Ein paar Module standen im falschen Bereich, teils seit Langem:
- Willkommens-Karte: Support → Brand. Sie ist Aussehen, kein Support-Fall, und
im Brand-Bereich steht der Rest vom Auftritt. Der Block ist woertlich
umgezogen, nur in eine eigene Konstante — so kann sich beim Umzug nichts am
Inhalt geaendert haben.
- Twitch & YouTube: System → Feeds. Es ist ein Feed.
- Events: System → Community.
- Auto-Rolle und Rollen merken: Ihre Felder standen laengst im Rollen-Bereich,
nur das Register sagte "Community" — dadurch sprang "Einrichten" woanders hin
als die Einstellung liegt.
- Playtester und Alpha-Keys: Community → eigener Bereich.
Damit ist System kein Sammelbecken mehr, sondern nur noch Technik: Waechter,
Sprachkanaele, Erinnerungen, Sicherungen, Member-Gate.
Geprueft: Routen (alle vier neuen bewacht), SQL gegen das Schema, und im
Browser beide Dialoge (Escape schliesst, Hintergrund wird wieder freigegeben,
Hinzufuegen bleibt bei leerem Feld aus), die Liste in allen drei Zustaenden,
und die Suche — "willkommen" fuehrt jetzt nach Brand, "twitch" zu Feeds,
"sticky" zu Rollen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bisher sah jedes Ticket gleich aus: ein Knopf, ein Thread, und im Thread stand
als Erstes die Frage, worum es ueberhaupt geht. Und wer sich darum kuemmert,
stand nirgends — man sah hinterher nur, wer zufaellig auf "Schliessen" geklickt
hat.
Anliegen sind Kategorien, die man in der Config anlegt: Name, Emoji, eine
Zeile Beschreibung, ein Einstiegstext und optional eine Rolle, die beim
Oeffnen erfaehrt, dass etwas anliegt. Der Ticket-Post wird dadurch vom Knopf
zum Auswahlmenue; das gewaehlte Anliegen steht im Thread-Namen und der
Einstiegstext gleich im ersten Beitrag — gute Stelle fuer "was wir wissen
muessen".
Wer keine anlegt, merkt nichts: ohne Eintrag bleibt es beim einen Knopf. Wer
nur eine Sorte Tickets hat, soll nichts einrichten muessen.
Uebernehmen ist ein Knopf im Thread, sichtbar fuers Team (wer aufraeumen darf,
darf auch uebernehmen). Danach steht im Thread, wer sich kuemmert, und der
Knopf wird zu "Freigeben". Die Bedingung "nur wenn noch niemand dran ist"
steckt im UPDATE und nicht davor — zwei gleichzeitige Klicks wuerden sonst
beide gewinnen. Beim Schliessen landen Anliegen und Bearbeiter im Transcript
und im Mod-Log-Eintrag.
Die Erstellung ist dabei aus client.js nach tickets.js gewandert. Knopf und
Menue gehen jetzt denselben Weg, und in client.js sind drei Importzeilen
weggefallen, die nur noch dort standen.
Geprueft: alsEmoji gegen 20 Eingaben (Unicode, Hautfarbe, ZWJ-Ketten,
Server-Emoji, Murks) und das fertige Panel gegen discord.js selbst — ein
ungueltiges Emoji oder ein zu langes Label laesst Discord sonst die ganze
Nachricht fallen, und das faellt erst beim Senden auf. Im Browser: Liste,
Bearbeiten-Formular und der leere Zustand.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Funktionsliste war eine einzige Tabelle mit 46 Zeilen — in der Reihenfolge,
in der die Sachen entstanden sind, also in keiner. Jetzt ist sie nach denselben
Bereichen sortiert wie das Panel: Inhalte, Community, Moderation, Server, dazu
Plattform fuer alles, was keine Discord-Funktion ist. Wer im Panel etwas sucht,
findet den Abschnitt in der README am selben Platz.
Nachgetragen, was seither dazugekommen ist: AutoMod, Raid-Schutz, verknuepfte
Rollen, native Umfragen, Statusseite, Herzschlag, Seiten-Editor, Englisch fuer
die oeffentlichen Seiten. Und die Trennung in zwei Domains stand bisher gar
nicht drin, obwohl sie das Erste ist, was man verstehen muss — sie steht jetzt
ganz oben, mit einer Tabelle welche Seite was zeigt.
Korrigiert: 35 Module waren es mal, es sind 39. 15 Config-Bereiche waren es
mal, es sind 16. Die Seitentabelle fuehrte Hub-Seiten unter der Bot-Domain.
Die Projektstruktur kannte die Haelfte der Dateien nicht. In der
Ersteinrichtung fehlten die Rechte fuer AutoMod und Kanal-Anlegen sowie die
zusaetzlichen OAuth-Redirects.
Dazu ein Inhaltsverzeichnis, eine docs/README.md als Wegweiser, und in
konzept-zwei-seiten.md steht nicht mehr "muss noch aktiviert werden" — es
laeuft seit Ende Juli.
Geprueft mit einem kleinen Skript: 11 Markdown-Dateien, keine toten
Datei-Links, keine toten Anker, keine Tabelle mit falscher Spaltenzahl.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nach dem Umbau lagen alle Funktionen hinter dem Raster — für die vier, fünf,
die man dauernd anfasst, ist das ein Umweg zu viel.
Oben in der Seitenleiste steht jetzt „Meine Funktionen": ein Klick öffnet
die Seite direkt. Welche dort stehen, entscheidet der Stern auf der
Modul-Karte oder auf der Seite selbst.
Bewusst nicht alle 35: Deckel bei acht, sonst ist der Gewinn gegenüber dem
Raster wieder weg. Der Standard ist eine Vermutung (Devlogs, Willkommen,
Tickets, Starboard, Galerie) und ausdrücklich zum Ändern gedacht.
Die Auswahl liegt als Einstellung in der Datenbank, nicht im Browser — sie
gilt damit auf jedem Gerät und fürs ganze Team. Eine geleerte Auswahl wird
als solche gespeichert, sonst käme beim nächsten Laden der Standard zurück.
Der Stern ist ungeheftet nur angedeutet und wird erst beim Überfahren
deutlich — 35 Karten mit vollen Sternen sähen aus wie ein Sternenhimmel.
Nebenbei: bei geöffneter Modul-Seite war sowohl „Module" als auch die
Funktion in der Leiste hervorgehoben. Jetzt nur noch die Funktion.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bisher war eine Funktion über vier Bereiche verteilt: Schalter unter Module,
Kanal unter Support, Texte unter Texte, Werte unter Werte. Wer die
Begrüßung ändern wollte, brauchte drei Bereiche.
Jetzt öffnet ein Klick auf die Karte die Seite dieser Funktion — mit
Schalter, ihren Kanälen und Feldern, ihren Texten, ihren Werten und dem Weg
zum passenden Werkzeug. Für „Willkommen" also Kanal, Karte, Farben,
Vorschau und beide Texte untereinander.
Bewusst keine 35 Einträge in der Seitenleiste: das Modul-Raster ist die
Navigation, wie bei MEE6. Die Seitenleiste bleibt so lang wie vorher.
Die Seite wird aus den Registern gebaut, nicht von Hand: modules.js kennt
die Felder (neu: `fields` für alles jenseits der Pflichtkanäle, `tool` für
den Verweis auf einen Editor), templates.js hatte die Zuordnung schon,
tuning.js hat sie bekommen. Eine neue Funktion bekommt ihre Seite dadurch
geschenkt — es gibt keinen Ort, an dem man sie vergessen könnte.
Was ein eigenes Werkzeug hat — Rollen-Menüs, Bewerbungen, Composer,
Server-Monitor —, bleibt in seinem Bereich; die Modul-Seite verlinkt nur
dorthin. Dort ist mehr Platz, und die Editoren lassen sich nicht sinnvoll
aus Registern erzeugen.
Texte und Werte bleiben zusätzlich als flache Übersicht: wer alle Texte am
Stück durchgehen will, soll dafür nicht 27 Seiten öffnen müssen.
Beim Umbau ist mir ein Skript in die Quelldatei gelaufen — der Schreibvorgang
scheiterte an einem Emoji und hinterließ modules.js leer. Wiederhergestellt
aus dem letzten Commit; die Skripte schreiben jetzt erst in eine
Zwischendatei und benennen danach um.
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>
Die Seitenleisten-Suche kannte nur Bereiche: „starboard" führte zu
Community, aber nicht zum Schwellwert-Feld. Bei rund 60 Einstellungen
heißt das raten, in welchem Bereich etwas liegt.
setting-index.js hält jetzt fest, wo jede Einstellung wohnt, mit
Suchwörtern und Synonymen. Die Suche zeigt Treffer als eigene Gruppe unter
den Bereichen; ein Klick wechselt den Bereich, scrollt zum Feld und hebt es
kurz hervor. Enter nimmt den ersten Treffer, wobei Einstellungen Vorrang
vor Bereichen haben — wer „schwellwert" tippt, will das Feld.
Die Felder tragen dafür data-setting="<schlüssel>"; das Attribut wurde
skriptgesteuert ergänzt, ohne die Struktur anzufassen. Fünf Einstellungen
teilen sich ein Feld mit einer anderen (etwa die beiden Markenfarben) —
die zeigen über `target` auf den Nachbarn.
Textfelder werden beim Sprung fokussiert, Kanal-Auswahlen bewusst nicht:
deren Fokus klappt die Liste auf und verdeckt alles darunter.
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>
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>
Antwort auf den CMS-Wunsch aus der Community — statt eines Forums, das
neben aktivem Discord erfahrungsgemäß verwaist, ein schlanker Editor für
eigene Seiten: Regeln, Über uns, Mitmachen, FAQ.
- neuer Setup-Tab "Seiten" (Scope content): Titel, Adress-Kürzel wird aus
dem Titel abgeleitet, Markdown-Editor mit Live-Vorschau daneben
- pro Seite wählbar: veröffentlicht oder Entwurf, im Community-Menü oder
nur per Direktlink, Reihenfolge
- Anzeige unter /<kürzel>, gerendert mit dem vorhandenen Markdown-Parser
- Entwürfe sind öffentlich nicht abrufbar — sie brauchen den content-Scope
- reservierte Kürzel (devlogs, settings, api, impressum …) werden
abgelehnt, damit nichts die festen Routen überschreibt
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Repo-Backups: nachts um 04:00 werden alle Gitea-Repos als git-Bundle
gesichert — eine Datei pro Repo mit kompletter Historie, aus der sich
direkt wieder klonen lässt. 14 Tage Rotation wie beim DB-Backup,
Schalter und "Repos jetzt sichern" im System-Tab. Nutzt den bereits
vorhandenen Gitea-Token; Dockerfile installiert dafür git mit.
- Gitea-Theme im D4RKST3R-Look (docs/gitea-theme/): Neon-Gelb/Orange auf
Schwarz, gebaut gegen die Variablen von Gitea 1.26, Diff-Farben bleiben
lesbar. Einbau-Anleitung liegt daneben.
- Auto-Deploy statt manuellem "Pull and redeploy": Workflow für Gitea
Actions (prüft Server-Syntax und Frontend-Build, bevor deployed wird)
plus dokumentierter Runner-loser Weg über den Portainer-Webhook.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Dienste (Gitea, Kanban, Cloud …) werden im System-Tab gepflegt und
erscheinen als Kacheln auf der Startseite; GET /api/services ist
öffentlich, Pflege braucht den settings-Scope
- /brand.css liefert die Design-Tokens (Farben live aus dem Brand-Tab,
ändern sich damit überall mit) plus Basis-Klassen d4rk-card,
d4rk-btn, d4rk-title, d4rk-tag
- /brand-nav.js baut die D4RKST3R-Leiste in jede fremde App ein, mit
Links zum Hub und zu allen gepflegten Diensten; die Links stecken
fertig im Skript, dadurch kein zweiter Request und kein CORS nötig
- Beide Dateien mit offenem CORS-Header und 5 Minuten Cache
- Doku für Anbindung + Schriften in docs/sso.md ergänzt
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Andere Apps (Kanban, Platform …) nutzen ab sofort den Discord-Login des
Bots mit, statt jeweils eigenes OAuth zu bauen — und bekommen die
Discord-Rollen des Users gleich mitgeliefert.
Ablauf: App leitet auf /sso/authorize weiter, der Bot prüft die Session
(ggf. erst Discord-Login) und schickt einen signierten Token zurück, den
die App serverseitig per POST /sso/verify gegen die Nutzerdaten tauscht.
Sicherheit:
- Rücksprung-Ziele müssen einem registrierten Präfix entsprechen
(kein Open Redirect, kein Token-Abgriff über fremde Hosts)
- Token HMAC-signiert, 60 Sekunden gültig, nur einmal einlösbar
- Verify braucht das App-Secret (timing-safe verglichen)
- SSO bleibt Mitgliedern des Discord-Servers vorbehalten
- App-Verwaltung ist Owner-only, Secret wird nur einmal angezeigt
Dazu: sso_apps-Tabelle, Verwaltung im API-Tab, Rücksprung nach dem
Login (return-Cookie, nur interne Pfade), Anleitung in docs/sso.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- /impressum (§ 5 DDG) und /datenschutz als öffentliche Seiten im
Brand-Look; Betreiber-Angaben (Name, Anschrift, E-Mail, Zusatz)
liegen als Settings und sind bewusst Owner-only — Team-Mitglieder
mit settings-Scope kommen an die private Anschrift nicht heran
- Die Datenschutzerklärung beschreibt die tatsächliche Verarbeitung
dieser Instanz: Server-Logs, Session-Cookies (einwilligungsfrei nach
§ 25 Abs. 2 TDDDG, kein Tracking), Discord-Login, Level, Wünsche,
Geburtstage, Playtester/Alpha-Keys, Bewerbungen, Tickets/Modmail,
Erinnerungen, Moderation und Audit-Log
- DSGVO Art. 17: DELETE /api/profile löscht XP, Geburtstag,
Playtester-Eintrag, Abstimmungen, Erinnerungen, Rollen-Sicherung,
Giveaway-Teilnahmen und gibt den Alpha-Key wieder frei; Button im
Profil unter „Meine Daten", Löschung landet im Audit-Log
- Footer verlinkt beide Rechtstexte, neues GET /api/legal (öffentlich)
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>
Der Discord-Login ist jetzt für alle Member nützlich:
- /profil: Rang + XP-Fortschritt, Discord-Rollen als Chips, Playtester-
Badge, eigener Alpha-Key mit Kopier-Button
- Rollen-Selfservice: veröffentlichte Rollen-Menüs direkt im Browser
togglen (gleiche Exklusiv-Logik wie die Discord-Buttons; nur Rollen
aus aktiven Menüs erlaubt)
- Wunsch-Voting im Web: auf /roadmap Wünsche einreichen (postet wie
/wunsch in den Voting-Kanal) und upvoten (wish_votes-Tabelle,
ein Vote pro Member, unabhängig von Discord-👍)
- Member-Gate: Login nur für Mitglieder der konfigurierten Guild;
Abgewiesene sehen einen Banner mit Discord-Invite-Link (neue
Settings member_gate_enabled + discord_invite_url im System-Tab)
- Membership-Check mit 5-Minuten-Cache, Owner immer erlaubt
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>
- Feature-Tabellen (Discord + Webinterface + Setup-Seite)
- Ersteinrichtung gestrafft: Discord-App inkl. aller nötigen Permissions,
Env-Tabelle mit Pflicht/Optional, Gitea-Webhook mit allen drei Trigger-Events
- Deployment (Portainer inkl. Re-pull-Hinweis), lokale Entwicklung
- HTTP-Endpoint-Referenz und aktualisierte Projektstruktur
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>
- 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>
- POST /webhooks/devlog/<secret> — Discord-Webhook-kompatibel (JSON + Multipart),
devlog.py braucht nur die neue URL in tools/.devlog_webhook
- Bot postet Embed in Brand-Gelb mit 'D4RKST3R // DEVLOG'-Footer, bis zu 4 Bilder
als Grid (Embed-Gruppierung über gemeinsame URL)
- Direkt-Archivierung inkl. Bilder (kein Umweg über den Live-Listener)
- Neue Env-Var DEVLOG_POST_SECRET, README-Abschnitt neu geschrieben
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Discord-OAuth2-Login (identify-Scope, CSRF-State, signierte Session-Cookies, keine Token-Speicherung)
- REST-API: /api/devlogs (öffentlich), /api/commits (nur ADMIN_DISCORD_ID), /api/me
- React + Vite Frontend: Devlog-Archiv mit Mini-Markdown-Renderer, Commit-Tabelle, dunkles EcoGame-Theme
- Fastify liefert frontend/dist mit SPA-Fallback aus; Vite-Dev-Proxy für lokale Entwicklung
- Multi-Stage-Dockerfile (Frontend-Build im Image), neue Env-Vars in Compose + .env.example
- README: OAuth2-Setup (Redirect-URLs, Client Secret) und Frontend-Workflow
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>
- docker-compose.yml: Variablen per Interpolation (lokal aus .env, in Portainer aus Stack-Env)
- tools/test-webhook.mjs: signierte Fake-Testzustellung gegen die lokale Instanz
- README: Portainer-Anleitung präzisiert (Git-Auth für privates Repo, Pull and redeploy)
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>