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>
This commit is contained in:
@@ -25,12 +25,19 @@ export const MODULES = [
|
||||
desc: 'Entwicklungs-Berichte als gebrandetes Embed posten und durchsuchbar archivieren.',
|
||||
tab: 'feeds', default: true,
|
||||
requires: [{ setting: 'devlog_channel_id', label: 'Devlog-Kanal', create: { name: 'dev-log', topic: 'Entwicklungs-Updates vom Bot' } }],
|
||||
fields: [
|
||||
{ setting: 'devlog_ping_role_id', label: 'Ping-Rolle', art: 'rolle', hint: 'wird bei jedem Devlog erwähnt; leer = kein Ping' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'commit_feed', group: 'inhalte', name: 'Commit-Feed',
|
||||
desc: 'Pushes aus Gitea landen als Embed im Kanal. Archiviert wird unabhängig davon immer.',
|
||||
tab: 'feeds', setting: 'commit_feed_enabled', defaultOn: true,
|
||||
requires: [{ setting: 'commit_channel_id', label: 'Commit-Kanal', create: { name: 'commits', privat: true, topic: 'Pushes aus Gitea' } }],
|
||||
fields: [
|
||||
{ setting: 'commit_branch_filter', label: 'Branch-Filter', art: 'text', hint: 'kommagetrennt, leer = alle' },
|
||||
{ setting: 'ignored_repos', label: 'Ignorierte Repos', art: 'text', hint: 'kommagetrennt, volle Namen' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'releases', group: 'inhalte', name: 'Release-Ankündigungen',
|
||||
@@ -54,6 +61,10 @@ export const MODULES = [
|
||||
desc: 'Meldet Livestreams und neue Videos.',
|
||||
tab: 'system', default: true,
|
||||
requires: [{ setting: 'social_announce_channel_id', label: 'Social-Kanal', create: { name: 'streams', topic: 'Twitch- und YouTube-Meldungen' } }],
|
||||
fields: [
|
||||
{ setting: 'twitch_channel', label: 'Twitch-Kanal', art: 'text', hint: 'Login-Name, z. B. d4rkst3r' },
|
||||
{ setting: 'youtube_channel_id', label: 'YouTube-Kanal', art: 'text', hint: 'Kanal-ID (UC…)' },
|
||||
],
|
||||
},
|
||||
|
||||
/* ── Community ────────────────────────────────── */
|
||||
@@ -61,12 +72,19 @@ export const MODULES = [
|
||||
id: 'levels', group: 'community', name: 'Level-System',
|
||||
desc: 'XP fürs Mitreden, Rollen-Belohnungen und öffentliche Bestenliste.',
|
||||
tab: 'community', setting: 'levels_enabled', defaultOn: false,
|
||||
fields: [
|
||||
{ setting: 'levels_announce', label: 'Level-Up im Kanal feiern', art: 'schalter' },
|
||||
{ setting: 'level_rewards', label: 'Rollen-Belohnungen', art: 'text', hint: 'Level=Rollen-ID, kommagetrennt — z. B. 5=1234, 10=5678' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'starboard', group: 'community', name: 'Starboard',
|
||||
desc: 'Nachrichten mit genug Sternen landen in einem Best-of-Kanal.',
|
||||
tab: 'community', default: true,
|
||||
requires: [{ setting: 'starboard_channel_id', label: 'Starboard-Kanal', create: { name: 'best-of', topic: 'Nachrichten mit genug Sternen' } }],
|
||||
fields: [
|
||||
{ setting: 'starboard_threshold', label: 'Schwellwert', art: 'zahl', hint: 'so viele ⭐ braucht eine Nachricht' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'gallery', group: 'community', name: 'Screenshot-Galerie',
|
||||
@@ -90,6 +108,9 @@ export const MODULES = [
|
||||
desc: 'Gratulation am großen Tag, optional mit Tages-Rolle.',
|
||||
tab: 'community', default: true,
|
||||
requires: [{ setting: 'birthday_channel_id', label: 'Geburtstags-Kanal', create: { name: 'geburtstage', topic: 'Der Bot gratuliert hier' } }],
|
||||
fields: [
|
||||
{ setting: 'birthday_role_id', label: 'Tages-Rolle', art: 'rolle', hint: 'wird am nächsten Morgen wieder entfernt' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'events', group: 'community', name: 'Event-Ankündigungen',
|
||||
@@ -102,21 +123,25 @@ export const MODULES = [
|
||||
desc: 'Bewerbungs-Knopf, Rolle und Teilnehmerliste.',
|
||||
tab: 'community', default: true,
|
||||
requires: [{ setting: 'playtester_role_id', label: 'Playtester-Rolle', create: { rolle: true, name: 'Playtester', farbe: '#23a55a' } }],
|
||||
tool: { tab: 'community', label: 'Playtester-Liste' },
|
||||
},
|
||||
{
|
||||
id: 'alpha_keys', group: 'community', name: 'Alpha-Keys',
|
||||
desc: 'Schlüssel-Pool mit Verteilung per Direktnachricht.',
|
||||
tab: 'community', default: true,
|
||||
tool: { tab: 'community', label: 'Schlüssel-Pool' },
|
||||
},
|
||||
{
|
||||
id: 'role_menus', group: 'community', name: 'Rollen-Menüs',
|
||||
desc: 'Selbstbedienungs-Rollen per Knopf — im Discord und im Profil.',
|
||||
tab: 'rollen', default: true,
|
||||
tool: { tab: 'rollen', label: 'Rollen-Menüs bauen' },
|
||||
},
|
||||
{
|
||||
id: 'applications', group: 'community', name: 'Bewerbungen',
|
||||
desc: 'Formulare als Eingabefenster, Prüfung mit Annehmen/Ablehnen.',
|
||||
tab: 'bewerbungen', default: true,
|
||||
tool: { tab: 'bewerbungen', label: 'Formulare bearbeiten' },
|
||||
},
|
||||
|
||||
/* ── Moderation & Support ─────────────────────── */
|
||||
@@ -124,6 +149,7 @@ export const MODULES = [
|
||||
id: 'moderation', group: 'moderation', name: 'Moderation',
|
||||
desc: 'Verwarnen, Auszeiten, Aufräumen — mit Verlauf und Protokoll.',
|
||||
tab: 'support', default: true,
|
||||
tool: { tab: 'support', label: 'Moderation' },
|
||||
},
|
||||
{
|
||||
id: 'modlog', group: 'moderation', name: 'Protokoll',
|
||||
@@ -148,6 +174,18 @@ export const MODULES = [
|
||||
desc: 'Begrüßung neuer Mitglieder mit gerendertem Bild.',
|
||||
tab: 'support', default: true,
|
||||
requires: [{ setting: 'welcome_channel_id', label: 'Willkommens-Kanal', create: { name: 'willkommen', topic: 'Begruessung neuer Mitglieder' } }],
|
||||
fields: [
|
||||
{ setting: 'welcome_card_enabled', label: 'Karte rendern', art: 'schalter', hint: 'aus = nur das Text-Embed' },
|
||||
{ setting: 'welcome_card_kicker', label: 'Kleine Zeile', art: 'text', hint: 'über dem Namen — leer = weglassen' },
|
||||
{ setting: 'welcome_card_sub', label: 'Zeile darunter', art: 'text', hint: '{count} wird zur Mitglieds-Nummer' },
|
||||
{ setting: 'welcome_card_image', label: 'Hintergrundbild', art: 'text', hint: 'https-Adresse, 900×300 passt genau' },
|
||||
{ setting: 'welcome_card_bg', label: 'Hintergrund', art: 'farbe' },
|
||||
{ setting: 'welcome_card_accent', label: 'Akzent 1', art: 'farbe' },
|
||||
{ setting: 'welcome_card_accent2', label: 'Akzent 2', art: 'farbe' },
|
||||
{ setting: 'welcome_card_text', label: 'Name', art: 'farbe' },
|
||||
{ setting: 'welcome_card_watermark', label: 'Markenname im Hintergrund', art: 'schalter' },
|
||||
],
|
||||
vorschau: 'welcome',
|
||||
},
|
||||
{
|
||||
id: 'autorole', group: 'moderation', name: 'Auto-Rolle',
|
||||
@@ -167,6 +205,9 @@ export const MODULES = [
|
||||
// System-Bereich — sonst landet „Einrichten" an der falschen Stelle.
|
||||
tab: 'brand', default: true,
|
||||
requires: [{ setting: 'gitea_api_token', label: 'Gitea-Token' }],
|
||||
fields: [
|
||||
{ setting: 'bug_report_repo', label: 'Ziel-Repository', art: 'text', hint: 'Besitzer/Name, z. B. D4rkst3r/EcoGame' },
|
||||
],
|
||||
},
|
||||
|
||||
/* ── Server & Technik ─────────────────────────── */
|
||||
@@ -174,12 +215,20 @@ export const MODULES = [
|
||||
id: 'server_monitor', group: 'server', name: 'Server-Monitor',
|
||||
desc: 'Live-Status der Game-Server mit Verlauf und Ausfall-Alarm.',
|
||||
tab: 'server', default: true,
|
||||
fields: [
|
||||
{ setting: 'status_channel_id', label: 'Status-Kanal', art: 'kanal', hint: 'hier steht das Live-Embed' },
|
||||
{ setting: 'server_alert_channel_id', label: 'Alarm-Kanal', art: 'kanal', hint: '🚨 down / ✅ wieder online' },
|
||||
],
|
||||
tool: { tab: 'server', label: 'Game-Server verwalten' },
|
||||
},
|
||||
{
|
||||
id: 'watchdog', group: 'server', name: 'Erreichbarkeits-Wächter',
|
||||
desc: 'Prüft hinterlegte Adressen und meldet Ausfälle per Direktnachricht.',
|
||||
tab: 'system', default: true,
|
||||
requires: [{ setting: 'watchdog_urls', label: 'Adressen' }],
|
||||
fields: [
|
||||
{ setting: 'watchdog_urls', label: 'Zu prüfende Adressen', art: 'text', hint: 'kommagetrennt, mit http(s)' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'temp_voice', group: 'server', name: 'Temporäre Sprachkanäle',
|
||||
@@ -191,6 +240,7 @@ export const MODULES = [
|
||||
id: 'triggers', group: 'server', name: 'Auto-Antworten',
|
||||
desc: 'Der Bot antwortet auf hinterlegte Schlüsselwörter.',
|
||||
tab: 'composer', default: true,
|
||||
tool: { tab: 'composer', label: 'Auto-Antworten pflegen' },
|
||||
},
|
||||
{
|
||||
id: 'reminders', group: 'server', name: 'Erinnerungen',
|
||||
@@ -201,16 +251,21 @@ export const MODULES = [
|
||||
id: 'scheduled_posts', group: 'server', name: 'Geplante Beiträge',
|
||||
desc: 'Nachrichten zu festen Zeiten, einmalig oder wiederkehrend.',
|
||||
tab: 'composer', default: true,
|
||||
tool: { tab: 'composer', label: 'Geplante Beiträge' },
|
||||
},
|
||||
{
|
||||
id: 'tags', group: 'server', name: 'Textbausteine',
|
||||
desc: 'Gespeicherte Texte per Befehl abrufen.',
|
||||
tab: 'composer', default: true,
|
||||
tool: { tab: 'composer', label: 'Textbausteine' },
|
||||
},
|
||||
{
|
||||
id: 'backups', group: 'server', name: 'Datenbank-Sicherung',
|
||||
desc: 'Nächtliche Sicherung mit Aufbewahrung über 14 Tage.',
|
||||
tab: 'system', setting: 'backup_enabled', defaultOn: true,
|
||||
fields: [
|
||||
{ setting: 'backup_channel_id', label: 'Kanal für die Sicherung', art: 'kanal', hint: 'leer = nur lokal' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'repo_backups', group: 'server', name: 'Repository-Sicherung',
|
||||
@@ -221,6 +276,9 @@ export const MODULES = [
|
||||
id: 'member_gate', group: 'server', name: 'Login nur für Mitglieder',
|
||||
desc: 'Nur wer auf dem Discord-Server ist, kann sich im Web anmelden.',
|
||||
tab: 'system', setting: 'member_gate_enabled', defaultOn: true,
|
||||
fields: [
|
||||
{ setting: 'discord_invite_url', label: 'Einladungs-Link', art: 'text', hint: 'bekommt, wer nicht auf dem Server ist' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -270,6 +328,38 @@ export function creationPlan(setting) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alles, was zu einem Modul gehört — für seine eigene Seite im Panel.
|
||||
* Bringt Kanäle/Rollen, Zusatzfelder und den Verweis auf ein Werkzeug
|
||||
* zusammen; Texte und Werte holt die API aus ihren eigenen Registern.
|
||||
*/
|
||||
export function moduleDetail(id) {
|
||||
const mod = MODULES.find((m) => m.id === id);
|
||||
if (!mod) return null;
|
||||
return {
|
||||
id: mod.id,
|
||||
name: mod.name,
|
||||
desc: mod.desc,
|
||||
group: mod.group,
|
||||
tab: mod.tab ?? null,
|
||||
enabled: moduleEnabled(mod.id),
|
||||
missing: moduleMissing(mod),
|
||||
// Voraussetzungen zuerst — ohne sie tut das Modul nichts
|
||||
felder: [
|
||||
...(mod.requires ?? []).map((r) => ({
|
||||
setting: r.setting,
|
||||
label: r.label,
|
||||
art: r.create?.rolle ? 'rolle' : 'kanal',
|
||||
hint: null,
|
||||
pflicht: true,
|
||||
})),
|
||||
...(mod.fields ?? []).map((f) => ({ ...f, hint: f.hint ?? null, pflicht: false })),
|
||||
],
|
||||
tool: mod.tool ?? null,
|
||||
vorschau: mod.vorschau ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
/** Alle Module mit Zustand — für das Webinterface */
|
||||
export function moduleStates() {
|
||||
return MODULES.map((mod) => ({
|
||||
|
||||
+24
-20
@@ -1,6 +1,9 @@
|
||||
// Stellwerte: alle Zahlen, die vorher fest im Code standen — XP-Beträge,
|
||||
// Wartezeiten, Prüf-Intervalle, Obergrenzen.
|
||||
//
|
||||
// `module` verbindet einen Wert mit seiner Funktion — die Modul-Seite zeigt ihn
|
||||
// dann dort, wo er hingehört, statt nur in der flachen Werte-Liste.
|
||||
//
|
||||
// Wie bei den Modulen und Vorlagen lebt der Standard im Code und die
|
||||
// Datenbank enthält nur Abweichungen. `min`/`max` sind keine Kosmetik: sie
|
||||
// verhindern, dass ein Vertipper den Bot in eine Prüfschleife im
|
||||
@@ -17,94 +20,94 @@ export const TUNING_GROUPS = [
|
||||
export const TUNING = [
|
||||
/* ── Level & Aktivität ────────────────────────────── */
|
||||
{
|
||||
id: 'xp_min', group: 'level', label: 'XP pro Nachricht — mindestens',
|
||||
id: 'xp_min', module: 'levels', group: 'level', label: 'XP pro Nachricht — mindestens',
|
||||
unit: 'XP', default: 15, min: 1, max: 500,
|
||||
},
|
||||
{
|
||||
id: 'xp_max', group: 'level', label: 'XP pro Nachricht — höchstens',
|
||||
id: 'xp_max', module: 'levels', group: 'level', label: 'XP pro Nachricht — höchstens',
|
||||
hint: 'Der Bot würfelt zwischen beiden Werten.',
|
||||
unit: 'XP', default: 25, min: 1, max: 500,
|
||||
},
|
||||
{
|
||||
id: 'xp_cooldown', group: 'level', label: 'Wartezeit zwischen zwei XP-Gutschriften',
|
||||
id: 'xp_cooldown', module: 'levels', group: 'level', label: 'Wartezeit zwischen zwei XP-Gutschriften',
|
||||
hint: 'Verhindert, dass Vielschreiber das Level-System leerlaufen lassen.',
|
||||
unit: 'Sekunden', default: 60, min: 5, max: 3600,
|
||||
},
|
||||
{
|
||||
id: 'trigger_cooldown', group: 'level', label: 'Wartezeit für Auto-Antworten',
|
||||
id: 'trigger_cooldown', module: 'triggers', group: 'level', label: 'Wartezeit für Auto-Antworten',
|
||||
hint: 'Pro Schlüsselwort und Kanal — sonst antwortet der Bot sich in Grund und Boden.',
|
||||
unit: 'Sekunden', default: 30, min: 5, max: 3600,
|
||||
},
|
||||
|
||||
/* ── Wann der Bot postet ──────────────────────────── */
|
||||
{
|
||||
id: 'birthday_hour', group: 'zeiten', label: 'Geburtstags-Gratulation ab',
|
||||
id: 'birthday_hour', module: 'birthdays', group: 'zeiten', label: 'Geburtstags-Gratulation ab',
|
||||
hint: 'Uhrzeit in Europe/Berlin. Gratuliert wird einmal am Tag.',
|
||||
unit: 'Uhr', default: 9, min: 0, max: 23,
|
||||
},
|
||||
{
|
||||
id: 'recap_weekday', group: 'zeiten', label: 'Wochen-Rückblick am',
|
||||
id: 'recap_weekday', module: 'weekly_recap', group: 'zeiten', label: 'Wochen-Rückblick am',
|
||||
hint: '0 = Sonntag, 1 = Montag … 6 = Samstag.',
|
||||
unit: 'Wochentag', default: 0, min: 0, max: 6,
|
||||
},
|
||||
{
|
||||
id: 'recap_hour', group: 'zeiten', label: 'Wochen-Rückblick ab',
|
||||
id: 'recap_hour', module: 'weekly_recap', group: 'zeiten', label: 'Wochen-Rückblick ab',
|
||||
unit: 'Uhr', default: 20, min: 0, max: 23,
|
||||
},
|
||||
|
||||
/* ── Prüf-Intervalle ──────────────────────────────── */
|
||||
{
|
||||
id: 'monitor_interval', group: 'pruefung', label: 'Game-Server prüfen alle',
|
||||
id: 'monitor_interval', module: 'server_monitor', group: 'pruefung', label: 'Game-Server prüfen alle',
|
||||
unit: 'Minuten', default: 2, min: 1, max: 120,
|
||||
},
|
||||
{
|
||||
id: 'monitor_timeout', group: 'pruefung', label: 'Game-Server — Antwort abwarten',
|
||||
id: 'monitor_timeout', module: 'server_monitor', group: 'pruefung', label: 'Game-Server — Antwort abwarten',
|
||||
unit: 'Sekunden', default: 8, min: 2, max: 60,
|
||||
},
|
||||
{
|
||||
id: 'monitor_fails', group: 'pruefung', label: 'Game-Server — Alarm nach',
|
||||
id: 'monitor_fails', module: 'server_monitor', group: 'pruefung', label: 'Game-Server — Alarm nach',
|
||||
hint: 'Fehlversuchen in Folge. 1 meldet jeden Aussetzer, höher meldet nur echte Ausfälle.',
|
||||
unit: 'Fehlversuchen', default: 2, min: 1, max: 10,
|
||||
},
|
||||
{
|
||||
id: 'watchdog_interval', group: 'pruefung', label: 'Adressen prüfen alle',
|
||||
id: 'watchdog_interval', module: 'watchdog', group: 'pruefung', label: 'Adressen prüfen alle',
|
||||
unit: 'Minuten', default: 2, min: 1, max: 120,
|
||||
},
|
||||
{
|
||||
id: 'watchdog_timeout', group: 'pruefung', label: 'Adressen — Antwort abwarten',
|
||||
id: 'watchdog_timeout', module: 'watchdog', group: 'pruefung', label: 'Adressen — Antwort abwarten',
|
||||
unit: 'Sekunden', default: 10, min: 2, max: 60,
|
||||
},
|
||||
{
|
||||
id: 'watchdog_fails', group: 'pruefung', label: 'Adressen — Alarm nach',
|
||||
id: 'watchdog_fails', module: 'watchdog', group: 'pruefung', label: 'Adressen — Alarm nach',
|
||||
unit: 'Fehlversuchen', default: 2, min: 1, max: 10,
|
||||
},
|
||||
{
|
||||
id: 'social_interval', group: 'pruefung', label: 'Twitch & YouTube prüfen alle',
|
||||
id: 'social_interval', module: 'social', group: 'pruefung', label: 'Twitch & YouTube prüfen alle',
|
||||
hint: 'Zu kurz bringt nichts — YouTube-Feeds aktualisieren sich ohnehin nur alle paar Minuten.',
|
||||
unit: 'Minuten', default: 5, min: 1, max: 180,
|
||||
},
|
||||
{
|
||||
id: 'birthday_interval', group: 'pruefung', label: 'Geburtstage prüfen alle',
|
||||
id: 'birthday_interval', module: 'birthdays', group: 'pruefung', label: 'Geburtstage prüfen alle',
|
||||
unit: 'Minuten', default: 15, min: 1, max: 120,
|
||||
},
|
||||
|
||||
/* ── Obergrenzen ──────────────────────────────────── */
|
||||
{
|
||||
id: 'ticket_transcript_max', group: 'grenzen', label: 'Nachrichten im Ticket-Protokoll',
|
||||
id: 'ticket_transcript_max', module: 'tickets', group: 'grenzen', label: 'Nachrichten im Ticket-Protokoll',
|
||||
hint: 'Ältere fallen weg. Hoch gesetzt dauert das Schließen länger.',
|
||||
unit: 'Nachrichten', default: 500, min: 50, max: 2000,
|
||||
},
|
||||
{
|
||||
id: 'gallery_max_images', group: 'grenzen', label: 'Bilder je Galerie-Beitrag',
|
||||
id: 'gallery_max_images', module: 'gallery', group: 'grenzen', label: 'Bilder je Galerie-Beitrag',
|
||||
unit: 'Bilder', default: 4, min: 1, max: 10,
|
||||
},
|
||||
{
|
||||
id: 'commits_shown', group: 'grenzen', label: 'Commits je Push-Embed',
|
||||
id: 'commits_shown', module: 'commit_feed', group: 'grenzen', label: 'Commits je Push-Embed',
|
||||
hint: 'Der Rest wird als „… und N weitere" zusammengefasst.',
|
||||
unit: 'Commits', default: 10, min: 1, max: 20,
|
||||
},
|
||||
{
|
||||
id: 'thread_archive_days', group: 'grenzen', label: 'Threads archivieren nach',
|
||||
id: 'thread_archive_days', module: 'tickets', group: 'grenzen', label: 'Threads archivieren nach',
|
||||
hint: 'Für Ticket- und Modmail-Threads. Discord erlaubt 1, 3 oder 7 Tage.',
|
||||
unit: 'Tagen', default: 7, min: 1, max: 7, choices: [1, 3, 7],
|
||||
},
|
||||
@@ -167,7 +170,8 @@ export function tuningStates() {
|
||||
return TUNING.map((t) => {
|
||||
const custom = getSetting(`tune_${t.id}`);
|
||||
return {
|
||||
id: t.id, label: t.label, hint: t.hint ?? null, group: t.group,
|
||||
id: t.id, label: t.label, hint: t.hint ?? null,
|
||||
group: t.group, module: t.module ?? null,
|
||||
unit: t.unit, min: t.min, max: t.max,
|
||||
choices: t.choices ?? null,
|
||||
default: t.default,
|
||||
|
||||
+14
-1
@@ -21,7 +21,7 @@ import {
|
||||
savePage, getPage, listPages, listPublishedPages, deletePage,
|
||||
} from '../db.js';
|
||||
import crypto from 'node:crypto';
|
||||
import { moduleStates, setModuleEnabled, MODULE_GROUPS, MODULES } from '../modules.js';
|
||||
import { moduleStates, moduleDetail, setModuleEnabled, MODULE_GROUPS, MODULES } from '../modules.js';
|
||||
import { templateStates, setTemplate, TEMPLATE_GROUPS } from '../templates.js';
|
||||
import { tuningStates, setTuning, TUNING_GROUPS } from '../tuning.js';
|
||||
import { lastResults, GAME_ICONS } from '../bot/server-monitor.js';
|
||||
@@ -193,6 +193,19 @@ export function registerApiRoutes(app, client) {
|
||||
return { modules: moduleStates(), groups: MODULE_GROUPS };
|
||||
});
|
||||
|
||||
// Alles zu einer Funktion an einem Ort: ihre Kanäle und Felder, ihre Texte,
|
||||
// ihre Werte. Genau das zeigt die Modul-Seite.
|
||||
app.get('/api/modules/:id', async (request, reply) => {
|
||||
if (requireAnyScope(request, reply)) return;
|
||||
const detail = moduleDetail(String(request.params.id));
|
||||
if (!detail) return reply.code(404).send({ error: 'Modul unbekannt' });
|
||||
return {
|
||||
...detail,
|
||||
texte: templateStates().filter((t) => t.module === detail.id),
|
||||
werte: tuningStates().filter((w) => w.module === detail.id),
|
||||
};
|
||||
});
|
||||
|
||||
app.put('/api/modules/:id', async (request, reply) => {
|
||||
if (requireScope(request, reply, 'settings')) return;
|
||||
const id = String(request.params.id);
|
||||
|
||||
Reference in New Issue
Block a user