Sistema de Cooldown para Bot do Discord: Como Implementar Limites por Usuário
🔍 WiseChecker

Sistema de Cooldown para Bot do Discord: Como Implementar Limites por Usuário

Quando um bot do Discord responde a todos os comandos instantaneamente sem qualquer atraso, os usuários podem enviar spam e sobrecarregar os recursos do bot. Um sistema de cooldown evita isso aplicando um período de espera entre comandos para cada usuário. Este artigo explica como implementar limites de cooldown por usuário em um bot do Discord usando Discord.js v14 e Node.js. Você aprenderá o conceito central de cooldowns, verá um exemplo de código funcional e entenderá como evitar armadilhas comuns, como vazamentos de memória e cálculos incorretos de tempo.

Principais Conclusões: Implementando Cooldowns por Usuário em Bots do Discord

  • Mapa de cooldown baseado em Collection: Use um Map ou Collection para armazenar o timestamp do último comando de cada usuário.
  • Comparação de timestamps: Subtraia o timestamp armazenado de Date.now() e compare com a duração do cooldown em milissegundos.
  • Resposta de erro com tempo restante: Envie uma mensagem mostrando quantos segundos faltam para o usuário poder usar o comando novamente.

Como Funciona um Cooldown por Usuário no Discord.js

Um cooldown por usuário limita a frequência com que um único usuário pode executar um comando específico. O bot armazena o horário em que o usuário executou o último comando. Quando o usuário executa o mesmo comando novamente, o bot verifica se tempo suficiente já passou. Se não, o bot recusa o comando e informa ao usuário quanto tempo deve esperar.

A abordagem mais comum usa um objeto Map do JavaScript ou Collection do Discord.js. A chave é o ID do usuário, e o valor é um timestamp em milissegundos. Quando um comando é executado, o bot armazena Date.now(). Na próxima invocação, o bot compara Date.now() com o valor armazenado. Se a diferença for menor que a duração do cooldown, o comando é bloqueado.

Este método funciona tanto para comandos de barra quanto para comandos de prefixo. Para comandos de barra, a verificação de cooldown ocorre dentro da função execute. Para comandos de prefixo, a verificação ocorre dentro do manipulador de eventos messageCreate. A mesma lógica se aplica a ambos.

Pré-requisitos

Antes de implementar cooldowns, certifique-se de ter:

  • Node.js versão 16.9.0 ou superior instalado
  • Um aplicativo de bot do Discord criado no Portal do Desenvolvedor do Discord
  • Discord.js v14 instalado (npm install discord.js@14)
  • Conhecimento básico de JavaScript assíncrono e manipulação de eventos

Passos para Implementar Cooldowns por Usuário em um Comando de Barra

Os passos a seguir mostram como adicionar um cooldown de 10 segundos a um comando de barra. O bot armazena cooldowns em um Collection global e verifica timestamps antes de executar a lógica do comando.

  1. Crie um novo Collection no arquivo principal do bot
    Adicione esta linha no topo do seu index.js ou arquivo principal: const cooldowns = new Discord.Collection();. Este collection armazenará IDs de usuários e seus timestamps de cooldown para cada comando.
  2. Defina a duração do cooldown em milissegundos
    Dentro do manipulador de comandos ou diretamente no arquivo do comando, defina uma constante: const cooldownDuration = 10000;. Isso equivale a 10 segundos. Ajuste este número conforme necessário.
  3. Verifique se o usuário já possui uma entrada de cooldown
    Dentro da função execute do seu comando de barra, use if (!cooldowns.has(commandName)) cooldowns.set(commandName, new Discord.Collection());. Isso garante que cada comando tenha seu próprio collection de cooldown.
  4. Obtenha o timestamp atual e o timestamp armazenado para o usuário
    Escreva: const now = Date.now(); e const timestamps = cooldowns.get(commandName);. Em seguida, obtenha o timestamp armazenado do usuário: const cooldownAmount = cooldownDuration; e const userTimestamp = timestamps.get(interaction.user.id);.
  5. Compare timestamps e bloqueie se ainda estiver em cooldown
    Escreva: if (userTimestamp) { const expirationTime = userTimestamp + cooldownAmount; if (now < expirationTime) { const timeLeft = (expirationTime - now) / 1000; return interaction.reply({ content: `Aguarde ${timeLeft.toFixed(1)} segundos antes de usar este comando novamente.`, ephemeral: true }); } }. Isso envia uma resposta privada mostrando o tempo restante.
  6. Defina o novo timestamp após o comando ser executado
    Após a lógica do comando ser executada, escreva: timestamps.set(interaction.user.id, now);. Isso atualiza o cooldown para que o usuário tenha que esperar novamente. Opcionalmente, adicione um timeout para limpar entradas antigas: setTimeout(() => timestamps.delete(interaction.user.id), cooldownAmount);.

Após seguir estes passos, o comando de barra recusará a execução se o usuário executá-lo mais de uma vez a cada 10 segundos.

Passos para Implementar Cooldowns por Usuário em um Comando de Prefixo

Comandos de prefixo funcionam de forma diferente porque dependem do evento messageCreate. A lógica de cooldown é semelhante, mas você usa message.author.id em vez de interaction.user.id.

  1. Crie o Collection de cooldown fora do manipulador de eventos
    No arquivo principal do bot, adicione: const cooldowns = new Discord.Collection();. Este collection armazena timestamps para cada nome de comando.
  2. Dentro do evento messageCreate, verifique o prefixo do comando
    Escreva: if (!message.content.startsWith(prefix) || message.author.bot) return;. Extraia o nome do comando e argumentos como de costume.
  3. Inicialize o collection de cooldown para este comando
    Escreva: if (!cooldowns.has(commandName)) cooldowns.set(commandName, new Discord.Collection());.
  4. Obtenha timestamps e verifique o cooldown
    Escreva: const now = Date.now(); const timestamps = cooldowns.get(commandName); const cooldownAmount = 10000; const userTimestamp = timestamps.get(message.author.id);. Em seguida, compare: if (userTimestamp) { const expirationTime = userTimestamp + cooldownAmount; if (now < expirationTime) { const timeLeft = (expirationTime - now) / 1000; return message.reply(`Aguarde ${timeLeft.toFixed(1)} segundos antes de usar este comando novamente.`); } }.
  5. Defina o novo timestamp e opcionalmente limpe
    Escreva: timestamps.set(message.author.id, now); setTimeout(() => timestamps.delete(message.author.id), cooldownAmount);.

Agora seu comando de prefixo também aplica um cooldown de 10 segundos por usuário.

Erros Comuns e Limitações a Evitar

Cooldown Não Persiste Após Reinicialização do Bot

O Collection em memória é perdido quando o bot reinicia. Os usuários podem executar comandos imediatamente após uma reinicialização. Para persistir cooldowns entre reinicializações, armazene os timestamps em um banco de dados como SQLite ou Redis. Para a maioria dos bots pequenos, a abordagem em memória é suficiente.

Cooldown se Aplica a Todos os Comandos em Vez de Por Comando

Se você usar um único collection para todos os comandos, o cooldown de um usuário para um comando bloqueia todos os outros comandos. A correção é aninhar collections: um collection por nome de comando, cada um contendo timestamps de usuários. Os exemplos de código acima já implementam isso corretamente.

Erros de Cálculo de Tempo Devido à Deriva do Relógio do Servidor

Date.now() usa o relógio do sistema do servidor. Se o relógio sofrer deriva ou for alterado manualmente, os cooldowns podem expirar antes ou depois do previsto. Use process.hrtime.bigint() para tempo monotônico se precisar de alta precisão. Para a maioria dos bots, Date.now() é aceitável.

Vazamento de Memória por Entradas de Cooldown Não Utilizadas

Se você nunca excluir entradas de cooldown antigas, o Collection cresce indefinidamente. Sempre use setTimeout para excluir a entrada após o cooldown expirar. O exemplo acima inclui esta etapa de limpeza.

Comparação de Métodos de Armazenamento de Cooldown

Método de Armazenamento Collection em Memória Banco de Dados (SQLite / Redis)
Persistência Perdida na reinicialização Sobrevive à reinicialização
Desempenho Muito rápido, sem I/O Mais lento devido a I/O
Complexidade Simples, sem dependências externas Requer configuração de banco e consultas
Caso de uso Bots pequenos com baixo tráfego Bots grandes ou cooldowns entre servidores

Você pode implementar cooldowns por usuário usando a abordagem de Collection em memória abordada neste artigo. O sistema de cooldown usa um mapa de IDs de usuários para timestamps, compara-os a cada invocação de comando e bloqueia o comando até que o tempo de espera expire. Para o próximo passo, adicione uma duração de cooldown configurável por comando usando um arquivo JSON ou banco de dados. Para uso avançado, considere implementar um cooldown global que se aplique a vários servidores ou comandos usando Redis.