feat(bot): Whitelist-Rolle kommt vom Panel, nicht aus der Umgebung
Deploy / check (push) Canceled after 0s
Deploy / deploy (push) Canceled after 0s

Zwei Aenderungen, die zusammengehoeren.

1. darfWhitelist las ein Feld, das es nicht mehr gibt
------------------------------------------------------
/person im Spielserver nimmt jetzt das zu pruefende Recht als Parameter
und antwortet mit `darf` statt mit `darfWhitelist`. Der alte Zugriff
haette ab sofort still immer false ergeben - also: jeder im Team
ausgesperrt, ohne eine Fehlermeldung, die darauf hindeutet.

`person()` nimmt das Recht jetzt optional mit; ohne Recht bleibt der
Aufruf, was er war.

2. Die Rollen-ID stand an zwei Orten
------------------------------------
Sie muss zu der passen, die der Spielserver prueft. Stimmen sie nicht
ueberein, zeigt /whitelist status "hat die Rolle" fuer eine Rolle, die
den Zutritt gar nicht oeffnet - ein Fehler, der wie eine richtige
Antwort aussieht.

Deshalb wird sie nur noch an EINEM Ort eingetragen: auf der
Einstellungsseite des Adminpanels. Der Bot holt sie ueber /bot/config,
30 Sekunden gepuffert. Ein Fehlschlag wird NICHT gepuffert, sonst bliebe
eine Stoerung eine halbe Minute stehen, nachdem sie vorbei ist.

WHITELIST_ROLE_ID bleibt als Rueckfall, damit der Befehl auch dann noch
etwas anzeigen kann, wenn das Panel nicht antwortet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-04 10:58:47 +02:00
co-authored by Claude Opus 5
parent c840f6fc7e
commit 8b4efb2cb9
3 changed files with 66 additions and 15 deletions
+6 -2
View File
@@ -49,8 +49,12 @@ services:
# eingerichtet" sagt. # eingerichtet" sagt.
ACP_URL: ${ACP_URL:-} ACP_URL: ${ACP_URL:-}
ACP_BOT_TOKEN: ${ACP_BOT_TOKEN:-} ACP_BOT_TOKEN: ${ACP_BOT_TOKEN:-}
# Die Discord-Rolle, die den Zutritt oeffnet. Muss dieselbe sein, die der # Die Discord-Rolle, die den Zutritt oeffnet. NUR NOCH RUECKFALL: seit
# Spielserver prueft (Convar d4rk_discord:role). # dem 04.09.2026 holt der Bot sie vom Adminpanel (/bot/config), wo sie
# auf der Seite "Einstellungen" eingetragen wird. Was dort steht, gewinnt.
#
# Der Rueckfall bleibt, damit /whitelist auch dann noch etwas anzeigen
# kann, wenn das Panel gerade nicht antwortet.
WHITELIST_ROLE_ID: ${WHITELIST_ROLE_ID:-} WHITELIST_ROLE_ID: ${WHITELIST_ROLE_ID:-}
# HTTP-Endpoint für Gitea-Webhooks + Webinterface # HTTP-Endpoint für Gitea-Webhooks + Webinterface
# NPM leitet bot.d4rkst3r.de → host.docker.internal:3080 # NPM leitet bot.d4rkst3r.de → host.docker.internal:3080
+25 -9
View File
@@ -24,13 +24,27 @@
// gemessen hat, ist ein Schreibbefehl ins Ungewisse. // gemessen hat, ist ein Schreibbefehl ins Ungewisse.
import { SlashCommandBuilder, MessageFlags } from 'discord.js'; import { SlashCommandBuilder, MessageFlags } from 'discord.js';
import { person, panelBereit, darfWhitelist } from '../../panel.js'; import { person, panelBereit, darfWhitelist, konfig } from '../../panel.js';
// Die Rollen-ID kommt aus derselben Umgebung wie der Rest der Botkonfiguration. /*
// Sie ist kein Geheimnis, aber sie muss zu der passen, die der Spielserver DIE ROLLEN-ID STEHT IM PANEL, NICHT HIER.
// prüft (`d4rk_discord:role`) — stimmen sie nicht überein, zeigt dieser Befehl
// „hat die Rolle" für eine Rolle, die den Zutritt gar nicht öffnet. Sie ist kein Geheimnis, aber sie muss zu der passen, die der Spielserver
const WHITELIST_ROLLE = process.env.WHITELIST_ROLE_ID || ''; prüft (`d4rk_discord:role`) — stimmen sie nicht überein, zeigt dieser Befehl
„hat die Rolle" für eine Rolle, die den Zutritt gar nicht öffnet. Genau
deshalb wird sie an EINEM Ort eingetragen: auf der Einstellungsseite des
Adminpanels.
Die Umgebungsvariable bleibt als Rückfall, damit der Bot auch dann läuft,
wenn das Panel gerade nicht antwortet.
*/
async function whitelistRolle() {
const k = await konfig();
if (k.ok && k.whitelistRolle) return k.whitelistRolle;
return process.env.WHITELIST_ROLE_ID || '';
}
const GRUENDE = { const GRUENDE = {
nicht_eingerichtet: nicht_eingerichtet:
@@ -99,16 +113,18 @@ export async function execute(interaction) {
Verbindung. Wer sie hier vom Server erwartet, bekommt eine Antwort, die Verbindung. Wer sie hier vom Server erwartet, bekommt eine Antwort, die
geraten wäre. geraten wäre.
*/ */
let rolleText = 'nicht prüfbar — `WHITELIST_ROLE_ID` ist nicht gesetzt'; const rolle = await whitelistRolle();
if (WHITELIST_ROLLE) { let rolleText = 'nicht prüfbar — im Adminpanel ist keine Whitelist-Rolle eingetragen';
if (rolle) {
const mitglied = await interaction.guild?.members const mitglied = await interaction.guild?.members
.fetch(ziel.id) .fetch(ziel.id)
.catch(() => null); .catch(() => null);
rolleText = !mitglied rolleText = !mitglied
? 'nicht auf diesem Discord' ? 'nicht auf diesem Discord'
: mitglied.roles.cache.has(WHITELIST_ROLLE) : mitglied.roles.cache.has(rolle)
? '**ja**' ? '**ja**'
: '**nein**'; : '**nein**';
} }
+35 -4
View File
@@ -74,8 +74,11 @@ async function hole(weg) {
* fehlende Quelle als „nichts gefunden" zu lesen wäre der Fehler, den diese * fehlende Quelle als „nichts gefunden" zu lesen wäre der Fehler, den diese
* Flags verhindern. * Flags verhindern.
*/ */
export async function person(discordId) { export async function person(discordId, recht) {
const a = await hole(`person?discord=${encodeURIComponent(discordId)}`); // `recht` ist optional. Wird eines mitgegeben, prueft der Spielserver es
// gleich mit und legt `darf` in die Antwort - eine Frage, eine Runde.
const zusatz = recht ? `&recht=${encodeURIComponent(recht)}` : '';
const a = await hole(`person?discord=${encodeURIComponent(discordId)}${zusatz}`);
if (!a.ok) return a; if (!a.ok) return a;
@@ -91,12 +94,40 @@ export async function person(discordId) {
* Beides wäre geraten, also wird es unterschieden. * Beides wäre geraten, also wird es unterschieden.
*/ */
export async function darfWhitelist(discordId) { export async function darfWhitelist(discordId) {
const p = await person(discordId); const p = await person(discordId, 'd4rk.whitelist');
if (!p.ok) return { bekannt: false, grund: p.grund }; if (!p.ok) return { bekannt: false, grund: p.grund };
if (p.rolleGelesen === false) return { bekannt: false, grund: 'rechtesystem_stumm' }; if (p.rolleGelesen === false) return { bekannt: false, grund: 'rechtesystem_stumm' };
if (!p.rolle) return { bekannt: true, darf: false, grund: 'keine_rolle' }; if (!p.rolle) return { bekannt: true, darf: false, grund: 'keine_rolle' };
if (p.rechtGelesen === false) return { bekannt: false, grund: 'recht_nicht_lesbar' }; if (p.rechtGelesen === false) return { bekannt: false, grund: 'recht_nicht_lesbar' };
return { bekannt: true, darf: p.darfWhitelist === true, rolle: p.rolle }; return { bekannt: true, darf: p.darf === true, rolle: p.rolle };
}
/**
* Die Einstellungen, die im Panel eingetragen sind.
*
* WARUM DER BOT SIE NICHT SELBST FUEHRT: sonst stuende die Whitelist-Rolle an
* zwei Orten — hier in der Umgebung und dort auf der Seite. Zwei Orte fuer
* dieselbe Wahrheit heisst, dass eine Aenderung eine der beiden vergisst, und
* der Fehler sieht dann aus wie „die Rolle wird nicht erkannt".
*
* Kurz gepuffert, damit nicht jeder Befehl eine Runde ueber das Netz macht.
* Ein Fehlschlag wird NICHT gepuffert — sonst bliebe eine Stoerung eine Minute
* lang stehen, nachdem sie vorbei ist.
*/
let konfigStand = null;
let konfigZeit = 0;
export async function konfig() {
if (konfigStand && Date.now() - konfigZeit < 30000) return konfigStand;
const a = await hole('config');
if (!a.ok) return a;
konfigStand = a;
konfigZeit = Date.now();
return a;
} }