Command handler change

Would it be possible (is there an example) of a command handler, that looks into a folder with all of the commands, but instead, if there are subcommands, you don't handle them with like switch case statements for example, but you have a folder, and all of the subcommands have their own modules (so you can work easier and not having to scroll through god know how long of a single module to get to a specific subcommand's code.
19 Replies
d.js toolkit
d.js toolkit4mo ago
- What's your exact discord.js npm list discord.js and node node -v version? - Not a discord.js issue? Check out #other-js-ts. - Consider reading #how-to-get-help to improve your question! - Explain what exactly your issue is. - Post the full error stack trace, not just the top part! - Show your code! - Issue solved? Press the button!
astro
astro3mo ago
what do you mean nevermind, i'll try to work on it, you gave me an idea coming back to this, how would i edit the deploy-commands.js file in order to make it do the following - read all the files inside commands/utility as, obviously, commands - read all files in any potential subdirectories within the utility directory, and it would automatically know that if the filetype that it's reading is a directory, that directory name is the slash command name and any modules inside it are just subcommands My current deploy-commands.js looks like this
const { REST, Routes } = require('discord.js');
const { clientId, guildId, token } = require('./config.json');
const fs = require('node:fs');
const path = require('node:path');

const commands = [];
// Grab all the command folders from the commands directory you created earlier
const foldersPath = path.join(__dirname, 'commands');
const commandFolders = fs.readdirSync(foldersPath);

for (const folder of commandFolders) {
// Grab all the command files from the commands directory you created earlier
const commandsPath = path.join(foldersPath, folder);
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const command = require(filePath);
if ('data' in command && 'execute' in command) {
commands.push(command.data.toJSON());
} else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
}

// Construct and prepare an instance of the REST module.
const rest = new REST().setToken(token);

// and deploy your commands!
(async () => {
try {
console.log(`Started refreshing ${commands.length} application (/) commands.`);

// The put method is used to fully refresh all commands in the guild with the current set
const data = await rest.put(
Routes.applicationGuildCommands(clientId, guildId),
{ body: commands },
);

console.log(`Successfully reloaded ${data.length} application (/) commands.`);
} catch (error) {
// And of course, make sure you catch and log any errors!
console.error(error);
}
})();
const { REST, Routes } = require('discord.js');
const { clientId, guildId, token } = require('./config.json');
const fs = require('node:fs');
const path = require('node:path');

const commands = [];
// Grab all the command folders from the commands directory you created earlier
const foldersPath = path.join(__dirname, 'commands');
const commandFolders = fs.readdirSync(foldersPath);

for (const folder of commandFolders) {
// Grab all the command files from the commands directory you created earlier
const commandsPath = path.join(foldersPath, folder);
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const command = require(filePath);
if ('data' in command && 'execute' in command) {
commands.push(command.data.toJSON());
} else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
}

// Construct and prepare an instance of the REST module.
const rest = new REST().setToken(token);

// and deploy your commands!
(async () => {
try {
console.log(`Started refreshing ${commands.length} application (/) commands.`);

// The put method is used to fully refresh all commands in the guild with the current set
const data = await rest.put(
Routes.applicationGuildCommands(clientId, guildId),
{ body: commands },
);

console.log(`Successfully reloaded ${data.length} application (/) commands.`);
} catch (error) {
// And of course, make sure you catch and log any errors!
console.error(error);
}
})();
NyR
NyR3mo ago
This would not be a djs question, #other-js-ts
astro
astro3mo ago
oop
NyR
NyR3mo ago
Hmm, tbh their question was how to treat directories differently so to me it sounded non-djs
astro
astro3mo ago
okay so what i've figured out is that the for (const folder of commandFolders) loop basically takes command files from within any folders inside of the commands folder (im assuming this is so you can label the command types? like dev commands, mod commands etc.) now, do i need to make another for of loop inside of that to check if there is another directory, and make a slashcommandbuilder... inside of deploy-commands.js? just want to make sure i understood you right before i go on a ghost hunt how can i check if soemthing is a directory though? there's no isDirectory or anything similar or none that i can see at least Like, unless I change it from
const commandFiles = fs.readdirSync(commandsPath);
const commandFiles = fs.readdirSync(commandsPath);
to
const commandFiles = fs.statSync(commandsPath);
const commandFiles = fs.statSync(commandsPath);
I can't do it, but if I do this, will it change anything?
NyR
NyR3mo ago
If you are using statSync you can do commandFiles.isDirectory() to check if it is a directory
astro
astro3mo ago
yes but then the other for loop errors
NyR
NyR3mo ago
Errors in what way?
NyR
NyR3mo ago
Because it isn't, with what you showed there, it is fs.Stats which is a class, you need to check if it's a directory inside the loop
d.js docs
d.js docs3mo ago
:node: Class: fs.Stats A <fs.Stats> object provides information about a file.
NyR
NyR3mo ago
You should still be using readdirSync to read all the files
astro
astro3mo ago
oh do you want me to have both fs.readdirSync and fs.statSync in the same loop?
NyR
NyR3mo ago
I don't understand what you mean in the same loop, you get all the files by doing const commandFiles = fs.readdirSync(path) Then loop through commandFiles and inside it use statSync and check if the file is a directory and do what you want to do with it Basically the same code as here, but remove the filter for .js and in your for..of loop, check if filePath is a directory using statSync
astro
astro3mo ago
So something like this?
for (const folder of commandFolders) {
// Grab all the command files from the commands directory you created earlier
const commandsPath = path.join(foldersPath, folder);
const commandFiles = fs.readdirSync(commandsPath);

// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const commandFiles = fs.statSync(filePath);
const command = require(filePath);
if ('data' in command && 'execute' in command) {
commands.push(command.data.toJSON());
} else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
}
for (const folder of commandFolders) {
// Grab all the command files from the commands directory you created earlier
const commandsPath = path.join(foldersPath, folder);
const commandFiles = fs.readdirSync(commandsPath);

// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const commandFiles = fs.statSync(filePath);
const command = require(filePath);
if ('data' in command && 'execute' in command) {
commands.push(command.data.toJSON());
} else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
}
NyR
NyR3mo ago
Well yeah, but you can't redefine a variable, you already have a commandFiles name it something else, you'd check if it's a directory and handle accordingly
astro
astro3mo ago
right, but how can i make it recognize the folder as a "command", and what should I put inside of the data field in module.exports for the subcommands? How would I "handle" it if it is a directory Do I just put the slashcommandbuilder in deploy-commands instead and just have the subcommands be formatted as JSON (removing the .toJSON() when pushing subcommands)
NyR
NyR3mo ago
It'd be hard to determine which directory is for base command and which is for subcommands unless you add some kind of identifier in the directory name, like you can name it "subcommand" and check the directory name if it is that, and treat is as such, you can make it a json, that would hold the subcommand name, and execute function that would handle its interaction, you can load it to client like you do with slash commands, and on interaction, get the subcommand with interaction.options.getSubcommand() and then get it from the collection and call execute, it'll be very similar to how you handle slash commands, just the intial steps is what you need to figure out