Discord.js Cooldowns with Time Remaining using Command Handler - javascript

I am looking to make a cool down system for discord bot commands using discord.js. I am looking for it to show the time remaining left on the cool down when the user tries to do the command. Currently I have it so it does a cool down using the command handler so that I just have to add "timeout: '10000'," although I cant seem to find a way to get it show the time remaining by using this system.
This is the code that I currently have in my message.js file so that is can be used with the command handler so that I do not have to write the timeout code on every command file. Code below is the whole message.js file.
const Timeout = new Set();
const { MessageEmbed } = require('discord.js')
const {prefix} = require('../../config.json')
const ms = require('ms')
module.exports=async(bot, message)=>{
if(message.author.bot) return;
if(!message.content.toLowerCase().startsWith(prefix)) return;
if(!message.member) message.member = await message.guild.fetchMember(messsage);
if(!message.guild) return;
const args = message.content.slice(prefix.length).trim().split(/ +/g);
const cmd = args.shift().toLowerCase();
if(cmd.length === 0) return;
let command = bot.commands.get(cmd);
if(!command) command = bot.commands.get(bot.aliases.get(cmd));
if(command){
if(command.timeout){
if(Timeout.has(`${message.author.id}${command.name}`)){
return message.reply(`**Slow down, you can only use this command every ${ms(command.timeout)}!**`)
} else {
command.run(bot, message, args);
Timeout.add(`${message.author.id}${command.name}`)
setTimeout(() => {
Timeout.delete(`${message.author.id}${command.name}`)
}, command.timeout);
}
} else {
command.run(bot, message, args)
}
}
}
Current response is above in bold text.
For reference, the message.js file is referenced in the following code in my index.js file.
bot.on('message', async message =>{
require('./events/guild/message')(bot, message)
})
The following is what I have to put at the beginning of each command file, with a simple command example shown for reference.
const Discord = require('discord.js');
module.exports={
name: 'test',
category: 'info',
timeout: '15000', //This would result in a 15 second cooldown as time is in ms.
run: async(bot, message, args) =>{
message.channel.send(`test`)
}
}
To conclude, I am looking to keep my system, but instead of it saying "Slow down, you can only use this command every 15000! (For example above) I would like it to say something in the lines of "Slow it down, you can use this command again in 10s.
The default cooldown is 15s.

If I'm not mistaken you want to convert every 15000 into every 15s?
You already have the ms module so looks like you are just confused how to use it:
If it receives a string it converts it into ms, if it receives a number it converts it into a readable format like 1d 2h 3m,
In your module.exports you have it a string, so make it a number and everything is fixed.
That string might also intercept with setTimeout(func, time)
If for some reason you don't want to change the module.exports.timeout into a string, before you call ms, you will have to do parseInt(command.timeout)
code:
let command = bot.commands.get(cmd) || bot.commands.get(bot.aliases.get(cmd));
if (!command) return;
if (!command.timeout) return command.run(bot, message, args);
//if you changed it to a number in module.exports you don't have to parseInt it
const timeout = parseInt(command.timeout);
if (Timeout.has(`${message.author.id}${command.name}`)) {
return message.reply(`**Slow down, you can only use this command every ${ms(timeout)}!**`)
} else {
command.run(bot, message, args);
Timeout.add(`${message.author.id}${command.name}`)
setTimeout(() => {
Timeout.delete(`${message.author.id}${command.name}`)
}, timeout);
}
Second part:
You will need to track when you set the timeout, the issue with using the Set class is that it's
not a key value based, so there's two options:
Set.add({ key: key, time: Date.now()}) or use Discord.Collection / Map
First: still using Set, setting objects instead:
const timeout = command.timeout;
const key = message.author.id + command.name;
let found;
for(const e of Timeout) {
if(e.key === key) {
found = e;
//possibly bad practice, arguable
break;
}
}
if(found) {
const timePassed = Date.now() - found.time;
const timeLeft = timeout - timePassed;
//the part at this command has a default cooldown of, did you want to hard code 15s? or have it be the commands.config.timeout?
return message.reply(`**Slow down, you can use this command again in ${ms(timeLeft)} This command has a default cooldown of ${timeout}!**`)
} else {
command.run(bot, message, args);
Timeout.add({ key, time: Date.now() });
setTimeout(() => {
Timeout.delete(key);
}, timeout);
}
Second: Discord.Collection or Map works too since its just an extended class from that
I'm going with Map, if you use Collection just do:
const { MessageEmbed, Collection } = require("discord.js");
const Timeout = new Collection();
Map code:
const Timeout = new Map();
After code:
const timeout = command.timeout;
const key = message.author.id + command.name;
const found = Timeout.get(key);
if(found) {
const timePassed = Date.now() - found;
const timeLeft = timeout - timePassed;
//the part at this command has a default cooldown of, did you want to hard code 15s? or have it be the commands.config.timeout?
return message.reply(`**Slow down, you can use this command again in ${ms(timeLeft)} This command has a default cooldown of ${timeout}!**`);
} else {
command.run(bot, message, args);
Timeout.set(key, Date.now());
setTimeout(() => {
Timeout.delete(key);
}, timeout);
}

Related

Cool down discord.js

Here is my code, I want it to send a message if the command has been used twice within like 10 seconds. Idk but it is very wrong
var bumpEmbedTwo = new Discord.MessageEmbed()
.setTitle('Cool Down!!!')
.setColor(0xFF0000)
.setDescription('Please waitt 30 more seconds before you use this command again')
setTimeout(() => {
message.channel.send(bumpEmbedTwo)
}, 5000)
var bumpEmbed = new Discord.MessageEmbed()
.setTitle('Time to Bump!')
.setColor(0xFF0000)
.setDescription('Please use the command `!d bump` to bump the server!!!')
setTimeout(() => {
message.channel.send('<#&812133021590880316>')
message.channel.send(bumpEmbed)
}, 1000)
The code you have provided isn't correct as your doing a set timeout without an await so it would completely ignore the purpose of the timeout. Also, whenever the .on('message') function is used, it would still carry out the command, what you have to do is to create an object: const cooldowns = new Discord.Collection(); and whenever the 'message' event is triggered, you would have to add whatever amount of cool down you want.
So here's the code:
const now = Date.now();
let cooldownAmount = 5 * 1000; // In this case, it is 5 seconds
if (cooldowns.has(message.author.id)) {
const expirationTime = cooldowns.get(message.author.id) + cooldownAmount;
if (now < expirationTime) {
const timeLeft = (expirationTime - now) / 1000;
return message.channel.send(`Please wait ${timeLeft.toFixed(1)} more second(s) before using another command`);
}
}
if(!cooldowns.has(message.author.id)){
<command>.run() // Be sure to edit <command to whatever the fetched command is> and add to the (), e.g. message, args, bot, etc.
}
cooldowns.set(message.author.id, now);
setTimeout(() => cooldowns.delete(message.author.id), cooldownAmount);
PS: the client in the arrow symbols are for you to change.
If there are any extra questions, you can comment in this post! Or dm me on discord: N̷i̷g̷h̷t̷m̷a̷r̷e̷ ̷ Jeff#2616
One way to approach this problem would be to save user+command+time in DB or in some global variable.
And then perform a check.
Pseudo-code:
var storage = {
"/command": {
"user-id-123": "22/02/2021 5:27pm"
"user-id-345": "22/02/2021 3:12pm"
}
};
Chat.on("/command", (user) => {
if (!storage["/command"].hasOwnProperty(user.id)) {
storage["/command"][user.id] = Date.now();
}
if (storage["/command"][user.id] > Date.now() + 10 seconds) {
return "Please wait more";
} else {
return "I'm running your command";
}
});

Discord.js why is my help command not working?

I have created a dynamic help command following the instructions on the documantation of discord.js. When I'm using //help, it's working properly, but //help ping, for example, is not.I'm not sure why this is happening, I've tried many things to fix that and nothing has worked. Any insight? code below:
index.js
// nodejs for filesystem
const fs = require("fs");
// require the discord.js module
const Discord = require("discord.js");
global.Discord = Discord;
// require canvas module for image manipulation
const Canvas = require("canvas");
// link to .json config file
const { prefix, token, adminRole } = require("./config.json");
// create a new discord client
const client = new Discord.Client();
client.commands = new Discord.Collection();
global.client = client;
const commandFiles = fs.readdirSync("./commands").filter(file => file.endsWith(".js"));
// ...
let target;
let targetName;
global.target = "000000000000000000";
global.targetName = "null";
global.adminRole = "738499487319720047";
// 737693607737163796
let hasRun = false;
for (const file of commandFiles) {
const command = require(`./commands/${file}`);
// set a new item in the Collection
// with the key as the command name and the value as the exported module
client.commands.set(command.name, command);
}
const cooldowns = new Discord.Collection();
// event triggers only once, right after bot logs in
client.once("ready", () => {
console.log("Ready!");
console.log(adminRole);
client.user.setActivity("You", { type: "WATCHING" });
});
// for new member join - sends message including attachent
client.on("guildMemberAdd", async member => {
const channel = member.guild.channels.cache.find(ch => ch.name === "welcome");
global.channel = channel;
if (!channel) return;
const canvas = Canvas.createCanvas(700, 250);
const ctx = canvas.getContext("2d");
const background = await Canvas.loadImage("./wallpaper.jpg");
ctx.drawImage(background, 0, 0, canvas.width, canvas.height);
ctx.strokeStyle = "#74037b";
ctx.strokeRect(0, 0, canvas.width, canvas.height);
// Slightly smaller text placed above the member's display name
ctx.font = "28px sans-serif";
ctx.fillStyle = "#ffffff";
ctx.fillText("Welcome to the server,", canvas.width / 2.5, canvas.height / 3.5);
// Add an exclamation point here and below
ctx.fillStyle = "#ffffff";
ctx.fillText(`${member.displayName}!`, canvas.width / 2.5, canvas.height / 1.8);
ctx.beginPath();
ctx.arc(125, 125, 100, 0, Math.PI * 2, true);
ctx.closePath();
ctx.clip();
const avatar = await Canvas.loadImage(member.user.displayAvatarURL({ format: "jpg" }));
ctx.drawImage(avatar, 25, 25, 200, 200);
const attachment = new Discord.MessageAttachment(canvas.toBuffer(), "welcome-image.png");
channel.send(`Welcome to the server, ${member}!`, attachment);
});
// listening for messages.
client.on("message", message => {
hasRun = false;
// if (!message.content.startsWith(prefix) || message.author.bot) return;
// log messages
console.log(`<${message.author.tag}> ${message.content}`);
// create an args var (const), that slices off the prefix entirely, removes the leftover whitespaces and then splits it into an array by spaces.
const args = message.content.slice(prefix.length).trim().split(/ +/);
global.args = args;
// Create a command variable by calling args.shift(), which will take the first element in array and return it
// while also removing it from the original array (so that you don't have the command name string inside the args array).
const commandName = args.shift().toLowerCase();
const command = client.commands.get(commandName) ||
client.commands.find(cmd => cmd.aliases && cmd.aliases.includes(commandName));
if (message.author.id === global.target) {
// more code (excluded because its too long)
}
if (!command) return;
if (command.guildOnly && message.channel.type !== "text") {
return message.reply("I can't execute that command inside DMs!");
}
if (command.args && !args.length) {
let reply = `You didn't provide any arguments, ${message.author}!`;
if (command.usage) {
reply += `\nThe proper usage would be: \`${prefix}${command.name} ${command.usage}\``;
}
return message.channel.send(reply);
}
if (!cooldowns.has(command.name)) {
cooldowns.set(command.name, new Discord.Collection());
}
const now = Date.now();
const timestamps = cooldowns.get(command.name);
const cooldownAmount = (command.cooldown || 3) * 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.name}\` command.`);
}
}
timestamps.set(message.author.id, now);
setTimeout(() => timestamps.delete(message.author.id), cooldownAmount);
try {
target, targetName = command.execute(message, command, args, target, targetName);
}
catch (error) {
console.error(error);
message.reply("there was an error trying to execute that command!");
}
});
client.login(token);
help.js
const { prefix } = require("../config.json");
module.exports = {
name: "help",
description: "List all of my commands or info about a specific command.",
aliases: ["commands"],
usage: "[command name]",
cooldown: 5,
execute(message, args) {
const data = [];
const { commands } = message.client;
if (!args.length) {
data.push("Here's a list of all my commands:");
data.push(commands.map(command => command.name).join(", "));
data.push(`\nYou can send \`${prefix}help [command name]\` to get info on a specific command!`);
return message.author.send(data, { split: true })
.then(() => {
if (message.channel.type === "dm") return;
message.reply("I've sent you a DM with all my commands!");
})
.catch(error => {
console.error(`Could not send help DM to ${message.author.tag}.\n`, error);
message.reply("it seems like I can't DM you!");
});
}
const name = args[0].toLowerCase();
const command = commands.get(name) || commands.find(c => c.aliases && c.aliases.includes(name));
if (!command) {
return message.reply("that's not a valid command!");
}
data.push(`**Name:** ${command.name}`);
if (command.aliases) data.push(`**Aliases:** ${command.aliases.join(", ")}`);
if (command.description) data.push(`**Description:** ${command.description}`);
if (command.usage) data.push(`**Usage:** ${prefix}${command.name} ${command.usage}`);
data.push(`**Cooldown:** ${command.cooldown || 3} second(s)`);
message.channel.send(data, { split: true });
},
};
I'd need to see your ping command, and the error itself to know for sure, but I think your problem is in your index.js file. I followed the same guide for my bots, and I haven't run into this issue. Without being able to see the error and the ping command, here are some places to look to help your troubleshooting:
If your problem is in your index.js file, my guess is it'll be in your try, catch statement, possibly where you're passing in arguments, it could be that help isn't receiving the arguments properly. For example:
Function(arg1, arg2) and Function(arg1) aren't the same thing. They may be named the same, and share an argument, but the arguments you pass in determine which one is executed, so if you pass in two arguments, then it should execute the first function, and ignore the second. If you pass in only one, then it should execute the second function, and ignore the first.
I see in your try catch, that you're passing in a ton of arguments to the command, but the arguments help accepts don't match what you're trying to pass in, therefore it may not be seeing the arguments at all, which could explain why it works with no arguments, but fails when you try to pass one in.
This is where seeing the error/result could help, as you only said it doesn't work properly, you didn't say what it did. If the command executed as if there was no arguments despite one existing, then that would count as "not working properly", but if the command gave you an error due to it not being able to process the parameters properly, then the issue would be in your help.js command
If your problem is in your help.js file, since you said it works with no arguments, and the error occurs when you try to get info on a specific command, then the problem will be closer to the bottom of the code provided, as that's where the info is gathered, and printed.
The issue may be that it's not seeing which command you're talking about, doesn't know you're asking for it, or it can't get the requested information because it doesn't exist.
If your problem is in your ping.js file, it could be because you're help.js is working fine, but ping.js might not have the information that help is looking for, for instance, if it didn't have a name, or the name in the code doesn't match the name of the file (i've run into that issue a lot...). It could also be that you're missing a "}" in the file, as that would wreck it as well.
Added args = global.args; in the top of my help file, after the modules.export, it fixed the issue.

Why are my Commands Only Working for 1 Person

I’m not sure what happened, I can’t seem to find anything in the code that would cause this, but suddenly only one person can use commands. Even commands set for certain permissions can’t be used by anyone except that person. The person it got stuck to is fairly new to the server too which is strange. It seems to work fine on our testing server but on the main one it’s only working for 1 person.
Below is my main file where I have the command handler
const fs = require('fs');
const Discord = require('discord.js');
const { prefix, token } = require('./config.json');
const profanities = require('profanities/index.json');
var used = false;
const client = new Discord.Client();
client.commands = new Discord.Collection();
const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const command = require(`./commands/${file}`);
client.commands.set(command.name, command);
}
client.once('ready', () => {
console.log('Bot Online!');
});
// This is the start of the main function when the bot is turned on
client.on('message', message => {
if (message.author.bot || !message.guild) return;
const words = message.content.toLowerCase();
if (words.includes('shalomi')) {
setTimeout(function() {
message.channel.send(`Shut up ${message.author}`);
}, 1500);
}
if (words.includes(' bum ')) {
setTimeout(function() {
message.channel.send('Are we talking about <#458068171241553921>?!');
}, 1500);
}
if (words == 'prefix') {
message.channel.send(`The current prefix is "${prefix}".`);
}
if (used) return;
else {
if (words == 'f') {
message.channel.send('F');
used = true;
setTimeout(() => {
used = false;
}, 1000 * 20);
}
}
for (let x = 0; x < profanities.length; x++) {
if (message.member.roles.some(role => role.id === '483641589193900043')) return;
else {
if (message.content.toUpperCase().includes(profanities[x].toUpperCase())) {
message.channel.send('Oooooooh you said a bad word!');
client.channels.get('484375912389935126').send(`Message was deleted due to use of a blocked word:\n\n"${message.content}"`);
message.delete();
return;
}
}
}
// The bot will not respond if there is no prefix,
// the user that typed it was a bot,
// or if it was not sent from in the server
if (!message.content.startsWith(prefix) || message.author.bot || !message.guild) return;
// Creates the arguments variable and separates it with a space
// and creates the command variable
const args = message.content.slice(prefix.length).split(' ');
const commandName = args.shift().toLowerCase();
if (!client.commands.has(commandName)) return;
const command = client.commands.get(commandName);
if (command.guildOnly && message.channel.type !== 'text') {
return message.reply('I can\'t execute that command inside DMs!');
}
try {
command.execute(message, args);
}
catch (error) {
console.error(error);
message.channel.send('There was an error trying to execute that command!\nCheck the console for details.');
}
});
// This logs in the bot with the specified token found in config
client.login(token);
Sorry, couldn't format the code properly in comments. Try putting
if (!message.content.startsWith(prefix) || message.author.bot || !message.guild) return;
// Creates the arguments variable and separates it with a space
// and creates the command variable
const args = message.content.slice(prefix.length).split(' ');
const commandName = args.shift().toLowerCase();
if (!client.commands.has(commandName)) return;
All the way at the top, right after the if (message.author.bot || !message.guild) return; line.
So here is the most recent command I did which was done a couple days ago. When I first did this command, I forgot to put the comma and semi colon at the bottom (the ones that are there now) like I was supposed to. When I tested it in the normal server, the first person to use the command is now the only person that can use every command.
Would forgetting those punctuations be the reason why every command now only works for that person who used the "hug" command first? I've tried deleting that command and redoing it thinking it would delete information that could possibly be saved to a variable. Like maybe it's looking for that one author only since it never closed out of the loop? I'm not sure
module.exports = {
name: 'hug',
description: 'Used to hug everyone or mention a user to hug them specifically.',
execute(message, args) {
args = message.mentions.users.size;
if (!args) {
message.delete();
return message.channel.send(`${message.author} gives a big ol' hug to everyone!`);
}
else {
message.delete();
return message.channel.send(`${message.author} sends love to ${message.mentions.users.first()} with a hug!`);
}
},
};

Discord.js Delete message through an existing function

The Issue
When executing my code i am really getting no errors at the moment but would like to add to a function but have tried almost every way of handling it. At this point the variable has been removed due to confusion and frustration.
What needs to happen is, the User that initiates the command, their message gets deleted after a short delay. I have tried message.delete(1000) and other Variants for v12 but no luck. Not able to pass the "message" variable from my active function?
Maybe i am completely off and just making it hard on myself, i don't know. Honestly embarrassing that i couldn't figure out a message.delete. Please help. Apologies for the ignorance.
const Discord = require('discord.js');
const bot = new Discord.Client();
const token = "";
const PREFIX = "!";
const fs = require('fs');
bot.commands = new Discord.Collection();
const commandFiles = fs.readdirSync('./commands/').filter(file => file.endsWith('.js'));
for(const file of commandFiles){
const command = require(`./commands/${file}`);
bot.commands.set(command.name, command);
}
bot.on('ready', () => {
console.log("The bot is active and ready to go!");
});
bot.on('message', function(message) {
if(message.content[0] === PREFIX) {
let command = message.content.substring(message.content.indexOf(" ") + 1, message.content.length);
}
});
bot.on('message', message => {
let args = message.content.substring(PREFIX.length).split(" ");
switch (args[0]) {
case "crash":
bot.commands.get('crash').execute(message, args);
break;
case "hello":
bot.commands.get('hello').execute(message, args);
break;
case "purge":
bot.commands.get('purge').execute(message, args);
break;
}
});
bot.login(token);
Here is an example of "crash.js" for reference. Don't know if i need to execute delete from there?
module.exports = {
name: 'crash',
description: "Crash",
execute(message, args){
message.author.send("Here is the link you requested");
}
}
You can execute delete from within your module. The message is passed as a full object so you just call the delete method on it. However, the Options are an Object which means it needs to be defined as such. For clarity, I'm going to use another variable but this can be done inline.
let options = {
timeout: 1000,
reason: 'Because I said so.'
}
message.delete(options);
or inline...
message.delete({timeout: 1000});

How to make a command that stores channelID in database?

How can I make a command that stores a channelID in a database, also by database, kind of like a big text file on my machine that has a server id, then a channel id. How should I go about this?
Im using:
discord.js
sqlite3
sequelize
How i think it would go would be:
User: ?setlog #channelmentionhere
Bot: makes new entire into a textfile or something
I've done a ton of research, i cannot understand much of it, if i did understand it, i would have troubles with it.
My bot code:
client.on('message', message => {
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).split(/ +/);
const commandName = args.shift().toLowerCase();
const user = message.mentions.users.first();
const command = client.commands.get(commandName)
|| client.commands.find(cmd => cmd.aliases && cmd.aliases.includes(commandName));
if (!command) return;
if (command.guildOnly && message.channel.type !== 'text') {
return message.reply('I can\'t execute that command inside DMs!');
}
if (command.args && !args.length) {
let reply = `You didn't provide any arguments, ${message.author}!`;
if (command.usage) {
reply += `\nThe proper usage would be: \`${prefix}${command.name} ${command.usage}\``;
}
return message.channel.send(reply);
}
if (!cooldowns.has(command.name)) {
cooldowns.set(command.name, new Discord.Collection());
}
const now = Date.now();
const timestamps = cooldowns.get(command.name);
const cooldownAmount = (command.cooldown || 3) * 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.name}\` command.`);
}
}
timestamps.set(message.author.id, now);
setTimeout(() => timestamps.delete(message.author.id), cooldownAmount);
try {
command.execute(message, args, user);
} catch (error) {
console.error(error);
message.reply('there was an error trying to execute that command!');
}
});
I would recommend Quick.db (npm i quick.db) This database stores persistently (doesn't wipe on bot restarts)
In the following code snippets, I am using quick.db but this shouldn't be too hard using a Json file.
Setting the channelID per guild: (You don't have to use member but I do out of habit)
let member = message.guild.member(message.member);
db.set(`${member.guild.id}-modlogs`, message.mentions.channels.first().id);
Grabbing the Database in other commands then sending the channel messages:
let dbchannelID = db.get(`${member.guild.id}-modlogs`)
let msgchannel = member.guild.channels.get(dbchannelID);
msgchannel.send("Blah Blah")
Explaining the db.set part:
So db.set is setting the database and the database here would be modlogs. The text between db.set & modlogs is how you want to set it as, in this it is setting per guild. You can change that to message.author.id and it would set to the author etc.
Hopefully, this helped in some way.

Categories

Resources