Обработчик команд discord js
Simple Discord.js Command Handler
A quite simple command handler for the discord.js dependency.
Launch your bot along with the handler.
Login when you choose (supply a Client instance instead of a token).
Example command using the prompt function.
I try to keep this dependency as customizable as possible, here are some benefits of the customizability.
All references to classes come directly from the classes added onto the handler function. This means that you are able to easily extend these classes, either by access their prototype directly, or if you wish, redefining them, e.g. handler.Call = . However redefining them is not recommended, and if you do so without being fully aware, it may cause some internal issues.
Already have existing command layouts, and you don't want to bother switching all command.info.name references to command.id ? Simple, check out the customProps option that can be supplied to the handle function's options.
Other forms of custom traits are
- custom & multiple prefixes
- custom command error handling
- options for loading categories, allowing bot input, etc.
If you have any suggestions for this dependency feel free to leave an issue with your idea!
Documentation may not be complete. If you find something undocumented make a pull request on the repository.
handle(path, client, options?): Client
- path A string representing the path to the commands folder.
- client A token to create a Client instance and login with, or a pre-existing Client instance to use.
- options Options to use with the handle function. See here.
- Promise The Promise class (purely for redefining and using a promise library different than the native js one, such as bluebird).
- Call The Call class.
- Prompt The Prompt class.
- prompts A array of all current Prompt instances.
- promptOptionsDefaults The default prompt options. Adjusted purely for code convenience.
An instance of this is supplied to a command's exec function when a command is called. All parameters translate directly into properties.
- message
- command
- commands
- cut
- args
- prefixUsed
- aliasUsed
Properties (when instantiated):
- message The Message instance sent to trigger the command.
- client The Client instance of the bot.
- command The command object, e.g. < id: 'ping', exec: () =><> > .
- commands A Collection instance representing all the command objects mapped by the command id's.
- args A array of strings representing the arguments supplied to the message, e.g '!ban @gt_c for bullying me' would make this array ['@gt_c', 'for', 'bullying', 'me'] .
- prefixUsed A string representing the prefix used to call the command. Possibly your client's mention if that is how the user triggered the command.
- aliasUsed The alias (or command id) used in calling the command, e.g. '!ping' would make this property 'ping'.
- cut A string representing the content of the message, excluding the prefix and alias used.
Functions (when instantiated):
- prompt(message, options) A function to prompt a user with the text supplied, and wait for a response. See here.
Returns: A collection of messages recieved by the user that passed all requirements.
Note: To force cancel a prompt, do .end('cancelled') .
Note: Setting the time option to Infinity is strongly disadvised, as it can cause confusion for the user, and may also cause the promise to never be garbage collected if the prompt is never fulfilled.
Unless your bot project is a small one, it's not a very good idea to have a single file with a giant if / else if chain for commands. If you want to implement features into your bot and make your development process a lot less painful, you'll want to implement a command handler. Let's get started on that!
Here are the base files and code we'll be using:
- index.js
- deploy-commands.js
- config.json
Your project directory should look something like this:
Create a new folder named commands , which is where you'll store all of your commands.
We'll be using utility methods from the @discordjs/builders
open in new window package to build the slash command data, so open your terminal and install it.
Next, create a commands/ping.js file for your ping command:
You can go ahead and do the same for the rest of your commands, putting their respective blocks of code inside the execute() function.
open in new window is how you export data in Node.js so that you can require()
If you need to access your client instance from inside a command file, you can access it via interaction.client . If you need to access external files, packages, etc., you should require() them at the top of the file.
In your index.js file, make these additions:
open in new window is Node's native file system module. Collection
open in new window is a class that extends JavaScript's native Map
open in new window class, and includes more extensive, useful functionality.
This next step is how to dynamically retrieve your command files. The fs.readdirSync()
Use the same approach for your deploy-commands.js file, but instead .push() to the commands array with the JSON data for each command.
First, fetch the command in the Collection with that name and assign it to the variable command . If the command doesn't exist, it will return undefined , so exit early with return . If it does exist, call the command's .execute() method, and pass in the interaction variable as its argument. In case something goes wrong, log the error and report back to the member to let them know.
And that's it! Whenever you want to add a new command, make a new file in your commands directory, name it the same as the slash command, and then do what you did for the other commands. Remember to run node deploy-commands.js to register your commands!
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
Node.js uses an event-driven architecture, making it possible to execute code when a specific event occurs. The discord.js library takes full advantage of this. You can visit the discord.js documentation site
(opens new window) to see the full list of Client events.
Here's the base code we'll be using:
Currently, the event listeners are in the index.js file. The ready event emits once when the Client becomes ready for use, and the message event emits whenever a message is received. Moving the event listener code into individual files is simple, and we'll be taking a similar approach to the command handler.
Your folder structure should look something like this:
Create an events folder in the same directory. You can now take your existing events code in index.js and move them to individual files inside the events folders. Create a ready.js and a message.js file in the events folder and place in the code for the respective files:
The name property states which event this file is for, the once property is a boolean and specifies if the event should run only once, and the execute function is for your event logic. The event handler will call this function whenever the event emits.
Now, you'll write the code for dynamically retrieving all the event files in the events folder. Add this below the const client line in index.js :
This same method is used in our command handler section. The fs.readdirSync().filter() calls return an array of all the file names in the given directory and filter for only .js files, i.e. ['ready.js', 'message.js'] .
To listen for events, you have to register an event listener. This is done using the on or once methods of an EventEmitter instance. The on method for events can emit multiple times, while once will run once and unregister the listener after a single emit.
You can learn more about EventEmitter here
The Client class in discord.js extends the EventEmitter class. Therefore, the client object also has these on and once methods that you can use to register events. These methods take two arguments: the name of the event and a callback function.
The callback function passed takes argument(s) returned by its respective event, collects them in an args array using the . rest parameter syntax
(opens new window) , then calls event.execute function while passing in the args array using the . spread syntax
(opens new window) . They are used here because different events in discord.js have different numbers of arguments. The rest parameter collects these variable number of arguments into a single array, and the spread syntax then takes these elements and passes them to the execute function.
After this, listening for other events is as easy as creating a new file in the events folder. The event handler will automatically retrieve and register it whenever you restart your bot.
You may have noticed how important the Client class is. You created a client instance of this class in the index.js file. Most of the time, you can use this client instance in other files by either obtaining it from one of the other discord.js structures or function parameters. In your message event, you can use message.client . When you don't have access to any of the structures with the client property, you'll have to use the latter method. A prime example of this is the ready event.
The ready event does not have arguments, meaning that args will be an empty array, thus nothing will be passed to the execute function in ready.js . To obtain the client instance, you'll have to pass it as an argument along with the args array in the event handler. Back in index.js , make the following changes:
This allows client to be available as the last argument to the execute function in each event file. You can make use of client in ready.js by logging your bot's tag in the console when it becomes ready:
You can omit the client argument from the execute function in files where you don't need it. For example, it isn't required in the message.js file because its first argument is a Message instance, meaning you can use message.client .
It is worth noting that the position of client argument matters. For example, the messageUpdate event has two arguments: oldMessage and newMessage . Events like this should be handled as:
If you were to try execute(newMessage, client) , this would mean that newMessage is an oldMessage object and client is a newMessage object.
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
Unless your bot project is a small one, it's not a very good idea to have a single file with a giant if/else if chain for commands. If you want to implement features into your bot and make your development process a lot less painful, you'll want to implement a command handler. Let's get started on that!
Here's the base code we'll be using:
We'll be moving over the commands created in the previous page as well, but for the sake of keeping the base code short, the code block above omits those commands.
Before anything, you may want to create a backup of your current bot file. If you've followed along so far, your entire folder structure should look something like this:
In the same folder, create a new folder and name it commands . This is where you'll store all of your commands, of course. Head over to your commands folder, create a new file named ping.js , and copy & paste in the following code:
You can go ahead and do the same for the rest of your commands and put their respective blocks of code inside the execute() function. If you've been using the same code as the guide thus far, you can copy & paste your commands into their own files now, following the format above. The description property is optional but will be useful for the dynamic help command we'll be covering later.
module.exports is how you export data in Node.js so that you can require() it in other files. If you're unfamiliar with it and want to read more, you can look at the documentation
If you need to access your client instance from inside one of your command files, you can access it via message.client . If you need to access external files, modules, etc., you should re-require them at the top of the file.
Back in your main file, make these two additions:
fs is Node's native file system module. You can read the docs about it here
If you aren't exactly sure what Collections are, they're a class that extend JavaScript's native Map class and include more extensive, useful functionality. You can read about Maps here
(opens new window) , and see all the available Collection methods here
This next step is how you'll dynamically retrieve all your newly created command files. The fs.readdirSync()
(opens new window) method will return an array of all the file names in a directory, e.g. ['ping.js', 'beep.js'] . To ensure only command files get returned, use Array.filter() to leave out any non-JavaScript files from the array. With that array, you can loop over it and dynamically set your commands to the Collection you made above.
If there isn't a command with that name, you don't need to do anything further, so exit early with return . If there is, .get() the command, call its .execute() method, and pass in your message and args variables as its arguments. In case something goes wrong, log the error and report back to the member to let them know.
And that's it! Whenever you want to add a new command, you make a new file in your commands directory, name it what you want, and then do what you did for the other commands.
In the next chapter, we'll be going through how to implement some basic features into your brand new command handler. Currently, it's hardly a command "handler" at this point; it's a command loader and executor if you wish to see it that way. You'll learn how to implement some new features and the logic behind them, such as:
- Command aliases
- Cooldowns
- Guild only commands
- A dynamic help message
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
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
7 contributors
Users who have contributed to this file
- Open with Desktop
- View raw
- Copy raw contents Copy raw contents
Copy raw contents
Copy raw contents
A Basic Command Handler
A Command Handler is essentially a way to separate your commands into different files, instead of having a bunch of if/else conditions inside your code (or a switch/case if you're being fancy).
In this case, the code shows you how to separate each command into its own file. This means that each command can be edited separately, and also reloaded without the need to restart your bot. Yes, really!
Want a better, updated version of this code? We're now maintaining this command handler at the community level. Guide Bot is on Github and not only can you use the code, you can also contribute if you feel proficient enough!
What you need to know
In order to correctly write and use a command handler, I would suggest you get familiar with a few things.
- Check out Introduction to Modules for information on modules (which we'll use for each command)
- Understand how Events work, and how each event has different arguments provided.
- Have a good grasp of, at the very least, Commands with Arguments, which we'll be using as a base for most of our code.
Main File Changes
Because we're creating a separate file (module) for each event and each commands, our main file (app.js, or index.js, or whatever you're calling it) will change drastically from a list of commands to a simple file that loads other files.
Two main loops are needed to execute this master plan. First off, the one that will load all the events files. Each event will need to have a file in that folder, named exactly like the event itself. So for messageCreate we want ./events/messageCreate.js , for guildBanAdd we want ./events/guildBanAdd.js , etc.
The second loop is going to be for the commands themselves. For a couple of reasons, we want to put the commands inside of a structure that we can refer to later - we'll use a Discord Collection:
Ok so with that being said, our main file now looks like this (how clean is that, really?):
Our first Event: Message
The messageCreate event is obviously the most important one, as it will receive all messages sent to the bot. Create the ./events/messageCreate.js file (make sure it's spelled exactly like that) and look at this bit of code:
There are more things we could do here, like get per-guild settings or check permissions before running the command, etc. Out of a desire to keep this page simple, I've avoided all that extra code, but you can still find it on the GuideBot repository!
This would be the content of the ./commands/ping.js file, which is called with !ping (assuming ! as a prefix)
Another example would be the more complex ./commands/kick.js command, called using !kick @user
Notice the structure on the first line. exports.run is the "function name" that is exported, with 3 arguments: client (the client), message (the message variable from the handler) and args . Here, args is replaced by fancy destructuring that captures the reason (the rest of the message after the mention) in an array. See Commands with Arguments for details.
Events are handled almost exactly in the same way, except that the number of arguments depends on which event it is. For example, the ready event:
Note that the ready event normally doesn't have any arguments, it's just () . But because we're in separate modules, it's necessary to "pass" the client variable to it or it would not be accessible. That's what our fancy bind is for in the main file!
Here's another example with the guildMemberAdd event:
Now we have client and also member which is the argument provided by the guildMemberAdd event.
BONUS: The "reload" command
Because of the way require() works in node, if you modify any of the command files in ./commands , the changes are not reflected immediately when you call that command again - because require() caches the file in memory instead of reading it every time. While this is great for efficiency, it means we need to clear that cached version if we change commands.
The Reload command does just that, simply deletes the cache so the next time that specific command is run, it'll refresh its code from the file.
Remember that all of this is just a fairly basic version of the GuideBot command handler which also has permissions, levels, per-guild configurations, and a whole lot of example commands and events! Head on over to Github to see the completed handler.
Next up is making this basic command handler better with the addition of slash commands.
Читайте также: