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,
- We create a bot on Discord developers website (more details soon)
- Assign roles to the bot i.e granting permissions to the bot
- Create an OAuth scope for the bot (simply, a link for authentication)
- Add the bot to one of our servers
- 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
- Create the bot in Discord
- Create permissions for our bot
- Generate an OAuth link and use it to connect to our discord server
- We will then create a folder for the bot in our computer, open VSCode
- Install some dependencies, write some code to connect to Discord API
- 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
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.
- So head up to https://discord.com/developers/applications, click create New Application at the top right corner.
- 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.
- Click Bot in the left side nav
- Now click Add Bot
- 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.
- View channels (this is required).
- Send messages.
- Embed links.
- Manage messages.
- 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
"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
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 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
const { Intents } = require('discord.js');
Copy and paste the code below afterward
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
const botIntents = [
DIRECT_MESSAGES,
GUILD_MESSAGES,
GUILDS,
];
Now export botIntents
module.exports = { botIntents };
In app.js import the following
const { Client } = require('discord.js');
const { botIntents } = require('./config/config');
const config = require('./config/default');
Then paste this
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
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
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
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
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 likemessageDelete
,messageReactionAdd
, etc. Check the docs 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. 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
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. 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
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, 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
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
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
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
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
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
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
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
const { Client, MessageEmbed } = require('discord.js');
In the getLastMsgs
function, add this
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.
Our reply for the "!last-messages" command will now change to
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
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 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;
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
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
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. 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 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) 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.
Top comments (19)
I'm working on this piece of code for an upcoming test. This is also a useful source of knowledge for me. Fall guys will have you spinning in colorful chaotic battles. Where many players participate in fun combat and defeat the rest to win
Hi, I'm pretty new to coding and I am stuck. When attempting to run the app for the first time, I get.
TypeError: Cannot read properties of undefined (reading 'FLAGS')
Thanks for any help.
Hello, if you're still having issues with this, here is the updated code for intents:
For the
MessageContent
intent, you'd have to give your bot that permission in the developers dashboard.Oh yeah, that's probably because there have been updates to the discord library making some methods or properties in this article outdated.
So try going to the docs and check for any updates.
I'll try and update the article soon
Very well written! Thanks for this great article. A few notes, though:
This isn't true. Intents allow you to subscribe to only the events you want to receive from the discord API to decrease memory usage. Also, why did you include the typing and reaction intents? It didn't seem like those were used in the guide code (and would in fact inflate memory usage unneededly).
Especially since you're assuming the prefix is at the beginning when you slice it off of the message, you should probably use
startsWith
, notincludes
. For example, this would currently pass the "it's a command" check:sad is what I am!
Oh thanks for this. I never really understood the intents yet. Will check more on it and make the changes.
Also the intents I stated were originally to be used, but the article had grown to large already.
And thanks for the correction on the prefix. I checked the message properties, but that of content doesn't seem to have any prop (from the docs). Or is it
msg.startsWith
?, because i still can't find that.Update: I just verified the
startsWith
method on VSCode, but I still can't find it in the docs though. It's like it's hidden or somethingmessage.content
is a string. You can use any string method on it. It wouldn't be listed on discord.js docsdeveloper.mozilla.org/en-US/docs/W...
oh wow! I didn't know about this method before. Thanks for sharing
Just like to stress that "YouTube fetchers" as I believe the author phrases it, are against YouTube's API terms of service and risk being sent a Cease and Desist letter (as happened in the cases of Groovy and Rythm).
Ok let me explain what I mean by YouTube fetchers.
You can actually fetch new videos from a channel as the videos are uploaded. And it usually comes as a link to the video on youtube.
It could also be in an analytical sense, as a channel owner i can connect with the youtube api to see my analytics in the bot.
I really don't know about groovy so well, but from what I have read, I mean nothing like that in that statement.
Thanks for this, got me up and running nicely.
One tweak:
// export startBot as default
module.export = startBot;
to
// export startBot as default
module.exports = startBot;
Thanks for the correction.
I get some errors from node modules files showing the use of "??=" in the place of "=",
Is any one facing the same?
can you describe the issue on github, try pasting the error and how we can reproduce the error.
This is the repo
Done
Not all the time though.
I believe it depends on the purpose of the bot really.
There are some boys that are dedicated to lofi music which is great for coding in my opinion.
Don't get your comment
Great job. Thanks for sharing. Very well written
Glad you liked it