# Commands

URL: https://zerodev.ca/docs/kora/developer-documentation/commands

> Add Discord slash commands to Kora's bot from any module.



A command class adds a slash command to Kora's Discord bot. Any module can ship commands. The [Discord extension](/docs/kora/extensions/discord) registers and runs them, so it must be turned on with a bot token and server set.

Put commands in `src/commands/`, one class per file.

```js title="modules/example/src/commands/sync.js"
import { Commands } from "#kora/modules";

export class SyncCommand extends Commands {
    constructor(kora) {
        super(kora, {
            name: "sync",
            description: "Sync licenses with the store",
            permission: "licenses",
            options: [
                { type: "Subcommand", name: "edit", description: "Sync one product", options: [
                    { type: "String", name: "product", description: "Product", required: true, autocomplete: true }
                ] }
            ]
        });
    }

    async complete(interaction, { bot }) {
        return bot.suggest(interaction.options.getFocused(true));
    }

    async run(interaction, { bot, actor }) {
        const count = await this.kora.modules.handlers.get("example", "sync").product(interaction.options.getString("product", true), actor);
        return bot.embed(bot.accent(), "Sync Complete").setDescription(`Synced **${count}** licenses.`);
    }
}
```

| Option        | Default  | What it does                           |
| ------------- | -------- | -------------------------------------- |
| `name`        | Required | The slash command name.                |
| `description` | `""`     | Shown in Discord's command picker.     |
| `permission`  | `""`     | The permission prefix. See below.      |
| `options`     | `[]`     | The command's options and subcommands. |

Commands are registered in the server set in the Discord config each time the bot starts.

## Options

Options use Discord's option fields, with `type` written as the name of the type:

| `type`                                                 | Option                                |
| ------------------------------------------------------ | ------------------------------------- |
| `Subcommand`, `SubcommandGroup`                        | A subcommand, with its own `options`. |
| `String`, `Integer`, `Number`, `Boolean`               | A value.                              |
| `User`, `Channel`, `Role`, `Mentionable`, `Attachment` | A Discord object.                     |

`required`, `choices`, `autocomplete`, `min_value` and `max_value` work as in Discord.

## Permissions

With a `permission`, the bot checks the member's Kora role for `<permission>.<subcommand>`. In the example, `/sync edit` needs `licenses.edit`. Discord accounts are matched to Kora team members by their linked Discord ID. A member without the permission gets the "No permission" message from the Discord lang file.

Leave `permission` empty for a command anyone in the server can use, then check access yourself:

```js
if (!this.kora.api.allowed(actor, "whitelists.create")) return bot.failure(this.kora.modules.lang.phrase("discord", "messages", "denied"));
```

## Methods

| Method                            | When it runs                                | Returns                             |
| --------------------------------- | ------------------------------------------- | ----------------------------------- |
| `run(interaction, context)`       | The command is used.                        | The reply.                          |
| `complete(interaction, context)`  | An `autocomplete` option is being typed.    | Up to 25 `{ name, value }` choices. |
| `component(interaction, context)` | A button or menu from this command is used. | The new message.                    |

`context` holds `bot`, the Discord handler, and `actor`, the member running it.

### Replies

Every reply is private to the member. Return an `EmbedBuilder`, or a message payload such as `{ embeds, components }`. Embeds are turned into a Components V2 container in the bot's accent colour. If `run` throws, the error message is sent in its place.

### Buttons and Menus

Give a component a custom ID of `<command>:<action>:<args>`:

```js
new ButtonBuilder().setCustomId(`sync:retry:${product.id}`).setLabel("Retry")
```

When it is pressed, `component` runs with `action` and `args` in `context`. The permission check uses the action, so this one needs `licenses.retry`. The return value replaces the message. Add `ephemeral: true` to send a new private reply instead.

## Bot Helpers

| Helper                          | What it does                                                                                                 |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `bot.embed(tone, title)`        | A new embed in the accent colour with a title and timestamp.                                                 |
| `bot.accent()`                  | The accent colour from the Discord config, or Kora's accent colour.                                          |
| `bot.failure(message)`          | An error reply.                                                                                              |
| `bot.suggest(focused)`          | Autocomplete for options named `license`, `product`, `customer`, `status`, `currency`, `expires` or `group`. |
| `bot.input(interaction, names)` | The subcommand's options as an object, with keys renamed by `names`.                                         |
| `bot.time(value)`               | A readable date, or "Never".                                                                                 |
| `bot.relative(value)`           | A time such as `5m ago`.                                                                                     |
