Slash commands

CommandBuilder produces application command definitions matching Discord's API. Use it with the commands framework or hand the JSON to...

CommandBuilder produces application command definitions matching Discord's API. Use it with the commands framework or hand the JSON to client.createCommand / client.bulkEditCommands.

A basic command

import { CommandBuilder } from 'athena';
 
const cmd = new CommandBuilder('avatar', "Show a user's avatar")
  .addUserOption({ name: 'user', description: 'Whose avatar', required: false });
 
await client.createCommand(cmd.toJSON());

A builder created as new CommandBuilder(name, description) serialises to a chat-input command without any extra call.

Options

new CommandBuilder('search', 'Search the docs')
  .addStringOption({ name: 'query', description: 'Text', required: true, autocomplete: true, min_length: 1, max_length: 100 })
  .addIntegerOption({ name: 'page', description: 'Page', min_value: 1, max_value: 100 })
  .addNumberOption({ name: 'amount', description: 'A decimal' })
  .addBooleanOption({ name: 'verbose', description: 'Extra detail' })
  .addUserOption({ name: 'user', description: 'A user' })
  .addChannelOption({ name: 'channel', description: 'A channel', channel_types: [ChannelType.GuildText] })
  .addRoleOption({ name: 'role', description: 'A role' })
  .addMentionOption({ name: 'target', description: 'User or role' })
  .addAttachmentOption({ name: 'file', description: 'Upload' });

String, integer, and number options accept up to 25 choices:

.addStringOption({ name: 'lang', description: 'Language', choices: [
  { name: 'English', value: 'en' },
  { name: 'Espanol', value: 'es' }
]})

Subcommands and groups

new CommandBuilder('settings', 'Server settings')
  .addSubcommand((s) => s.setName('view').setDescription('View settings'))
  .addSubcommandGroup((g) =>
    g.setName('notifications').setDescription('Manage notifications')
      .addSubcommand((s) => s.setName('enable').setDescription('Turn on'))
      .addSubcommand((s) => s.setName('disable').setDescription('Turn off'))
  );

Permissions and contexts

new CommandBuilder('purge', 'Bulk delete')
  .setMemberPermission(PermissionFlagsBits.ManageMessages)
  .setNSFW(false)
  .setContexts([InteractionContextType.Guild, InteractionContextType.BotDM])
  .setIntegrationTypes([ApplicationIntegrationType.GuildInstall, ApplicationIntegrationType.UserInstall]);
  • setMemberPermission(bits) sets the default required permission.
  • setContexts([...]) replaces the deprecated setDMPermission. Use Guild, BotDM, PrivateChannel.
  • setIntegrationTypes([...]) distinguishes guild installs from user installs.

Context menu commands

new CommandBuilder('Report message', '').setCommandType(ApplicationCommandType.Message);
new CommandBuilder('View profile', '').setCommandType(ApplicationCommandType.User);

Localization

new CommandBuilder('ping', 'Replies with pong')
  .setNameLocalizations({ 'es-ES': 'ping' })
  .setDescriptionLocalizations({ 'es-ES': 'Responde pong' });

Autocomplete

Mark an option autocomplete: true and implement handleAutocomplete:

async handleAutocomplete(context, interaction) {
  const focused = interaction.focused();
  const matches = await search(focused.value as string);
  await interaction.acknowledge(matches.slice(0, 25).map((m) => ({ name: m.title, value: m.id })));
}

Deploying

await client.createCommand(builder.toJSON());                 // one global command
await client.bulkEditCommands([builder.toJSON()]);            // replace all global
await client.bulkEditGuildCommands(guildID, [builder.toJSON()]); // instant, per guild

With the framework, call deployCommands() once on ready and it picks guild or global scope from NODE_ENV and DEV_GUILD. See Commands framework.

Receiving a command without the framework

client.on('interactionCreate', async (interaction) => {
  if (!interaction.isCommand() || interaction.data.name !== 'avatar') return;
  const user = interaction.getUser('user') ?? interaction.user;
  await interaction.createMessage({ embeds: [{ title: user.username, image: { url: user.dynamicAvatarURL('png', 1024) } }] });
});

Option getter quick map

Builder methodReceiver
addStringOptiongetString
addIntegerOptiongetInteger
addNumberOptiongetNumber
addBooleanOptiongetBoolean
addUserOptiongetUser / getMember
addChannelOptiongetChannel
addRoleOptiongetRole
addMentionOptiongetMentionable
addAttachmentOptiongetAttachment