DEV Community

Cover image for Build a Discord Bot with Discord.js V14: A Step-by-Step Guide
LordCodex
LordCodex

Posted on • Edited on

Build a Discord Bot with Discord.js V14: A Step-by-Step Guide

Overview

Sometimes when we are overloaded with too many boring and numerous mundane activities we think of ways to automate them. Consequently, bots and AI tools are created to make our lives easier.

In this article, I will explore how to create a custom discord bot with Discordv14 that responds to slash messages.

Sounds interesting?

Alright let begin!!

Prerequisite

  • A good knowledge of Javascript
  • A basic knowledge of Nodejs

Intro To Discord.js.

Before you start building, do you know what discord.js is and what it does?

No Problem, I will explain:

Discord.js is a powerful Node.js module that allows you to interact with the Discord API very much easily.

Discord.js

It takes a much more object-oriented approach than most other JS Discord libraries, making your bot's code significantly tidier and easier to comprehend.

Image description

If you aren’t aware, discord.js just pushed their latest release on their docs(discord.js v14). You can check out what's new here

Installation

Installing Node.js
Very Important: To use discord.js, you'll need to install Node.js. discord.js v14 requires Node v16.11.0 or higher.

To check if you already have Node installed on your machine (e.g., if you're using a VPS), run node -v in your terminal. If it outputs v16.11.0 or higher, then you're good to go!
Alright, Let's Continue.

On Windows, it's as simple as installing any other program. Download the latest version from the Node.js website, open the downloaded file, and follow the steps from the installer.

On macOS, either:

Download the latest version from the Node.js website, open the package installer, and follow the instructions
Use a package manager like Homebrew with the command
brew install node
On Linux, you can consult this page to determine how you should install Node.

Preparing the essentials

To use discord.js, you'll need to install it via npm (Node's package manager). npm comes with every Node installation, so you don't have to worry about installing that. However, before you install anything, you should set up a new project folder.

Navigate to a suitable place on your machine and create a new folder named "discord-bot" (or whatever you want). Next you'll need to open your terminal.

Opening the terminal

If you use Visual Studio Code, you can press Ctrl + ` (backtick) to open its integrated terminal.

Running on Windows

On Windows, either:

Shift + Right-click inside your project directory and choose the "Open command window here" option
Press Win + R and run cmd.exe, and then cd into your project directory

Running on macOS:

On macOS, either:

Open Launchpad or Spotlight and search for "Terminal"

In your "Applications" folder, under "Utilities", open the Terminal app

Running on Linux

On Linux, you can quickly open the terminal with Ctrl + Alt + T.

With the terminal open, run the node -v command to make sure you've successfully installed Node.js. If it outputs v16.11.0 or higher on the terminal, then you are good to go!!

Initiating a project folder with NPM

To start building, ensure to run the following command to set up your project for you.

npm init -y

Once you're done with that, you're almost ready to install discord.js!

Installing discord.js

Now that you've installed Node.js and know how to open your console and run commands, you can finally install discord.js!

Next, run the following command in your terminal:

npm install discord.js

And that's it! With all the necessities installed, you're almost ready to start coding your bot.

Linter Setup

With linter, it is easier to spot bugs and errors in our code, it is not required but recommended. You can check here on how to install it.

Setup a Bot Application

Now that you have followed the steps outlined above, I guess the real cooking can start!!!.

To set up your bot application, follow these steps:

Open theDiscord developer portal and log into your account.

Click on the New Application button.
Enter a name and confirm the pop-up window by clicking the Create button.

Image description

You can edit your application name, description, and avatar however you wish.

Getting a Token

The token is basically what your bot uses to log into your discord, you can regenerate it but need your password for it to happen.

Pls ensure you don’t share your token with anyone to avoid malicious acts

Try generating the token which will require you to enter the password for your discord account as shown below:

generate-token

Adding the bot to servers

You need to add the bot to servers to test and interact with its functionality. To create a bot, an invite link is required.

What is the Invite Link?

An invite link is Discord's standard structure for authorizing an OAuth2 application (such as your bot application) for entry to a Discord server.

Follow these steps to get your invite link:

  • Navigate to the MyApps Page Under the application

  • Click on the bot created

  • Head to the OAuth page

  • Click on OAuth generator page

Select the bot and application.commands, when you select them, you are given a list of permissions to configure for your bot

app.commands

Grab the link via the "Copy" button and enter it in your browser.

copy commands

You should see something like this (with your bot's username and avatar):

bot username

Congratulations, you have just added the bot to the server!!.

Creating a Bot

Creating config files

Now that you have created your bot and added it to the server, it is time to start the coding part and get it online!

Creating Config.json

Next, create the config.json file. This is where you will store your private values like your tokens.

Creating the main File
Inside the root of your project, create a file and name it index.js. Inside the file, copy this code snippet:

`
const fs = require('node:fs');
const path = require('node:path');
const { Client, Collection, Events, GatewayIntentBits } = require('discord.js');
const { token } = require('./config.json');

const client = new Client({ intents: [GatewayIntentBits.Guilds] });

client.once(Events.ClientReady, readyClient => {
console.log(Ready! Logged in as ${readyClient.user.tag});
});

client.login(token);
`

creating main file

In the code above, here are the basic things to understand:

  1. A client instance is created for the bot so that it logins the bot in with a token
  2. The GatewayIntentBits allows the discord.js client to work the way it is expected to
  3. The intent allows you to define events that discord send to your bot when triggered.
  4. Guild is your server id

Now that you have understood these basic things, hopefully, you are ready for the next phrase.

Running Your Application

To run your application, open your terminal inside your visual code editor. Press this command to run your project

node index.js

Congratulations, you have just successfully run your project. The next step is to create slashing commands

Creating Slash Commands

With the Discord Client, you can create slash commands

Slash commands provide a huge number of benefits over manual message parsing, including:

  • Integration with the Discord client interface.

  • Automatic command detection and parsing of the associated options/arguments.

  • Typed argument inputs for command options, e.g. "String", "User", or "Role".

  • Validated or dynamic choices for command options.

  • In-channel private responses (ephemeral messages).

  • Pop-up form-style inputs for capturing additional information.

Before Going Further

This is how the structure of your project should look like:

discord-bot/
├── node_modules
├── config.json
├── index.js
├── package-lock.json
└── package.json

Individual Command files

In this section, you will learn how to create command files that help you register and handle commands. Here are important instructions you should follow:

  • Create a new folder named commands
  • Create a subfolder named utility, which is where you store all your utility command files.
  • Now, create a server.js file inside the utility sub-folder, server.js is an example of a utility file where your slash commands are executed. After that, copy this command below.

`
const { SlashCommandBuilder } = require('discord.js');

module.exports = {
data: new SlashCommandBuilder()
.setName('server')
.setDescription('Provides information about the server.'),
async execute(interaction) {
await interaction.reply(This server is ${interaction.guild.name} and has ${interaction.guild.memberCount} members.);
},
};
`

slash-commands

Looking at the code snippet above,
The data property provides the command definition with its description.
The execute is the asynchronous function that contains the functionality to run from our event handler when the command defined in the data is used.
Then, they are placed inside the module.exports so they are read by other files

That is it for your basic ping command.

Command handling

If you are working on a big project, you are most likely to use command handling. So, let's get started on that!

Loading Command files

After you have created your command files, make these new additions inside the index.js file.

client.commands = new Collection();

Loading Command files

Important: it is recommended to add a commands Property to your client instance so that you can access your commands in other files.

here are the things to know about the new additions:

  • The Fs is node’s native file system module. It is used to read the commands directory and identify the files

  • The path is Node’s native path utility module. path helps construct paths to access files and directories. One of the advantages of the path module is that it automatically detects the operating system and uses the appropriate joiners.

  • The Collection class extends JavaScript's native Map class, and includes more extensive, useful functionality. Collection is used to store and efficiently retrieve commands for execution.

Retrieving your command files

Next, after using the imported modules above, make a few more additions to the index.js file.


for (const folder of commandFolders) {
const commandsPath = path.join(foldersPath, folder);
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const command = require(filePath);
if ('data' in command && 'execute' in command) {
client.commands.set(command.data.name, command);
} else {
console.log(
[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.);
}
}
}

Retrieving Command files

Here are few things to know about these additions:

  • The path.join helps in constructing the root path to the command directory

  • The first fs.readdirSync reads the path that is joined to the directory(in this case, the command directory), returns an array of the folder the directory contains, currently [“utility”]
    When the first iteration is done inside the commandFolders, you take out each folder from the array(in this case, utility), use the path.join to construct the path to each folder.

  • Next, the second fs.readditSync reads inside of the folder(in ths case, the utility folder that has been constructed to the path and checks for what the utility folder contains, currently [“ping.js”]. To ensure only commands file are only read, in this case javascript files, Array.filter() ensures non-javascript files are removed.

  • Finally, set each command into the client.commands collection

Receiving and handling command interactions

In this section, you will create event listeners that execute and handle commands whenever there is interaction in your discord application. Inside the index.js file, add the following code:

client.on(Events.InteractionCreate, async interaction => {
console.log(interaction)
});

Receiving Commands

Now in discord.js, not every interaction is a slash command, for example, the messageComponent interactions. So to ensure your code listens for only slash commands,
You will make use of the BaseInteraction#isChatInputCommand() method to handle slash commands and exit the handler if another command type is encountered.


client.on(Events.InteractionCreate, async interaction => {
if (!interaction.isChatInputCommand()) return;
console.log(interaction)
});

commandType

Executing Commands

When executing commands, we check out the client.commands collection and see if it contains the interaction commandName before executing the code.

Again, if you check the server.js command file, you will notice there is a data and execute asynchronous function property defined in the file:

server.js

Inside the data property, it contains the name and description of the command defined. Then in the execute function, it contains what happens after the command gets triggered.

So like I explained from the beginning of this section, a matching command is gotten from the clients.command collection based on the interaction.commandName, if no match is found, the error is logged out, otherwise you execute the command with the interaction passed as the parameter.

`
client.on(Events.InteractionCreate, async interaction => {
if (!interaction.isChatInputCommand()) return;
const command = interaction.client.commands.get(interaction.commandName);

if (!command) {
    console.error(`No command matching ${interaction.commandName} was found.`);
    return;
}

try {
    await command.execute(interaction);
} catch (error) {
    console.error(error);
    if (interaction.replied || interaction.deferred) {
        await interaction.followUp({ content: 'There was an error while executing this command!', ephemeral: true });
    } else {
        await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
    }
}
Enter fullscreen mode Exit fullscreen mode

});
`

commandName

Congrats, you have just initiated the method to respond and execute slash commands. Next, you will create a deployment script that registers the slash commands.

Registering Slash Commands

Command registration

Command registration is done in two ways; in a specific guild, or in every guild the bot is in. so let get started with single-guild registration first which is the easier way to develop and test commands before a global deployment.

N.B:

Your application will need the applications.commands scope authorized in a guild for any of its slash commands to appear, and to be able to register them in a specific guild without error.

Guild Commands

To register and update slash commands for your bot, follow the instructions below

Create a deploy-commands.js file in your root directory.
Add two more properties to the config.json in your root directory; in this case, your clientId(your application’s client id) and guildId(your development server’s id)

After following these steps, your config.json file should look like this following the new additions:


{
"token": "your-token-goes-here",
"clientId": "your-application-id-goes-here",
"guildId": "your-server-id-goes-here"
}

creating the config.json file for discord bot

With these defined, copy this deployment script below inside the deploy-command.js file:

`
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);
}
Enter fullscreen mode Exit fullscreen mode

})();
`

deploy-commands

After you have copied the script, open another terminal inside your visual code editor. Then press this command node deploy-command.js to deploy your script. If you have followed these steps above, you should be able to register your slash commands inside the server your bot is in, run your commands, and see your bot’s response in Discord.

Testing the Bot

Testing the bot

Conclusion

If you have made it this far, give yourself a round of applause.

Again, If you aren’t aware, discord recently pushed their latest release on their docs(discord.js v14). You can check here to learn more about what they offer. Happy Coding!

Top comments (0)