// 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() ); }