Een Discord-moderatiebot is de meest praktische manier om een groeiende server beheersbaar te houden: hij reageert binnen seconden op overtredingen, registreert elke actie en geeft moderatierechten alleen aan de juiste mensen. In deze gids bouwen we ban-, kick- en mute- (timeout-)commando's vanaf nul met discord.js v14. Het doel is niet alleen dat de commando's werken; het draait om de details die op echte servers het verschil maken — rechtencontrole, rolhiërarchie en audit log-redenen.
Vereiste rechten en intents
Moderatiecommando's vereisen dat de bot leden kan beheren. Geef je bot in het Discord Developer Portal de rechten Ban Members, Kick Members en Moderate Members. Moderate Members is verplicht voor timeouts (mutes). Je moet ook de GuildMembers intent in de code inschakelen en deze privileged intent in het portal aanzetten om bij ledendata te kunnen.
const { Client, GatewayIntentBits } = require('discord.js');
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMembers,
],
});
In je serverinstellingen moet de rol van de bot boven de rollen staan van de leden die hij gaat modereren. Discord laat hem niemand bannen die hoger staat dan hijzelf, en die regel dwingen we ook in de code af.
Commando-definitie en standaardrecht
Bij het definiëren van slash commands kun je hun standaardzichtbaarheid beperken met setDefaultMemberPermissions. Dit verbergt het commando voor leden zonder het recht, maar het is slechts een UI-filter — de echte beveiligingscontrole moet je nog steeds zelf tijdens runtime uitvoeren.
const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js');
const banCommand = new SlashCommandBuilder()
.setName('ban')
.setDescription('Verbant een lid van de server')
.addUserOption(o =>
o.setName('lid').setDescription('Te verbannen lid').setRequired(true))
.addStringOption(o =>
o.setName('reden').setDescription('Reden voor de ban'))
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers);
Rechten- en hiërarchiecontrole
Bij elk moderatiecommando moet je twee dingen verifiëren: het recht van degene die het uitvoert en de rolhiërarchie. discord.js biedt kant-en-klare getters die dit vereenvoudigen: member.bannable, member.kickable en member.moderatable. Deze houden rekening met zowel de rechten van de bot als de rolvolgorde. Toch is het een goede gewoonte om ook te controleren of de moderator die het commando uitvoert hoger staat dan het doellid.
function canModerate(interaction, target) {
const actor = interaction.member;
// De servereigenaar mag alles
if (actor.id === interaction.guild.ownerId) return true;
// Staat de moderator boven het doel?
return actor.roles.highest.comparePositionTo(target.roles.highest) > 0;
}
Als comparePositionTo een positieve waarde teruggeeft, staat actor hoger. Deze controle overslaan kan een moderator laten bannen die minder bevoegd is dan het doel.
Ban, kick en timeout implementeren
We bundelen de commando's onder één handler. Belangrijk punt: de audit log-reden verschijnt in Discords audit log wanneer je hem via het reason-veld aan de betreffende methode meegeeft. Zo blijft de vraag "wie deed wat met wie, en waarom" later niet onbeantwoord.
client.on('interactionCreate', async (interaction) => {
if (!interaction.isChatInputCommand()) return;
const target = interaction.options.getMember('lid');
const reden = interaction.options.getString('reden') ?? 'Niet opgegeven';
if (!target) {
return interaction.reply({ content: 'Lid niet gevonden.', ephemeral: true });
}
if (!canModerate(interaction, target)) {
return interaction.reply({ content: 'Je kunt dit lid niet modereren.', ephemeral: true });
}
if (interaction.commandName === 'ban') {
if (!target.bannable) {
return interaction.reply({ content: 'Ik kan dit lid niet bannen.', ephemeral: true });
}
await target.ban({ reason: reden, deleteMessageSeconds: 60 * 60 * 24 });
await interaction.reply(`🔨 ${target.user.tag} is verbannen. Reden: ${reden}`);
}
if (interaction.commandName === 'kick') {
if (!target.kickable) {
return interaction.reply({ content: 'Ik kan dit lid niet kicken.', ephemeral: true });
}
await target.kick(reden);
await interaction.reply(`👢 ${target.user.tag} is gekickt. Reden: ${reden}`);
}
if (interaction.commandName === 'mute') {
if (!target.moderatable) {
return interaction.reply({ content: 'Ik kan dit lid niet muten.', ephemeral: true });
}
const minuten = interaction.options.getInteger('minuten') ?? 10;
await target.timeout(minuten * 60 * 1000, reden);
await interaction.reply(`🔇 ${target.user.tag} is voor ${minuten} minuten gemute.`);
}
});
Voor mute maken we geen aparte rol aan; we gebruiken Discords ingebouwde timeout-functie. member.timeout() neemt een duur in milliseconden en is begrensd op 28 dagen. Geef null als duur mee om de timeout op te heffen. De ingebouwde timeout is betrouwbaarder dan een aparte "muted"-rol, omdat de stilte ook geldt wanneer de gebruiker nieuwe kanalen binnenkomt.
Slik fouten niet in
Netwerkfouten, ontbrekende rechten of een lid dat al vertrokken is komen in de praktijk vaak voor. Wikkel de commando's in een try/catch en geef de gebruiker een betekenisvolle melding terug; een fout die stilletjes in de console belandt is de meest voorkomende oorzaak van "de bot werkt niet"-klachten.
try {
await target.ban({ reason: reden });
await interaction.reply(`🔨 ${target.user.tag} is verbannen.`);
} catch (err) {
console.error(err);
await interaction.reply({ content: 'De actie is mislukt.', ephemeral: true });
}
Best practices
- Ephemeral antwoorden: toon fout- en rechtenmeldingen met
ephemeral: truezodat alleen de uitvoerder ze ziet; dat houdt het kanaal schoon. - Reden verplichten: zet bij zware acties
setRequired(true)om de moderator te dwingen een uitleg te schrijven. - Apart logkanaal: stuur elke moderatieactie ook als embed naar een privé logkanaal; dat is een blijvend record bovenop de audit log.
- Bescherm jezelf en de bot: weiger de actie als het doel de uitvoerder of de bot zelf is.
Veelgestelde vragen
Moet ik een aparte rol voor mute instellen?
Nee. Met discord.js v14 is het gebruik van Discords ingebouwde timeout-functie (member.timeout()) veel robuuster. Een aparte "muted"-rol vereist rechten-overrides in elk kanaal en kan gaten laten wanneer een gebruiker nieuwe kanalen binnenkomt; een timeout geldt automatisch voor de hele server.
Waarom kan mijn bot hoger geplaatste leden niet bannen?
Discord laat een bot of lid alleen leden beheren die onder zijn eigen hoogste rol staan. Verplaats de rol van de bot boven die leden in Serverinstellingen → Rollen. Als de getter member.bannable false teruggeeft, is de oorzaak vrijwel altijd de hiërarchie.
Hoe schrijf ik een reden naar de audit log?
Geef gewoon het reason-veld mee aan de betreffende methode: target.ban({ reason: 'Spam' }) of target.timeout(ms, 'Reclame'). Discord plaatst die tekst naast de actie in de audit log van de server.
Wil je een veilige, rechten-bewuste moderatiebot voor je server? Laten we samen een opzet plannen met ban/kick/timeout, een automatisch logkanaal en anti-raid-maatregelen — neem contact met me op.