Een van de meest kritieke onderdelen van elke bot is de discord bot permissie-laag die bepaalt wie welk commando mag uitvoeren. Een ban-, kick- of serverconfiguratiecommando voor iedereen openlaten is een ramp die staat te gebeuren. In dit artikel bouwen we een gelaagd rechtensysteem met discord.js v14: we leunen op het permissiemodel van Discord zelf en ontwerpen daarnaast eigen flows die specifieke rollen vereisen. Het doel is om ongeoorloofd gebruik niet stilzwijgend te blokkeren, maar met duidelijke feedback aan de gebruiker.
Hoe permissies werken in Discord
Het helpt om twee aparte begrippen uit elkaar te houden:
- Permissie: atomaire bevoegdheden die door Discord zijn gedefinieerd, zoals
BanMembers,ManageGuildofKickMembers. De effectieve permissies van een lid zijn de vereniging van de permissies die al zijn rollen verlenen. - Rol: servereigen groepen met een naam en een kleur. Rollen als "Moderator" of "VIP" zijn jouw eigen bedenksel; Discord kent ze niet, het ziet alleen de permissies die ze dragen.
Een goed rechtensysteem gebruikt beide: het grove filter komt van de permissievlaggen van Discord, terwijl de fijne controle uit je eigen rollogica komt.
Eerste verdedigingslinie: setDefaultMemberPermissions
Wanneer je een slash-commando definieert, kun je Discord rechtstreeks vertellen welke permissie het vereist. Daardoor zien gebruikers zonder die permissie het commando niet eens in het menu — de schoonste bescherming, want de controle gebeurt aan de kant van Discord.
// commands/moderation/ban.js
const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('ban')
.setDescription('Verbant een lid van de server')
.addUserOption((o) =>
o.setName('lid').setDescription('Te verbannen lid').setRequired(true))
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers),
async execute(interaction) {
// ...
},
};
Hier toont setDefaultMemberPermissions het commando alleen aan rollen die BanMembers bezitten. Dit is een standaard startpunt; serverbeheerders kunnen het per rol of kanaal overschrijven via de "Integraties"-instellingen van Discord. Vertrouw er toch niet alleen op — je moet ook in je code valideren.
De permissie in code valideren
Bovenop het UI-filter van Discord is het opnieuw controleren van de permissie van de aanroeper aan de serverkant, terwijl het commando draait, de basis van beveiliging. interaction.memberPermissions geeft een PermissionsBitField terug, die je met .has() bevraagt.
async execute(interaction) {
if (!interaction.memberPermissions.has(PermissionFlagsBits.BanMembers)) {
return interaction.reply({
content: 'Je hebt de permissie **Leden verbannen** nodig voor dit commando.',
ephemeral: true,
});
}
// permissie toegekend, ga door met verwerken
}
Dankzij ephemeral: true ziet alleen degene die het commando uitvoerde de waarschuwing; het kanaal raakt niet vol met berichten van ongeoorloofde pogingen.
Flows die een rol vereisen
Soms hangt een commando niet af van een ingebouwde Discord-permissie, maar van een rol die jij hebt gedefinieerd — bijvoorbeeld dat alleen mensen met de rol "Support Team" een ticket mogen sluiten. Je controleert de rollen van een lid via interaction.member.roles.cache. Een rol op ID opvragen is veel betrouwbaarder dan op naam, omdat rollen hernoemd kunnen worden.
const SUPPORT_ROL_ID = '123456789012345678';
async execute(interaction) {
const toegestaan = interaction.member.roles.cache.has(SUPPORT_ROL_ID);
if (!toegestaan) {
return interaction.reply({
content: 'Alleen het Support Team kan deze actie uitvoeren.',
ephemeral: true,
});
}
// ticket-sluitingsflow...
}
Wil je een van meerdere rollen accepteren, zet de toegestane ID's dan in een array en controleer met some:
const TOEGESTANE_ROLLEN = ['111...', '222...'];
const toegestaan = TOEGESTANE_ROLLEN.some((id) => interaction.member.roles.cache.has(id));
Vermijd herhaling: een herbruikbare permissielaag
Dezelfde if-blokken in elk commando kopiëren is foutgevoelig. Een schonere aanpak is om een permissiecontract aan het commandobestand toe te voegen en de controle in het centrale interactionCreate-event uit te voeren. Het commando verklaart alleen zijn vereiste; de handler dwingt die af.
// commands/moderation/clear.js
module.exports = {
data: /* ...SlashCommandBuilder... */,
permissions: [PermissionFlagsBits.ManageMessages], // vereiste permissies
roles: ['MOD_ROL_ID'], // vereiste rollen (optioneel)
async execute(interaction) { /* ... */ },
};
// in events/interactionCreate.js
const command = interaction.client.commands.get(interaction.commandName);
if (!command) return;
// permissiecontrole
if (command.permissions?.length) {
const ontbrekend = command.permissions.filter(
(p) => !interaction.memberPermissions.has(p));
if (ontbrekend.length) {
return interaction.reply({
content: 'Je hebt niet de vereiste permissies voor dit commando.',
ephemeral: true,
});
}
}
// rolcontrole
if (command.roles?.length) {
const ok = command.roles.some(
(id) => interaction.member.roles.cache.has(id));
if (!ok) {
return interaction.reply({
content: 'Je mag dit commando niet gebruiken.',
ephemeral: true,
});
}
}
await command.execute(interaction);
Om nu een nieuw beschermd commando toe te voegen, hoef je alleen een permissions- of roles-veld in het bestand te schrijven; de herhalende controlecode leeft op één plek.
Vergeet de eigen permissies van de bot niet
Een veelgemaakte fout: zelfs als de gebruiker geautoriseerd is, kan de bot zelf de permissie missen om de actie uit te voeren. Controleer voor het verbannen de rolhiërarchie en de permissies van de bot, anders geeft de API een fout terug.
const doel = interaction.options.getMember('lid');
const me = interaction.guild.members.me;
if (!me.permissions.has(PermissionFlagsBits.BanMembers)) {
return interaction.reply({
content: 'Ik heb geen verbanrecht. Controleer de rolpermissies van de bot.',
ephemeral: true,
});
}
if (doel && doel.roles.highest.position >= me.roles.highest.position) {
return interaction.reply({
content: 'Dit lid heeft een hogere rol dan ik, dus ik kan niets doen.',
ephemeral: true,
});
}
In Discord kan een lid alleen optreden tegen leden wiens hoogste rol onder de zijne staat. Dezelfde regel geldt voor de bot; denk er dus aan om de rol van de bot hoog genoeg in de rollijst te zetten.
Veelgestelde vragen
Is setDefaultMemberPermissions niet genoeg — waarom ook in code controleren?
Omdat setDefaultMemberPermissions alleen de UI-zichtbaarheid beïnvloedt en door serverbeheerders kan worden overschreven. De echte beveiligingsbeslissing moet in code worden genomen, aan de serverkant. De twee lagen samen bieden zowel een schone gebruikerservaring als betrouwbare bescherming.
Moet ik een rol op naam of op ID controleren?
Altijd op ID. Rolnamen kunnen veranderen en meerdere rollen kunnen dezelfde naam delen; de ID is uniek en permanent. Je kopieert de ID door met de rechtermuisknop op de rol te klikken met de Ontwikkelaarsmodus ingeschakeld in Discord.
Kan ik de permissieconfiguratie van de code scheiden?
Ja. Naarmate je opschaalt, is het opslaan van rol-ID's en commandopermissies in een database of een config-bestand een goed idee. Zo kan elke server zijn eigen rollen definiëren, en hoef je geen code te deployen om te wijzigen wie toegang heeft.
Lekt het rechtensysteem van je bot, of wil je er een vanaf nul bouwen? We kunnen permissie- en rolcontroles, de permissiehiërarchie van de bot en een centrale autorisatielaag samen op een solide basis zetten. Neem contact met me op en laten we je bot veilig maken.