1
votes

This bot works perfectly except for the cooldown. If someone types the command it responds with one of the random sentences. I want to add a user cooldown to the bot, so each user has to wait until they can use it again. The thing is, the cooldown part of the bot is useless, it doesn't work at all. Can someone please help me? Detailed answers are very appreciated.

const Discord = require('discord.js');

const client = new Discord.Client();

const prefix = '!';

const used = new Map();

const talkedRecently = new Set();

client.once('ready', () => {
    console.log('Ready!');
});


  client.on("message", (message) => {
    if (message.content.startsWith("!random") && talkedRecently.has(msg.author.id)) {
    msg.channel.send("Wait 1 minute before getting typing this again. - " + msg.author);}
    else {
        let tomb=["something1","something2", "something3",];
  
  let i = tomb[Math.floor(Math.random()*tomb.length)];
      message.channel.send(i);
    }
            talkedRecently.add(msg.author.id);
        setTimeout(() => {
          // Removes the user from the set after a minute
          talkedRecently.delete(msg.author.id);
        }, 60000);
    )};
 

client.login(process.env.token);
2

2 Answers

1
votes

I have done a tidy-up of your code and modified it to allow it to be extended easier. I have also adapted the Discord.js cooldown tutorial to allow cooldowns to automatically be added to each command.

If you would like to make the code even better, I highly recommend that you read through the Discord.js guide.

A full explanation can be found at the bottom of this answer.

Demonstration Playground: discord.gg/2duzjPT.

Demonstration

Discord command cooldown example

Code

const Discord = require('discord.js');
const client = new Discord.Client();

const prefix = '!';

const cooldowns = new Discord.Collection();

client.once('ready', () => {
  console.log('Ready!');
});

client.on('message', (message) => {
  if (message.author.bot || !message.content.startsWith(prefix)) return;

  const [ command, ...args ] = message.content.slice(prefix.length).split(/\s+/g);

  if (!cooldowns.has(command)) {
    cooldowns.set(command, new Discord.Collection());
  }

  const now = Date.now();
  const timestamps = cooldowns.get(command);
  const cooldownAmount = 1 * 60 * 1000;

  if (timestamps.has(message.author.id)) {
    const expirationTime = timestamps.get(message.author.id) + cooldownAmount;

    if (now < expirationTime) {
      const timeLeft = (expirationTime - now) / 1000;
      return message.reply(`Please wait ${timeLeft.toFixed(1)} more second(s) before reusing the \`${command}\` command.`);
    }
  }

  timestamps.set(message.author.id, now);
  setTimeout(() => timestamps.delete(message.author.id), cooldownAmount);

  switch (command) {
    case 'random':
      let tomb = ['something1', 'something2', 'something3'];
  
      let i = tomb[Math.floor(Math.random() * tomb.length)];
      message.channel.send(i);
      break;
    default:
      message.reply(`The command \`${command}\` was not recognized.`);
  }
});

client.login(process.env.token);

Explanation

A collection is created to store commands that have cooldowns on them:

const cooldowns = new Discord.Collection();

The message handler exits if the message was sent by a bot or the message doesn't start with the prefix (!):

if (message.author.bot || !message.content.startsWith(prefix)) return;

The message is split into a command and arguments array; this is done by removing the prefix on the start with .slice(prefix.length) and then splitting the message at each white space gap with .split(/\s+/g):

const [ command, ...args ] = message.content.slice(prefix.length).split(/\s+/g);

If the requested command is not in the cooldowns collection, it is added and its value is set to a new collection:

if (!cooldowns.has(command)) {
  cooldowns.set(command, new Discord.Collection());
}

The current time in milliseconds is placed in a variable:

const now = Date.now();

The collection for the requested command is placed in a variable; this contains all the users that have used the requested command within 1 minute:

const timestamps = cooldowns.get(command);

The default cooldown time is set to 1 minute:

const cooldownAmount = 1 * 60 * 1000;

This checks if the user is part of the collection for the requested command; if they are, the time when they can use the command again is calculated and a check is done to see if the expiration time has passed; if it hasn't, the time remaining is calculated, the user is sent a message and the message handler exits:

if (timestamps.has(message.author.id)) {
  const expirationTime = timestamps.get(message.author.id) + cooldownAmount;

  if (now < expirationTime) {
    const timeLeft = (expirationTime - now) / 1000;
    return message.reply(`Please wait ${timeLeft.toFixed(1)} more second(s) before reusing the \`${command}\` command.`);
  }
}

If the user has not used the command within 1 minute, their user ID is added to the collection and a timer is created to automatically delete their user ID from the collection after a minute has passed:

timestamps.set(message.author.id, now);
setTimeout(() => timestamps.delete(message.author.id), cooldownAmount);

Finally, the bot checks what command the user entered and executes certain code; if the command is not recognized, the user is sent a message:

switch (command) {
  case 'random':
    let tomb = ['something1', 'something2', 'something3'];

    let i = tomb[Math.floor(Math.random() * tomb.length)];
    message.channel.send(i);
    break;
  default:
    message.reply(`The command \`${command}\` was not recognized.`);
}

References

Discord.js (documentation)

JavaScript (documentation)

0
votes

In some detail:

The first issue is from the fact that your reference to msg doesn't exist, so your second conditional is not passing ever. What it seems you're trying to do is reference the parameter message and this would hold the object with the data you need.

{
    author: {
        id: 'm5000'
    }
}

Try the following

if (message.content.startsWith("!random") && talkedRecently.has(message.author.id)) {
   ... Your code here
}

The second issue is just a "Woops", and in the future you could look into ESLint for catching these early.

it would also seem you're closing the client.on() method wrongly with )}; rather than });