aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Bots Discord

Cooldown de bot Discord et protection anti-spam

À mesure qu'un bot Discord grandit, l'un des problèmes les plus fréquents est l'inondation de commandes : un utilisateur déclenche la même commande des dizaines de fois par seconde, et le bot répond à chaque fois. Un bon système de cooldown de bot Discord est exactement ce qui résout cela ; il impose à chaque utilisateur un court délai avant de pouvoir relancer une commande, protégeant à la fois votre bot et les limites de l'API Discord. Dans ce guide, nous allons construire la logique de cooldown de zéro, écrire un exemple fonctionnel avec discord.js et ajouter des couches supplémentaires contre le spam.

Pourquoi un cooldown et une protection anti-spam sont nécessaires

Un cooldown n'est pas seulement une fonction pour « ralentir l'utilisateur » ; c'est une ligne de défense pour la stabilité de votre bot. Les principales raisons :

  • Rate limits de l'API : Discord limite le nombre de requêtes qu'un bot peut envoyer. Si vous envoyez trop de messages/réponses, vous recevez une réponse 429 Too Many Requests et un blocage temporaire.
  • Consommation de ressources : les commandes coûteuses comme une requête de base de données, un appel d'API externe ou une génération d'image peuvent bloquer votre serveur sous le spam.
  • Abus : sans cooldown, les commandes d'économie, de tirage au sort ou de récompense sont exploitées.
  • Encombrement des salons : les réponses répétées du bot rendent les salons illisibles.

La logique du cooldown : stocker un horodatage

Tout système de cooldown repose sur une seule idée : stocker le moment où un utilisateur a lancé la commande pour la dernière fois et vérifier si assez de temps s'est écoulé. Le pseudo-code fonctionne ainsi :

  • L'utilisateur lance la commande.
  • Vérifier s'il existe un horodatage stocké pour cet utilisateur.
  • S'il existe et que (maintenant - stocké) < cooldown : refuser et indiquer le temps restant.
  • Sinon : exécuter la commande et enregistrer un nouvel horodatage.

Pour le stockage, la mémoire (une Map) suffit pour les petits bots. Si vous ne voulez pas que les cooldowns se réinitialisent au redémarrage du bot, utilisez Redis ou une base de données.

Un système de cooldown par commande avec discord.js

Dans discord.js v14, l'approche courante et officiellement recommandée pour les slash commands consiste à garder une Collection distincte par commande. Collection étend la classe Map de JavaScript. Vous pouvez ajouter un champ optionnel cooldown à votre fichier de commande :

// commands/ping.js
const { SlashCommandBuilder } = require('discord.js');

module.exports = {
  cooldown: 5, // secondes
  data: new SlashCommandBuilder()
    .setName('ping')
    .setDescription('Affiche la latence du bot'),
  async execute(interaction) {
    await interaction.reply(`Pong! ${interaction.client.ws.ping}ms`);
  },
};

La vérification réelle se fait dans l'événement interactionCreate, avant l'exécution de la commande :

// events/interactionCreate.js
const { Events, Collection } = require('discord.js');

module.exports = {
  name: Events.InteractionCreate,
  async execute(interaction) {
    if (!interaction.isChatInputCommand()) return;

    const command = interaction.client.commands.get(interaction.commandName);
    if (!command) return;

    const { cooldowns } = interaction.client;
    if (!cooldowns.has(command.data.name)) {
      cooldowns.set(command.data.name, new Collection());
    }

    const now = Date.now();
    const timestamps = cooldowns.get(command.data.name);
    const cooldownAmount = (command.cooldown ?? 3) * 1000;

    if (timestamps.has(interaction.user.id)) {
      const expiration = timestamps.get(interaction.user.id) + cooldownAmount;
      if (now < expiration) {
        const expiredTimestamp = Math.round(expiration / 1000);
        return interaction.reply({
          content: `Patiente avant de reutiliser cette commande. Tu pourras la relancer <t:${expiredTimestamp}:R>.`,
          ephemeral: true,
        });
      }
    }

    timestamps.set(interaction.user.id, now);
    setTimeout(() => timestamps.delete(interaction.user.id), cooldownAmount);

    try {
      await command.execute(interaction);
    } catch (error) {
      console.error(error);
    }
  },
};

N'oubliez pas de définir la collection client.cooldowns une fois au démarrage : ajoutez client.cooldowns = new Collection(); lors de la configuration de votre client. <t:...:R> est le format d'horodatage relatif de Discord et affiche à l'utilisateur un texte qui se met à jour automatiquement, comme « dans 3 secondes ».

Types de cooldown : utilisateur, serveur et global

L'exemple ci-dessus applique un cooldown par utilisateur, ce qui est le besoin le plus courant. Mais différents scénarios exigent différentes clés :

  • Par utilisateur : utilisez interaction.user.id comme clé. Idéal pour les commandes personnelles (profil, solde).
  • Par serveur : utilisez interaction.guildId comme clé. Pertinent pour les commandes qui affectent tout le serveur (annonces, opérations en masse).
  • Par salon : limitez certains salons avec interaction.channelId.
  • Global : utilisez une clé fixe (par ex. le nom de la commande) pour les commandes qui protègent une API externe coûteuse.

Vous pouvez réutiliser la même logique en changeant uniquement le champ stocké comme clé. Pour combiner plusieurs types, construisez une clé composite : `${interaction.guildId}-${interaction.user.id}`.

Les rate limits de Discord (429) et discord.js

Votre propre cooldown empêche les utilisateurs de spammer le bot ; mais le bot lui-même est aussi soumis à des limites face à l'API Discord. Bonne nouvelle : la couche @discordjs/rest sous discord.js gère automatiquement les rate limits globaux et par route, met les requêtes en file d'attente et attend en cas de 429. Quelques points à connaître malgré tout :

  • N'envoyez pas des centaines de messages dans une boucle ; regroupez les opérations ou insérez un délai.
  • Utiliser ephemeral: true sur les réponses transitoires (visibles seulement par la personne qui a lancé la commande) réduit l'encombrement et le trafic inutile.
  • Pour un travail long, appelez d'abord interaction.deferReply() ; sinon vous atteignez le délai d'interaction de 3 secondes.
  • Si vous atteignez un rate limit global, écoutez l'événement rateLimited pour le journaliser et réduire votre volume de requêtes.

Mesures supplémentaires contre le spam

Le cooldown est la première ligne de défense ; contre les abus agressifs, ajoutez ces couches :

  • Pénalité progressive : augmentez temporairement le délai d'attente pour un utilisateur qui se heurte sans cesse au cooldown.
  • Liste de surveillance : ignorez un utilisateur un court instant une fois qu'il dépasse un seuil de tentatives (un limiteur de débit simple).
  • Vérification des permissions : ajoutez un contrôle interaction.memberPermissions pour exempter les administrateurs des cooldowns.
  • Stockage persistant : conservez les cooldowns dans Redis ou une base de données pour les commandes de récompense/économie afin qu'elles ne puissent pas être détournées après un redémarrage.

Questions fréquentes

Est-il sûr de conserver les données de cooldown en mémoire ?

Pour les petits et moyens bots, la mémoire (une Collection/Map) est rapide et suffisante. Cependant, elle se réinitialise à chaque redémarrage du bot. Pour les commandes propices aux abus comme les tirages, récompenses ou l'économie, je recommande de stocker les cooldowns de façon persistante dans Redis ou une base de données.

Si discord.js gère les rate limits, pourquoi écrire un cooldown ?

Ils servent des objectifs différents. La couche REST de discord.js régule les requêtes que le bot envoie à Discord. Votre cooldown contrôle la fréquence à laquelle les utilisateurs peuvent déclencher le bot ; cette logique empêche le spam, la consommation de ressources et l'abus des mécaniques de jeu.

La même logique s'applique-t-elle à discord.py ?

Oui, le principe est identique : stocker l'heure de dernière utilisation et vérifier l'écart. discord.py dispose en plus d'un décorateur intégré @commands.cooldown et d'options BucketType, qui permettent de définir un cooldown par utilisateur/serveur/salon en une seule ligne.

Votre bot s'effondre sous le spam ? Je peux vous aider à le renforcer avec des cooldowns de commandes, la gestion des rate limits et une architecture résistante aux abus. Contactez-moi et parlons de votre projet.

Bu kategorideki tüm yazılar →

Devamı için