Skip to content

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 ​

CommandePermission requiseMéthode SDK utilisée
!kick @userKICK_MEMBERSMember.kick(reason?)
!ban @userBAN_MEMBERSMember.ban({ reason?, deleteHistory? })
!warn @user [raison]MANAGE_MESSAGESSimple 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 ​

SDK publié sous licence ISC.