15babcd75d4faaf45545f2b4c334cd2ccd2c8ded
162
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8981eb91b8 | Ganze Abfrage-URL einfuegen statt drei Felder tippen | ||
|
|
4e7fc99999 |
Server-Seite ueberarbeitet, und die Spielauswahl kommt jetzt aus gamedig
LS25 fehlte, aber nicht nur LS25: die Auswahl im Panel war eine von Hand gepflegte Liste mit neunzehn Eintraegen, und fuenf davon gab es bei gamedig gar nicht — "arkse", "ark", "sevendaystodie", "minecraftbe" und "terraria". Wer eines davon auswaehlte, bekam einen Server, der dauerhaft offline stand, ohne dass irgendwo ein Grund stand. Die echten IDs heissen ase, sdtd, mbe und terrariatshock. Also nicht LS25 nachtragen, sondern die Liste abschaffen: sie kommt jetzt aus gamedig selbst, 360 Eintraege, sortiert, mit Suche. Jedes Update bringt neue Spiele automatisch mit. Dazu der Standard-Query-Port als Vorschlag, sobald man ein Spiel waehlt. Bestehende Eintraege mit den toten IDs werden beim Start einmalig umgebogen. Farming Simulator hat noch eine zweite Huerde: der Server antwortet nur mit Zugangs-Code, und den hat queryServer nie durchgereicht. LS25 waere also auch mit richtiger ID offline geblieben. Jetzt gibt es eine Spalte dafuer, das Feld erscheint nur bei Spielen, die einen brauchen (Farming Simulator, Terraria — bei Satisfactory optional), und Speichern ohne Code wird abgelehnt statt stillschweigend hingenommen. Der Code steht nicht in der oeffentlichen API. Nebenbei aufgefallen: input[type=password] war im CSS nirgends erfasst. Das erste solche Feld kam voellig ungestylt daher. Die /server-Seite dazu: - Auslastungs-Balken mit eigener Farbe statt des XP-Balkens vom Level-System. Gruen frei, gelb ab 80 Prozent, rot voll — die Zahl steht rechtsbuendig, mittig laege sie bei 50 Prozent genau auf der Fuellkante. - Map, Version und Ping als Chips. Vorher standen sie mit Punkten getrennt in der Statuszeile und wurden auf dem Handy zu Brei. - Wer gerade drauf ist. Die Namen holt der Monitor ohnehin schon fuers Embed, auf der Seite standen sie nur nirgends. - Welches Spiel es ueberhaupt ist. Das stand nirgends ausser im Emoji. - Offline-Karten waren bisher leer bis auf das Wort. Jetzt steht dort, seit wann — soweit der Verlauf reicht, sonst gar nichts statt einer Schaetzung. - Zeitachse und Peak-Linie am Verlauf. Eine Flaeche ohne Bezugspunkt sagt nicht, wie hoch hoch ist. - Plaetze insgesamt in der Uebersicht oben. Geprueft: Token speichern und aendern, Spielnamen inklusive unbekannter ID, Token-Bedarf pro Protokoll, Liste ohne Doppler, alle Icon-Schluessel gegen gamedig, "zuletzt online" ignoriert Offline-Proben, und die Migration mit einer Datenbank im alten Stand. Beide Seiten am laufenden Frontend angesehen, auch auf Handybreite. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
22054c109e |
Mod-Log: eigenes Gedaechtnis, damit beim Loeschen nicht "unbekannt" dasteht
Dein Eintrag war der Normalfall, nicht die Ausnahme:
User: unbekannt
Inhalt: [kein Text / nicht im Cache]
Discord schickt beim Loeschen nur eine Huelle, sobald die Nachricht nicht mehr
im Cache des Bots liegt. Der reicht ein paar hundert Nachrichten weit und faengt
erst beim Start des Bots an — bei einem Post von gestern ist sie also nie drin.
Ein Protokoll, das genau dann nichts weiss, wenn es interessant wird, ist keins.
Also merkt sich der Bot Nachrichten jetzt kurz selbst: Verfasser, Wortlaut,
Namen der Anhaenge. Beim Loeschen steht das im Eintrag, danach wird der Merker
weggeworfen. Wie lange aufbewahrt wird, steht unter Werte — sieben Tage
voreingestellt, 0 heisst gar nicht mitschreiben. Bewusst knapp und einstellbar:
das ist ein Werkzeug zum Nachvollziehen von Loeschungen, kein Archiv.
Dazu drei Sachen, die vorher fehlten:
- Wer geloescht hat. Steht in Discords Audit-Log, aber nur wenn es jemand
anders war als der Verfasser. Gemeldet wird nur, was frisch ist und zu Kanal
und Verfasser passt — Discord zaehlt bei wiederholtem Loeschen denselben
Eintrag hoch statt neue anzulegen, da ist eine falsche Angabe schnell
gemacht. Lieber keine als eine falsche.
- Sammel-Loeschungen. /purge und Discords eigenes Aufraeumen standen gar nicht
im Log: eine geloeschte Nachricht fiel auf, hundert nicht. Jetzt mit Anzahl,
Kanal und den ersten fuenfzehn Zeilen.
- Anhaenge. Dass ein Bild dranhing, war vorher nirgends zu sehen — und das
Bild selbst ist nach dem Loeschen ohnehin weg.
Nebenbei: "[kein Text / nicht im Cache]" waren zwei Aussagen in einer und beide
unklar. Jetzt steht dort entweder der Wortlaut, oder dass die Nachricht keinen
hatte, oder dass sie aelter ist als die Aufbewahrung reicht.
Beim Testen gefunden: pruneMessageCache(0) hat nichts geloescht. "-0 days" ist
genau jetzt, und `created_at < jetzt` laesst die eben geschriebene Zeile stehen.
Bei 0 wird jetzt alles geleert, wie es gemeint war.
Geprueft: Merken, Wiederfinden, Anhaenge durch JSON, Bearbeiten zieht mit,
Vergessen nach dem Loggen, unbekannte ID, doppelte ID, Aufbewahrung mit einem
zu alten und einem frischen Eintrag, und der 0-Fall. Ereignisnamen und
AuditLogEvent gegen die installierte discord.js geprueft.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
b6b767acb7 |
Bis fuenf Eintraege Knoepfe statt Auswahlmenue
Du hattest recht. Bei vier Anliegen ist ein Auswahlmenue der schlechtere Weg: zwei Klicks statt einem, und die Moeglichkeiten sieht man erst nach dem Aufklappen. Ein Knopf ist beides — sichtbar und sofort. Umgekehrt gilt es nicht immer. Ab sechs wird die Knopfreihe zur Wand und Discord bricht sie um; dann ist das Menue das ruhigere Bild. Also nicht "Knoepfe statt Menue", sondern der Uebergang: bis fuenf Knoepfe, darueber Menue. Fuenf, weil genau so viele in eine Reihe passen. Betroffen waren nur zwei Stellen — Ticket-Anliegen und Wunsch-Bereiche. Alles andere im Bot waren ohnehin schon Knoepfe. Eine Sache verliert man dabei: die graue Beschreibungszeile, die ein Menue unter jedem Eintrag anzeigt. Die steht jetzt im Text des Aufruf-Posts, und da ist sie ehrlich gesagt besser aufgehoben — man liest sie, bevor man klickt, statt danach. Geprueft gegen discord.js selbst, weil Ueberlaengen und ungueltige Emojis sonst erst beim Posten auffallen: null Eintraege (der eine alte Knopf), eins bis fuenf (Knoepfe mit Emoji und der ID in der Kennung), sechs (zurueck zum Menue), Murks-Emoji (faellt weg, Knopf bleibt), 200 Zeichen langer Name (auf 80 gekuerzt), und die Beschreibungszeile in allen drei Faellen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
b3758f1f76 |
Screenshots zu Ideen, und der Bot legt die Forum-Tags selbst an
Zwei Fragen, zwei Antworten — eine davon ein Nein mit Umweg. ## Bilder im Eingabefenster: geht nicht Discord-Modals koennen nur Textfelder. Kein Datei-Anhang, keine Umgehung — das ist eine Grenze der Plattform, nicht unseres Codes. Der Umweg ist besser als er klingt: Wer nach dem Einreichen einen Screenshot in den Thread haengt, sieht ihn auf der Ideen-Seite. Die Bestaetigung nach dem Absenden sagt das jetzt auch. Damit kann man auch nachtraeglich noch Bilder dazulegen, was im Eingabefenster gar nicht ginge. ## Warum nicht einfach der Discord-Link Discord haengt seit einiger Zeit eine Signatur an die Anhang-Links (?ex=&is=&hm=), die nach etwa einem Tag ablaeuft. Ein gespeicherter Link zeigt morgen ein kaputtes Bild. Deshalb laden Devlogs und Galerie seit dem ersten Tag herunter — im Kopf von devlog-archive.js steht der Grund. Wuensche machen es jetzt genauso, ausgeliefert unter /wish-assets/. Beim Loeschen eines Beitrags oder eines ganzen Wunsches gehen die Dateien mit; sonst waechst der Ordner mit Bildern, auf die nichts mehr zeigt. Das Herunterladen stand vorher zweimal fast wortgleich im Code. Mit den Wuenschen waeren es drei Kopien geworden — also einmal nach bot/bilder.js, und Devlogs und Galerie ziehen mit um. Ein Fehler steckt jetzt an einer Stelle statt an dreien. ## Forum-Tags per Knopf Bisher musstest du sie von Hand anlegen. Jetzt macht es das Panel: die Bereiche von dort plus die fuenf Staende, mit ihren Emojis. Vorhandene bleiben, auch fremde — die Liste wird ergaenzt und nicht ersetzt. Discords Grenze von 20 Tags wird eingehalten, was nicht mehr reinpasst, wird gemeldet statt still verschluckt. Geprueft: forumTagsAnlegen gegen eine Kanal-Attrappe — leeres Forum, teilweise vorhandene, Gross/Kleinschreibung, fremde Tags, nichts zu tun (dann wird auch nicht geschrieben), volles Forum, kein Forum, kein Kanal. Im Browser die Ideen-Seite mit Bildern in einem Beitrag, einem Beitrag nur aus Bild ohne Text, und der Lightbox darueber. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
5073e7bcf4 |
Jede Idee bekommt ihre eigene Seite mit der Unterhaltung aus dem Thread
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> |
||
|
|
1856a1ed83 |
Foren waehlbar, Emoji-Tafel repariert, Balken sagt seit wann er misst
Drei Dinge, die beim Ausprobieren aufgefallen sind. ## Foren standen nicht zur Auswahl Ein Forum-Kanal ist fuer discord.js nicht "textBased" — er hat keine Nachrichten, nur Beitraege. Die Kanal-Liste im Panel hat genau danach gefiltert, also tauchte das Forum nirgends auf, und beim Speichern haette dieselbe Pruefung es abgelehnt. Beides kennt jetzt Foren und Medien-Kanaele; in der Auswahl stehen sie mit 📋 statt # und dem Hinweis "Forum". Das erklaert auch das "Unknown Channel" beim Testen: eingestellt war noch der alte Kanal, und der neue liess sich gar nicht erst waehlen. ## Emoji-Tafel landete in der Nachbarspalte Die Config laeuft ab genug Breite zweispaltig — CSS `columns: 2`. In einem Mehrspalten-Layout zerreisst es absolut positionierte Kaesten: die Tafel wurde fragmentiert und ihr Inhalt in der anderen Spalte gezeichnet, waehrend beim Feld nur der leere Rahmen stehen blieb. Genau so sah es auch aus. Die Tafel haengt jetzt an <body> und wird per JS an den Knopf gerechnet, wie der Dialog auch. Sie klappt nach oben, wenn unten kein Platz ist, wandert beim Scrollen mit und bleibt beim Fenstergroesse-Aendern an ihrem Feld. ## Balken sagt jetzt, seit wann er misst Die Waechter-Balken sahen leer aus. Sind sie nicht: 545 Messpunkte, alle gruen — aber eben nur von heute, weil die Dienste gestern angelegt wurden. Sechs von sieben Tagen sind zu Recht grau, das sieht nur aus wie kaputt. Steht links jetzt "misst seit 01.08." statt stur "vor 7 Tagen", sobald die erste Messung deutlich spaeter liegt als der Anfang des Fensters. Bei vollem Verlauf bleibt es bei "vor 7 Tagen". Geprueft im Browser: Tafel an <body> und `position: fixed`, richtig positioniert und nach oben geklappt, alle sieben Gruppen und 127 Symbole in einem Stueck; Forum in der Auswahlliste mit Kennzeichnung; beide Beschriftungen des Balkens (angebrochener und voller Verlauf). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
cc7a51f02e |
Emojis zum Anklicken, und Wuensche als Forums-Beitraege mit Tags
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>
|
||
|
|
edf8fa14c5 |
Ideen bekommen Bereiche und einen Thread zum Reden
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> |
||
|
|
fb61e7279d |
Wuensche bekommen einen Ausgang statt nur einer Punktzahl
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> |
||
|
|
1c5c7d2470 |
Playtester werden jetzt beworben, nicht angeklickt
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> |
||
|
|
ddd7610d6c |
Playtester bekommen einen eigenen Bereich, Module ziehen um
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> |
||
|
|
bb2aee7818 |
Tickets: Anliegen zur Auswahl, und das Team kann uebernehmen
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> |
||
|
|
2292dedeaa |
README auf den heutigen Stand bringen und neu ordnen
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> |
||
|
|
e32152b5ac |
Eigener Herzschlag: der Bot kann jetzt auch ueber sich selbst Auskunft geben
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> |
||
|
|
3080f52e71 |
AutoMod sichtbar machen: Treffer ins Protokoll, Regeln ins Panel
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> |
||
|
|
c8b1f604f0 |
Umfragen: /umfrage als native Discord-Abstimmung
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> |
||
|
|
52a735f83a |
SPA-Fallback beantwortet auch HEAD
Discord prueft die im Portal hinterlegte Datenschutz- und AGB-Adresse mit einer HEAD-Anfrage. Der Fallback behandelte nur GET, alles andere lief in reply.code(404) — Discord bekam eine 404 und lehnte die Adresse mit "URL ist nicht erlaubt" ab. Im Cloudflare-Log nachgewiesen: 35.190.130.193 (Google LLC, dort laeuft Discord), python-requests, Methode HEAD, Pfad /datenschutz, und ausdruecklich "nicht eingedaemmt" — Cloudflare hat also durchgelassen, die 404 kam von uns. Damit sind auch zwei Verdaechtigungen vom Tisch: weder Bot Fight Mode (war ohnehin aus) noch AI Crawl Control. Letzteres hatte nur meine eigenen Abrufe geblockt, was mich faelschlich glauben liess, der RSS-Feed sei fuer alle tot. Er ist es nicht. Geprueft gegen den echten Fastify-Aufbau, ueber eine echte TCP-Verbindung statt app.inject(): HEAD /datenschutz und /impressum liefern 200, text/html, korrekte Content-Length und keinen Rumpf. API-Pfade bleiben unangetastet (HEAD /api/settings weiter 401). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d9f13b4d92 |
Leere Seiten behoben: IconLink war benutzt, aber nicht importiert
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> |
||
|
|
137452da11 |
Linked Roles: Rollen, die an geprueften Werten haengen
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> |
||
|
|
55e9ef57e2 |
Oeffentliche Statusseite nach Statuspage-Vorbild
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>
|
||
|
|
fd2d31f07f |
Watchdog: Embeds statt Textzeilen, dazu eine Uebersicht im Panel
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> |
||
|
|
2d4afad3cb |
Level-Embeds aufgewertet, Server-Footer korrigiert
Server-Monitor: Der Footer behauptete fest "alle 2 min", obwohl das Intervall
seit den einstellbaren Werten aus monitor_interval kommt. Wer im Panel etwas
anderes setzt, bekam eine falsche Angabe. Liest jetzt den echten Wert.
Level-Aufstieg: War als einziges im Community-Bereich reiner Text, waehrend
Geburtstage, Verlosungen und Bewerbungen laengst Embeds sind. Jetzt ueber
brandEmbed mit Avatar, Gesamt-XP und Rest bis zum naechsten Level. Der Text
bleibt die Vorlage aus dem Panel — nur der Rahmen ist neu.
Dazu zeigt das Embed die Belohnungs-Rolle: entweder die gerade vergebene oder
die naechste, auf die man hinspielt. grantRewards() gibt dafuer zurueck, was
wirklich dazukam.
Ist die Vorlage im Panel leer, wirft setDescription('') — der Aufstieg waere
still ausgefallen. Faellt jetzt auf eine schlichte Zeile zurueck.
/rank: Avatar als Thumbnail, Podest-Zeichen fuer die ersten drei, Rest-XP und
naechste Belohnung als eigene Felder. Das Feld erscheint nur, wenn es
Belohnungs-Rollen gibt — sonst stuende dort ein leeres Feld.
Nebenbei zwei veraltete ephemeral: true auf flags: MessageFlags.Ephemeral
umgestellt, wie es der Rest der Befehle schon macht.
Geprueft: alle vier Embeds wirklich gebaut und ausgegeben, discord.js
validiert dabei. Das Intervall im Footer zog den auf 7 gestellten Wert.
Die negative XP-Differenz im ersten Testlauf war ein inkonsistenter
Testdatensatz — mit 4200 XP ist man Level 9, nicht 7; ueber 0..60000 XP
durchgespielt wird die Differenz nie negativ.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
8fa43a3e47 |
Code-Review: drei Fehler behoben
Session-Cookie ohne secure-Flag (src/web/auth.js)
Das Anmelde-Cookie war httpOnly, sameSite und signiert — aber ohne secure.
Beim ersten Aufruf ueber http, bevor der Proxy auf https umleitet, geht es
damit im Klartext mit. Auf einem fremden WLAN ist das die Sitzung.
Das Flag laesst sich nicht aus request.protocol ableiten: hinter dem
Reverse-Proxy kommt jede Anfrage als http an, trustProxy ist nicht gesetzt.
Stattdessen aus der konfigurierten Adresse des Hosts, mit derselben
Zuordnung, die der OAuth-Redirect schon benutzt. Lokal ueber
http://localhost bleibt es aus, sonst kaeme man beim Entwickeln nicht rein.
Geprueft gegen sechs Hosts: hub und bot ergeben secure, localhost nicht.
Gilt auch fuer das OAuth-State- und das SSO-Ruecksprung-Cookie.
Unbehandelte Rejection beim Start (src/bot/client.js)
registerCommands() faengt nichts ab, der Aufrufer im ClientReady-Handler
auch nicht. Seit Node 15 beendet eine unbehandelte Rejection den Prozess —
ein Rate-Limit von Discord beim Start haette den Bot also abgeschossen und
mit restart: unless-stopped in eine Neustart-Schleife geschickt. Dabei
stehen die zuvor registrierten Befehle bei Discord weiter, und alles
andere koennte laufen. Jetzt wird der Fehler geloggt und der Bot bleibt an.
Number('') ist 0 (src/bot/anti-raid.js)
Beim Aufheben der Raid-Sperre wird die gemerkte Verifizierungsstufe
zurueckgesetzt. Fehlt die Notiz, ergab Number('') die 0 — und 0 ist eine
gueltige Stufe, naemlich "keine". Der Server waere also auf offen gestellt
worden statt so gelassen, wie er war. Jetzt wird nur zurueckgesetzt, was
auch wirklich als Zahl notiert ist.
Ohne Befund geprueft: alle 168 SQL-Abfragen gegen das echte Schema inklusive
Migrationen, alle 109 Routen auf Rechtepruefung (53 von 54 schreibenden haben
eine, die 54. haengt am timing-safe verglichenen Secret), Farbwerte vor dem
Einbetten in ausgeliefertes CSS, das Escaping der OG-Meta-Tags, Timer-Cleanup
im Frontend und die uebrigen async-Handler.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
499761a0e2 |
Test-Harness aus dem Repo nehmen und ignorieren
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> |
||
|
|
55085d7e2b |
NaN im Dashboard: zwei Zeitstempel-Formate, ein Parser
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>
|
||
|
|
91397d8a44 |
Raid-Sperre von Hand setzen, aufheben oder nachsehen
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> |
||
|
|
aef708c405 |
Raid-Schutz: erkennt Beitritts-Wellen und sperrt voruebergehend
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> |
||
|
|
fb7ee78548 |
Status-Seite wird ein Dashboard
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> |
||
|
|
1e2d295665 |
Erreichbarkeits-Balken auf der Server-Seite
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> |
||
|
|
c0a56c1eb5 |
Logo auf der Willkommens-Karte und in geteilten Links
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> |
||
|
|
6a47cb43ab |
Marken-Doku: es ist eine Katze, plus fehlende favicon.png fuer Gitea
Zwei Korrekturen am vorigen Commit: - Ich hatte das Maskottchen durchgehend als Wolfskopf beschrieben. Es ist eine Katze. Betrifft README und die Kommentare in build.py. - Gitea setzt neben favicon.svg noch ein <link rel="alternate icon"> auf favicon.png. Die Datei fehlte, dort stand also weiter die Gitea-Tasse — am Livesystem geprueft, das Bild kam in Gitea-Gruen (96,153,38) zurueck. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
33c48cb898 |
Logo einbinden: Favicons, Apple-Icon und Marken-Quellen
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> |
||
|
|
5360c8229f |
Englisch als zweite Sprache fuer die oeffentlichen Seiten
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> |
||
|
|
570e45fe60 |
Team-Bereich aufgehuebscht: Avatare, Rechte-Chips, Protokoll mit relativer Zeit
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>
|
||
|
|
c026b2a603 |
Wichtige Funktionen direkt in der Seitenleiste
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> |
||
|
|
de4e812896 |
Werte für mehrere Funktionen, und Anlegen frischt die Auswahl auf
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> |
||
|
|
c88926aba0 |
Modul-Seiten: alles zu einer Funktion an einem Ort
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> |
||
|
|
bea35234c6 |
„Einrichten" zeigt jetzt, was fehlt — und Env-Werte gelten als gesetzt
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> |
||
|
|
01fadadcd8 |
Grundgerüst-Assistent: alle fehlenden Kanäle auf einmal
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> |
||
|
|
684573b88c |
Fehlende Kanäle und Rollen aus dem Panel anlegen
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> |
||
|
|
0240225165 |
Config-Suche findet einzelne Einstellungen
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> |
||
|
|
1c3fda8d64 |
Config: Speichern-Leiste und durchsuchbare Kanal-Auswahl
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> |
||
|
|
2e58268f8d |
Bestenliste, Devlog-Filter, Galerie und leere Bereiche
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>
|
||
|
|
c6977f9c4a |
Modul-Tab im Plugin-Raster: Filter-Reiter und Symbole
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> |
||
|
|
07afa094a2 |
tools/post-devlog.mjs: Devlogs aus diesem Repo posten
devlog.py im EcoGame-Repo taugt für Bot-Devlogs nicht: es zählt EcoGame-Commits, bricht ohne solche ganz ab, betitelt den Post als „Devlog — EcoGame" und löscht die Bilder nach dem Senden. Das Skript hier zählt die Commits von d4rkbot, nimmt einen Bilder-Ordner (max. 4, wie der Endpoint), lässt die Bilder liegen und kann mit --dry vorher zeigen, was rausginge. Die Webhook-Adresse kommt aus der Umgebung oder einer Datei und wird nie ausgegeben — auch nicht im Fehlerfall. Beim Testen gegen einen lokalen Bot ohne Discord gefunden: payload_json als Blob anzuhängen kommt beim Endpoint als Datei-Teil an und wird zusammen mit den Nicht-Bildern verworfen — der Post wäre mit „empty devlog" abgelehnt worden. Muss ein Textfeld sein. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
db1ce87fc7 |
Devlog-Entwurf zum Community-Hub
Text für den Devlog-Post über hub.d4rkst3r.de: eigene Adresse, Wegweiser zwischen Hub und Bot-Seite, die neuen Eingabefenster bei /bug und /wunsch, frei angelegte Seiten. Bilder liegen unter A:\eco\devlog-hub-bilder. Bewusst ohne Wiederholung des Posts von heute früh — Startseite, Server-Verlauf, Profil und Willkommens-Karten stehen dort schon. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
3549b06cef |
/bug und /wunsch als Eingabefenster
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> |
||
|
|
197c2a1ada |
Werte: 19 feste Zahlen im Panel einstellbar
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>
|
||
|
|
fb927d4399 |
Willkommens-Karte einstellbar: Farben, Bild, Texte, Live-Vorschau
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> |