DEV Community

Cover image for How to make a slash commands bot with Discord.js V13
Kunal Bagaria
Kunal Bagaria

Posted on • Updated on

How to make a slash commands bot with Discord.js V13

So Discord has implemented a pretty big change in their API starting from early 2022 which will make message content a privileged intent and verified bots will have to request that intent additionally.

So I'm here to share how you can migrate your bot to slash commands entirely so that it doesn't stop working.

Before we get started, if you'd like to hang out with the community, you can join our Discord server here: DevHQ

If you are here just for the resulting code, you can find out the repository here: Repository

Deploy on Railway

Let's get started.

You have to invite your bot with an updated scope of application.commands which is required to register slash commands.

Screen Shot 2021-09-04 at 7.24.58 PM

First things first, its always recommended to use yarn since its much better than npm, so you can install that using:

npm -g install yarn

Let's make a new directory for our bot, cd it, and initialize it using:

yarn init --yes

Let's install Discord.js and some other dependencies,

yarn add discord.js @discordjs/rest discord-api-types/v9 @discordjs/builders
Enter fullscreen mode Exit fullscreen mode

Time to create an index.js file, and import the following stuff:

const fs = require('fs');
const {
    REST
} = require('@discordjs/rest');
const {
    Routes
} = require('discord-api-types/v9');
// Require the necessary discord.js classes
const {
    Client,
    Intents,
    Collection
} = require('discord.js');

// Create a new client instance
const client = new Client({
    intents: [Intents.FLAGS.GUILDS]
});
Enter fullscreen mode Exit fullscreen mode

Now we have created a client instance which only uses the Guild intent required to respond for slash commands, it is much less memory intensive than processing every single message throughout Discord.

Now, we will create a dynamic command structure for our bot, so lets create a directory called commands and add this to the index.js file:

// Loading commands from the commands folder
const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));
Enter fullscreen mode Exit fullscreen mode

Now it's time for tokens, pushing tokens into GitHub is very not recommended so we have to use env files to store our tokens safely.

yarn add dotenv

Create a .env file and add your token and your test guild / server id here, we'll talk about the test server later.

TOKEN=YOUR_TOKEN_HERE
TEST_GUILD_ID=YOUR_GUILD_ID_HERE
Enter fullscreen mode Exit fullscreen mode

You can copy the Guild ID like this
Screen Shot 2021-09-04 at 7.16.36 PM

Create a .gitignore file and add this to the file:

.env
Enter fullscreen mode Exit fullscreen mode

Time to configure it in our index file:

// Loading the token from .env file
const dotenv = require('dotenv');
dotenv.config();
const TOKEN = process.env['TOKEN'];
Enter fullscreen mode Exit fullscreen mode

Now that we have finally gotten our token, it's time to talk about the Test Guild ID, so slash commands in Discord requires us to register it first, and we can't register it globally every time in development or we are gonna get rate-limited and potentially banned from accessing their API for some time.

We only need this in development, we don't need to do this when we are deploying our bot in production. So please don't add the Test Guild ID when you want your commands globally available. Now let's add the Guild ID to the index file:

const TEST_GUILD_ID = process.env['TEST_GUILD_ID'];
Enter fullscreen mode Exit fullscreen mode

It's time to register our commands now, we will make a condition that will automatically switch between global and development registration depending upon whether the Test Guild ID is available.

const commands = [];

// Creating a collection for commands in client
client.commands = new Collection();

for (const file of commandFiles) {
    const command = require(`./commands/${file}`);
    commands.push(command.data.toJSON());
    client.commands.set(command.data.name, command);
}

// When the client is ready, this only runs once
client.once('ready', () => {
    console.log('Ready!');
    // Registering the commands in the client
    const CLIENT_ID = client.user.id;
    const rest = new REST({
        version: '9'
    }).setToken(TOKEN);
    (async () => {
        try {
            if (!TEST_GUILD_ID) {
                await rest.put(
                    Routes.applicationCommands(CLIENT_ID), {
                        body: commands
                    },
                );
                console.log('Successfully registered application commands globally');
            } else {
                await rest.put(
                    Routes.applicationGuildCommands(CLIENT_ID, TEST_GUILD_ID), {
                        body: commands
                    },
                );
                console.log('Successfully registered application commands for development guild');
            }
        } catch (error) {
            if (error) console.error(error);
        }
    })();
});
Enter fullscreen mode Exit fullscreen mode

Now that we have registered our commands in Discord, it's time to listen and respond to them

client.on('interactionCreate', async interaction => {
    if (!interaction.isCommand()) return;
    const command = client.commands.get(interaction.commandName);
    if (!command) return;
    try {
        await command.execute(interaction);
    } catch (error) {
        if (error) console.error(error);
        await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
    }
});
Enter fullscreen mode Exit fullscreen mode

Finally for our index file, we need to log in to our bot

// Login to Discord with your client's token
client.login(TOKEN);
Enter fullscreen mode Exit fullscreen mode

We can now move into the commands sub-directory, and let's make a ping.js file with the following code:

const { SlashCommandBuilder } = require('@discordjs/builders');

module.exports = {
    data: new SlashCommandBuilder()
        .setName('ping')
        .setDescription('Replies with pong'),
    async execute(interaction) {
        interaction.reply({ content: 'Pong' })
    }
};
Enter fullscreen mode Exit fullscreen mode

We have now successfully created a working ping pong bot in Discord.js V13. Now you can create as many commands as you want for your bot by following the same format as the ping command. The code for embeds are same, and there are a lot of new features like buttons and select menus which we haven't talked about here. If you want to send an embed, you just need to do:

interaction.reply({ embeds: [embed] })
Enter fullscreen mode Exit fullscreen mode

Hope you like this short tutorial and you can follow me on Twitter here

Free Deployment Platforms:

Hope to see you on the next one :)

Top comments (4)

Collapse
 
errolm profile image
ErrolM

I'm Having issues with the code in the for loop where it states commands.push(command.data.toJSON())

its states .toJSON() is not defined

Collapse
 
soulsoccer73 profile image
Soulsoccer

i tried these few changes and it worked out for me, ive commented the corrections along

Client.command=new Collection(); //command instead of commands

Client.command.set(command.data.name, command);
commands.push(command.data.toJSON());

Collapse
 
darahask profile image
Darahas Kopparapu • Edited

How can i register commands on every server my bot is added to?

Collapse
 
kmohzaid profile image
KMohZaid

Don't provide guild id in .env

See in if/else if guild id is not provided so it is registering globally