JavaScript Discord Bot Tutorial

Introduction

In this tutorial, we will walk through the process of creating a bot in JavaScript. We will start by creating a Discord server, creating a bot account, and inviting it to the server. Then we will demonstrate how to write JavaScript (Node.js) code to power the bot. By the end, you will have your very own bot that can be invited to any server that you have full control over.

If you are interested in learning how to make a Discord bot in Python, check out Make a Discord Bot in Python and Part 2. Be sure to also check out all of the other Discord related tutorials on DevDungeon.

Video tutorial

Create a Discord server

In order to create a Discord server you will need to have a Discord account. If you don't have a Discord account already, sign up and log in at https://discordapp.com/channels/@me. Once you are registered, you can either use the web browser version or you can download the app. They both behave the same. Once you are logged in to the app, look to the bottom-left corner, for the plus (+) sign. Click on the plus sign and it will ask if you want to create or join a server. Click "Create a Server." Provide a name, and click "Create." The new server will appear in the left column.

Create a Discord app

The next step is to create a Discord App. We aren't writing any code yet, we are just registering the app with Discord. Start by going to https://discordapp.com/developers/applications/me and create a new app. On your app detail page, save the Client ID. You will need it later to invite the bot to a server. Try to give your app a unique name.

Create a bot user for your app

After creating app, you need to take one further step and add a bot user to the app. You can do this on the app details page. On the app details page, scroll down to the section named bot, and create a bot user. Save the token, you will need it later to run the bot. If you get an error saying "Too many users have this username, please try another" then you need to rename your application to something more unique.


Note that by default, a bot has limited permissions. It can send and receive messages, add reactions, and do things a normal low-privileged user could do on a server, but it cannot perform admin actions like kicking members or managing channels. To perform those admin actions you would need to add the specific permissions to the bot as well as perform additional authentication steps. We are not going to cover that in this tutorial. We will stick with the default permissions like a regular non-admin user.

Authorize/invite the bot to your server

After you have registered your app and created the bot user for your app, you are ready to invite(authorize) the bot to your server. To do this, you will need the Client ID for your app. You can get this from your app's "General Information" page.

When you have your Client ID, visit the URL https://discordapp.com/oauth2/authorize?client_id=XXXXXXXXXXXX&scope=bot but replace XXXXXXX with your client ID. Choose the server you want to add it to and select authorize.


The Client ID does not necessarily need to be kept secret. If you want to share your bot and allow others to invite the bot to their server, then you can share this link. It will allow them to invite your bot to their server. The bot's token is different though. Always keep the token a secret, as that is the equivalent to your bot's password. Anyone with the token can log in as your bot.

After you authorize the bot for the server, it should then show up in the member list of the server as an offline user. Once we write some code and run it, the bot will appear online.

Prepare your Node.js project workspace

First, you will need to have Node.js installed. If you don't already have it installed, get it from Nodejs.org. I will be using Node.js 10.10.0. You will need Node.js 8.0.0 or newer. You will also need npm but that should come packaged with Node.js.

Once you have Node.js installed, you will need to create a new directory to store your project. Once you have a new directory, open your system terminal/command prompt/shell and navigate to your new directory. You can create the directory in your home folder or anywhere you have permission. You may prefer to create a folder on your desktop.

# For example in Windows Command Prompt:
mkdir C:\Users\NanoDano\my_discord_bot
cd C:\Users\NanoDano\my_discord_bot

# Or with the Mac Terminal
mkdir /Users/NanoDano/my_discord_bot
cd /Users/NanoDano/my_discord_bot

# Or with Linux Shell
mkdir /home/NanoDano/my_discord_bot
cd /home/NanoDano/my_discord_bot

Keep your terminal open. You will need it in the next step.

Install the Discord.js module

The next step is to install the Discord.js module and its dependencies. Use npm in the command prompt to install the module:

npm install discord.js    

You might see warnings. Those are OK, it is just telling you about some optional modules. As long as you do not see any errors it should be good. Keep your terminal open here so you can run the program you create in the next step.

At the time of this writing, Discord.js is at version 11.4.2. Future version might change how the code works.

Write a simple test bot

At this point, you should have your development environment ready to go with Node.js and the necessary Discord.js module installed. You should also have your own Discord server, app, and bot user that are ready to be utilized. We should take a moment to make sure everything is set up properly. Let's write the simplest bot possible just to make sure it works. Let's create a new JS file and write some code to have the bot log in. You can name the file my_bot.js though the file name is not important.

Create/open the new file in your favorite editor, and put the following code in the file:

const Discord = require('discord.js')
const client = new Discord.Client()

client.on('ready', () => {
    console.log("Connected as " + client.user.tag)
})

// Get your bot's secret token from:
// https://discordapp.com/developers/applications/
// Click on your application -> Bot -> Token -> "Click to Reveal Token"
bot_secret_token = "XXXXXXXXXXX"

client.login(bot_secret_token)

You will need to replace the XXXXXXXXXXX with your bot's secret token. This is equivalent to your bot's password, so keep it secret! You can find the bot's token by going to https://discordapp.com/developers/applications/ and then clicking on your app. On the app detail page, click on the "Bot" section. On the bot detail page, look for the "Token" section. You will need to "click to reveal token."

Save the file and run the code from the terminal:

node my_bot.js

The output should say Connected as [your bot's name]. If you check the server's member list, the bot should appear online as well. If you can see the bot online in the server, then everything is working properly and we can move on to making the bot actually do something fun! The JavaScript file will continue to run although it doesn't do anything except connect, so use CTRL-C to terminate the program or exit the terminal to kill the process. It is normal for your bot to still appear online for a few minutes after the program is terminated. After a few minutes it will appear offline again.

If you have any errors or get stuck at this spot, try joining the DevDungeon Discord server (https://discord.gg/JWsSHJC) or post a comment to ask for help.

Get a list of servers the bot is connected to

Once your bot is connected, the client object keep a list of servers it is connected to. In Discord terminology, it is called a guild. The client object has an property named guilds which we can access through client.guilds. In the following example, we iterate through the list of servers(guilds) and print out the name of each one. Most likely your bot is only connected to just one server, your test server. Bots can be connected to many servers at once though.

const Discord = require('discord.js')
const client = new Discord.Client()

client.on('ready', () => {
    // List servers the bot is connected to
    console.log("Servers:")
    client.guilds.forEach((guild) => {
        console.log(" - " + guild.name)
    })
})

client.login("XXXXXXXXXXX") // Replace XXXXX with your bot token

The Discord.js Client object has many other properties, like how long it has been connected, all channels available, and what emojis are available. To see a full list of properties on the Client object, check out the official documentation for the Client object at https://discord.js.org/#/docs/main/stable/class/Client.

Get a list of channels from a server

In the previous example we saw how to get a list of servers the client has access to. Each server can have many channels. The guild object has a property named channels that we can access. That will give us the list of channels for that specific guild(server). Another option is to get channels property from the client object instead of the guild object. If we use client.channels as opposed to guild.channels then we will get the list of all channels from all servers.

In this example, we will extend the code from the previous section. We will still list each server we are connected to, but we will also list each channel's name, type, and ID for each server.

const Discord = require('discord.js')
const client = new Discord.Client()

client.on('ready', () => {
    // List servers the bot is connected to
    console.log("Servers:")
    client.guilds.forEach((guild) => {
        console.log(" - " + guild.name)

        // List all channels
        guild.channels.forEach((channel) => {
            console.log(` -- ${channel.name} (${channel.type}) - ${channel.id}`)
        })
    })
})

client.login("XXXXXXXXXXX") // Replace XXXXX with your bot token

Note that there are multiple types of channels including text, voice, and category. The type "category" is not a channel that can be used directly, it is just a label for a group of channels.

Find a channel you would like to use to send messages. There is usually a text channel named "general" that you can use. Take note of the channel ID of the text channel you would like to use. You will need it in the next section to send a message to the channel. For our example, let's pretend the channel ID is 123456789.

Sending a message to a channel

In the previous example we demonstrated how to get all of the channels available. You should have identified a text channel that you want to use. You need the channel.id. For our example, we will pretend the channel ID is 123456789, but you will want to replace that with the actual channel ID. The following code will print "Hello, world!" to the specified channel as soon as the bot connects.

const Discord = require('discord.js')
const client = new Discord.Client()

client.on('ready', () => {
    var generalChannel = client.channels.get("123456789") // Replace with known channel ID
    generalChannel.send("Hello, world!") 
})

client.login("XXXXXXXXXXX") // Replace XXXXX with your bot token

Posting an image or file to a channel

This example will extend the previous example that sends a message to a text channel. Instead of sending a regular text message though, we are going to send an attachment. An attachment can be an image or it can be a file. The attachment can come from your local hard disk or from a web URL. The code below demonstrates how to create an attachment from a local file and then send it to the channel as well as how to create an attachment from an HTTP URL. Note that for the localFileAttachment to wor, you will need to replace the example filepath with the path to a real file on your computer. The webAttachment should work as it links to the DevDungeon logo but you can change it to any image or file on the web.

const Discord = require('discord.js')
const client = new Discord.Client()

client.on('ready', () => {
    var generalChannel = client.channels.get("123456789") // Replace with known channel ID
 
    // Provide a path to a local file
    const localFileAttachment = new Discord.Attachment('D:\\logo.png')
    generalChannel.send(localFileAttachment)

    // Provide a URL to a file
    const webAttachment = new Discord.Attachment('https://www.devdungeon.com/sites/all/themes/devdungeon2/logo.png')
    generalChannel.send(webAttachment)
})

client.login("XXXXXXXXXXX") // Replace XXXXX with your bot token

Replying to a message

In all of the previous examples, we have taken action as soon as the bot connected by using the client.on('ready') event. In this example, we are going to trigger our code whenever a message is received. The client object actually has several events that we can use as triggers. For example: whenever a channel is created, whenever a message is deleted, or when a user updates their presence(e.g. starts playing a game). See the full list of events that you can use on the Client documentation page at https://discord.js.org/#/docs/main/stable/class/Client. For this example we are using the message event, which gets triggered whenever a message is received, either via private message or in a text channel.

The code below will respond any time a message is received with an acknowledgement. Note that the message event gets triggered even when the bot sends a message. This means the bot could enter an infinite loop where it keeps responds to its own messages. To prevent that, the first thing we are going to do is make sure the message received did not come from our bot itself. If it does, we will just return and take no action. If the message received comes from another user, it will continue and reply with the acknowledgement message.

To test the code below, run the bot and using your own Discord user (not the bot) send a message to a text channel on your server that the bot has access to. You can also send the bot a direct/private message and it will respond in the same channel.

const Discord = require('discord.js')
const client = new Discord.Client()

client.on('message', (receivedMessage) => {
    // Prevent bot from responding to its own messages
    if (receivedMessage.author == client.user) {
        return
    }

    receivedMessage.channel.send("Message received: " + receivedMessage.content)
})

client.login("XXXXXXXXXXX") // Replace XXXXX with your bot token

Tagging a user

Let's extend the previous example to also tag the user who sent the message. To tag the user, we can take the user who sent the message, receivedMessage.author, and use the toString method to get the properly formatted tag.

const Discord = require('discord.js')
const client = new Discord.Client()

client.on('message', (receivedMessage) => {
    // Prevent bot from responding to its own messages
    if (receivedMessage.author == client.user) {
        return
    }

    receivedMessage.channel.send("Message received from " + receivedMessage.author.toString() + ": " + receivedMessage.content)
})

client.login("XXXXXXXXXXX") // Replace XXXXX with your bot token

Checking if your bot was tagged in a message

Let's extend the previous example even further by only responding to messages if the bot user was tagged. We will have to inspect the message content and see if our bot's tag was mentioned. We do this by converting our bot's user to a string, and then seeing if the message includes that string.

const Discord = require('discord.js')
const client = new Discord.Client()

client.on('message', (receivedMessage) => {
    // Prevent bot from responding to its own messages
    if (receivedMessage.author == client.user) {
        return
    }
   
    // Check if the bot's user was tagged in the message
    if (receivedMessage.content.includes(client.user.toString())) {
        // Send acknowledgement message
        receivedMessage.channel.send("Message received from " +
            receivedMessage.author.toString() + ": " + receivedMessage.content)
    }
})

client.login("XXXXXXXXXXX") // Replace XXXXX with your bot token

Creating commands

A common task with Discord bots is to create "commands". A command in this sense is a message that triggers a special action. Commands are often specified by beginning a message with a special character. For example !help or ?help or something similar.

In this example we will create a few functions to break up the logic. We will still have our event triggered when a message is received, but this time, we will see if the message starts with an exclamation mark (!) and if it does, we will route it to a custom processCommand function. The processCommand function is responsible for taking the full message from the user and breaking it in to pieces. The user can provide extra parameters to the command by separating words with a space. For example, commands can have no parameters or multuiple parameters like this:

!help
!help someTopic
!multiply 2 4

Inside the processCommand function, after we remove the leading exclamation mark and separate the command from the arguments, we then inspect the main command and then pass the logic to another function. We create a helpCommand and a multiplyCommand. Depending on what command is triggered, the corresponding function is called.

There are limitless ways to abstract out a command handler, but this method should be simple enough to understand and be flexible enough for the needs of most people. I provided two sample commands in order to provide a template for creating your own commands. Try extending this example with a custom command of your own. Here are a few ideas:

  • A !ping command that simply responds with "Pong!"
  • An !8ball command that returns random Yes, No, or Maybe responses.
  • A !fortune command that replies with a random fortune cookie quote.
  • A !weather [zip_code] command that queries a weather API and replies with the local weather.
  • A !bitcoin command that looks up the current BitCoin price.
const Discord = require('discord.js')
const client = new Discord.Client()

client.on('message', (receivedMessage) => {
    if (receivedMessage.author == client.user) { // Prevent bot from responding to its own messages
        return
    }
   
    if (receivedMessage.content.startsWith("!")) {
        processCommand(receivedMessage)
    }
})

function processCommand(receivedMessage) {
    let fullCommand = receivedMessage.content.substr(1) // Remove the leading exclamation mark
    let splitCommand = fullCommand.split(" ") // Split the message up in to pieces for each space
    let primaryCommand = splitCommand[0] // The first word directly after the exclamation is the command
    let arguments = splitCommand.slice(1) // All other words are arguments/parameters/options for the command

    console.log("Command received: " + primaryCommand)
    console.log("Arguments: " + arguments) // There may not be any arguments

    if (primaryCommand == "help") {
        helpCommand(arguments, receivedMessage)
    } else if (primaryCommand == "multiply") {
        multiplyCommand(arguments, receivedMessage)
    } else {
        receivedMessage.channel.send("I don't understand the command. Try `!help` or `!multiply`")
    }
}

function helpCommand(arguments, receivedMessage) {
    if (arguments.length > 0) {
        receivedMessage.channel.send("It looks like you might need help with " + arguments)
    } else {
        receivedMessage.channel.send("I'm not sure what you need help with. Try `!help [topic]`")
    }
}

function multiplyCommand(arguments, receivedMessage) {
    if (arguments.length < 2) {
        receivedMessage.channel.send("Not enough values to multiply. Try `!multiply 2 4 10` or `!multiply 5.2 7`")
        return
    }
    let product = 1
    arguments.forEach((value) => {
        product = product * parseFloat(value)
    })
    receivedMessage.channel.send("The product of " + arguments + " multiplied together is: " + product.toString())
}

client.login("XXXXXXXXXXX") // Replace XXXXX with your bot token

Try running this code and then sending a message to the bot or to the channel with !help and !multiply.

Changing game presence/activity

Changing the bot's activity is relatively simple. We just need to call setActivity() on the bot's user object. By default it will say "Playing ____" but you can also change the verb to "Streaming", "Listening", or "Watching". Try out the code below. After running the code, look at the bot in the member list of the server and you should see it's status change.

const Discord = require('discord.js')
const client = new Discord.Client()

client.on('ready', () => {
    // Set bot status to: "Playing with JavaScript"
    client.user.setActivity("with JavaScript")

    // Alternatively, you can set the activity to any of the following:
    // PLAYING, STREAMING, LISTENING, WATCHING
    // For example:
    // client.user.setActivity("TV", {type: "WATCHING"})
})

client.login("XXXXXXXXXXX") // Replace XXXXX with your bot token

Adding a reaction emoji to a message

There are two types of emojis you can use to react to messages. Each server is allowed to have custom emojis. You can also use Unicode emojis. Unicode is an international standard. You can actually copy and paste the Unicode emoji right in to the source code. The code below demonstrates how to use Unicode emojis and custom emojis. It will react using two standard Unicode emojis and then react with every custom emoji on the server (if the server has any). You can upload custom emojis by going to your Server Settings -> Emoji -> Upload Emoji. Note that not every single Unicode emoji is supported, but Discord does support a huge number of them.

const Discord = require('discord.js')
const client = new Discord.Client()

client.on('message', (receivedMessage) => {
    if (receivedMessage.author == client.user) { // Prevent bot from responding to its own messages
        return
    }

    // You can copy/paste the actual unicode emoji in the code (not _every_ unicode emoji works)
    receivedMessage.react("👍")
    receivedMessage.react("🛐")
    // Unicode emojis: https://unicode.org/emoji/charts/full-emoji-list.html

    // Get every custom emoji from the server (if any) and react with each one
    receivedMessage.guild.emojis.forEach(customEmoji => {
        console.log(`Reacting with custom emoji: ${customEmoji.name} (${customEmoji.id})`)
        receivedMessage.react(customEmoji)
    })
    // If you know the ID of the custom emoji you want, you can get it directly with:
    // let customEmoji = receivedMessage.guild.emojis.get(emojiId)
})

client.login("XXXXXXXXXXX") // Replace XXXXX with your bot token

Playing audio files in voice chat

Playing audio in voice channels requires installing extra dependencies. We will not cover this topic here, but you can find more details and instructions in the official documentation on this page: https://discord.js.org/#/docs/main/stable/topics/voice. Time permitting in the future, this topic might be covered here in more detail.

How to run a bot 24/7

To keep a bot online all the time, you will need to leave the program running. You can do this by simply leaving your computer on and never turning it off. This is not practical for most people. An alternative is to rent a cheap $5/month server from a provider like Linode or Digital Ocean. They provide cheap linux servers that run all the time in their datacenter. You can run your code there. Another alternative is to use a Raspberry Pi from your house that you leave running all the time.

Where to get help

If you have questions or are having any trouble, you can join the DevDungeon community Discord server at https://discord.gg/JWsSHJC or leave a comment on this page.

Conclusion

After following the examples in this tutorial, you should have a solid understanding of some common tasks like sending messages, receiving messages, and adding emoji reactions. With these building blocks, you can build bigger, better, more complex bots. I did not cover every possible aspect though. There are still more advanced things you can do like waiting for replies to messages, waiting for reactions on messages, and creating embedded messages. You can find out more about all of those things inside the official Discord.js documentation. If you have questions about any advanced topics, hop in the DevDungeon Discord server https://discord.gg/JWsSHJC and ask.

References