Build A Modern Discord Bot from Scratch. Learn the basics
Tags: javascript, beginners, webdev, tutorial
Published: 2021-10-28 18:49:24

Discord bots help you interact with members of a server as well as moderate the server. A discord bot can send messages on the server, message a user directly (DM), ban a user, promote and demote a user and so much more.

As a server owner, you are not always going to be present to monitor your server, but a bot can, and it does it way faster.

You may not be a server owner, but you want to create a bot for a server you belong to or maybe for public use (available for other servers) this article will help you do that.

Before we jump right into code, let's see how Discord bot works.

**Audience Intended for**
This article is mainly focused on beginners who don't know how Discord bots work, and how to build them. So if you're already familiar with building discord bots, you may not find something new here.

Though it's for beginners I do expect you to know a little about working with NodeJS and npm.

## How Discord bot works

If you're in a server where there are bots you may have noticed that these bots are similar to users account.

They usually have these bot-looking profile pictures, seem to always be online, reply to messages very fast. These are cool, but how do all these things work?

There is a type of user dedicated for automation called bot accounts. They look a lot like the user's account.

The bot accounts are authenticated using a token (rather than a username, password), and this token gives these accounts full access to all Discord API routes.

So basically,

1. We create a bot on Discord developers website (more details soon)
2. Assign roles to the bot i.e granting permissions to the bot
3. Create an OAuth scope for the bot (simply, a link for authentication)
4. Add the bot to one of our servers
5. Boom! The bot starts performing magic like replying to messages.

Pretty easy. Though I must mention before the bots start to perform magic you'd need to have connected to Discord API, and logged the bot in. This is how we will create this bot

1. Create the bot in Discord
2. Create permissions for our bot
3. Generate an OAuth link and use it to connect to our discord server
4. We will then create a folder for the bot in our computer, open VSCode
5. Install some dependencies, write some code to connect to Discord API
6. With that our bot is online

Don't fret if you don't get it now. More will be explained in detail later.

## What can you build with a discord bot?

Discord bots can span from a hubby-friendly bot to a very powerful bot. You can build anything with a discord bot. But here are some ideas.

- A YouTube video fetcher
- Interesting Tweet fetcher
- A meme fetcher from Reddit
- A game
- A scheduler with a calendar
- A music player and song fetcher
- Server manager
- Quiz bot

And so much more. Here are some more [Discord bot ideas](https://turbofuture.com/internet/Discord-Bot-Ideas)

## About bot

The bot we will create for this article is going to be very basic, but it will contain almost all you need to build that super bot of yours.

With this bot, we will be able to reply to messages (commands), view message history, send DM's.

So try to follow along as much as you can. I will use my discord server for this project.

If you don't have a server you own or manage, you should create one.

## Let's Create Our First Bot

Just a quick reminder that this is a follow along with this article. So try to do what I do/did as you read.

### Create Bot

The first step we will take is to create the bot on Discord developers page. To create a bot you first need to create an application.

1. So head up to https://discord.com/developers/applications, click create **New Application** at the top right corner.
2. Enter the name of the app. You can name it whatever you want, but for the sake of this tutorial, I'll name it *Buddy*

Hurray! You just created your first discord application. Now let's create the bot.

1. Click **Bot** in the left side nav
2. Now click **Add Bot**
3. A modal will pop up, simply click the blue button to continue

Yahoo! A wild bot has appeared! Ready to give this bot life?.

### Bot Permissions and OAuth

Now we need to define some permissions for this bot, but to do this we have to create an OAuth scope first. It's simple

Click **OAuth2** in the left sidenav.

Here you will find some checkboxes with a sub-heading called "SCOPES".

Look for bot in the middle column, tick it.

#### Defining Permissions

Another set of checkboxes under a sub-heading called "BOT PERMISSIONS" will display (only if you clicked tick in the first set of checkboxes)

Now select the permissions you want for your bot, again for the sake of this tutorial we will select.
1. View channels (this is required).
2. Send messages.
3. Embed links.
4. Manage messages.
5. Read message history.

That would be all the permissions we need for this bot.

Once you're done, scroll back to the first set of checkboxes ("SCOPES") and copy the link below.

Open a new tab in your browser and paste that link, next thing is to select the server you want the bot in. Then click **Continue**.

Next, you will see a list of permissions that we selected, you can simply click **Authorize** to move on, verify you are a human and that will be all.

If you check the Discord server you invited this bot into, you'd see that the bot is there but offline. Now it's time to make it come alive.

## Connecting to Discord API

I believe you already have a folder set up on your local machine. If not do that now.

For this tutorial, I will make use of NodeJS. You can use other languages like Python to build Discord bots too.

### Setting up our environment

Since we have our folder ready, open up a terminal and run `npm init -y`.

For this to run you need to have NodeJS and NPM installed in your local machine (specifically NodeJS 16.6.0 or newer).

#### Installing Dependencies

We will need just two dependencies. - Discord.js: `npm install discord.js` - Nodemon (dev dependency): `npm install -D nodemon`
Run the commands above to install the dependencies.

Discord.js allows us to interact with the Discord API in NodeJS.

Nodemon restarts the app whenever will make and save new changes.

**Moving on**

Create a file called _app.js_. You can call it anything like bot.js or index.js.

Open your _package.json_ file and change **main** to the name of the file you just created.

Next copy these JSON scripts into the scripts property in the  _package.json_ file

```json
  "scripts": {
    "app": "nodemon app",
    "start": "node app"
  },
```

**Moving on**

Create a folder called _config_ and a file called _default.js_; we will store our secrets here.

Copy the following into _config/default.js_

```js
const config = {
  DISCORD_TOKEN: 'YOUR TOKEN HERE',
};
module.exports = config;
```

Replace 'YOUR TOKEN HERE' with your discord token.

You can find your discord token in the discord developers. Click your application, click _Bot_ at the left side nav, now click _Copy_ (close to the bot's profile pic).

**Moving on**

Create a file in the config folder, call it _config.js_. So you have _config/config.js_. In this file, we will have all of our configurations.

These configurations include commands, prefix(es), and Intents.

- Commands are simply commands that the bot will respond to. So whenever a user types a command in the discord server, the bot will respond accordingly.

- Prefix or prefixes (can vary) is a command prefix. For this bot, we will have just one prefix. A prefix is used just before a command, e.g !get-meme. ! Is a prefix while get-meme is the command.

You can as well call !get-meme as the command

- [Intents](https://discordjs.guide/popular-topics/intents.html#gateway-intents) are new, they state the events you want your bot to receive based on what the bot does. Without these intents stated, Discord API will throw an error.

So let's get started.

### Build a Discord Bot

Let's first make the bot come online.

Go to _config/config.js_ and import Intents as

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

Copy and paste the code below afterward

```js
const {
  DIRECT_MESSAGES,
  GUILD_MESSAGES,
  GUILDS,
} = Intents.FLAGS;
```

These are the permissions we want our bot to have, so we are simply destructuring it from Intents.FLAGS provided by 'discord.js'.

Create an array, call it "botIntents", and copy-paste the variables above into it, so you have something like

```js
const botIntents = [
  DIRECT_MESSAGES,
  GUILD_MESSAGES,
  GUILDS,
];
```

Now export botIntents

```js
module.exports = { botIntents };
```

In _app.js_ import the following

```js
const { Client } = require('discord.js');
const { botIntents } = require('./config/config');
const config = require('./config/default');
```

Then paste this

```js
const client = new Client({
  intents: botIntents,
  partials: ['CHANNEL', 'MESSAGE'],
});
```

Here we simply create a new client through the `Client` class from 'discord.js', and pass in some props.

The first prop is intents which are our botIntents, and the last is partials; an array, this is so our bot can be able to send direct messages. If you don't need this feature you can remove the prop

**Moving on**

Now we have access to the Discord API, we can now make listen for events.

The first event we will listen for is onready. In other words, when the bot is ready to go online

```js
client.on('ready', () => {
  console.log('Logged in as ' + client.user.tag);
});
```

We simply log to the console the name of the bot when the bot is ready to come online.

We are almost there. Before our bot will come online, we will need to log in with our Discord token.

At the bottom of _app.js_ copy-paste this

```js
client.login(config.DISCORD_TOKEN);
```

Recall, the config file is an object that holds our Discord token.

Now run the app, go to your discord server and you'll see the bot online.

Though the bot is online, it cannot send any messages or reply to any messages. So let' work on that next.

#### Setting up Commands

I usually use RegEx to set up commands and use switch and case to check for what command was used. This is when the bot listens for different commands.

But this bot is a simple one, so we will keep things simple.

In _config/config.js_, let's register some commands. Create an object called `commands` and paste in the following like

```js
const commands = {
  getName: 'get-name',
  tellJoke: 'tell-a-joke',
  sad: 'sad',
  lastMsgs: 'last-messages',
};
```

So these are the commands our bot will listen for.

Before we export, create a variable and call it `prefix`, assign '!' to it. You can use any other prefix of your choice like '$'. So we have `const prefix = '!';`

Export both the commands and prefix as commands and prefix respectively.

In _app.js_, import commands and prefix from _config/config.js_. Simply add `commands, prefix` to the curly braces around `botIntents`.

**Moving on**

Copy-paste the following into _app.js_

```js
client.on('messageCreate', (msg) => {
  if (msg.author.bot) return;
  if (!msg.content.startsWith(prefix)) return; // do nothing if command is not preceded with prefix

  const userCmd = msg.content.slice(prefix.length);

  if (userCmd === commands.getName) {
    msg.reply(msg.author.username);
  } else {
    msg.reply('I do not understand your command');
  }
});
```

Oh wow, a lot is going on here. Let's break it down, shall we?

- We listened for an event called `messageCreate`, there are others like `messageDelete`, `messageReactionAdd`, etc. Check the [docs](https://discord.js.org/#/docs/main/stable/class/Client) for all.
- The `messageCreate` event returns a msg parameter containing the message info.
- Next thing we did is check if the message is from a bot in `msg.author.bot`. Here we want to make sure we ignore messages that are from bots.
- Also we ignore messages that do not contain our declared prefix ('!').
- Next stop is to get the actual message without the prefix, that's why we slicing out the prefix. And then we assign it to userCmd (as in user command).
- Finally, we checked if the content of the message (without the prefix now) is the same thing as our first command (i.e getName). If it is the same then
- we replied to the user with his/her username using (msg.author.username). Find more on [messages in the docs](https://discord.js.org/#/docs/main/stable/class/Message). If it's not the same
- we replied with another message "I do not understand your command".

Save the changes. Go to your discord server, type in any message with the prefix and see the response. Now type in '!get-name' and see the response as well.

You can make the message a little nicer with `Your discord username is ${msg.author.username}`. This is not exactly useful in real life bot; returning the user's username. But at least it shows you what's possible.

**Moving on**
To add the rest commands, we will just add more `else if` to the initial if-chain. Like this

```js
if (userCmd === commands.getName) {
  msg.reply(msg.author.username);
} else if (userCmd === commands.tellJoke) {
  msg.reply('HTML is a programming language'); // bad joke i guess, unless i don't have any jokes
} else if (userCmd === commands.sad) {
  msg.reply("Don't be sad! This is not the end of the road");
} else if (userCmd === commands.lastMsgs) {
  const reply = await getLastMsgs(msg);
  msg.reply(reply);
} else {
  msg.reply('I do not understand your command');
}
```

To get the last messages we will create a function in _app.js_ called `getLastMsgs` and pass in one argument.

Traditionally if each command your bot listens to has an ambiguous amount of things to do, it is often recommended to break these tasks into functions, for readability.

Also, you could put the functions in a separate file inside the same folder, you can call the folder _actions_ or something.

Am not saying you should do this now, am just saying it's better to do it this way if the bot has a lot to do. But this bot doesn't do much so.

[Here is an example](https://github.com/Elijah-trillionz/advanced-discord-bot). The bot's project was canceled though, but it should show you how bots with lots of tasks get structured.

**Moving on**
Copy-paste this into the `getLastMsgs` function, (You can create an asynchronous function if you haven't) like so

```js
const getLastMsgs = async (msg) => {
  // fetching the last 10 messages
  const res = await msg.channel.messages.fetch({ limit: 10 });

  return 'Last ten messages';
};
```

Technically we are passing the msg parameter we received from the `onmessageCreate` event. So in the current channel where the command was received (could be a DM or server), the last ten messages will be fetched.

The fetch method is provided by the [Discord API](https://discord.js.org/#/docs/main/stable/class/MessageManager?scrollTo=fetch), you should read about it after this.

The result of this is an array of ten messages, it's not like a traditional array that you can access each item using an index. For example, if you want to get the first message in the array, you'd have to use the `.first()` method.

So the first messages' content would be accessed like

```js
res.first().content; // don't add this to the function, just a showcase
```

Another good thing is, we can loop through each array item. So before the `return` statement in the `getLastMsgs` function, add the following

```js
const lastTenMsgs = messages.map((message) => {
  return message.content;
});
```

We can loop through with `forEach` or `map`, we also have access to the `filter` method

Now change the `return` statement to lastTenMsgs. In other words, your function should look like this

```js
const getLastMsgs = async (msg) => {
  // fetching the last 10 messages
  const res = await msg.channel.messages.fetch({ limit: 10 });

  const lastTenMsgs = res.map((message) => {
    return message.content;
  });

  return lastTenMsgs;
};
```

Before you save, remember to pass in `async` in your `messageCreate` event function. I.e

```js
client.on('messageCreate', async (msg) => {});
```

Now save the app, and test the new commands. The "!last-messages" command will throw an array, we will fix that soon. But for now, let's spice up the bot a little

First thing is first, not all messages would be replied, rather a message would be created by the bot. Let's do that with the "!tell-a-joke" command.

Instead of msg.reply, do this

```js
msg.channel.send('HTML bla bla bla');
```

You will know more of these when you study the docs, the docs is well written.

Another thing is, we said the bot should be able to send direct messages. So let's do that with the "!last-messages" command.

Instead of msg.reply, do this

```js
msg.author.send(reply);
```

This doesn't fix the error yet. We are getting to that now.

Lastly, you must have noticed some bots in Discord sending/replying messages with colors by the side, bold words, with footers and headers like it's a blog post.

Well, it's not difficult to do. But before we do that, I should let you know that you can make a word or text bold traditionally.

It's almost like it's markdown, but not all recognized markdown syntax can be used. Let's make the "!tell-a-joke" text bold with

```js
msg.channel.send("**HTML** bla bla bla.\nI really don't have a joke");
```

If you test the command, you'd notice HTML is now bold, and "I really don't have a joke" on a new line.

With that being said let's move on.

To make our messages like it's a blog post with nice colors, let's use the "!last-messages" command for this.

In _app.js_, first import `MessageEmbed` from 'discord.js'. So you have

```js
const { Client, MessageEmbed } = require('discord.js');
```

In the `getLastMsgs` function, add this

```js
const embeds = [];

lastTenMsgs.forEach((msg, index) => {
  const embed = new MessageEmbed()
    .setColor('ORANGE') // can be hex like #3caf50
    .setTitle(`Message ${index + 1}`)
    .setDescription(`${msg}`)
    .setFooter('Buddy says Hi');

  embeds.push(embed);
});
return embeds;
```

We are simply creating a new message embed and using some methods on it. For each message (from the ten messages), we will create an embed and push it to an array of embeds which we later returned.

The methods `setColor`, `setTitle`, etc are pretty descriptive. Learn more on [embeds here](https://discord.js.org/#/docs/main/stable/class/MessageEmbed).

Our reply for the "!last-messages" command will now change to

```js
msg.author.send({ embeds: reply });
```

We need to let discord know that it's an embed for it to work.

If it was just one embed you should also make sure you wrap it in an array i.e

```js
msg.author.send({ embed: [onlyEmbed] });
```

Now save the changes and test your command. Now the error is gone. Now that we have all of these working. Let's now publish the bot and make it online forever!

I will use Heroku's free plan for this. But the thing is, our [Heroku's dyno will go to sleep](https://dev.to/elijahtrillionz/your-heroku-app-is-slow-to-load-because-of-this-4lep) after 30 minutes of inactivity.

The solution to that is Uptime robot. Uptime robot will keep your app alive. There is a side effect of doing this though, so usually, the best alternative to Heroku is Replit.

But whatever the case, you'd still need Uptime robot to keep the server alive, and you'd need a server (not a discord server).

So whether you are using Replit or Heroku, you need to have a server first and connect your bot to the server. So let's create a server in our local machine.

Since this is NodeJS let's use 'express'. Install express with `npm i express`.

Create a file in the root directory called _server.js_. In your _package.json_ change your main to "server.js" and your scripts to point to "server.js" not "app.js".

In _server.js_ paste the following;

```js
const express = require('express');
const app = express();
const PORT = process.env.PORT || 5000;

app.get('/', (req, res) => {
  res.send('Buddy bot is running');
});

app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
```

Not a lot going on here, we only just created a server with express.

We created just one route with a simple reply message. If you've never worked with express or NodeJS servers, trust me you really don't have much to worry about here.

Just copy-paste that in and you are good to go.

If you save. Rerun the program with `npm run app` and you'd see the log message 'Server running on port 5000'.

If you go to your web browser, open a new tab, and enter 'http://localhost:5000' you'd receive the message 'Buddy bot is running'.

Now the server is working fine. But the bot doesn't seem to be working with it. Let's fix this

In _app.js_, where we have `client.login`, create a function called `startBot` and wrap it around the `client.login`. So you have

```js
const startBot = () => {
  client.login(config.DISCORD_TOKEN);
};

// export startBot as default
module.exports = startBot;
```

In _server.js_, import `startBot` from './app.js'.

Now call the function just before the first route i.e

```js
startBot();

// before app.get()
```

You can call the function anywhere though, as long as it's before the listen method. But I prefer doing it before the routes.

Before you push, don't forget to great a _.gitignore_ file to ignore node*modules. And be careful where you push to. If you're going to push to GitHub, add */config/default.js* to \_gitignore*.

Now push to Heroku or Replit. I already wrote an article on [using uptime robot](https://dev.to/elijahtrillionz/your-heroku-app-is-slow-to-load-because-of-this-4lep). So check that out.

### Conclusion

I believe this is clear enough and can help you get started making bots for dozens of servers or just a server. If you have any challenges, just let me know in the comments.

The [source code for this project](https://github.com/Elijah-trillionz/discord-bot-buddy/tree/master) is on GitHub, please give it a star, and you know give me a follow if you enjoyed this.

Finally, before I go, I really do make tweets daily on Twitter ([@elijahtrillionz](https://twitter.com/elijahtrillionz)) on web development tips and resources. You should give me a follow, turn on notification, and let's stay connected.

Thanks for reading. I'll see you and your bot next time.