Exemple : bot de modération
Commandes !kick, !ban, !warn protégées par vérification de permission, avec journal dans un salon dédié. Illustre Member.hasPermission(), Member.kick()/ban(), l'event guildBanAdd, et une utilisation basique de Channel.bulkDelete().
Bot complet
ts
import { BloumeChat, PermissionFlags, BloumeChatGatewayError } from "bloumechat";
const client = new BloumeChat();
const LOG_CHANNEL_NAME = "mod-logs";
async function logAction(serverId: string, text: string) {
const guild = client.guilds.cache.get(serverId);
const logChannel = guild?.channels.find((c) => c.name === LOG_CHANNEL_NAME);
await logChannel?.send(text);
}
client.on("messageCreate", async (message) => {
if (message.author.bot || !message.serverId) return;
const [command, targetMention, ...rest] = message.content.split(" ");
const targetId = targetMention?.replace(/[<@>]/g, "");
if (!targetId) return;
const moderator = message.member;
const target = await client.members.fetch(message.serverId, targetId);
switch (command) {
case "!kick": {
if (!moderator?.hasPermission(PermissionFlags.KICK_MEMBERS)) {
return message.reply("❌ Tu n'as pas la permission d'expulser des membres.");
}
try {
await target.kick("Expulsé via commande de modération");
await message.reply(`👢 ${target.user.username} a été expulsé.`);
await logAction(message.serverId, `👢 ${target.user.tagString} expulsé par ${moderator.user.tagString}`);
} catch (error) {
// kick()/ban() passent par le socket (server:kick/server:ban) — un
// refus serveur (hiérarchie, cible propriétaire...) ou une expiration
// d'ack lève une BloumeChatGatewayError plutôt que de réussir en silence.
const code = error instanceof BloumeChatGatewayError ? error.code : "unknown";
await message.reply(`❌ Expulsion refusée (${code}).`);
}
break;
}
case "!ban": {
if (!moderator?.hasPermission(PermissionFlags.BAN_MEMBERS)) {
return message.reply("❌ Tu n'as pas la permission de bannir des membres.");
}
try {
await target.ban({ reason: "Banni via commande de modération", deleteHistory: "1d" });
await message.reply(`🔨 ${target.user.username} a été banni.`);
await logAction(message.serverId, `🔨 ${target.user.tagString} banni par ${moderator.user.tagString}`);
} catch (error) {
const code = error instanceof BloumeChatGatewayError ? error.code : "unknown";
await message.reply(`❌ Bannissement refusé (${code}).`);
}
break;
}
case "!warn": {
if (!moderator?.hasPermission(PermissionFlags.MANAGE_MESSAGES)) {
return message.reply("❌ Tu n'as pas la permission d'avertir des membres.");
}
const reason = rest.join(" ") || "Aucune raison fournie";
await message.reply(`⚠️ ${target.user.username} a été averti : ${reason}`);
await logAction(message.serverId, `⚠️ ${target.user.tagString} averti par ${moderator.user.tagString} — ${reason}`);
break;
}
}
});
// Journalise aussi les bans effectués depuis l'interface web
client.on("guildBanAdd", (data) => {
logAction(data.serverPublicId, `🔨 Ban détecté (hors bot) : ${JSON.stringify(data)}`);
});
client.login(process.env.BOT_TOKEN!);js
const { BloumeChat, PermissionFlags, BloumeChatGatewayError } = require("bloumechat");
const client = new BloumeChat();
const LOG_CHANNEL_NAME = "mod-logs";
async function logAction(serverId, text) {
const guild = client.guilds.cache.get(serverId);
const logChannel = guild && guild.channels.find((c) => c.name === LOG_CHANNEL_NAME);
if (logChannel) await logChannel.send(text);
}
client.on("messageCreate", async (message) => {
if (message.author.bot || !message.serverId) return;
const parts = message.content.split(" ");
const command = parts[0];
const targetMention = parts[1];
const rest = parts.slice(2);
const targetId = targetMention && targetMention.replace(/[<@>]/g, "");
if (!targetId) return;
const moderator = message.member;
const target = await client.members.fetch(message.serverId, targetId);
switch (command) {
case "!kick": {
if (!moderator || !moderator.hasPermission(PermissionFlags.KICK_MEMBERS)) {
return message.reply("❌ Tu n'as pas la permission d'expulser des membres.");
}
try {
await target.kick("Expulsé via commande de modération");
await message.reply(`👢 ${target.user.username} a été expulsé.`);
await logAction(message.serverId, `👢 ${target.user.tagString} expulsé par ${moderator.user.tagString}`);
} catch (error) {
const code = error instanceof BloumeChatGatewayError ? error.code : "unknown";
await message.reply(`❌ Expulsion refusée (${code}).`);
}
break;
}
case "!ban": {
if (!moderator || !moderator.hasPermission(PermissionFlags.BAN_MEMBERS)) {
return message.reply("❌ Tu n'as pas la permission de bannir des membres.");
}
try {
await target.ban({ reason: "Banni via commande de modération", deleteHistory: "1d" });
await message.reply(`🔨 ${target.user.username} a été banni.`);
await logAction(message.serverId, `🔨 ${target.user.tagString} banni par ${moderator.user.tagString}`);
} catch (error) {
const code = error instanceof BloumeChatGatewayError ? error.code : "unknown";
await message.reply(`❌ Bannissement refusé (${code}).`);
}
break;
}
case "!warn": {
if (!moderator || !moderator.hasPermission(PermissionFlags.MANAGE_MESSAGES)) {
return message.reply("❌ Tu n'as pas la permission d'avertir des membres.");
}
const reason = rest.join(" ") || "Aucune raison fournie";
await message.reply(`⚠️ ${target.user.username} a été averti : ${reason}`);
await logAction(message.serverId, `⚠️ ${target.user.tagString} averti par ${moderator.user.tagString} — ${reason}`);
break;
}
}
});
// Journalise aussi les bans effectués depuis l'interface web
client.on("guildBanAdd", (data) => {
logAction(data.serverPublicId, `🔨 Ban détecté (hors bot) : ${JSON.stringify(data)}`);
});
client.login(process.env.BOT_TOKEN);Toujours revérifier côté serveur
Le SDK expose hasPermission() pour un contrôle rapide côté bot, mais l'API BloumeChat revalide systématiquement les permissions côté serveur. Ce garde-fou côté bot n'est là que pour donner un message d'erreur clair — ce n'est pas la seule barrière de sécurité.
Détail des commandes
| Commande | Permission requise | Méthode SDK utilisée |
|---|---|---|
!kick @user | KICK_MEMBERS | Member.kick(reason?) |
!ban @user | BAN_MEMBERS | Member.ban({ reason?, deleteHistory? }) |
!warn @user [raison] | MANAGE_MESSAGES | Simple message + log — BloumeChat n'a pas de système d'avertissement natif, ce bot les journalise uniquement dans mod-logs. |
Voir la table complète des flags disponibles sur Permissions (bitmask).
Variante : nettoyer les messages d'un utilisateur (!clean)
Utilise Channel.bulkDelete() (requiert MANAGE_MESSAGES) pour purger rapidement les messages récents d'un utilisateur dans le salon courant :
ts
client.on("messageCreate", async (message) => {
if (message.author.bot || !message.content.startsWith("!clean ")) return;
if (!message.member?.hasPermission(PermissionFlags.MANAGE_MESSAGES)) {
return message.reply("❌ Permission refusée.");
}
const targetId = message.content.split(" ")[1]?.replace(/[<@>]/g, "");
const recent = await message.channel.fetchMessages(100);
const toDelete = recent.filter((m: any) => m.author?.publicId === targetId).map((m: any) => m.publicId);
if (toDelete.length === 0) {
return message.reply("Aucun message récent trouvé pour cet utilisateur.");
}
await message.channel.bulkDelete(toDelete);
await message.reply(`🧹 ${toDelete.length} message(s) supprimé(s).`);
});js
client.on("messageCreate", async (message) => {
if (message.author.bot || !message.content.startsWith("!clean ")) return;
if (!message.member || !message.member.hasPermission(PermissionFlags.MANAGE_MESSAGES)) {
return message.reply("❌ Permission refusée.");
}
const targetId = message.content.split(" ")[1] && message.content.split(" ")[1].replace(/[<@>]/g, "");
const recent = await message.channel.fetchMessages(100);
const toDelete = recent.filter((m) => m.author && m.author.publicId === targetId).map((m) => m.publicId);
if (toDelete.length === 0) {
return message.reply("Aucun message récent trouvé pour cet utilisateur.");
}
await message.channel.bulkDelete(toDelete);
await message.reply(`🧹 ${toDelete.length} message(s) supprimé(s).`);
});Variante : lister les bannissements actifs (!bans)
ts
client.on("messageCreate", async (message) => {
if (message.author.bot || message.content !== "!bans" || !message.serverId) return;
const guild = client.guilds.cache.get(message.serverId);
if (!guild) return;
const bans = await guild.fetchBans();
if (bans.length === 0) return message.reply("Aucun bannissement actif.");
const list = bans.map((b: any) => `• ${b.user?.name ?? b.userPublicId}`).join("\n");
await message.reply(`🔨 Bannissements actifs :\n${list}`);
});js
client.on("messageCreate", async (message) => {
if (message.author.bot || message.content !== "!bans" || !message.serverId) return;
const guild = client.guilds.cache.get(message.serverId);
if (!guild) return;
const bans = await guild.fetchBans();
if (bans.length === 0) return message.reply("Aucun bannissement actif.");
const list = bans.map((b) => `• ${b.user ? b.user.name : b.userPublicId}`).join("\n");
await message.reply(`🔨 Bannissements actifs :\n${list}`);
});Voir aussi
- Serveurs, rôles & membres — API complète de
Member,Guild.fetchBans(),Guild.unbanMember(). - Gestion des erreurs —
BloumeChatGatewayError, levée parkick()/ban()/unbanMember()en cas de refus serveur ou de timeout d'ack. - Permissions (bitmask) et guide Permissions — comprendre le système de bitmask
BigInt. - Événements —
guildBanAdd,guildBanRemove,guildMemberRemove. - Messages & salons —
Channel.bulkDelete(),Channel.fetchMessages().
