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
Let's get started.
You have to invite your bot with an updated scope of application.commands which is required to register slash commands.
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
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]
});
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'));
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
You can copy the Guild ID like this
Create a .gitignore file and add this to the file:
.env
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'];
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'];
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);
}
})();
});
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 });
}
});
Finally for our index file, we need to log in to our bot
// Login to Discord with your client's token
client.login(TOKEN);
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' })
}
};
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] })
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)
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
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());
How can i register commands on every server my bot is added to?
Don't provide guild id in .env
See in if/else if guild id is not provided so it is registering globally