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>
This commit is contained in:
2026-07-31 18:11:31 +02:00
co-authored by Claude Fable 5
parent 2e58268f8d
commit 1c3fda8d64
3 changed files with 382 additions and 122 deletions
+20 -3
View File
@@ -664,6 +664,11 @@ ${rssItems}
if (requireScope(request, reply, 'settings')) return;
const body = request.body ?? {};
// Stand vor der Änderung — und zwar genau so, wie ihn GET /api/settings
// ausliefert (Einstellung ODER Env-Fallback). Nur daran lässt sich
// ablesen, was das Formular wirklich geändert hat.
const before = currentSettings();
const unchanged = (key, value) => value === String(before[key] ?? '');
// Sensible Felder sind Owner-only — für Team-Mitglieder still ignorieren
if (!isAdmin(getSessionUser(request))) {
@@ -683,10 +688,21 @@ ${rssItems}
'events_announce_channel_id', 'social_announce_channel_id', 'server_alert_channel_id',
'birthday_channel_id',
];
// Auch Devlog- und Commit-Kanal dürfen leer sein: leer heißt „noch nicht
// eingerichtet", und der Bot kommt damit klar. Vorher scheiterte das
// Speichern JEDER Einstellung, solange einer der beiden nicht gesetzt war
// — auf einer frischen Installation also immer.
for (const key of ['commit_channel_id', 'devlog_channel_id', ...OPTIONAL_CHANNELS]) {
if (body[key] === undefined) continue;
const value = String(body[key]);
if (value === '' && OPTIONAL_CHANNELS.includes(key)) {
// `?? ''` ist wichtig: nicht gesetzte Kanäle kommen als null aus der
// API zurück, und String(null) wäre "null" — also ein Kanal, den es
// nie gibt.
const value = String(body[key] ?? '');
// Nur geprüft, was sich tatsächlich ändert. Sonst blockiert ein
// inzwischen gelöschter Kanal das Speichern *aller* Einstellungen,
// weil das Formular ihn unverändert mitschickt.
if (unchanged(key, value)) continue;
if (value === '') {
setSetting(key, '');
continue;
}
@@ -699,7 +715,8 @@ ${rssItems}
// Rollen: müssen existieren ('' = Feature aus)
for (const key of ['devlog_ping_role_id', 'playtester_role_id', 'autorole_id', 'birthday_role_id']) {
if (body[key] === undefined) continue;
const value = String(body[key]);
const value = String(body[key] ?? ''); // null = keine Rolle, siehe oben
if (unchanged(key, value)) continue; // unverändert → nicht prüfen
if (value !== '') {
const exists = [...client.guilds.cache.values()].some((g) =>
g.roles?.cache?.has?.(value)