'Map items gets replaced every time node.js

I'm trying to implement cooldown to my command handler in my Discord bot, but I'm stuck.

The cooldown itself works, but when I try to use a command that is different from the one that I executed before, that cooldown gets replaced with the last one. Here's the code:

const cooldowns = new Map();

       try {
    
            const cooldown = cooldowns.get(interaction.user.id);
    
            if (cooldown && cooldowns.get("command") == command.data.name) {
    
                const remaining = ms(cooldown - Date.now(), {till: "second"});
    
                return interaction.reply({ content: `Please wait until cooldown expires! Remaining: ${codeLine(remaining)}`});
            }
    
            await command.execute(client, interaction, Discord, profileData);
    
            let startTime = Date.now() + command.cooldown;
            cooldowns.set(interaction.user.id, startTime);
            cooldowns.set("command", command.data.name); 
    
            setTimeout(async () => cooldowns.delete(interaction.user.id, "command"), command.cooldown);
        }
        catch (error) {
    
            console.log(`The interaction error is ${error}`);
    
            await interaction.reply({ content: "There was an error trying to execute this command. 🍐", ephemeral: true});
        }
    }

So for example when I use the command /hello, with a cooldown of 30 seconds, the 1st time it gets executed, and if I try to execute it again the bot responds with the cooldown saying that I can't use the command until the cooldown expires, which is correct, but if I use another command, for example /world with 0 cooldown, the command gets executed correctly, but the cooldown for the /hello command gets canceled, and it can be used again.

I'm assuming that the interaction.user.id and the "command" items get replaced with new ones, and I can't find a method to create new ones.



Solution 1:[1]

UPDATED

According to your requirement, I created a data structure like below

{
   "userA": {
      "/hello": startTime
      "/world": startTime
   },
   "userB": {
      "/hello": startTime
      "/world": startTime
   }
}

userA is for user id

/hello and /world are commands

startTime is the cooldown for each command

You can check this implementation with some comments



const cooldowns = {}; //similar to Map but simpler

try {

  //cannot find any user in cooldowns map
  if (!cooldowns[interaction.user.id]) {
    cooldowns[interaction.user.id] = {} //create map to keep commands
  }

  //check if the cooldown exists in the commands
  const cooldown = cooldowns[interaction.user.id][command.data.name];

  if (cooldown) {

    const remaining = ms(cooldown - Date.now(), {
      till: "second"
    });

    if (remaining > 0) {
      return interaction.reply({
        content: `Please wait until cooldown expires! Remaining: ${codeLine(remaining)}`
      });
    }

  }

  await command.execute(client, interaction, Discord, profileData);

  let startTime = Date.now() + command.cooldown;

  cooldowns[interaction.user.id][command.data.name] = startTime

  setTimeout(async () => delete cooldowns[interaction.user.id][command.data.name], command.cooldown);
} catch (error) {

  console.log(`The interaction error is ${error}`);

  await interaction.reply({
    content: "There was an error trying to execute this command. ?",
    ephemeral: true
  });
}

OLD ANSWER

 if (cooldown && cooldowns.get("command") == command.data.name) {
    const remaining = ms(cooldown - Date.now(), {till: "second"});
    return interaction.reply({ content: `Please wait until cooldown expires! Remaining: ${codeLine(remaining)}`});
}

I think this part is causing the problem cooldowns.get("command") == command.data.name. If your new command matches with the previous command, you check the cooldown.

BUT whenever your new command does not match with the previous one, you bypass that condition which is the system won't check the remaining time.

The possible fix should be

 if (cooldown) {
    const remaining = ms(cooldown - Date.now(), {till: "second"});
    return interaction.reply({ content: `Please wait until cooldown expires! Remaining: ${codeLine(remaining)}`});
}

You only need to check the cooldown from the user id, and don't need to check command matching.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1