Setting Up Slash Commands For Discord Bot
Tags: javascript, node, discord, npm
Published: 2022-06-06 03:47:24

## Getting Started

Having a head start with this bot already we should have certain dependencies install, if not here they are:

`npm install @discordjs/builders @discordjs/rest discord-api-types`

These will be used in creating the Slash Commands for use in Discord.

This blog assumes you have already read what has been built previously in this post [here](https://dev.to/swislokdev/discord-bot-with-javascript-1bg2)

You will also need to have generated a new URL that include "bot" and "applications.commands" in order to create commands for the Discord server to used for the bot.

### deploy-commands.js

```JavaScript
const { SlashCommandBuilder } = require('@discordjs/builders')
const { REST } = require('@discordjs/rest')
const { Routes } = require('discord-api-types/v9')
const { clientId, guildId, token } = require('./config.json')
const rest = new REST({ version: '9' }).setToken(token)

const commands = [
  new SlashCommandBuilder()
    .setName('ping')
    .setDescription('Replies with pong'),
  new SlashCommandBuilder()
    .setName('server')
    .setDescription('Replies with server info'),
  new SlashCommandBuilder()
    .setName('user')
    .setDescription('Replies with user info'),
].map((command) => command.toJSON())

rest
  .put(Routes.applicationGuildCommands(clientId, guildId), { body: commands })
  .then(() => console.log('Successfully registered application commands.'))
  .catch(console.error())
```

The `clientId` is taken from the developer portal for the bot and the `guildId` is which guild you would like to target these commands for.

There is a way to create commands which will omit the guildId in order to pass for all servers that the bot joins.

### index.js

The following is to be added after the client has been called on initialization.

```JavaScript
clinet.on('interactionCreate', async interaction => {
  if (!interaction.isCommand()) return

  const { commandName } = interaction

  if (commandName === 'ping') {
    await interaction.reply('Pong!')
  } else if (commandName === 'server') {
    await interaction.reply(`
      Server name: ${interaction.guild.name}\n
      Total members: ${interaction.guild.memberCout}\n
      Server created on: ${interaction.guild.createdAt}
    `)
  } else if (commandName === 'user') {
    await interaction.reply(`
      Your tag: ${interaction.user.tag}\n
      Your id: ${interaction.user.id}
    `)
  }
})
```

You should now run `node deploy-commands.js`. This will only need to be run once unless you change anything how the command is built or what server you want the commands deployed to.

Running the bot in the server with the guildId that was used, in the text field placing a `"/"` should now have commands that you created for use by your bot. Using the slash commands will show the command name and the `description` as you have written it.

## Refactorize!

If you're like me and tend to add a ton of commands to help out the general moderation of a server then you'll get a bit tired of writing commands out like this. Not to mention how messy things will end up getting.

We can create a few directories and refactor some of the existing ones

Let's start by creating the new files which will ultimately be the new home for all the commands that you wish to have.

Create a new directory called `commands`.

Within the command directory the `ping`, `server`, and `user` will become their own ".js" files.

### ping.js

```JavaScript
// commands/ping.js
const { SlashCommandBuilder } = require('@discordjs/builders')

module.exports = {
  data: new SlashCommandBuilder()
    .setName('ping')
    .setDescription('Replies with Pong!'),
  async execute(interaction) {
    await interaction.reply('Pong!')
  },
}
```

### server.js

```JavaScript
// commands/server.js
const { SlashCommandBuilder } = require('@discordjs/builders')

module.exports = {
  data: new SlashCommandBuilder()
    .setName('server')
    .setDescription('Display info about this server.'),
  async execute(interaction) {
    return interaction.reply(
      `Server name: ${interaction.guild.name}\nTotal members: ${interaction.guild.memberCount}`
    )
  },
}
```

### user.js

```JavaScript
// commands/user.js
const { SlashCommandBuilder } = require('@discordjs/builders')

module.exports = {
  data: new SlashCommandBuilder()
    .setName('user')
    .setDescription('Display info about yourself.'),
  async execute(interaction) {
    return interaction.reply(
      `Your username: ${interaction.user.username}\nYour ID: ${interaction.user.id}`
    )
  },
}
```

Next edit the deploy-commands.js and index.js files

### deploy-commands.js

```JavaScript
// node modules that will be included to existing file
const fs = require('node:fs')
const path = require('node:path')

...

// remove everything inside the commands array and replace with an empty array
const commands = []

const commandsPath = path.join(__dirname, 'commands')
const commandFiles = fs
  .readdirSync(commandsPath)
  .filter((file) => file.endsWith('.js'))

for (const file of commandFiles) {
  const filePath = path.join(commandsPath, file)
  const command = require(filePath)
  commands.push(command.data.toJSON())
}

...
```

A similar thing will be done with the index.js file to create all the commands.

### index.js

```JavaScript
const fs = require('node:fs')
const path = require('node:path')
const { Client, Intents, Collection } = require('discord.js')
const { token } = require('./config.json')

const client = newClient({ intents: [Intents.FLAGS.GUILDS] })

client.commands = new Collection()
const commandsPath = path.join(__dirname, 'commands')
const commandFiles = fs
  .readdirSync(commandsPath)
  .filter((file) => file.endsWith('.js'))

for (const file of commandFiles) {
  const filePath = path.join(commandsPath, file)
  const command = require(filePath)
  client.commands.set(command.data.name, command)
}

client.once('ready', () => {
  console.log('Ready!')
})

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) {
    console.error(error)
    await interaction.reply({
      content: 'There was an error while executing this command!',
      ephemeral: true,
    })
  }
})

client.login(token)
```

Now we have cleaned up the index.js and deploy-commands.js, we'll add one more command that will open up the options to add `options` on a command.

### echo.js

```JavaScript
// commands/echo.js
const { SlashCommandBuilder } = require('@discordjs/builders')

module.exports = {
  data: new SlashCommandBuilder()
    .setName('echo')
    .setDescription('Replies with you input!')
    .addStringOption((option) =>
      option
        .setName('input')
        .setDescription('The input to echo back')
        .setRequired(true)
    ),
  async execute(interaction) {
    await interaction.reply({
      content: interaction.options.getString('input'),
      ephemeral: true,
    })
  }
}
```

The `.addStringOption()` will allow for an input, in this case to reply what we say, and do something with it. In the event you wish you mute someone on the server you will be able to use `/mute @this-noisy-user` and the bot will take of it for you. Setting up for how long they will be muted for is up to you and if your bot stays running to unmute them.