Files
d4rkbot/src/runtime-settings.js
T
D4rkst3randClaude Fable 5 079c75b184
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Host-Routing: Bot-Produktseite und Community-Hub getrennt
Eine Anwendung, zwei Gesichter — der Server erkennt an der Domain, welche
Seite gefragt ist, und schreibt das als data-site an den <body>. Das
Frontend rendert daraufhin entweder den Community-Hub wie bisher oder die
neue Produktseite.

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

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 15:20:53 +02:00

282 lines
8.9 KiB
JavaScript

// Effektive Laufzeit-Konfiguration: DB-Setting (Webinterface) vor Env-Variable.
// Wird bei jedem Zugriff frisch aufgelöst — Änderungen greifen ohne Neustart.
import { getSetting } from './db.js';
import { config } from './config.js';
export function commitChannelId() {
return getSetting('commit_channel_id') || config.commitChannelId;
}
export function devlogChannelId() {
return getSetting('devlog_channel_id') || config.devlogChannelId;
}
/** Öffentliche Basis-URL (Buttons, RSS, OAuth-Redirect) */
export function publicUrl() {
return (getSetting('public_url') || config.publicUrl).replace(/\/$/, '');
}
/**
* Community-Hub — Ziel für Devlog-Buttons, RSS, Vorschaubilder und die
* geteilte Navigation. Ohne eigene Einstellung bleibt alles wie bisher.
*/
export function hubUrl() {
return (getSetting('hub_url') || publicUrl()).replace(/\/$/, '');
}
/** Bot-Produktseite mit Dashboard */
export function botUrl() {
return (getSetting('bot_url') || publicUrl()).replace(/\/$/, '');
}
/** Cookie-Domain für den Login über mehrere Subdomains (leer = nur diese) */
export function cookieDomain() {
return getSetting('cookie_domain') || null;
}
/** Gitea-Basis-URL (API für /bug) */
export function giteaUrl() {
return (getSetting('gitea_url') || config.giteaUrl).replace(/\/$/, '');
}
/** Release-Ankündigungs-Kanal — nur per Settings-Seite, leer = Feature aus */
export function releaseChannelId() {
return getSetting('release_channel_id') || null;
}
/** Diskussions-Thread unter jedem Devlog-Post (Default: an) */
export function devlogThreadsEnabled() {
return getSetting('devlog_threads_enabled') !== '0';
}
/** Playtester-Rolle (Bewerbungs-Button) — leer = Feature aus */
export function playtesterRoleId() {
return getSetting('playtester_role_id') || null;
}
/** Starboard-Kanal — leer = Feature aus */
export function starboardChannelId() {
return getSetting('starboard_channel_id') || null;
}
/** Ab wie vielen ⭐ landet eine Nachricht im Starboard (Default: 3) */
export function starboardThreshold() {
const n = Number(getSetting('starboard_threshold'));
return Number.isInteger(n) && n >= 1 ? n : 3;
}
/** Gitea-API-Token (für /bug) — Setting hat Vorrang vor Env */
export function giteaApiToken() {
return getSetting('gitea_api_token') || config.giteaApiToken || null;
}
/** Guild-ID für die Slash-Command-Registrierung (greift beim nächsten Start) */
export function discordGuildId() {
return getSetting('discord_guild_id') || config.discordGuildId || null;
}
/* ── Branding (Setup → Brand-Tab) ── */
/** Brand-Name für Footer/Embeds (Default: D4RKST3R) */
export function brandName() {
return getSetting('brand_name') || 'D4RKST3R';
}
/** Primärfarbe als Discord-Int (Default: #f5c518) */
export function brandColor() {
const hex = (getSetting('brand_color') || '#f5c518').replace('#', '');
const n = parseInt(hex, 16);
return Number.isNaN(n) ? 0xf5c518 : n;
}
/** Sekundärfarbe (Default: #ff4d00) */
export function brandColor2() {
const hex = (getSetting('brand_color2') || '#ff4d00').replace('#', '');
const n = parseInt(hex, 16);
return Number.isNaN(n) ? 0xff4d00 : n;
}
/** Embed-Footer im Brand-Stil: "D4RKST3R // DEVLOG" */
export function brandFooter(tag) {
return `${brandName()} // ${tag}`;
}
/** Bot-Status: Text (leer = keiner; Server-Monitor darf dann die Presence nutzen) */
export function botStatusText() {
return getSetting('bot_status_text') || '';
}
/** Bot-Status: Typ (playing|watching|listening|custom) */
export function botStatusType() {
return getSetting('bot_status_type') || 'custom';
}
/** Bot-Presence-Status: online | idle | dnd */
export function botPresenceStatus() {
return getSetting('bot_presence_status') || 'online';
}
/** Ankündigungs-Kanal für neue Discord-Events — leer = aus */
export function eventsAnnounceChannelId() {
return getSetting('events_announce_channel_id') || null;
}
/** Announce-Kanal für Twitch/YouTube — leer = aus */
export function socialAnnounceChannelId() {
return getSetting('social_announce_channel_id') || null;
}
/** YouTube-Kanal-ID (UC…) für neue-Video-Benachrichtigungen */
export function youtubeChannelId() {
return getSetting('youtube_channel_id') || null;
}
/** Twitch-Login-Name + App-Credentials (Client-Credentials-Flow) */
export function twitchChannel() {
return getSetting('twitch_channel') || null;
}
export function twitchClientId() {
return getSetting('twitch_client_id') || null;
}
export function twitchClientSecret() {
return getSetting('twitch_client_secret') || null;
}
/** "Join to Create"-Voice-Kanal — leer = Temp-Voice aus */
export function tempVoiceChannelId() {
return getSetting('tempvoice_channel_id') || null;
}
/** Auto-Rolle für neue Member — leer = aus */
export function autoroleId() {
return getSetting('autorole_id') || null;
}
/** Sticky-Roles: Rollen bei Rejoin wiederherstellen (Default: aus) */
export function stickyRolesEnabled() {
return getSetting('sticky_roles_enabled') === '1';
}
/** Level-System an/aus (Default: aus — bewusst opt-in) */
export function levelsEnabled() {
return getSetting('levels_enabled') === '1';
}
/** Level-Up-Nachricht im Kanal (Default: an, wenn Levels an) */
export function levelsAnnounce() {
return getSetting('levels_announce') !== '0';
}
/** Rollen-Belohnungen: "5=roleId, 10=roleId" → [{level, roleId}] aufsteigend */
export function levelRewards() {
return (getSetting('level_rewards') ?? '')
.split(',')
.map((entry) => {
const [lvl, roleId] = entry.split('=').map((s) => s.trim());
return Number(lvl) > 0 && roleId ? { level: Number(lvl), roleId } : null;
})
.filter(Boolean)
.sort((a, b) => a.level - b.level);
}
/** Betreiber-Angaben fürs Impressum (§ 5 DDG) — leer = Seite zeigt Hinweis */
export function legalInfo() {
return {
name: getSetting('legal_name') ?? '',
address: getSetting('legal_address') ?? '',
email: getSetting('legal_email') ?? '',
extra: getSetting('legal_extra') ?? '',
};
}
/** Member-Gate: Web-Login nur für Discord-Member (Default: an, wenn Guild gesetzt) */
export function memberGateEnabled() {
return getSetting('member_gate_enabled') !== '0';
}
/** Discord-Invite-Link für die „Tritt erst dem Server bei"-Seite — leer = keiner */
export function discordInviteUrl() {
return getSetting('discord_invite_url') || null;
}
/** Geburtstags-Kanal (morgendliche Gratulation) — leer = Feature aus */
export function birthdayChannelId() {
return getSetting('birthday_channel_id') || null;
}
/** Geburtstags-Rolle für den Tag — leer = keine Rolle */
export function birthdayRoleId() {
return getSetting('birthday_role_id') || null;
}
/** Kanal für Feature-Wünsche (/wunsch) — leer = Feature aus */
export function votingChannelId() {
return getSetting('voting_channel_id') || null;
}
/** Ticket-Kanal (dort entstehen die privaten Ticket-Threads) — leer = Feature aus */
export function ticketChannelId() {
return getSetting('ticket_channel_id') || null;
}
/** Modmail-Staff-Kanal (privat!) — leer = Feature aus */
export function modmailChannelId() {
return getSetting('modmail_channel_id') || null;
}
/** Willkommens-Kanal — leer = Feature aus */
export function welcomeChannelId() {
return getSetting('welcome_channel_id') || null;
}
/** Mod-Log-Kanal (privat!) — leer = Feature aus */
export function modlogChannelId() {
return getSetting('modlog_channel_id') || null;
}
/** Screenshot-Kanal für die Galerie — leer = Feature aus */
export function screenshotChannelId() {
return getSetting('screenshot_channel_id') || null;
}
/** Ziel-Repo für /bug-Reports (Default: EcoGame) */
export function bugReportRepo() {
return getSetting('bug_report_repo') || 'D4rkst3r/EcoGame';
}
/** Repo, dessen Milestones die öffentliche Roadmap speisen */
export function roadmapRepo() {
return getSetting('roadmap_repo') || 'D4rkst3r/EcoGame';
}
/** Rolle, die bei Devlog-Posts gepingt wird — leer = kein Ping */
export function devlogPingRoleId() {
return getSetting('devlog_ping_role_id') || null;
}
/** Commit-Feed global an/aus (Default: an). Aus = weiter archivieren, nur nicht posten. */
export function commitFeedEnabled() {
return getSetting('commit_feed_enabled') !== '0';
}
/** Kommagetrennte Liste in Array übersetzen ('' → leer) */
function csv(value) {
return (value ?? '')
.split(',')
.map((s) => s.trim())
.filter(Boolean);
}
/** Branch-Filter: leer = alle Branches werden gepostet */
export function branchAllowed(branch) {
const allowed = csv(getSetting('commit_branch_filter'));
return allowed.length === 0 || allowed.includes(branch);
}
/** Ignorierte Repos (z. B. "D4rkst3r/ecobot") — werden komplett übersprungen */
export function repoIgnored(repo) {
return csv(getSetting('ignored_repos')).some(
(r) => r.toLowerCase() === repo.toLowerCase()
);
}