Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00e4774a5f |
@@ -14,17 +14,36 @@
|
|||||||
// Matrix.
|
// Matrix.
|
||||||
//
|
//
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// DIESER BEFEHL LIEST NUR
|
// DREI UNTERBEFEHLE, EINE RECHTEPRUEFUNG
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// `status` beantwortet drei Fragen an einem Ort: hat die Person die Rolle,
|
// `status` liest, `add` und `remove` schreiben. Alle drei fragen zuerst den
|
||||||
// kennt der Spielserver ihr Konto, und welche Teamrolle hat sie bei uns.
|
// Spielserver, ob der Aufrufer `d4rk.whitelist` hat.
|
||||||
//
|
//
|
||||||
// Das Vergeben und Entziehen kommt als eigener Schritt — erst wenn dieser
|
// Das Schreiben kam bewusst SPAETER als das Lesen: erst am 04.09.2026, nachdem
|
||||||
// Weg nachweislich durchläuft. Ein Schreibbefehl auf einem Kanal, den niemand
|
// der lesende Weg mit echten Daten durchgelaufen war („ja (2 von 2
|
||||||
// gemessen hat, ist ein Schreibbefehl ins Ungewisse.
|
// Zutrittsrollen)"). Ein Schreibbefehl auf einem Kanal, den niemand gemessen
|
||||||
|
// hat, ist ein Schreibbefehl ins Ungewisse.
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
// WAS DER BOT VERGIBT, ENTSCHEIDET DER SPIELSERVER
|
||||||
|
// ============================================================================
|
||||||
|
// Die Rolle steht in `d4rk_discord:grantrole` und MUSS eine der Rollen sein,
|
||||||
|
// die auch Zutritt oeffnen. Ist sie es nicht, verweigert dieser Befehl — eine
|
||||||
|
// Rolle zu vergeben, die niemanden hereinlaesst, waere ein Knopf, der nichts
|
||||||
|
// tut und dabei „erledigt" meldet.
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
// WO DAS LANDET
|
||||||
|
// ============================================================================
|
||||||
|
// Im Devlog-Kanal des Bots, nicht im Serverprotokoll. Der Weg dorthin ginge
|
||||||
|
// nur ueber einen Schreibzugang fuer den Bot, und der wuerde die absichtlich
|
||||||
|
// schmale Tuer aufmachen (siehe panel.js). Die Freischaltung selbst steht
|
||||||
|
// ausserdem in Discords eigenem Audit-Log, und wenn die Person verbindet,
|
||||||
|
// protokolliert d4rk_lib die Zulassung mit Namen.
|
||||||
import { SlashCommandBuilder, MessageFlags } from 'discord.js';
|
import { SlashCommandBuilder, MessageFlags } from 'discord.js';
|
||||||
|
|
||||||
import { person, panelBereit, darfWhitelist, konfig } from '../../panel.js';
|
import { person, panelBereit, darfWhitelist, konfig } from '../../panel.js';
|
||||||
|
import { inDevlog } from '../../melden.js';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
DIE ROLLEN WERDEN GELESEN, NICHT GEPFLEGT.
|
DIE ROLLEN WERDEN GELESEN, NICHT GEPFLEGT.
|
||||||
@@ -71,6 +90,26 @@ export const data = new SlashCommandBuilder()
|
|||||||
.addUserOption((o) =>
|
.addUserOption((o) =>
|
||||||
o.setName('person').setDescription('Wen nachschlagen?').setRequired(true)
|
o.setName('person').setDescription('Wen nachschlagen?').setRequired(true)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
.addSubcommand((s) =>
|
||||||
|
s.setName('add')
|
||||||
|
.setDescription('Jemanden freischalten')
|
||||||
|
.addUserOption((o) =>
|
||||||
|
o.setName('person').setDescription('Wen freischalten?').setRequired(true)
|
||||||
|
)
|
||||||
|
.addStringOption((o) =>
|
||||||
|
o.setName('grund').setDescription('Warum? Steht später im Log.').setRequired(false)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.addSubcommand((s) =>
|
||||||
|
s.setName('remove')
|
||||||
|
.setDescription('Freischaltung zurücknehmen')
|
||||||
|
.addUserOption((o) =>
|
||||||
|
o.setName('person').setDescription('Wem?').setRequired(true)
|
||||||
|
)
|
||||||
|
.addStringOption((o) =>
|
||||||
|
o.setName('grund').setDescription('Warum? Steht später im Log.').setRequired(false)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
export async function execute(interaction) {
|
export async function execute(interaction) {
|
||||||
@@ -106,6 +145,12 @@ export async function execute(interaction) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ziel = interaction.options.getUser('person', true);
|
const ziel = interaction.options.getUser('person', true);
|
||||||
|
const was = interaction.options.getSubcommand();
|
||||||
|
|
||||||
|
if (was === 'add' || was === 'remove') {
|
||||||
|
return schreiben(interaction, ziel, was, erlaubnis.rolle);
|
||||||
|
}
|
||||||
|
|
||||||
const p = await person(ziel.id);
|
const p = await person(ziel.id);
|
||||||
|
|
||||||
if (!p.ok) {
|
if (!p.ok) {
|
||||||
@@ -174,3 +219,120 @@ export async function execute(interaction) {
|
|||||||
|
|
||||||
return interaction.editReply(zeilen.filter(Boolean).join('\n'));
|
return interaction.editReply(zeilen.filter(Boolean).join('\n'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Freischalten oder zurücknehmen.
|
||||||
|
*
|
||||||
|
* SIEBEN PRÜFUNGEN, BEVOR ETWAS PASSIERT. Jede davon hat einen eigenen Satz —
|
||||||
|
* „geht nicht" ist keine Auskunft, mit der jemand weiterkommt.
|
||||||
|
*/
|
||||||
|
async function schreiben(interaction, ziel, was, aufruferRolle) {
|
||||||
|
const z = await zutritt();
|
||||||
|
|
||||||
|
if (!z) {
|
||||||
|
return interaction.editReply(
|
||||||
|
'Der Spielserver sagt gerade nicht, welche Rollen gelten — ich schalte nichts frei, was ich nicht prüfen kann.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!z.vergaberolle) {
|
||||||
|
return interaction.editReply(
|
||||||
|
'Es ist keine Rolle zum Vergeben eingetragen (`d4rk_discord:grantrole` in `secrets.cfg`). Bis dahin kann ich niemanden freischalten.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
DIE ROLLE MUSS AUCH ZUTRITT OEFFNEN.
|
||||||
|
|
||||||
|
Sonst vergibt der Befehl etwas Wirkungsloses und meldet Erfolg — die
|
||||||
|
unangenehmste Sorte Fehler, weil sie erst auffaellt, wenn jemand vor der
|
||||||
|
Tuer steht.
|
||||||
|
*/
|
||||||
|
if (!z.vergabeGueltig) {
|
||||||
|
return interaction.editReply(
|
||||||
|
`Die eingetragene Vergaberolle (\`${z.vergaberolle}\`) ist **keine** der Rollen, die Zutritt öffnen. ` +
|
||||||
|
'Ich vergebe sie nicht — sie würde niemanden hereinlassen.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mitglied = await interaction.guild?.members.fetch(ziel.id).catch(() => null);
|
||||||
|
|
||||||
|
if (!mitglied) {
|
||||||
|
return interaction.editReply(`${ziel.tag} ist nicht auf diesem Discord.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
KANN ICH DIE ROLLE UEBERHAUPT VERGEBEN?
|
||||||
|
|
||||||
|
Discord laesst einen Bot nur Rollen UNTERHALB seiner eigenen hoechsten
|
||||||
|
vergeben. Ohne diese Pruefung kommt eine nackte HTTP-50013, und die liest
|
||||||
|
sich wie „der Bot ist kaputt" statt wie „die Rolle steht zu weit oben".
|
||||||
|
*/
|
||||||
|
const rolle = interaction.guild.roles.cache.get(z.vergaberolle)
|
||||||
|
?? await interaction.guild.roles.fetch(z.vergaberolle).catch(() => null);
|
||||||
|
|
||||||
|
if (!rolle) {
|
||||||
|
return interaction.editReply(
|
||||||
|
`Die Rolle \`${z.vergaberolle}\` gibt es auf diesem Discord nicht. Steht in \`secrets.cfg\` die richtige ID?`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ich = interaction.guild.members.me;
|
||||||
|
|
||||||
|
if (!ich?.permissions.has('ManageRoles')) {
|
||||||
|
return interaction.editReply('Mir fehlt das Recht **Rollen verwalten** auf diesem Discord.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rolle.position >= ich.roles.highest.position) {
|
||||||
|
return interaction.editReply(
|
||||||
|
`**${rolle.name}** steht in der Rollenliste über meiner höchsten Rolle — Discord lässt mich sie deshalb nicht vergeben. ` +
|
||||||
|
'Schieb meine Rolle darüber.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const hatte = mitglied.roles.cache.has(rolle.id);
|
||||||
|
|
||||||
|
// NICHTS ZU TUN IST EINE ANTWORT, KEIN FEHLSCHLAG.
|
||||||
|
if (was === 'add' && hatte) {
|
||||||
|
return interaction.editReply(`${ziel.tag} hat **${rolle.name}** bereits.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (was === 'remove' && !hatte) {
|
||||||
|
return interaction.editReply(`${ziel.tag} hat **${rolle.name}** gar nicht.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const grund = interaction.options.getString('grund') || null;
|
||||||
|
const spur = `${interaction.user.tag} (${aufruferRolle})${grund ? ` — ${grund}` : ''}`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (was === 'add') await mitglied.roles.add(rolle, spur);
|
||||||
|
else await mitglied.roles.remove(rolle, spur);
|
||||||
|
} catch (fehler) {
|
||||||
|
return interaction.editReply(
|
||||||
|
`Discord hat es abgelehnt: ${fehler?.message ?? 'unbekannt'}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
ERST HANDELN, DANN MELDEN - und die Meldung darf nicht mitreissen.
|
||||||
|
|
||||||
|
Ein fehlgeschlagener Logeintrag waere ein schlechter Grund, eine bereits
|
||||||
|
vergebene Rolle als Fehler darzustellen.
|
||||||
|
*/
|
||||||
|
await inDevlog(interaction.client, {
|
||||||
|
title: was === 'add' ? 'Whitelist: freigeschaltet' : 'Whitelist: zurückgenommen',
|
||||||
|
description:
|
||||||
|
`**${ziel.tag}** (\`${ziel.id}\`)\n` +
|
||||||
|
`Rolle: **${rolle.name}**\n` +
|
||||||
|
`Durch: **${interaction.user.tag}** (${aufruferRolle})` +
|
||||||
|
(grund ? `\nGrund: ${grund}` : ''),
|
||||||
|
color: was === 'add' ? 0x3fb950 : 0xd29922,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
return interaction.editReply(
|
||||||
|
was === 'add'
|
||||||
|
? `${ziel.tag} hat jetzt **${rolle.name}** und kommt auf den Server.`
|
||||||
|
: `${ziel.tag} hat **${rolle.name}** nicht mehr.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -55,3 +55,35 @@ export async function dmAdminEinmalig(client, schluessel, zustand, embedBauen =
|
|||||||
setSetting(merker, zustand);
|
setSetting(merker, zustand);
|
||||||
return dmAdmin(client, embedBauen());
|
return dmAdmin(client, embedBauen());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ein Embed in den Devlog-Kanal.
|
||||||
|
*
|
||||||
|
* WARUM NICHT ALS DM: eine DM erreicht genau eine Person. Was das Team tut,
|
||||||
|
* gehört dorthin, wo das Team es sieht — sonst ist es protokolliert und
|
||||||
|
* trotzdem unsichtbar.
|
||||||
|
*
|
||||||
|
* WIRFT NIE. Ein fehlgeschlagener Logeintrag darf den Vorgang nicht
|
||||||
|
* mitreißen, über den er berichtet — dieselbe Regel wie bei `dmAdmin`. Der
|
||||||
|
* Rückgabewert sagt, ob es geklappt hat; wer es wissen muss, fragt ihn.
|
||||||
|
*
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
export async function inDevlog(client, embed) {
|
||||||
|
if (!config.devlogChannelId || !client) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const kanal = await client.channels.fetch(config.devlogChannelId).catch(() => null);
|
||||||
|
|
||||||
|
// Kein Textkanal ist kein Absturz: der Kanal kann gelöscht oder in
|
||||||
|
// eine Kategorie umgewandelt worden sein, und der Bot soll dann
|
||||||
|
// weiterarbeiten statt zu scheitern.
|
||||||
|
if (!kanal?.isTextBased?.()) return false;
|
||||||
|
|
||||||
|
await kanal.send({ embeds: [embed] });
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user