Commit Graph
79 Commits
Author SHA1 Message Date
D4rkst3randClaude Opus 5 edf8fa14c5 Ideen bekommen Bereiche und einen Thread zum Reden
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
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>
2026-08-01 23:31:55 +02:00
D4rkst3randClaude Opus 5 fb61e7279d Wuensche bekommen einen Ausgang statt nur einer Punktzahl
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
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>
2026-08-01 23:18:29 +02:00
D4rkst3randClaude Opus 5 1c5c7d2470 Playtester werden jetzt beworben, nicht angeklickt
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
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>
2026-08-01 10:04:16 +02:00
D4rkst3randClaude Opus 5 ddd7610d6c Playtester bekommen einen eigenen Bereich, Module ziehen um
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
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>
2026-08-01 09:49:27 +02:00
D4rkst3randClaude Opus 5 bb2aee7818 Tickets: Anliegen zur Auswahl, und das Team kann uebernehmen
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
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>
2026-08-01 09:14:49 +02:00
D4rkst3randClaude Opus 5 e32152b5ac Eigener Herzschlag: der Bot kann jetzt auch ueber sich selbst Auskunft geben
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Der Waechter prueft alles ausser sich selbst — ist der Bot weg, prueft
niemand mehr, und hinterher sieht der Verlauf aus, als waere nie etwas
gewesen. Im Dashboard stand deshalb nur "seit 12d 4h", also wie lange es
diesmal gutgegangen ist. Was davor war, wusste niemand.

Jetzt schreibt der Bot alle zwei Minuten eine Zeile: laeuft der Prozess, und
steht die Verbindung zu Discord? Ein Bot, der laeuft aber nicht verbunden
ist, ist fuer alle draussen genauso weg — deshalb zaehlt beides.

Der Takt ist absichtlich nicht einstellbar. Ein spaeter geaenderter Abstand
wuerde den alten Verlauf falsch aussehen lassen: wie viele Proben eine
Stunde haette haben muessen, liesse sich rueckwirkend nicht mehr sagen.

Ausgewertet wird vor allem die Abwesenheit. Fuer einen fremden Dienst heisst
eine fehlende Stunde "niemand hat gemessen" und wird grau; beim Bot selbst
ist genau das die Aussage — fehlende Stunden werden rot. Grau bleibt nur,
was vor der allerersten Aufzeichnung liegt. Die angebrochene erste und die
laufende letzte Stunde rechnen anteilig, sonst waeren sie dauerhaft gelb.

Nebenbei mitgenommen:

- Statusseite und die frei angelegten Seiten stehen jetzt in der Fusszeile
  beider Seiten — dort sucht man Rechtliches. Die Navigationsleiste zeigt
  weiterhin nur, was "im Menü" gesetzt hat; wer die Nutzungsbedingungen dort
  raus nimmt, hat sie trotzdem noch in der Fusszeile.
- Die Config-Spalte hatte keine Obergrenze. min-width:0 allein reicht nicht,
  wenn ein Kind eine grosse Mindestbreite mitbringt — mit dem Balken wuchs
  sie auf 724 statt 335 px, und weil der Body waagerecht abschneidet, war
  der Ueberstand nicht scrollbar, sondern weg.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 07:10:53 +02:00
D4rkst3randClaude Opus 5 3080f52e71 AutoMod sichtbar machen: Treffer ins Protokoll, Regeln ins Panel
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Discords eigener Filter arbeitet serverseitig und ohne Latenz — daran ist
nichts zu verbessern. Was fehlte, war die Sicht darauf: Treffer landeten nur
in Discords eigenem Kanal, und die Regeln pflegte man in einer ganz anderen
Oberflaeche als alles andere.

Jetzt spiegelt der Bot jeden Treffer als Embed ins Mod-Log — mit Nutzer,
Regel, Ausloeser, Massnahme, getroffenem Wort und gekuerztem Wortlaut, ohne
Erwaehnungen, die den Kanal anpingen. Im Panel steht unter Support jede Regel
mit Ausloeser, Massnahmen und der Zahl ausgenommener Rollen und Kanaele —
letzteres oft der Grund, warum eine Regel scheinbar nicht greift. Schalten
geht dort direkt.

Das Bearbeiten der Wortlisten bleibt bewusst in Discord; dort ist es gut
geloest, und ein zweiter Ort dafuer waere nur eine weitere Stelle, an der
etwas auseinanderlaeuft.

Dazu noetig: der Gateway-Intent AutoModerationExecution. Nicht privilegiert,
aber Discord schickt die Ereignisse nur an Bots mit "Server verwalten" —
fehlt das Recht, sagt das Panel es statt still leer zu bleiben.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 06:53:33 +02:00
D4rkst3randClaude Fable 5 c8b1f604f0 Umfragen: /umfrage als native Discord-Abstimmung
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Bewusst Discords eigenes Poll-Objekt statt einer Nachbildung mit Knoepfen —
Oberflaeche, Auszaehlen und Ergebnisanzeige macht Discord dann selbst, auch
auf dem Handy. Wir bauen nur die Frage zusammen.

Bis zu vier Antworten (Discord erlaubt zehn, vier halten den Befehl
uebersichtlich), Laufzeit von einer Stunde bis einer Woche, Mehrfachauswahl
optional. Rechte: ManageMessages.

Zwei gleiche Antworten werden abgefangen. Discord stoert das nicht, aber
abstimmen kann darauf niemand sinnvoll.

Nicht zu verwechseln mit /wunsch: das sammelt Feature-Wuensche dauerhaft und
zeigt sie auf der Roadmap. /umfrage ist die schnelle Frage zwischendurch und
laeuft von selbst ab. Steht so auch als Kommentar im Befehl.

Geprueft gegen die installierte discord.js-Fassung (14.27) statt gegen die
Dokumentation: Feldnamen aus den Typdefinitionen gelesen, und die fertige
Nutzlast durch discord.js' eigene Serialisierung gejagt. Kommt als
poll_media / allow_multiselect / layout_type 1 raus, genau wie Discords API
es erwartet.

Dazu: 18 Befehle laden ohne Namenskollision, Befehl steht auf der
oeffentlichen Liste in beiden Sprachen, Zahl auf der Produktseite von 17
auf 18.

Nutzungsbedingungen als Entwurf unter docs/ — Text fuer den Seiten-Editor,
keine Rechtsberatung.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 06:34:04 +02:00
D4rkst3randClaude Fable 5 d9f13b4d92 Leere Seiten behoben: IconLink war benutzt, aber nicht importiert
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Mit dem Linked-Roles-Modul kam ein Symbol in die Icon-Zuordnung, dessen
Import ich vergessen habe. Das ist kein fehlender Import, sondern eine
undefinierte Variable — der Build laeuft sauber durch, und erst im Browser
wirft das Modul beim Laden einen ReferenceError. React mountet dann gar
nicht: weisse Seite auf Hub, Bot-Seite und Panel gleichzeitig.

Geprueft: Startseite rendert wieder (12050 Zeichen im Root), und der
Modul-Ueberblick, der die Icons wirklich zeichnet, liefert 36 Karten mit
100 Symbolen ohne Konsolenfehler.

Der Build schuetzt hier nicht — vor dem naechsten Ausrollen einer
Frontend-Aenderung gehoert ein Blick in die laufende Seite dazu, nicht nur
ein gruener Build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 06:04:56 +02:00
D4rkst3randClaude Fable 5 137452da11 Linked Roles: Rollen, die an geprueften Werten haengen
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Neues Modul, standardmaessig aus. Ein Mitglied klickt in Discord auf "Rolle
holen", landet auf /linked-roles, bestaetigt den OAuth-Dialog — und der Bot
schiebt ihm drei Kennzahlen zu, die Discord dann gegen die Rollen-Einstellung
prueft:

  level        Zahl >= eingestelltem Wert
  dabei_seit   Datum <= "vor X Tagen", also Mitglied seit mindestens X Tagen
  playtester   Wahrheitswert

Der Bot vergibt dabei keine Rollen. Er liefert nur die Zahlen; was daraus
wird, entscheidet die Rollen-Einstellung im Server. Die Daten kommen aus dem,
was ohnehin da ist: Level-Tabelle, Playtester-Liste, Discords Beitrittsdatum.

Eigener OAuth-Weg statt /auth/login, weil die Berechtigungen andere sind —
role_connections.write hat der normale Login nicht.

Die Tokens werden gespeichert, und das ist keine Bequemlichkeit: ohne
Refresh-Token bliebe jede Rolle auf dem Stand des Verknuepfungs-Tages stehen,
ein Level-Up kaeme nie an. Discords eigene Anleitung sagt dasselbe. Der
Umfang ist eng (nur identify + role_connections.write), beim Widerruf oder
Entkoppeln fliegt der Datensatz sofort raus. Steht so auch als Kommentar an
der Tabelle.

Abgeglichen wird alle 6 Stunden, einstellbar. Dafuer kann everyTuned jetzt
auch Stunden — und wirft bei einer unbekannten Einheit, statt den Abstand
still um Faktor 60 oder 3600 danebenzulegen.

Beim Bauen gefunden: moduleEnabled fehlte im Import von client.js. Der Bot
waere beim ClientReady mit ReferenceError gestorben.

Geprueft: alle drei Kennzahlen gegen Discords Formatgrenzen (Schluessel-Regex,
Laengen, gueltige Typ-Codes), Modul- und Stellwert-Register, die
Stunden-Umrechnung (6 h = 21600000 ms), Speichern und Loeschen einer
Verknuepfung, dazu 186 SQL-Abfragen und alle Routen auf Rechtepruefung.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 05:57:57 +02:00
D4rkst3randClaude Fable 5 55e9ef57e2 Oeffentliche Statusseite nach Statuspage-Vorbild
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
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>
2026-08-01 05:45:50 +02:00
D4rkst3randClaude Fable 5 fd2d31f07f Watchdog: Embeds statt Textzeilen, dazu eine Uebersicht im Panel
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
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>
2026-08-01 05:28:22 +02:00
D4rkst3randClaude Fable 5 499761a0e2 Test-Harness aus dem Repo nehmen und ignorieren
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Die Datei stubbt die API, damit das Panel ohne laufenden Bot rendert. Sie
wird beim Entwickeln erzeugt und ist mir beim letzten Commit mit reingerutscht.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 04:38:39 +02:00
D4rkst3randClaude Fable 5 55085d7e2b NaN im Dashboard: zwei Zeitstempel-Formate, ein Parser
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Bei Devlogs und Backup stand "vor NaN Tagen". Ursache war meine Annahme, die
Datenbank speichere Zeit durchgehend im SQLite-Format. Stimmt nur zur Haelfte:

  audit.ts             "2026-07-30 12:34:56"       datetime('now'), UTC ohne Zone
  devlogs.posted_at    "2026-07-30T12:34:56.789Z"  toISOString()
  Einstellung last_backup                          ebenso

Mein Code haengte immer ein Z an. Beim ersten Format ist das noetig, sonst
liest der Browser die Zeit als Ortszeit. Beim zweiten ergibt es "...ZZ" und
damit ein ungueltiges Datum — daher das NaN. Auf der Team-Seite fiel es nicht
auf, weil dort nur das Protokoll steht, und das hat das erste Format.

Jetzt erkennt alsDatum() die Zone am String und ergaenzt nur, was fehlt.
Unlesbares ergibt null statt NaN, die Anzeige faellt dann auf ihren Ersatztext
zurueck. Die Funktion liegt einmal auf Modulebene und wird von beiden Stellen
benutzt, damit sie nicht wieder auseinanderlaufen.

Geprueft mit acht Faellen direkt gegen die ausgelieferte Funktion, nicht gegen
eine Kopie: beide echten Formate ergeben denselben Zeitpunkt, dazu Offset
statt Z, und null/leer/Unsinn ergeben null. Danach im Browser mit dem Format
gegengeprueft, an dem es wirklich gescheitert ist — der erste Testlauf lief
noch mit dem SQLite-Format und waere gruen gewesen, ohne etwas zu beweisen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 04:38:11 +02:00
D4rkst3randClaude Fable 5 91397d8a44 Raid-Sperre von Hand setzen, aufheben oder nachsehen
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
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>
2026-08-01 04:32:39 +02:00
D4rkst3randClaude Fable 5 aef708c405 Raid-Schutz: erkennt Beitritts-Wellen und sperrt voruebergehend
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
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>
2026-08-01 04:25:25 +02:00
D4rkst3randClaude Fable 5 fb7ee78548 Status-Seite wird ein Dashboard
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Statt sieben flacher Kacheln jetzt: Kopfzeile mit Bot-Identitaet und Laufzeit,
vier Kennzahlen mit Nebenzeile, Aktivitaetsverlauf und Modul-Zustand pro
Bereich. Die Bereichs-Zeilen sind klickbar und springen in den Modul-Ueberblick.

Fast alles war schon da und musste nur zusammengestellt werden. Neu am Server
sind zwei Zeitstempel in archiveStats(): lastDevlog und lastCommit. Reine
Zaehler sagen nicht, ob noch etwas passiert — "142 Devlogs, zuletzt vor zwei
Tagen" schon.

Der Mitglieder-Zuwachs kommt aus joins/leaves der Tagesdaten, der Verlauf
fuellt fehlende Tage mit 0 statt sie wegzulassen, sonst staucht er sich.

Beim Bauen gefunden: die Spalte in commits heisst committed_at, nicht ts. Da
better-sqlite3 beim Vorbereiten sofort auswertet, waere der Bot beim Start
abgestuerzt. Beide neuen Abfragen gegen das echte Schema geprueft.

Dazu ein Dev-Server-Eintrag in launch.json: der Build ist minifiziert, im
Fehlerfall steht dort nur "at bp". Mit dem Dev-Server kommt der Komponenten-
und Zeilenverweis — genau das hat den Weg zur Ursache abgekuerzt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 04:19:51 +02:00
D4rkst3randClaude Fable 5 1e2d295665 Erreichbarkeits-Balken auf der Server-Seite
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Ein Strich pro Stunde ueber 7 Tage, gruen/gelb/rot je nachdem, ob in der Stunde
alle, ein Teil oder keine Messung erreichbar war.

Die Daten lagen laengst da: player_history haelt 7 Tage vor, die API schnitt
aber auf 24 Stunden ab. Statt Rohdaten (ueber eine Woche rund 5000 Zeilen pro
Server) fasst uptimeBuckets() in SQL stundenweise zusammen — hoechstens 168
Zeilen.

- Stunden ohne Messung werden als Luecke gezeichnet, nicht als "online". Sonst
  saehe eine Bot-Auszeit aus wie ein einwandfrei laufender Server.
- Die alte 24h-Uptime-Zahl neben dem Peak ist raus: zweimal dieselbe Groesse
  ueber verschiedene Zeitraeume verwirrt nur.
- Tooltip zeigt Ortszeit. Die Eimer werden in UTC abgeglichen (SQLite rechnet
  so), angezeigt wird lokal — sonst stuende dort eine Stunde, die niemand
  wiedererkennt.

Zwei Fehler beim Bauen gefunden und behoben:
- .srv-card ist ein Grid-Item mit min-width:auto und wuchs durch die 168
  Striche auf 724 statt 335 px, die Karte sprengte auf dem Handy das Raster.
- "Uhr" stand hart deutsch im Tooltip.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 04:09:19 +02:00
D4rkst3randClaude Fable 5 c0a56c1eb5 Logo auf der Willkommens-Karte und in geteilten Links
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
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>
2026-08-01 03:55:28 +02:00
D4rkst3randClaude Fable 5 33c48cb898 Logo einbinden: Favicons, Apple-Icon und Marken-Quellen
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Das Logo lag bisher nirgends im Einsatz — das Favicon war ein handgeschriebenes
Prisma in Gold/Orange, das Wasserzeichen der Willkommens-Karte der blosse
Markenname als Text.

- docs/brand/ mit lockup.png (volles Logo) und mark.png (nur der Wolfskopf)
  plus build.py, das beide und alle abgeleiteten Icons erzeugt
- frontend: favicon.svg ersetzt, apple-touch-icon.png dazu und verlinkt
- Zwei Fassungen, weil das Lockup bei 16 px zu einem orangen Balken wird; der
  Kopfzuschnitt wurde gegen drei Varianten geprueft
- SVG mit eingebettetem PNG statt Vektorisierung: das Original hat Verlaeufe
  und Schattierung, nachgezeichnet wuerde es matschen
- Die Icons auf dunklem Grund sind deckend, sonst legt iOS hinter transparente
  Lesezeichen Schwarz oder Weiss und der Wolf ist selbst fast schwarz

Geprueft: beide Dateien werden mit korrektem MIME-Typ ausgeliefert und das SVG
rendert wirklich (128x128), die Link-Tags stehen im gebauten HTML.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 03:40:19 +02:00
D4rkst3randClaude Fable 5 5360c8229f Englisch als zweite Sprache fuer die oeffentlichen Seiten
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Hub und Bot-Produktseite lassen sich jetzt auf Englisch umstellen. Die Wahl
haengt am Browser (Deutsch nur, wenn der Browser es will) und bleibt im
localStorage stehen.

- src/i18n.jsx: Context mit t(), tOr(), Datums- und Zahlenformat der Sprache.
  Keine Bibliothek, die Woerterbuecher werden mitgebaut — kein Nachlade-Blitzer.
- Platzhalter im %s-Stil, dieselbe Schreibweise wie in den Bot-Texten.
- Umschalter in der Navigation, zusaetzlich im Schubladen-Menue: .auth ist auf
  schmalen Schirmen ausgeblendet, sonst waere der Wechsel dort unerreichbar.
- Datums- und Zahlenformat folgen mit (31.07.2026 / 31/07/2026, 45.231 / 45,231).
- Die Funktionsliste kommt weiter deutsch vom Server; englische Modulnamen
  liegen unter feat.<id> im Woerterbuch. Fehlt einer, bleibt der Servertext
  stehen — ein neues Modul verschwindet nie von der Seite.

Das Config-Panel und die Discord-Texte bleiben deutsch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 22:41:32 +02:00
D4rkst3randClaude Fable 5 570e45fe60 Team-Bereich aufgehuebscht: Avatare, Rechte-Chips, Protokoll mit relativer Zeit
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Der Team-Reiter war funktional, aber roh: Rechte standen als "content,community"
da, es gab keine Gesichter, und das Protokoll war eine Wand aus Zeitstempeln.

- /api/webadmins und /api/auditlog liefern jetzt Avatar und Anzeigename aus dem
  Member-Cache — kostet keinen zusaetzlichen Discord-Aufruf
- Owner-Zeile mit eigenem Chip "Alle Bereiche", damit klar ist, wer alles darf
- Bereiche als lesbare Chips statt roher IDs; die Auswahl beim Anlegen sind
  klickbare Karten mit Beschreibung
- Protokoll zeigt relative Zeit ("vor 37 Min"), erst 12 Zeilen, Rest auf Klick
- Toenungen ueber color-mix aus --neon: stimmt im Hub (gold) wie auf der
  Bot-Seite (orange)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 22:25:51 +02:00
D4rkst3randClaude Fable 5 c026b2a603 Wichtige Funktionen direkt in der Seitenleiste
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
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>
2026-07-31 22:08:42 +02:00
D4rkst3randClaude Fable 5 de4e812896 Werte für mehrere Funktionen, und Anlegen frischt die Auswahl auf
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Zwei Nachzügler zu den Modul-Seiten.

Die Thread-Archivdauer gilt für Tickets und Modmail, hing aber nur an
Tickets — auf der Modmail-Seite fehlte sie also. `module` darf jetzt eine
Liste sein; nach außen kommt immer eine, damit niemand zwischen einem und
mehreren unterscheiden muss.

Und beim Anlegen aus dem Panel wurde nur die Einstellung gesetzt, nicht die
Kanalliste nachgeladen. Der frische Kanal stand damit zwar in der
Einstellung, fehlte aber in der Auswahl — das Feld sah leer aus, obwohl es
gesetzt war. Aufgefallen im Browser-Test: „Einsatzbereit" stand da, die
Auswahl daneben war leer. Jetzt holt reloadSettings Kanäle, Rollen und
Einstellungen frisch; das gilt für den Einzelknopf, den Assistenten und die
Modul-Seiten gleichermaßen.

Nachgeprüft: Kanal über die Status-Liste anlegen, dann ohne Neuladen nach
Feeds wechseln — dort steht #dev-log.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 22:00:36 +02:00
D4rkst3randClaude Fable 5 c88926aba0 Modul-Seiten: alles zu einer Funktion an einem Ort
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
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>
2026-07-31 21:54:31 +02:00
D4rkst3randClaude Fable 5 bea35234c6 „Einrichten" zeigt jetzt, was fehlt — und Env-Werte gelten als gesetzt
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Zwei Fehler, die zusammen für Ratlosigkeit gesorgt haben.

In der Liste stand „Fehlermeldungen — Gitea-Token", obwohl das Token über
GITEA_API_TOKEN längst gesetzt war und /bug lief. Grund: die
Voraussetzungs-Prüfung las die Einstellung roh statt den wirksamen Wert,
sah also nur in die Datenbank und nicht in die Umgebung. Betraf außerdem
Devlog-Kanal, Commit-Kanal und Guild-ID. runtime-settings hat dafür jetzt
effectiveSetting(), das beides zusammenführt.

Und wer dann auf „Einrichten" drückte, landete im System-Bereich — dort
gibt es gar kein Token-Feld, das steht bei Brand. Das Modul zeigte auf den
falschen Bereich.

Jetzt springt „Einrichten" auf das konkrete Feld und hebt es kurz hervor,
statt nur den Bereich zu wechseln. Der Bereich kommt dabei aus dem
Einstellungs-Register, das ohnehin weiß, wo jedes Feld liegt — damit kann
die Zuordnung am Modul nicht mehr auseinanderlaufen. Dieselbe Logik hängt
an „Einstellungen →" auf den Modul-Karten.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 21:43:34 +02:00
D4rkst3randClaude Fable 5 01fadadcd8 Grundgerüst-Assistent: alle fehlenden Kanäle auf einmal
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
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>
2026-07-31 21:17:36 +02:00
D4rkst3randClaude Fable 5 684573b88c Fehlende Kanäle und Rollen aus dem Panel anlegen
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
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>
2026-07-31 18:29:54 +02:00
D4rkst3randClaude Fable 5 0240225165 Config-Suche findet einzelne Einstellungen
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
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>
2026-07-31 18:22:35 +02:00
D4rkst3randClaude Fable 5 1c3fda8d64 Config: Speichern-Leiste und durchsuchbare Kanal-Auswahl
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Der Speichern-Knopf stand am Seitenende und war bei den zweispaltigen
Bereichen oft gar nicht zu sehen. Vor allem gab es keinen Hinweis, dass
etwas offen ist — wer einen Kanal umstellte und neu lud, verlor die
Änderung kommentarlos.

Jetzt erscheint unten eine Leiste, sobald sich etwas vom gespeicherten
Stand unterscheidet: Anzahl der Änderungen, Verwerfen, Speichern. Beim
Verlassen mit offenen Änderungen fragt der Browser nach. Der Vergleich
zieht Bot-Name und -Beschreibung mit ein, die außerhalb des Formulars
gepflegt werden.

Die 27 Kanal- und Rollen-Auswahlen sind keine nativen Dropdowns mehr,
sondern dieselbe durchsuchbare Liste wie bei den gamedig-IDs: tippen
filtert, Enter nimmt den ersten Treffer. Bei zwölf Kanälen merkt man das
schon, bei fünfzig ist es der Unterschied.

Beim Testen zwei echte Fehler gefunden, beide älter als diese Änderung:

Nicht gesetzte Kanäle kommen als null aus der API. String(null) ist
"null" — also eine Kanal-ID, die es nie gibt. Auf einer frischen
Installation scheiterte damit das Speichern *jeder* Einstellung, solange
Devlog- oder Commit-Kanal leer waren. Sichtbar wurde das erst, weil die
Leiste jetzt überall auftaucht statt nur auf vier Bereichen.

Außerdem wurde jeder Kanal im Formular geprüft, auch die unveränderten.
Ein inzwischen gelöschter Kanal hätte damit das Speichern aller anderen
Einstellungen dauerhaft blockiert. Geprüft wird jetzt nur, was sich
gegenüber dem ausgelieferten Stand wirklich ändert.

Nachtrag zum Selbsttest: der erste Versuch, die Auswahlen per Regex zu
ersetzen, lief mit DOTALL und griff dadurch über <select>-Grenzen hinweg —
zwei fremde Dropdowns wurden zerlegt. Der Build hat es gemeldet, die Datei
wurde zurückgesetzt und der Ersatz zeilenweise wiederholt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 18:11:31 +02:00
D4rkst3randClaude Fable 5 2e58268f8d Bestenliste, Devlog-Filter, Galerie und leere Bereiche
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Vier Stellen, die beim Durchsehen der Seiten aufgefallen sind.

Die Bestenliste zeigte nur Namen — und für Platz 1 bis 3 denselben Pokal
ohne Nummer, wodurch die Reihenfolge nur an der Position erkennbar war.
Jetzt: Rangnummer, Avatar und die ersten drei Plätze farblich abgesetzt.
Die Avatare kommen aus dem Member-Cache, der durch den GuildMembers-Intent
ohnehin gefüllt ist — das kostet keinen einzigen Discord-Aufruf. Wer nicht
im Cache liegt, bekommt seinen Anfangsbuchstaben statt eines leeren Kreises.

Die Projekt-Chips im Devlog-Archiv waren bloß Beschriftung. Jetzt filtern
Chips mit Anzahl nach Projekt (ECOGAME / D4RKBOT) — man will meist nur
eins von beiden lesen. Serverseitig über LIKE auf die erste Zeile, wo das
Projekt ohnehin steht; das spart eine Spalte und eine Migration. Eine
aktive Suche schlägt den Filter, sonst müsste die Volltextsuche beides
verbinden, ohne dass jemand danach fragt.

Galerie: Vorschaubilder von 240 auf 340 Pixel, und Name plus Datum stehen
dauerhaft am Bild statt nur beim Überfahren — am Handy gab es kein Hover
und damit nie eine Zuordnung.

Events, Changelog, Galerie und Level endeten bei wenig Inhalt mit einem
einzelnen Satz in einer sonst leeren Seite; das sah nach Fehler aus statt
nach „noch nichts da". Jetzt steht dort ein Block mit Symbol, Erklärung
was hier erscheinen wird, und Verweisen auf belebte Bereiche.

Dazu ein Sprung-nach-oben-Knopf im Devlog-Archiv und im Changelog, die
beide sehr lang werden.

Beim Testen gefunden: ESCAPE '\' in einem JS-String kommt als leeres
Zeichen in SQLite an ("ESCAPE expression must be a single character") —
der Backslash müsste doppelt entwertet werden. Jetzt ist das Escape-Zeichen
ein Ausrufezeichen, das die Falle gar nicht erst aufmacht.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 17:44:57 +02:00
D4rkst3randClaude Fable 5 c6977f9c4a Modul-Tab im Plugin-Raster: Filter-Reiter und Symbole
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Vorbild war das MEE6-Dashboard: Kategorie-Reiter über einem Raster aus
Karten, jede mit einem eigenen Symbol. Inhaltlich war unser Modul-Tab
schon dort — es fehlten genau diese zwei Dinge.

Die Gruppen standen als Überschriften untereinander, was bei 35 Karten
eine lange Rolle ergab. Jetzt filtern Reiter mit Anzahl: Alle, die vier
Gruppen, dazu „Noch einzurichten" und „Ausgeschaltet". Die beiden letzten
sind bewusst dabei — mit genau diesen Fragen kommt man am häufigsten her.
Die Gruppe steht klein unter dem Namen, damit sie im gefilterten Raster
nicht verlorengeht.

Jede Karte hat eine Symbol-Kachel, die bei laufendem Modul in der
Markenfarbe leuchtet und sonst grau bleibt — man erkennt eine Funktion
damit am Bild statt am Lesen. Die Zuordnung lag schon in der
Funktionsseite; sie liegt jetzt in module-icons.jsx, damit beide Seiten
dieselbe benutzen.

Farbige Kachelbilder wie bei MEE6 gibt es bewusst nicht: die Strichsymbole
passen zum Rest der Oberfläche, bunte Kacheln wären ein Fremdkörper.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 17:22:04 +02:00
D4rkst3randClaude Fable 5 197c2a1ada Werte: 19 feste Zahlen im Panel einstellbar
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
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>
2026-07-31 16:49:34 +02:00
D4rkst3randClaude Fable 5 fb927d4399 Willkommens-Karte einstellbar: Farben, Bild, Texte, Live-Vorschau
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
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>
2026-07-31 16:41:05 +02:00
D4rkst3randClaude Fable 5 90361fa55f Config-Layout: breiter, zweispaltig, Schalter lesbar, Texte aufklappbar
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Drei Sachen, die die Seite erschlagend gemacht haben.

Die Schalter klebten am Text, weil `.field > label` spezifischer ist als
`.toggle` — die Regel hat den Schalter zu einem Block in Versalien gemacht
und Beschriftung plus Hinweis in eine Zeile gequetscht. Jetzt steht der
Schalter links, Beschriftung und Hinweis darüber/darunter, in normaler
Schrift. Der Schalter ist außerdem so groß wie der auf den Modul-Karten.

Der Inhalt war auf 1060px begrenzt, obwohl hier Formulare stehen und keine
Fließtexte. Jetzt 1440px, und die reinen Formular-Tabs (Feeds, Community,
Support, System) stellen ihre Blöcke ab 1180px nebeneinander. Feeds ist
damit 1448 statt 2453 Pixel hoch. Tabs mit Editor oder Vorschau —
Composer, Seiten, Server, Brand — bleiben einspaltig.

Der Texte-Tab zeigte 27 offene Textfelder gleichzeitig. Jetzt ist es eine
Liste zum Aufklappen: pro Zeile Name und erste Zeile des Texts, geöffnet
wird eine nach der anderen. Ungespeicherte Änderungen sind an der Marke
und einem Balken am Rand erkennbar.

Nebenbei: eine geänderte Adresse (#feeds) wechselte den Bereich nicht,
weil niemand auf hashchange gehört hat. Aufgefallen ist das erst, weil ein
Test durch reine Hash-Navigation stillschweigend auf dem alten Tab blieb.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 16:33:09 +02:00
D4rkst3randClaude Fable 5 a32c267dda Feature-Seite: alle Funktionen aus dem Modul-Register
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
/features zeigte bisher dieselbe Landing wie /, obwohl die Navigation und
ein Knopf auf der Startseite eine echte Funktionsliste versprachen.

Die Seite listet jetzt alle 35 Bot-Funktionen, nach denselben Gruppen
sortiert wie im Panel, dazu sechs Punkte zum Drumherum (Webinterface,
Texte, API/SSO, eigene Seiten, Team-Rechte, Betrieb).

Die Einträge kommen über das neue öffentliche /api/features aus
src/modules.js — derselben Quelle, aus der die Modul-Schalter kommen. Eine
neue Funktion erscheint damit automatisch auf der Produktseite, statt dass
jemand daran denken muss. Der Endpunkt gibt bewusst nur Name, Beschreibung
und Gruppe heraus, nicht welche Module hier gerade laufen.

Auch die Zahl auf der Startseite kommt von dort, damit nicht zwei Stellen
dieselbe Zahl pflegen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 16:17:50 +02:00
D4rkst3randClaude Fable 5 2ce274707a Config statt Setup: Bereiche gruppiert, Suche, Status als Einstieg
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
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>
2026-07-31 16:13:33 +02:00
D4rkst3randClaude Fable 5 dadd75d452 Texte: 27 Bot-Nachrichten im Panel bearbeitbar
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
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>
2026-07-31 16:06:27 +02:00
D4rkst3randClaude Fable 5 59aad27534 Modul-System: alle 35 Funktionen im Panel ein- und ausschaltbar
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
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>
2026-07-31 15:53:44 +02:00
D4rkst3randClaude Fable 5 478e47684a Login über beide Domains reparieren, Hub-Link auf der Bot-Seite korrigieren
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Zwei Fehler aus dem ersten Praxistest:

1. "Ungültiger OAuth-State" beim Anmelden auf dem Hub. Der Login startete
   auf hub.d4rkst3r.de, aber die Rücksprung-Adresse für Discord war fest
   auf die öffentliche URL gesetzt — Discord schickte also zur Bot-Domain
   zurück. Das Schutz-Cookie gegen Sitzungsübernahme gilt aber nur für die
   Domain, auf der es gesetzt wurde, und war dort nicht lesbar.
   Jetzt bleibt der ganze Ablauf auf der Domain, von der er gestartet ist.
   Damit entfällt auch der Umweg über ein zusätzliches Cookie, das sich die
   Ursprungsseite merken sollte.

2. Der Link "zum Community-Hub" auf der Bot-Seite zeigte auf die Bot-Seite
   selbst — er benutzte die alte öffentliche URL statt der Hub-Adresse.
   /api/legal liefert jetzt beide Adressen getrennt; die Verweise zwischen
   den Seiten erscheinen nur, wenn die Domains wirklich verschieden sind.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 15:29:31 +02:00
D4rkst3randClaude Fable 5 079c75b184 Host-Routing: Bot-Produktseite und Community-Hub getrennt
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Eine Anwendung, zwei Gesichter — der Server erkennt an der Domain, welche
Seite gefragt ist, und schreibt das als data-site an den <body>. Das
Frontend rendert daraufhin entweder den Community-Hub wie bisher oder die
neue Produktseite.

- Bot-Produktseite (BotApp): Landing mit Feature-Übersicht, Befehls-
  referenz, Dashboard unter /dashboard, Orange als Leitfarbe
- Weiterleitungen: Community-Routen auf der Bot-Domain und umgekehrt
  werden dauerhaft (301) auf die richtige Adresse geschickt — geteilte
  Devlog-Permalinks laufen also nicht ins Leere. Rechtstexte bleiben auf
  beiden erreichbar.
- Open-Graph-Tags je Domain, inklusive eigener Vorschau für frei
  angelegte Seiten (Entwürfe bekommen bewusst keine)
- Login: neue Einstellung für die Cookie-Domain, damit die Anmeldung auf
  beiden Seiten gilt; nach dem Discord-Login landet man wieder auf der
  Seite, von der man gestartet ist (vorher immer auf der Hauptadresse)
- Alles greift erst, wenn beide Adressen im Setup eingetragen sind —
  bis dahin verhält sich die Anwendung unverändert

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 15:20:53 +02:00
D4rkst3randClaude Fable 5 e8158ce90b Seiten-Editor: Inhalte pflegen ohne Code-Änderung
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
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>
2026-07-31 15:08:21 +02:00
D4rkst3randClaude Fable 5 1afbe9d238 Webinterface: Emojis durch Lucide-Icons ersetzt
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Emojis werden vom Betriebssystem gerendert — auf Windows, Mac und Android
sehen sie unterschiedlich aus, und sie sind bunt. Im streng zweifarbigen
D4RKST3R-Look stachen sie entsprechend heraus.

Jetzt kommen die Symbole aus einem einzigen Set (Lucide, dünne Outlines),
zentral in src/icons.jsx gebündelt: eine Größe, eine Strichstärke, überall
gleich — und sie erben die Textfarbe, sind also Teil der Marke statt
Fremdkörper. Betroffen sind Navigation, Startseite, Server, Roadmap,
Profil, Level, Galerie, Devlogs, Lightbox, Composer und alle Setup-Tabs.

Bewusst Emojis geblieben sind:
- die Spiel-Presets der Server (landen als Icon im Discord-Embed)
- Rollen-Menü-Emojis und Platzhalter für Discord-Inhalte
- beschreibende Hinweise wie „🔔-Button am Post" oder „🟢/🔴 im Embed",
  die zeigen, wie etwas in Discord aussehen wird
- die selbst gepflegten Dienst-Icons im Portal

Dazu Detailarbeit: Server-Status als farbiger Punkt statt Emoji, Platz 1–3
der Bestenliste als Pokal in Gold/Silber/Bronze, Pagination mit Chevrons.
Bundle wächst um 19 kB (Tree-Shaking greift, nur die ~70 genutzten Icons).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 14:37:25 +02:00
D4rkst3randClaude Fable 5 b5617a9d18 Repo-Backups, Gitea-Theme und Auto-Deploy
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
- 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>
2026-07-31 14:01:31 +02:00
D4rkst3randClaude Fable 5 c73b4e1074 Portal-Dienste und geteiltes Design-System für die anderen Apps
- 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>
2026-07-31 13:28:50 +02:00
D4rkst3randClaude Fable 5 70cae85064 Single Sign-On: der Bot als Identity-Provider für die anderen Dienste
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>
2026-07-31 13:24:03 +02:00
D4rkst3randClaude Fable 5 8853bce2ec Impressum, Datenschutzerklärung und DSGVO-Löschfunktion
- /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>
2026-07-31 13:19:33 +02:00
D4rkst3randClaude Fable 5 676fc45bb1 Willkommens-Karten, Geburtstage, Devlog-Permalinks mit OG-Vorschau, Auto-Publish
- 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>
2026-07-30 22:43:15 +02:00
D4rkst3randClaude Fable 5 ef0fb066f7 Startseite, Server-Seite v2 mit Logos, Lightbox-Fix, Navbar-Ausrichtung
- 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>
2026-07-30 22:25:30 +02:00
D4rkst3randClaude Fable 5 be11398f7c UX-Paket: Navbar aufgeräumt, /server-Seite, Lightbox, Skeletons, Audit-Log, Reroll
- 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>
2026-07-30 22:15:58 +02:00