We already explored one event handler in your first bot, the message
handler. Now let's take a look at some of the most important handlers that you will use, along with an example.
DO NOT NEST EVENTS One important point: Do not nest any events (aka "put one inside another"). Ever. Events should be at the "root" level of your code, beside the message
handler and not within it.
Ah, asynchronous coding. So awesome. So hard to grasp when you first encounter it. The reality of discord.js and many, many other libraries you will encounter, is that code is not executed one line at a time, one after the other.
It should have been made obvious with the user of client.on("message")
which triggers for each message. To explain how the ready
event is important, let's look at the following code:
const { Client, Intents } = require("discord.js");
const client = new Client({
intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES]
});
client.user.setActivity("Online!");
client.login("yourToken");
This code will not work, because client
is not immediately available after it's been initialized. client.user
will be undefined in this case, even if we flipped the setActivity and login lines. This is because it takes a small amount of time for discord.js to load its servers, users, channels, and all that jazz. The more servers the bot is on, the longer it takes.
To ensure that client
and all its "stuff" is ready, we can use the ready
event. Any code that you want to run on bootup that requires access to the client
object, will need to be in this event.
Here's a simple example of using the ready
event handler:
client.on("ready", () => {
client.user.setActivity(`on ${client.guilds.cache.size} servers`);
console.log(`Ready to serve on ${client.guilds.cache.size} servers, for ${client.users.cache.size} users.`);
});
Another useful event once you've enabled the privileged intent and added GUILD_MEMBERS
to your intents array that is, is guildMemberAdd
. Which triggers whenever someone joins any of the servers the bot is on. You'll see this on smaller servers: a bot welcomes every new member in the #welcome channel. The following code does this.
client.on("guildMemberAdd", (member) => {
console.log(`New User "${member.user.username}" has joined "${member.guild.name}"` );
member.guild.channels.cache.find(c => c.name === "welcome").send(`"${member.user.username}" has joined this server`);
});
The objects available for each event are important: they're only available within these contexts. Calling message
from the guildMemberAdd
would not work - it's not in context. client
is always available within all its callbacks, of course.
Yes, bots fail sometimes. And yes, the library can too! There's a little trick we can use, however, to prevent complete crashes sometimes: Capturing the error
event.
The following small bit of code (which can be anywhere in your file) will catch all output message from discord.js. This includes all errors, warning and debug messages.
NOTE: The debug event WILL output your token partially, so exercise caution when handing over a debug log.
client.on("error", (e) => console.error(e));
client.on("warn", (e) => console.warn(e));
client.on("debug", (e) => console.info(e));
So now you're wondering, how do I test those events? Do I have to join a server with an alternate account to test the guildMemberAdd event? Isn't that, like, super annoying?
Actually, there's an easy way to test almost any event. Without going into too many details, client
, your Discord Client, extends something called the EventHandler
. Any time you see client.on("something")
it means you're handling an event
called "something"
. But EventHandler has another function other than on
. It has emit
. Emit is the counterpart for on
. When you emit
an event, it's handled by the callback for that event in on
.
So what does it mean??? It means that if you emit an event, your code can capture it. I know I know I'm rambling without giving you an example and you're here for examples. Here's one:
client.emit("guildMemberAdd", message.member);
This emits the event that normally triggers when a new member joins a server. So it's pretending like this particular member has rejoined the server even if they have not. This obviously works for any event but you have to provide the proper arguments for it. Since guildMemberAdd
requires only a member, any member will do (see FAQ to know how to get another member). I can trigger the ready
event again by using client.emit("ready")
(the ready event does not take any parameter).
What about other events? Let's see. guildBanAdd
takes 2 parameters: guild
and user
, to simulate that a user was banned. So, you could client.emit("guildBanAdd", message.guild, message.author)
to simulate banning the person sending a message. Again, getting those things (Guilds and Users) is in the FAQ.
You can do all this in a "test" command, or you can do what I do: use eval
. Check the Eval command when you're ready to go that route.