Skip to content

Create your first Slash Command

Slash Commands Overview

What are Slash Commands?

Slash commands are a type of Discord interface that allows users to interact directly with bots by typing commands that begin with a forward slash (/). Introduced by Discord in late 2020, slash commands have become an integral part of the Discord user experience. They offer a standardized method of sending commands to bots, ensuring that users have a consistent and intuitive way to interact across different servers.

Importance of Slash Commands

Slash commands are crucial for enhancing user engagement and simplifying interactions in Discord. They provide immediate feedback to the user about available commands and how to use them, thanks to the built-in auto-complete feature that guides the user through the command usage process. This not only improves the overall user experience but also reduces the learning curve associated with new bots. Furthermore, slash commands allow for more secure and controlled interactions, as they are processed entirely server-side by Discord, thus reducing the risk of security vulnerabilities associated with traditional text commands.

Implementing Slash Commands (with ban.js)

To define the /ban command, start by importing necessary elements from the umbrae library and setting up the command structure with CommandBuilder. For the example below, we created the command with the path ./commands/[dir]/ban.js.

Warning

In this path [dir] represents a mid-level directory situated between the root commands directory and the specific command file (ban.js). This structure helps organize commands into categorically relevant directories for better manageability and clarity.

js
import { CommandBuilder, Permissions} from 'umbrae';

export default CommandBuilder(
    {
        slash: true,
        name: 'ban',
        description: 'Ban a member',
        permissions: [Permissions.BanMembers, Permissions.ManageMessages],
        cooldown: 5,
        args: [
            {
                name: 'user',
                type: 'User',
                description: 'The user to ban',
                required: true
            },
            {
                name: 'reason',
                type: 'String',
                description: 'The reason for the ban',
                choices: [
                    {
                        name: 'Spam',
                        value: 'spam'
                    },
                    {
                        name: 'Harassment',
                        value: 'harassment'
                    },
                    {
                        name: 'Advertising',
                        value: 'advertising'
                    }
                ],
                required: true
            }
        ]
    },
    async (interaction, app, data) => {
        
        await interaction.deferReply({ ephemeral: true });
        const user = interaction.options.getUser('user', true);

        const member = interaction.guild.members.cache.get(user.id);
        const appMember = interaction.guild.members.cache.get(app.user.id);
        const reason = interaction.options.getString('reason', true);
        
        if (appMember.permissions.has(Permissions.BanMembers) && (appMember.roles.highest.position > (member.roles.highest.position as number))
        && member.manageable) {
            try {
                await member.ban({ reason });
                await interaction.editReply({ content: 'Sucefully banned '+` **${user.globalName}** (`+"`"+user.id+"`"+')' });
            } catch (e) {
                console.error(e);
                await interaction.editReply({ content: 'An error occured while banning '+` **${user.globalName}** (`+"`"+user.id+"`"+')' });
            }
        }
    }
)
ts
import { CommandBuilder, Permissions, SlashCommandBuilderOptions } from 'umbrae';

export default CommandBuilder<SlashCommandBuilderOptions>(
    {
        slash: true,
        name: 'ban',
        description: 'Ban a member',
        permissions: [Permissions.BanMembers, Permissions.ManageMessages],
        cooldown: 5,
        args: [
            {
                name: 'user',
                type: 'User',
                description: 'The user to ban',
                required: true
            },
            {
                name: 'reason',
                type: 'String',
                description: 'The reason for the ban',
                choices: [
                    {
                        name: 'Spam',
                        value: 'spam'
                    },
                    {
                        name: 'Harassment',
                        value: 'harassment'
                    },
                    {
                        name: 'Advertising',
                        value: 'advertising'
                    }
                ],
                required: true
            }
        ]
    },
    async (interaction, app, data) => {
        
        await interaction.deferReply({ ephemeral: true });
        const user = interaction.options.getUser('user', true);

        const member = interaction.guild?.members.cache.get((user.id as string));
        const appMember = interaction.guild?.members.cache.get((app.user?.id as string));
        const reason = interaction.options.getString('reason', true);
        
        if (appMember?.permissions.has(Permissions.BanMembers) && (appMember.roles.highest.position > (member?.roles.highest.position as number))
        && member?.manageable) {
            try {
                await member?.ban({ reason });
                await interaction.editReply({ content: 'Sucefully banned '+` **${user.globalName}** (`+"`"+user.id+"`"+')' });
            } catch (e) {
                console.error(e);
                await interaction.editReply({ content: 'An error occured while banning '+` **${user.globalName}** (`+"`"+user.id+"`"+')' });
            }
        }
    }
)

Warning

The option slash set to true is required to build a SlashCommand otherwise you are building a MessageCommand.

Key Features of the CommandBuilder 1.0.3

  • Simplicity and Clarity: The CommandBuilder function simplifies the definition and setup of commands by encapsulating options and logic in a single function call.
  • Permission Handling: It easily integrates permission checks to ensure that only authorized users can execute sensitive commands like banning.
  • Argument Handling: This setup enables the command to handle user inputs efficiently, providing auto-complete options for reasons, and requiring necessary arguments.

CommandBuilderOptions

If you are using TypeScript to build your project, you should opt for SlashCommandBuilderOptions instead of creating a standard CommandBuilder function. However, you should also review this section to ensure that you are creating commands correctly.

ts
type Permissions = BitFieldResolvable<keyof typeof PermissionFlagsBits, bigint>;
type ArgType = 'Attachment' | 'User' | 'Channel' | 'Role' | 'Mentionable' | 'String' | 'Integer' | 'Number' | 'Boolean' | 'Subcommand' | 'SubcommandGroup';

interface Choice {
    name: string;
    value: string | number;
}

interface Arg {
    name: string;
    description: string;
    type: ArgType;
    choices?: Choice[];
    required?: boolean;
}


interface CommandBuilderOptionsBase {
    name: string;
    description: string;
    cooldown?: number;
    permissions: Permissions[];
    args?: Arg[];
}

interface SlashCommandBuilderOptions extends CommandBuilderOptionsBase {
    slash: true;
}