L'une des parties les plus critiques d'un bot est la couche de permission de bot Discord qui décide qui peut exécuter quelle commande. Laisser une commande ban, kick ou de configuration du serveur ouverte à tout le monde est une catastrophe annoncée. Dans cet article, nous allons construire un système de permissions en couches avec discord.js v14 : nous nous appuierons sur le modèle de permissions de Discord et concevrons aussi des flux personnalisés exigeant des rôles précis. L'objectif est de bloquer tout usage non autorisé non pas en silence, mais avec un retour clair à l'utilisateur.
Comment fonctionnent les permissions sur Discord
Il est utile de bien distinguer deux concepts :
- Permission : des capacités atomiques définies par Discord, comme
BanMembers,ManageGuildouKickMembers. Les permissions effectives d'un membre sont l'union des permissions accordées par tous ses rôles. - Rôle : des groupes propres au serveur, avec un nom et une couleur. Des rôles comme « Modérateur » ou « VIP » sont votre invention ; Discord ne les connaît pas, il ne voit que les permissions qu'ils portent.
Un bon système de permissions combine les deux : le filtre grossier vient des indicateurs de permission de Discord, et le contrôle fin vient de votre propre logique de rôles.
Première ligne de défense : setDefaultMemberPermissions
En définissant une commande slash, vous pouvez indiquer directement à Discord quelle permission elle exige. Ainsi, les utilisateurs sans cette permission ne verront même pas la commande dans le menu — la protection la plus propre, car le contrôle a lieu côté Discord.
// commands/moderation/ban.js
const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('ban')
.setDescription('Bannit un membre du serveur')
.addUserOption((o) =>
o.setName('membre').setDescription('Membre à bannir').setRequired(true))
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers),
async execute(interaction) {
// ...
},
};
Ici, setDefaultMemberPermissions n'affiche la commande qu'aux rôles disposant de BanMembers. C'est un point de départ par défaut ; les administrateurs du serveur peuvent le remplacer par rôle ou par salon depuis les réglages « Intégrations » de Discord. Malgré tout, ne vous y fiez pas seul — vous devez aussi valider dans votre code.
Valider la permission dans le code
En plus du filtre d'interface de Discord, revérifier la permission de l'appelant côté serveur pendant l'exécution est la base de la sécurité. interaction.memberPermissions renvoie un PermissionsBitField, que l'on interroge avec .has().
async execute(interaction) {
if (!interaction.memberPermissions.has(PermissionFlagsBits.BanMembers)) {
return interaction.reply({
content: 'Tu as besoin de la permission **Bannir des membres** pour cette commande.',
ephemeral: true,
});
}
// permission accordée, on continue
}
Grâce à ephemeral: true, seul l'auteur de la commande voit l'avertissement ; le salon ne se remplit pas de messages de tentatives non autorisées.
Des flux qui exigent un rôle
Parfois, une commande ne dépend pas d'une permission Discord intégrée mais d'un rôle que vous avez défini — par exemple, seules les personnes ayant le rôle « Équipe support » peuvent fermer un ticket. On vérifie les rôles d'un membre via interaction.member.roles.cache. Interroger un rôle par ID est bien plus fiable que par nom, car les rôles peuvent être renommés.
const SUPPORT_ROLE_ID = '123456789012345678';
async execute(interaction) {
const autorise = interaction.member.roles.cache.has(SUPPORT_ROLE_ID);
if (!autorise) {
return interaction.reply({
content: 'Seule l’Équipe support peut effectuer cette action.',
ephemeral: true,
});
}
// flux de fermeture du ticket...
}
Pour accepter l'un quelconque de plusieurs rôles, placez les ID autorisés dans un tableau et vérifiez avec some :
const ROLES_AUTORISES = ['111...', '222...'];
const autorise = ROLES_AUTORISES.some((id) => interaction.member.roles.cache.has(id));
Éviter la répétition : une couche de permission réutilisable
Copier les mêmes blocs if dans chaque commande est source d'erreurs. Une approche plus propre consiste à ajouter un contrat de permission au fichier de commande et à effectuer le contrôle dans l'event central interactionCreate. La commande se contente de déclarer son exigence ; le handler l'applique.
// commands/moderation/clear.js
module.exports = {
data: /* ...SlashCommandBuilder... */,
permissions: [PermissionFlagsBits.ManageMessages], // permissions requises
roles: ['MOD_ROLE_ID'], // rôles requis (optionnel)
async execute(interaction) { /* ... */ },
};
// dans events/interactionCreate.js
const command = interaction.client.commands.get(interaction.commandName);
if (!command) return;
// contrôle des permissions
if (command.permissions?.length) {
const manquantes = command.permissions.filter(
(p) => !interaction.memberPermissions.has(p));
if (manquantes.length) {
return interaction.reply({
content: 'Tu n’as pas les permissions requises pour cette commande.',
ephemeral: true,
});
}
}
// contrôle des rôles
if (command.roles?.length) {
const ok = command.roles.some(
(id) => interaction.member.roles.cache.has(id));
if (!ok) {
return interaction.reply({
content: 'Tu n’es pas autorisé à utiliser cette commande.',
ephemeral: true,
});
}
}
await command.execute(interaction);
Désormais, pour ajouter une nouvelle commande protégée, il suffit d'écrire un champ permissions ou roles dans le fichier ; le code de vérification répétitif vit à un seul endroit.
N'oubliez pas les permissions du bot lui-même
Une erreur fréquente : même si l'utilisateur est autorisé, le bot lui-même peut ne pas avoir la permission d'effectuer l'action. Avant de bannir, vérifiez la hiérarchie des rôles et les permissions du bot, sinon l'API renvoie une erreur.
const cible = interaction.options.getMember('membre');
const me = interaction.guild.members.me;
if (!me.permissions.has(PermissionFlagsBits.BanMembers)) {
return interaction.reply({
content: 'Je n’ai pas la permission de bannir. Vérifie les permissions du rôle du bot.',
ephemeral: true,
});
}
if (cible && cible.roles.highest.position >= me.roles.highest.position) {
return interaction.reply({
content: 'Ce membre a un rôle plus élevé que le mien, je ne peux pas agir.',
ephemeral: true,
});
}
Sur Discord, un membre ne peut agir que sur les membres dont le rôle le plus élevé est en dessous du sien. La même règle s'applique au bot ; pensez donc à remonter suffisamment le rôle du bot dans la liste des rôles.
Questions fréquentes
setDefaultMemberPermissions ne suffit-il pas — pourquoi vérifier aussi dans le code ?
Parce que setDefaultMemberPermissions n'affecte que la visibilité dans l'interface et peut être remplacé par les administrateurs du serveur. La vraie décision de sécurité doit être prise dans le code, côté serveur. Les deux couches ensemble offrent à la fois une expérience propre et une protection fiable.
Faut-il vérifier un rôle par nom ou par ID ?
Toujours par ID. Les noms de rôle peuvent changer et plusieurs rôles peuvent partager le même nom ; l'ID est unique et permanent. Vous pouvez le copier en cliquant droit sur le rôle, le Mode développeur activé dans Discord.
Puis-je séparer la configuration des permissions du code ?
Oui. À mesure que vous montez en charge, stocker les ID de rôle et les permissions de commande dans une base de données ou un fichier config est une bonne idée. Ainsi, chaque serveur définit ses propres rôles, sans avoir à déployer du code pour changer les accès.
Le système de permissions de ton bot fuit, ou tu veux le construire de zéro ? Nous pouvons poser ensemble des bases solides : contrôles de permission et de rôle, hiérarchie des permissions du bot et couche d'autorisation centrale. Contacte-moi et sécurisons ton bot.