> ## Documentation Index
> Fetch the complete documentation index at: https://www.chapterjs.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Options

> Declare what people fill in after the name of a command, and receive it typed in your function

Options are what people fill in after the name of a command. Declare them as an object, and `run` receives their values **already typed**: you never write a type.

```ts src/commands/mod/ban.ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import { command } from 'chapterjs';

export default command({
  name: 'mod ban',
  description: 'Bans a member',
  permissions: ['BanMembers'],
  options: {
    target: { type: 'user', description: 'Who to ban', required: true },
    reason: { type: 'string', description: 'Why', maxLength: 200 },
    days: { type: 'integer', description: 'Days of messages to delete', min: 0, max: 7 },
  },
  async run({ interaction, options, guild }) {
    // options.target: User
    // options.reason: string | undefined
    // options.days: number | undefined
    await guild.ban(options.target, {
      reason: options.reason,
      deleteMessageSeconds: (options.days ?? 0) * 86_400,
    });
    await interaction.reply(`Banned ${options.target.displayName}.`);
  },
});
```

That is all: Discord asks for `target`, `reason` and `days` in that order, and your editor knows that `options.target` is a `User` and that `options.reason` may be missing.

> [!NOTE]
>
> Every field of every kind of option is in the [reference](https://www.chapterjs.org/reference/command.md#the-options).

## Declaring an option

The key is the name of the option, the value says what it is. Every option has a `type` and a `description` (1 to 100 characters, shown under its name):

| `type` | What the person fills in | What `run` receives |
| - | - | - |
| `'string'` | A text | `string` |
| `'integer'` | A whole number | `number` |
| `'number'` | Any number | `number` |
| `'boolean'` | True or false | `boolean` |
| `'user'` | A user | [`User`](https://www.chapterjs.org/reference/user) |
| `'role'` | A role | [`Role`](https://www.chapterjs.org/reference/role) |
| `'channel'` | A channel | [`Channel`](https://www.chapterjs.org/reference/channels) |
| `'mentionable'` | A user or a role | `User \| Role` |
| `'attachment'` | A file | [`Attachment`](https://www.chapterjs.org/reference/data#attachment) |

Names follow the rules of Discord: lowercase letters, digits, `-` and `_`, 32 characters at most. A command has 25 options at most.

## Required or optional

An option is **optional** unless it says `required: true`. In `run`, a required option is always there; an optional one may be `undefined`, and your editor reminds you:

```ts src/commands/greet.ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import { command } from 'chapterjs';

export default command({
  name: 'greet',
  description: 'Greets someone',
  options: {
    who: { type: 'user', description: 'Who to greet', required: true },
    message: { type: 'string', description: 'What to say' },
  },
  async run({ interaction, options }) {
    // options.who: User
    // options.message: string | undefined
    await interaction.reply(`${options.message ?? 'Hello'}, ${options.who.displayName}!`);
  },
});
```

Discord wants the required options first. You can declare them in any order: ChapterJS puts the required ones first for you, and keeps the order you wrote for the rest.

## Choices

With `choices`, the person picks in a list instead of typing, and the value is typed as one of the choices. A `string`, `integer` or `number` option can have between 1 and 25 choices:

```ts src/commands/color.ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import { command } from 'chapterjs';

export default command({
  name: 'color',
  description: 'Picks a color',
  options: {
    // A list: what the person sees is what your code receives.
    color: { type: 'string', description: 'Your color', choices: ['red', 'green'], required: true },
    // An object: the key is shown, the value is received.
    size: { type: 'integer', description: 'How big', choices: { Small: 1, Large: 2 } },
  },
  async run({ interaction, options }) {
    // options.color: 'red' | 'green'
    // options.size: 1 | 2 | undefined
    await interaction.reply(`You picked ${options.color}.`);
  },
});
```

The choices of a `string` option are texts, those of an `integer` or `number` option are numbers; what is shown is 1 to 100 characters. The same choice can't appear twice.

## Autocomplete

When the values are not known in advance, or change (a song, a player, a ticket), `autocomplete` suggests them **while the person types**: Discord asks your bot every few keystrokes, and shows what your function returns under the option. The person may still type something else.

```ts src/commands/play.ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import { command } from 'chapterjs';

const SONGS = [
  { id: '1', title: 'Daylight' },
  { id: '2', title: 'Dark Horse' },
  { id: '3', title: 'Darkside' },
];

export default command({
  name: 'play',
  description: 'Plays a song',
  options: {
    song: { type: 'string', description: 'Which song', required: true },
  },
  autocomplete: {
    // Called while the person types in "song", with what they typed so far.
    song: ({ value }) =>
      SONGS.filter(song => song.title.toLowerCase().startsWith(value.toLowerCase()))
        // Shown as the title, received as the id.
        .map(song => ({ name: song.title, value: song.id })),
  },
  async run({ interaction, options }) {
    // options.song: string, the id of the song, or whatever was typed
    const song = SONGS.find(song => song.id === options.song);
    await interaction.reply(song ? `Playing ${song.title}.` : 'I do not know that song.');
  },
});
```

`autocomplete` has one function per option, by its name, for a `string`, `integer` or `number` option without `choices`. It receives:

| Field | What it is |
| - | - |
| `value` | What the person typed so far: a text (maybe empty) for a `string` option; for an `integer` or `number` option, a number, or `undefined` while it is not one yet |
| `options` | What the person filled in so far, every option optional; a `user`, `role`, `channel`, `mentionable` or `attachment` option is its id |
| `user`, `locale` | Who is typing, and their language |
| `guild`, `member`, `channel` | Where, following `where` like `run` |
| `t` | The texts of the project, in the language of the person (see [translated messages](https://www.chapterjs.org/translated-messages.md)) |

It returns the suggestions (or a promise of them): a value, shown as is, or `{ name, value }` to show a text and receive another value. A `string` option suggests texts, an `integer` or `number` option numbers. Discord shows 25 at most, in the order you return them, and waits 3 seconds: return quickly, your best matches first.

> [!WARNING]
>
> A suggestion is a help, not a limit: Discord sends your `run` what the person typed, picked in the list or not. Check the value, as in the sample.

What the function returns is checked when it is sent. Too many suggestions are cut to 25, and a name over 100 characters is shortened, said once with `⚠`. What can't be fixed is an error, and nothing is suggested:

```text theme={"theme":{"light":"github-light","dark":"vesper"}}
✗ src/commands/play.ts A suggestion of /play song has the value 1: the option is a text, so its suggestions must be texts.
```

## Limits on what is typed

A text can be given a length, a number a range:

| Field | For | Meaning |
| - | - | - |
| `minLength` | `string` | The shortest text accepted, in characters (0 to 6000) |
| `maxLength` | `string` | The longest text accepted, in characters (1 to 6000) |
| `min` | `integer`, `number` | The smallest value accepted |
| `max` | `integer`, `number` | The largest value accepted |

Discord refuses what is outside before the command even reaches your bot. For an `integer` option, `min`, `max` and the choices are whole numbers.

## Channels of some kinds only

`channelTypes` says which kinds of channels can be picked. By default, all of them:

```ts src/commands/announce.ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import { command, ChannelType } from 'chapterjs';

export default command({
  name: 'announce',
  description: 'Announces something in a channel',
  permissions: ['ManageGuild'],
  options: {
    channel: { type: 'channel', description: 'Where', channelTypes: [ChannelType.GuildText], required: true },
    text: { type: 'string', description: 'What to say', required: true, maxLength: 2000 },
  },
  async run({ interaction, options }) {
    if (!options.channel.isTextBased()) return interaction.reply('Pick a text channel.');
    await options.channel.send(options.text);
    await interaction.reply({ content: 'Done.', ephemeral: true });
  },
});
```

The values are those of [`ChannelType`](https://www.chapterjs.org/reference/enums#channeltype). What `run` receives is still a [`Channel`](https://www.chapterjs.org/reference/channels): check its kind with `isTextBased()` or `isVoice()` before using what only that kind has.

## What is checked

Everything an option declares is checked when the file loads, against the limits of Discord. A mistake is explained in your terminal before Discord refuses it, with the file in front:

> [accordion] A name Discord refuses
>
> ```text theme={"theme":{"light":"github-light","dark":"vesper"}}
> ✗ src/commands/ban.ts "Target" can't be the name of an option: use lowercase letters, digits, - and _ only, 32 characters at most.
> ```

> [accordion] A type that does not exist
>
> ```text theme={"theme":{"light":"github-light","dark":"vesper"}}
> ✗ src/commands/ban.ts The type of the option "target" is "member", which does not exist. Types are: string, integer, number, boolean, user, role, channel, mentionable, attachment.
> ```

> [accordion] A field the type does not have
>
> ```text theme={"theme":{"light":"github-light","dark":"vesper"}}
> ✗ src/commands/ban.ts "min" is not something the option "reason" has. It can have: type, description, required, choices, minLength, maxLength.
> ```

> [accordion] A description missing or too long
>
> ```text theme={"theme":{"light":"github-light","dark":"vesper"}}
> ✗ src/commands/ban.ts the option "target" needs a description: a short text that says what it is for, like description: 'Bans a member'.
> ```
>
> ```text theme={"theme":{"light":"github-light","dark":"vesper"}}
> ✗ src/commands/ban.ts The description of the option "target" is 120 characters long: Discord accepts 100 at most.
> ```

> [accordion] Too many options
>
> ```text theme={"theme":{"light":"github-light","dark":"vesper"}}
> ✗ src/commands/big.ts This command has 26 options: Discord accepts 25 at most.
> ```

> [accordion] Choices
>
> ```text theme={"theme":{"light":"github-light","dark":"vesper"}}
> ✗ src/commands/color.ts the option "color" has 26 choices: Discord accepts between 1 and 25.
> ```
>
> ```text theme={"theme":{"light":"github-light","dark":"vesper"}}
> ✗ src/commands/color.ts The choices of the option "size" must be numbers, since its type is integer: got "big".
> ```
>
> ```text theme={"theme":{"light":"github-light","dark":"vesper"}}
> ✗ src/commands/color.ts The choice 1.5 of the option "size" is not a whole number.
> ```
>
> ```text theme={"theme":{"light":"github-light","dark":"vesper"}}
> ✗ src/commands/color.ts the option "color" has the same choice twice.
> ```

> [accordion] A range that can never be filled in
>
> ```text theme={"theme":{"light":"github-light","dark":"vesper"}}
> ✗ src/commands/ban.ts the option "days" can never be filled in: its "min" (8) is greater than its "max" (7).
> ```
>
> ```text theme={"theme":{"light":"github-light","dark":"vesper"}}
> ✗ src/commands/ban.ts "maxLength" of the option "reason" is between 0 and 6000, got 9000.
> ```

> [accordion] Channel types
>
> ```text theme={"theme":{"light":"github-light","dark":"vesper"}}
> ✗ src/commands/announce.ts "channelTypes" of the option "channel" is a list of ChannelType values, like [ChannelType.GuildText].
> ```

> [accordion] Autocomplete
>
> ```text theme={"theme":{"light":"github-light","dark":"vesper"}}
> ✗ src/commands/play.ts "title" of "autocomplete" is not an option of this command. Its options are: song.
> ```
>
> ```text theme={"theme":{"light":"github-light","dark":"vesper"}}
> ✗ src/commands/play.ts "who" of "autocomplete" can't have suggestions: the option "who" is a user. Only a string, integer, number option can.
> ```
>
> ```text theme={"theme":{"light":"github-light","dark":"vesper"}}
> ✗ src/commands/play.ts "song" of "autocomplete" can't have suggestions: the option "song" has "choices", and Discord takes one or the other. Remove its choices to suggest them from here, or take it out of "autocomplete".
> ```

Your editor underlines most of these before you even save: the types of `command()` only accept what exists.

> [card] [Answering](https://www.chapterjs.org/commands/answering.md)
>
> Reply, defer, edit, follow up.

> [card] [Translations](https://www.chapterjs.org/commands/translations.md)
>
> Options and choices in the language of each person.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.