How to load new commands without restarting bot - javascript

I have a working bot, with a functioning command handler, and I have a reload command to update my code for preexisting commands. Anytime I add a new command, I have to restart the whole bot. Since this particular bot has scripts on intervals running, restarting my bot would terminate all running intervals, forcing all users to manually restart them. I don't want to have to resort to restarting my bot anytime I add a new command, so I need help.
Here's my reload command right now:
const botconfig = require("../config.json");
module.exports = {
name: 'reload',
type: "Developer",
description: 'Reloads a command (developer only)',
cooldown: 1,
execute(message, args) {
if (message.author.id != botconfig.developerid) return message.channel.send("Only my developer can use this command...");
message.channel.send("Developer command confirmed!");
if (!args.length) return message.channel.send(`You didn't pass any command to reload!`);
const commandName = args[0].toLowerCase();
const command = message.client.commands.get(commandName) ||
message.client.commands.find(cmd => cmd.aliases && cmd.aliases.includes(commandName));
if (!command) return message.channel.send(`There is no command with name or alias \`${commandName}\`, ${message.author}!`);
delete require.cache[require.resolve(`./${command.name}.js`)];
try {
const newCommand = require(`./${command.name}.js`);
message.client.commands.set(newCommand.name, newCommand);
message.channel.send("Command `" + command.name + "` was reloaded!");
} catch (error) {
console.log(error);
message.channel.send("There was an error while reloading the `" + botconfig.prefix + command.name + "` command. \n\nError is as follows:\n``${error.message}`");
}
},
};
I want to add an optional "new" argument before the command name to look specifically for new commands, as the current code works, but it only sees preexisting commands. If it would be simpler to change the current code to look for new commands in addition, but still error out if it finds none, that'll be fine too.

Yes, you can use the following code so if the command is already loaded, it deletes it.
const botconfig = require("../config.json");
module.exports = {
name: 'reload',
type: "Developer",
description: 'Reloads a command (developer only)',
cooldown: 1,
execute(message, args) {
if (message.author.id != botconfig.developerid) return message.channel.send("Only my developer can use this command...");
message.channel.send("Developer command confirmed!");
if (!args.length) return message.channel.send(`You didn't pass any command to reload!`);
const commandName = args[0].toLowerCase();
if(message.client.commands.get(commandName)){
const command = message.client.commands.get(commandName) ||
message.client.commands.find(cmd => cmd.aliases && cmd.aliases.includes(commandName));
if (!command) return message.channel.send(`There is no command with name or alias \`${commandName}\`, ${message.author}!`);
delete require.cache[require.resolve(`./${command.name}.js`)];
}
try {
const newCommand = require(`./${commandName}.js`);
message.client.commands.set(commandName, newCommand);
message.channel.send("Command `" + commandName+ "` was reloaded!");
} catch (error) {
console.log(error);
message.channel.send("There was an error while reloading the `" + botconfig.prefix + commandName + "` command. \n\nError is as follows:\n``${error.message}`");
}
},
};

Related

Discord.JS Command Handler not working or updating

I am trying to add a command handler to my bot, however, only one command is working and no matter how I change the command, it does not update. Here is my code.
This is at the top underneath the packages and above the on ready code.
fs.readdir('./commands/', (_err, files) => {
files.forEach((file) => {
if (!file.endsWith('.js')) return;
bot.commands = new Discord.Collection()
let command = require(`./commands/${file}`); // access all details though this variable
bot.commands.set(command.name, command);
console.log(`๐Ÿ‘Œ Command loaded: ${command.name}`);
});
});
This is in the resources command
const Discord = require("discord.js")
const fs = require("fs")
module.exports = {
name: 'resources',
// any other details you might like, such as:
description: 'Shows resources',
execute(bot, message, args, prefix) {
// the actual function
let resourceText = fs.readFileSync('txtfiles/resources.txt', 'utf8')
message.suppressEmbeds(true)
message.channel.send(resourceText)
}
}
The resources command works just fine, but the ship command does not.
const Discord = require("discord.js")
const fs = require("fs")
module.exports = {
name: 'ship',
// any other details you might like, such as:
description: 'ships people',
execute(bot, message, args, prefix) {
// the actual function
message.channel.send("please work")
}
}
And this is in the "on messageCreate" thing
if(cmd===`${prefix}resources`){bot.commands.get('resources').execute(bot, message, args, prefix);}
if(cmd===`${prefix}ship`){bot.commands.get('ship').execute(bot, message, args, prefix);}
I have tried putting these underneath the packages
delete require.cache['./index.js'];
delete require.cache[require.resolve('./index.js')]
However, I am getting this error message
Unhandled promise rejection: TypeError: Cannot read properties of undefined (reading 'execute')
The resources command works just fine, even if we delete all the code in the file, and the ship command does not work at all.
So rather than put an if (cmd === block for every command, try using a dynamic one that won't require you to update your messageCreate section, every time you add a new command or delete one.
messageCreate section:
bot.on('messageCreate', async message => {
if (message.author.bot) return
if (!message.content.startsWith(config.prefix)) return
const args = message.content.slice(config.prefix.length).trim().split(/ +/)
const command = args.shift().toLowerCase()
const cmd = bot.commands.get(command)
if (!cmd) {
console.log("Unknown command")
return
} else {
cmd.run(bot, message, args)
}
})
Then you'll have to update your existing command that does work to match this style but just make any new ones like this:
module.exports = {
name: 'Command Name',
description: 'Command description',
run: async (bot, message, args) => {
// the actual function
}
}
So your ships command would look like this
module.exports = {
name: 'ships',
description: 'ships people',
run: async (bot, message, args) => {
message.channel.send({
content: 'Please work'
})
}
}
I'd change your command collection to this
bot.commands = new Discord.Collection()
const commandList = []
const commands = fs.readdirSync('./commands/').filter(file => file.endsWith('.js'))
for (const file of commands) {
const command = require(`./commands/${file}`)
commandList.push(`${file.split('.'[0])}`)
bot.commands.set(command.name, command)
}
console.log(`๐Ÿ‘Œ Commands loaded: ${commandList}`)

Discord only recognizing "ping" command in discord.js

In my Discord.JS bot, I have multiple commands setup (ping, beep, etc.) but Discord only recognizes "ping". I have tried multiple setups, and all are the same.
Here is my code:
const { Client, Intents } = require('discord.js');
const { token } = require('./config.json');
const client = new Client({ intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES] });
client.once('ready', () => {
console.log('Ready!');
});
client.on('interactionCreate', async interaction => {
if (!interaction.isCommand()) return;
const { commandName: command } = interaction;
if (command === 'ping') {
await interaction.reply('Pong!');
} else if (command === 'beep') {
await interaction.reply('Boop!');
} else if (command === 'server') {
await interaction.reply(`Server name: ${interaction.guild.name}\nTotal members: ${interaction.guild.memberCount}`);
} else if (command === 'user-info') {
await interaction.reply(`Your username: ${interaction.user.username}\nYour ID: ${interaction.user.id}`);
}
});
client.login(token);
And here is Discords command view when "/" is enter
As you can see, ping is the only thing being recognized by discord.
It is also worth noting the โ€˜pingโ€™ command has a description which the original description I setup, so it seems like issue is that Discord is not updating the commands each time the script changes. But, I donโ€™t know how to resolve that issue.
It seems like you only registered the ping command. You have to register each slash command individually.
I guess you registered the slashcommand some tile earlier, and have not removed it since. You are only responding in your code example to slashcommands, but you have to create them in the first hand.
Check here on how to do that.
it may take up to one hour to register a global command tho, so be patient. If you are fine, with slashcommands for one guild only, you can also only create guildCommands. These are up and running within a view minutes (under 10minutes max)
Here is a simple command, with which you can update the slashcommands (this is staright from the docs)
client.on('messageCreate', async message => {
if (!client.application?.owner) await client.application?.fetch();
if (message.content.toLowerCase() === '!deploy' && message.author.id === client.application?.owner.id) {
const data = [
{
name: 'ping',
description: 'Replies with Pong!',
},
{
name: 'pong',
description: 'Replies with Ping!',
},
];
const commands = await client.application?.commands.set(data);
console.log(commands);
}
});
NOTE: you have to be running the master branch of discord.js (aka Discord.js V13). If you have not installed it yet, you can install it by running: npm install discord.js#latest. Make sure, you have uninstalled the "normal" discord.js dependency beforehand, by running npm uninstall discord.js.
If you are not sure what version you currently have installed, simply run npm list
This worked for me, the idea is to render each command separately.
I also went for some hardcoded configs: guild_id & client_id (to win some time in dev mode)
const { Client, Collection, Intents } = require('discord.js');
const { token, client_id, guild_id } = require('./config.json');
const client = new Client({ intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES] });
client.once('ready', () => {
console.log('Ready!');
});
client.on('messageCreate', async message => {
if (!client.application?.owner) await client.application?.fetch();
// => client_id was just a quick fix, the correct value should be the id of the guild owner(s)
if (message.content.toLowerCase() === '!deploy' && message.author.id === client_id) {
const data = [
{
name: 'ping',
description: 'Replies with Pong!',
},
{
name: 'foo',
description: 'Replies with bar!',
},
{
name: 'chicken',
description: 'Replies with sticks!',
},
];
// same here, guild_id should also be replaced by the correct call to the idea number, just as client_id in this code!
for (let i = 0; data.length > i; i++) {
await client.guilds.cache.get(guild_id)?.commands.create(data[i]);
}
}
});
client.on('interactionCreate', async interaction => {
if (!interaction.isCommand()) return;
const command = interaction.commandName;
if (command === 'ping') {
await interaction.reply('pong')
} else if (command === 'foo') {
await interaction.reply('bar')
} else if (command === 'chicken') {
await interaction.reply('nuggets')
}
});
client.login(token);

Send user that got kicked the reason why they got kick

The only code I have is this:
module.exports = {
name: "kick",
description: "This command kicks a member!",
execute(message, args) {
const target = message.mentions.users.first();
if (target) {
const memberTarget = message.guild.members.cache.get(target.id);
memberTarget.kick();
message.channel.send("User has been kicked");
} else {
message.channel.send(`You coudn't kick that member!`);
}
},
};
Good Morning. So I'm trying to get it to message the person that got kicked the reason why they got kicked. (!kick user reason) I want it so the bot DMs the person what the reason was but I don't know how to do that.
You would only need to add the following:
const reason = args.splice(1).join(` `) || 'Not specified';
memberTarget.send(`You have been kicked: \nReason: ${reason}`)
module.exports = {
name: "kick",
description: "This command kicks a member!",
execute(message, args) {
const target = message.mentions.users.first();
if (target) {
const memberTarget = message.guild.members.cache.get(target.id);
const reason = args.splice(1).join(` `) || 'Not specified';
memberTarget.kick();
message.channel.send("User has been kicked");
memberTarget.send(`You have been kicked: \nReason: ${reason}`)
} else {
message.channel.send(`You coudn't kick that member!`);
}
},
};
The const reason = args.splice(1).join( ) || 'Not specified'; defines the 'reason' property, if there isn't a reason, it defaults to 'Not specified'.
memberTarget.send(You have been kicked: \nReason: ${reason})
Just sends the message to the targeted member.
Right off the bat, I see that you are getting the target using message.mentions.users and getting the memberTarget from the guild's cache. You should avoid this and use message.mentions.members.
You'll have to use the send method of GuildMember, but since it returns a Promise, you'll have to catch any errors. (e.g: the bot cannot DM the member)
// You should do some sanity checks in case you haven't. You don't want everyone to be able to kick members using the bot.
// Getting the first member in message.mentions.members.
const target = message.mentions.members.first();
// Making sure that target is defined.
if (!target) return message.channel.send('Please mention a member to kick.');
// Making sure a reason is provided. (args[0] is the mention)
if (!args[1]) return message.channel.send('Please provide a reason.');
// Making sure the bot can kick the target.
if (!target.kickable) return message.channel.send('Couldn\'t kick the target.');
// Trying to send a message to the target, notifying them that they got kicked, and catching any errors.
target.send(`You have been kicked from ${message.guild.name} for ${args.slice(1, 2000).join(' ')}`).catch(console.error).finally(() => {
// After the message was sent successfully or failed to be sent, we kick the target and catch any errors.
target.kick(`Kicked by ${message.author.tag} for ${args.slice(1, 2000).join(' ')}}.`).then(() => {
message.channel.send(`${target.user.tag} has been kicked for ${args.slice(1, 2000).join(' ')}.`);
}).catch(error => {
console.error(error)
message.channel.send(`Something went wrong...`);
});
});
This is working kick command with reason made by me (you can try it):-
It has every validation you should have in a kick command
const Discord = require('discord.js')
exports.kick = async(message , prefix , client) => {
if(!message.member.hasPermission("KICK_MEMBERS")) return message.channel.send('Missing Permission! You need to have `KICK_MEMBERS` permissions in order kick this member.')
if(!message.guild.me.hasPermission("KICK_MEMBERS")) return message.channel.send('Missing Permission! I need to have `KICK_MEMBERS` permissions to kick this member.')
const args = message.content.slice(prefix.length).trim().split(' ');
const command = args.shift().toLowerCase();
let member = message.mentions.members.first();
if(!member){
let err = "```css\n[ Agrument Error : You Have not mentioned the user on first args. ]\n```\n\n"
let embed = new Discord.MessageEmbed()
.setAuthor(`${client.user.username} Help Manual` , client.user.displayAvatarURL({format : "png"}))
.setTitle(`${message.guild.name}`)
.setDescription(err)
.addField('Help Command:' , `\`\`\`\n${prefix}kick #user#0001 Reason\n\`\`\``)
.setTimestamp()
.setColor('RED')
return message.channel.send(embed)
}
if(args[0] != `<#!${member.id}>`){
let err = "```css\n[ Agrument Error : You Have not mentioned the user on first args. ]\n```"
let embed = new Discord.MessageEmbed()
.setAuthor(`${client.user.username} Help Manual` , client.user.displayAvatarURL({format : "png"}))
.setTitle(`${message.guild.name}`)
.setDescription(err)
.addField('Help Command:' , `\`\`\`\n${prefix}kick #user#0001 Reason\n\`\`\``)
.setTimestamp()
.setColor('RED')
return message.channel.send(embed)
}
if(member.id === message.author.id) return message.channel.send(`Why? No Just Say Why Do you want to kick yourself?`)
let reason = args.slice(1).join(' ');
if(!reason || reason.length <= 1){
reason = "No Reason Was Provided."
}
if(!member.kickable){
return message.channel.send(`I Don't Have Permissions to Kick ${member.user.username}`)
}
member.kick().then(() => {
return message.channel.send(`Successfully Kicked ${member.user.username} for Reason ==> \`${reason}\``)
}).catch(() => {
return message.channel.send(`I Don't Have Permissions to Kick ${member.user.username}`)
})
}

Invalid interaction application command(discord.js slash commands using WOKCommands)

I'm using discord.js and WOKCommands to use slash commands, but when entered in Discord it gives me an error "Invalid interaction application command"
Here is the code for the command:
const { MessageEmbed } = require("discord.js");
// Simple command for the message
module.exports = {
name: "ping",
slash: "both",
testOnly: false,
description: "Command to figure out what your current ping is. Also shows API Latency",
// Executing the message command
execute(client, message, cmd, args, Discord) {
// Creating the Embed const
const newEmbed = new MessageEmbed()
// ALL EMBED VALUES
.setColor("#ffdbac")
.setTitle("Ping")
.setDescription("Pong! Latency is **" + (Date.now() - message.createdTimestamp) + "ms**. API Latency is **" + message.client.ws.ping + "ms**")
.setThumbnail(`https://cometiclachlan.github.io/Uploads/pingpong-removebg.png`)
.setTimestamp()
.setFooter("v1.2", `https://cometiclachlan.github.io/Uploads/Checkpoint-removebg.png`);
message.channel.send(newEmbed);
},
};
That is the code for the command only if I need to show the code for the main script as well. I will do so.
You Can not use Message in Slash Commands You'l Need to change it to
const { MessageEmbed } = require("discord.js");
// Simple command for the message
module.exports = {
name: "ping",
slash: "both",
testOnly: false,
description: "Command to figure out what your current ping is. Also shows API Latency",
// Executing the message command
callback : ({client, message, cmd, args, Discord}) => {
if (message) {
// Creating the Embed const
const newEmbed = new MessageEmbed()
// ALL EMBED VALUES
.setColor("#ffdbac")
.setTitle("Ping")
.setDescription("Pong! Latency is **" + (Date.now() - message.createdTimestamp) + "ms**. API Latency is **" + client.ws.ping + "ms**")
.setThumbnail(`https://cometiclachlan.github.io/Uploads/pingpong-removebg.png`)
.setTimestamp()
.setFooter("v1.2", `https://cometiclachlan.github.io/Uploads/Checkpoint-removebg.png`);
message.channel.send(newEmbed);
}
// Slash Command
const newEmbed = new MessageEmbed()
...
return newEmbed
}
};

Discord.js bot only detects last command in separate files

I am trying to code this discord bot by using Javascript and I am currently following the discordjs guide step by step and I am making the exact same steps as the guide. Now I am currently learning about the dynamic commands and it seems I've got stuck for like 4 days in this big hole. The problem is that the command handler does detect the command files inside the commands folder but it only detects the last command in that order. This is the code in my bot.js file from my main folder:
const fs = require('fs');
const {prefix, token} = require('./config.json');
const Discord = require('discord.js');
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('Imperial Bot is online!');
});
client.on('message', message => {
console.log(message.content);
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).trim().split(/ +/);
const commandName = args.shift().toLowerCase();
if (!client.commands.has(commandName)) return;
const command = client.commands.get(commandName);
try {
command.execute(message, args);
} catch (error) {
console.error(error);
message.reply('there was an error trying to execute that command!');
}
})
client.login(token);
And this is the code in the file fun.js from the commands folder:
module.exports = {
name: 'avatar',
description: '...',
execute(message, args){
if(!message.mentions.users.size){
message.channel.send(`Poza ta de profil: <${message.author.displayAvatarURL({ format: "png", dynamic: true })}>`)
}
// the array of avatars of the tagged users get put in a constant and shown in the chat.
const avatarList = message.mentions.users.map(user => { //for every user tagged in the message, an avatar of theirs will get shown
return `Avatarul lui ${user.username}: <${user.displayAvatarURL({ format: "png", dynamic: true })}>`;
});
message.channel.send(avatarList);
},
}
module.exports = {
name: 'gaymeter',
description: '...',
execute(message, args){
let gen = Math.floor(Math.random()*100 + 1);
let tagged = message.mentions.users.first();
if(!args[0]){
message.channel.send(`Esti ${gen}% gay!`);
} else {
message.channel.send(`${tagged.username} este ${gen}% gay! :gay_pride_flag: `);
}
}
}
module.exports = {
name: 'delete',
description: '...',
execute(message, args){
let checkINT = parseInt(args[0]);
let amount = args[0];
if(isNaN(checkINT) || !args[0]){
message.channel.send("Trebuie precizat un numar.");
} else if(amount < 2 || amount > 99){
message.channel.send("Limita de mesaje care pot fii sterse este intre 2 si 99.");
} else {
message.channel.bulkDelete(amount, true);
message.reply(`sterse ${amount} mesaje!`).catch(err => {
console.error(err);
message.channel.send('Eroare! Nu au putut fii sterse mesajele.');
});
}
},
}
If I run the bot, the only command that it detects is the delete command which is the last one in that file. My question is how can I arrange or fix the code so that all the module.exports work in that fun.js file and not only that delete command? Do I have to put only one command in one module export and make separate files for each command?
What I assume you want to achive by putting all the commands into one file is to create a category. Here is a way to do that more effectively. And yes, you need to create separate files for all your commands.
We create another folder inside your commands folder and place the commands in there.
Note: because this is a dynamic way of creating categories you can just add additional folders and a new category is born.
Your folder structure should look something like this
-- Your bot folder
- index.js
- package.json
- package-lock.json
-- commands
-- fun
- kick.js
- ban.js
-- some other category
Now we need to include a litte something in your command reader.
// create a new collection called catergories
client.categories = new Discord.Collection();
client.aliases = new Discord.Collection(); // only if you want aliases
// Read every commands subfolder
fs.readdirSync("./commands/").forEach(dir => {
// Read all files in the commands folder and that ends in .js
const commands = fs.readdirSync(`./commands/${dir}/`).filter(file => file.endsWith(".js"));
// Loop over the commands, and add all of them to a collection
// If there's no name found, prevent it from returning an error
for (let file of commands) {
const command = require(`../commands/${dir}/${file}`);
// Check if the command has a name, a category and a description
// add or remove things here as you need
if (command.name && command.category && command.description) {
client.commands.set(command.name, command);
} else {
console.log("A file is missing something");
continue;
}
// use this if you wish to include an alias array in your commands
// check if there is an alias and if that alias is an array
if (command.aliases && Array.isArray(command.aliases))
command.aliases.forEach(alias => client.aliases.set(alias, command.name));
};
})
Note: if you want to use aliases you need to check for them in your command handler after no command has been found.
// you only need this if you want to use aliases
if (!command) command = client.commands.get(client.aliases.get(commandName));
Now you should include the category in your command file.
module.exports = {
name: 'avatar',
description: '...',
category: "fun",
aliases: ["all", "your", "aliases"], // this is optional
execute(message, args) {
// your code
}
}

Categories