Cooldowns
Spam is something you generally want to avoid, especially if one of your commands require calls to other APIs or takes a bit of time to build/send.
TIP
This section assumes you followed the Command Handling part.
First, add a cooldown property to your command. This will determine how long the user would have to wait (in seconds) before using the command again.
const { SlashCommandBuilder } = require('discord.js');
module.exports = {
cooldown: 5,
data: new SlashCommandBuilder()
.setName('ping')
.setDescription('Replies with Pong!'),
async execute(interaction) {
// ...
},
};
2
3
4
5
6
7
8
9
10
11
In your main file, initialize a Collection to store cooldowns of commands:
client.cooldowns = new Collection();
The key will be the command names, and the values will be Collections associating the user's id (key) to the last time (value) this user used this command. Overall the logical path to get a user's last usage of a command will be cooldowns > command > user > timestamp
.
In your InteractionCreate
event, add the following code:
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 defaultCooldownDuration = 3;
const cooldownAmount = (command.cooldown ?? defaultCooldownDuration) * 1_000;
if (timestamps.has(interaction.user.id)) {
// ...
}
try {
// ...
} catch (error) {
// ...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
You check if the cooldowns
Collection already has an entry for the command being used. If this is not the case, you can add a new entry, where the value is initialized as an empty Collection. Next, create the following variables:
now
: The current timestamp.timestamps
: A reference to the Collection of user ids and timestamp key/value pairs for the triggered command.cooldownAmount
: The specified cooldown for the command, converted to milliseconds for straightforward calculation. If none is specified, this defaults to three seconds.
If the user has already used this command in this session, get the timestamp, calculate the expiration time, and inform the user of the amount of time they need to wait before using this command again. Note the use of the return
statement here, causing the code below this snippet to execute only if the user has not used this command in this session or the wait has already expired.
Continuing with your current setup, this is the complete if
statement:
if (timestamps.has(interaction.user.id)) {
const expirationTime = timestamps.get(interaction.user.id) + cooldownAmount;
if (now < expirationTime) {
const expiredTimestamp = Math.round(expirationTime / 1_000);
return interaction.reply({ content: `Please wait, you are on a cooldown for \`${command.data.name}\`. You can use it again <t:${expiredTimestamp}:R>.`, flags: MessageFlags.Ephemeral });
}
}
2
3
4
5
6
7
8
Since the timestamps
Collection has the user's id as the key, you can use the get()
method on it to get the value and sum it up with the cooldownAmount
variable to get the correct expiration timestamp and further check to see if it's expired or not.
The previous user check serves as a precaution in case the user leaves the guild. You can now use the setTimeout
method, which will allow you to execute a function after a specified amount of time and remove the timeout.
if (timestamps.has(interaction.user.id)) {
// ...
}
timestamps.set(interaction.user.id, now);
setTimeout(() => timestamps.delete(interaction.user.id), cooldownAmount);
2
3
4
5
6
This line causes the entry for the user under the specified command to be deleted after the command's cooldown time has expired for them.
Resulting code
If you want to compare your code to the code we've constructed so far, you can review it over on the GitHub repository here open in new window.