Build a Discord Bot with Discord.js V14: A Step-by-Step Guide
Tags: javascript, bot, beginners, node
Published: 2024-04-12 12:38:33



## Overview

<img src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExdTB1bjV6Y2RiOGM0eTB2Y2t5MnJpYjZpa2diejZ6dW1ia2FvYnNmYSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/3o7TKEP6YngkCKFofC/giphy-downsized.gif" width="480" height="454"/>

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!!

<img src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExdGE0Yzd4azFweDJucTNzN3EyZnY3cjZ4aTZydmdsajhpNTZsZmVweiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/64aBXTVfd90zyUH2da/giphy.gif" width="480" height="454"/>


## 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?

<img src= "https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExNG1tOWUyb3ppdXNlYXN6ZmJsYmczZjhhYndubHlyNDFxZHV4Z2NraSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/QfzMP70zmNQiDf5sGP/giphy.gif" width="480" height="454"/>

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3y2yltlc1a1ofhgqsd0s.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zlmsa4yolqrcp7klb30g.png)
 

> 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](https://discordjs.guide/whats-new.html)


##Installation

<img src="https://th.bing.com/th/id/OIP.-rrFjvtf_jqw40EikHCbGwHaDt?rs=1&pid=ImgDetMain"/>

**Installing Node.js**
Very Important: To use discord.js, you'll need to install [Node.js](https://nodejs.org). 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](https://nodejs.org/en/download), open the downloaded file, and follow the steps from the installer.

On macOS, either:

Download the latest version from the [Node.js website](https://nodejs.org), 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](https://www.npmjs.com) (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!](https://discord.js.org/) 

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](https://discordjs.guide/preparations/setting-up-a-linter) 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 the[Discord developer portal](https://discord.com/developers/applications) 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mptpid2qwtjbrm9ih85q.png)


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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5xgzzb22p15mmy1cgerr.png)

### 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/266soejbpjentvm8y1dg.png)

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

![copy commands](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/174l6qzf0pgd1noevtib.png)


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


![bot username](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q0hjxst0yo6ghzvrw0vh.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ts72psa4ppk93o46vvyw.png)


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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ij1ki4megpzjy5viprr3.png)



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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8yx57pxnm3wcsrzxwefi.png)



> 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8eosae6uienefgmg5bgo.png)



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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/krd80fh3jz8j4h6p6v2a.png)



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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p8r9voyg0417j8i6ywy2.png)



###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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nx6xy7cjzwprgjmt0bpb.png)



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 });
		}
	}
});
```

![commandName](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a273yidujnhylhncg8yf.png)



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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5pkua7ool8vf3r06m79t.png)


 
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);
	}
})();
```


![deploy-commands](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cq9c6816m6egwyc2l5b4.png)


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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dxm379hab129198x7crr.png)


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

<img src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExNWVxcmh5ajhhMGs5bGt2dmFrbmF2OHB3c3U5MjI2ZHBoZTQ2Z3YydiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/QTAVEex4ANH1pcdg16/giphy.gif"/>

Again, If you aren’t aware, discord recently pushed their latest release on their docs(discord.js v14). You can check [here](https://discordjs.guide/#before-you-begin) to learn more about what they offer. Happy Coding!