# Replying

Answer an interaction from its handler with reply, defer, edit, and followUp. Covers why the answer goes through the handler, a reply as a list of components, keeping it to one person, and the message you get back.

Discord gives your bot three seconds to answer an interaction. seedcord puts the methods for answering on the handler. [`this.reply()`](https://docs.seedcord.org/packages/gateway/latest/repliable-handler#reply) sends the first response. [`this.defer()`](https://docs.seedcord.org/packages/gateway/latest/repliable-handler#defer) shows a thinking placeholder while you work, then [`this.edit()`](https://docs.seedcord.org/packages/gateway/latest/repliable-handler#edit) fills it in. [`this.followUp()`](https://docs.seedcord.org/packages/gateway/latest/repliable-handler#follow-up) sends another message after either one.

```ts title="src/handlers/Ban.ts"
import { SlashHandler, SlashRoute } from '@seedcord/gateway';

@SlashRoute('ban')
export class Ban extends SlashHandler<'ban'> {
    public async execute(): Promise<void> {
        const target = this.options.getUser('target');

        await this.reply(`Banned ${target.username}.`);
    }
}
```

Passing a string is the short form. seedcord wraps it in one text component.

## Why answer through the handler?

On gateway, `this.event` is the discord.js interaction, so `this.event.reply()` sends a message too. On http it is Discord's raw payload, and you answer it through [`this.api`](https://docs.seedcord.org/packages/http/latest/repliable-handler#api). The handler's own methods take the same names on both transports, and each one records what it sent.

That record is the interaction's ack state. `this.reply()` in the handler above moves it to replied, and every method checks it before calling Discord. A second `reply()` throws at that line.

```txt output
reply() was called when this interaction was already replied to.
Use followUp() for a new message or edit() to rewrite the reply. (route slash:ban)
```

discord.js checks too, and its error reads "The reply to this interaction has already been sent or deferred." seedcord's says which method works from there and which route threw. Its `cause` carries a stack pointing at the line that answered first. On http, `this.api` doesn't check at all, so a second callback comes back from Discord as an API error. [When a reply throws](/replying/ack-states) covers every case.

The state matters most when your handler refuses. You refuse by throwing a [`Notice`](https://docs.seedcord.org/packages/core/latest/notice). seedcord then sends its card through the same handler with [`send()`](https://docs.seedcord.org/packages/gateway/latest/repliable-handler#send), which reads the state to pick a method. If nothing went out yet, the card is the reply. After `defer()` it fills the thinking placeholder, and after a reply it arrives as a follow-up. You write the same `throw` from any of those points.

That holds while every answer goes through the handler. `this.event.reply()` reaches Discord and leaves the state at `unacked`. A callback on `this.api.interactions` does the same on http. seedcord then sends a `Notice` thrown after it as a first reply, which Discord rejects because the interaction was already acknowledged. seedcord drops that rejection with a debug line. The person who ran the command sees your message and never sees the refusal.

## A reply is a list of components

A string gives you one block of text. A heading, a card, or a row of buttons goes in `components`, one entry per top-level component.

```ts title="src/handlers/Ban.ts"
class BanCard extends BuilderComponent<'container'> {
    constructor(name: string) {
        super('container');

        this.instance.addTextDisplayComponents((text) =>
            text.setContent(`Banned ${name}.`)
        );
    }
}

@SlashRoute('ban')
export class Ban extends SlashHandler<'ban'> {
    public async execute(): Promise<void> {
        const target = this.options.getUser('target');

        await this.reply({
            components: [new BanCard(target.username).component]
        });
    }
}
```

[`BuilderComponent`](https://docs.seedcord.org/packages/core/latest/builder-component) wraps a discord.js builder, and `.component` gives you the builder to put in `components`. A card that only `Ban` sends can stay in its file. Move `BanCard` to a file of its own once a second handler sends one, so rewording it takes one edit.

`files` uploads bytes alongside the components, covered in [Files and attachments](/replying/files). The whole shape is [`ReplyResponse`](https://docs.seedcord.org/packages/types/latest/reply-response). Put it on a helper's return type when the helper builds a reply, so a wrong field fails inside the helper.

> **Warning**
>
> seedcord sets Discord's ComponentsV2 flag on every reply it sends. Discord forbids `content`, `embeds`, and `poll` on a message carrying that flag. `reply`, `edit`, `followUp`, `update`, and `send` all build their message out of components.
>
> To send an embed or a poll, use discord.js's own methods on `this.event`. That skips the handler's ack state, so a later `this.edit()` throws and a thrown `Notice` never shows. Read [Raw acks](/replying/raw-acks) before you do.

### Mentions inside your components

seedcord sends `allowedMentions` only when you set one. Discord's default for an interaction reply is `{ parse: ['users'] }`, so a `<@id>` in your text pings that person. A role or `@everyone` mention stays plain text.

```ts
class ReasonText extends BuilderComponent<'text_display'> {
    constructor(reason: string) {
        super('text_display');

        this.instance.setContent(reason);
    }
}

@SlashRoute('ban')
export class Ban extends SlashHandler<'ban'> {
    public async execute(): Promise<void> {
        const reason = this.options.getString('reason') ?? '';

        await this.reply({
            components: [new ReasonText(reason).component],
            allowedMentions: { parse: [] }
        });
    }
}
```

`reason` there is text someone typed into your command, so it can hold anyone's `<@id>`. The empty `parse` blocks every mention in it, so your bot doesn't ping anyone. Set one on any reply that repeats what a person typed.

{/* prettier-ignore-start */}

| field   | takes                                     | effect                          |
| ------- | ----------------------------------------- | ------------------------------- |
| `parse` | any of `'users'`, `'roles'`, `'everyone'` | turns on each kind in the array |
| `users` | up to 100 ids                             | only those users ping           |
| `roles` | up to 100 ids                             | only those roles ping           |

{/* prettier-ignore-end */}

## Only the person who ran the command sees it

Ephemeral is on by default. Pass `ephemeral: false` to show the reply to the whole channel. `silent: true` skips push and desktop notifications.

```ts
await this.reply('Banned.', {
    ephemeral: false,
    silent: true
});
```

`followUp()` takes `ephemeral` and `silent` the same way. `edit()` doesn't take options, since it rewrites a message that already has its flags.

## The message you get back

`reply()` resolves to the message it created. Keep that message and you can rewrite or delete it later, which [Follow-ups and edits](/replying/more-messages) shows.

```ts
const message = await this.reply('Banned.');
```

> **Gateway and http differ**
>
> On gateway you get a discord.js `Message`. On http you get an
> `APIMessage`, which is Discord's raw payload. `followUp()`,
> `edit()`, and [`update()`](https://docs.seedcord.org/packages/gateway/latest/component-handler#update)
> all return the same type.
