# Gates

Run checks before your handler with @Gated, so a caller who fails one never reaches your code. Covers how a gate refuses, stacking several on one handler, the gates seedcord ships, and rewording the refusal a user sees.

Most handlers open with the same few checks. Is this a server? Can this person ban? If you write those checks inside `execute()`, every handler starts with its own copy of them. If you forget to copy one into a handler, that command runs for anyone.

A gate puts that check on the class and runs before `execute()`. If it passes, your handler runs, and if it refuses, the call stops there. Attach one with [`@Gated`](https://docs.seedcord.org/packages/gateway/latest/gated), above the route decorator. The `Ban` handler below runs only for someone in a server who holds `BanMembers`.

```ts title="src/handlers/Ban.ts"
import {
    Gated,
    GuildOnly,
    RequirePermissions,
    SlashHandler,
    SlashRoute
} from '@seedcord/gateway';
import { PermissionFlagsBits } from 'discord.js';

@Gated(
    GuildOnly(),
    RequirePermissions([PermissionFlagsBits.BanMembers])
)
@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}.`);
    }
}
```

`execute()` runs once both gates pass. `GuildOnly()` refuses a call from a DM, and `RequirePermissions` refuses a caller without `BanMembers`.

## A gate refuses by throwing

seedcord catches a gate's throw at the same boundary it catches your handler's. The user sees the same card either way. [Throwing](/replying/throwing) covers what each class produces.

Every gate in the table below throws a [`Notice`](https://docs.seedcord.org/packages/core/latest/notice) except [`IgnoreBots`](https://docs.seedcord.org/packages/gateway/latest/ignore-bots), which throws a [`Silence`](https://docs.seedcord.org/packages/core/latest/silence). The message gets dropped and your bot doesn't reply.

## Several gates on one handler

`@Gated` takes one gate or several, and all of them have to pass. They run left to right, and the first one to refuse stops the rest.

The order you pick matters once a gate does something costly, like reading a database. Put the cheap checks first, so a caller who fails one never reaches the read behind it.

A second `@Gated` on the same class adds its gates to the list. TypeScript applies class decorators from the bottom up, so the one nearer the class runs first. One `@Gated` with every gate in it keeps the order where you can read it.

## The catalog gates

{/* prettier-ignore-start */}

| gate                                                                                                    | passes when                                                                       |
| ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| [`OwnerOnly()`](https://docs.seedcord.org/packages/core/latest/owner-only)                              | the caller's id is in `ownerIds`                                                  |
| [`GuildOnly()`](https://docs.seedcord.org/packages/core/latest/guild-only)                              | the call came from a server                                                       |
| [`DmOnly()`](https://docs.seedcord.org/packages/core/latest/dm-only)                                    | the call came from a direct message                                               |
| [`RequireRole(id)`](https://docs.seedcord.org/packages/core/latest/require-role)                        | the caller holds that role                                                        |
| [`RequirePermissions(bits)`](https://docs.seedcord.org/packages/core/latest/require-permissions)        | the caller holds every bit you name                                               |
| [`RequireBotPermissions(bits)`](https://docs.seedcord.org/packages/core/latest/require-bot-permissions) | your bot holds every bit you name                                                 |
| [`Cooldown(duration)`](https://docs.seedcord.org/packages/core/latest/cooldown)                         | uses remain in the current window                                                 |
| [`Nsfw()`](https://docs.seedcord.org/packages/gateway/latest/nsfw)                                      | the channel is age-restricted. Gateway interaction handlers only, modals excluded |
| [`IgnoreBots`](https://docs.seedcord.org/packages/gateway/latest/ignore-bots)                           | the actor is a person. Gateway only, and event handlers only                      |

{/* prettier-ignore-end */}

> **Gateway only**
>
> `Nsfw` and `IgnoreBots` ship in `@seedcord/gateway` alone. `Nsfw` reads the discord.js channel object on the interaction, and `IgnoreBots` attaches to event handlers, which an http bot doesn't have.

Most of these are functions, so you call one and pass the result. `IgnoreBots` doesn't take options, so you attach it directly.

```ts title="src/events/Mentioned.ts"
import {
    EventHandler,
    Gated,
    IgnoreBots,
    RegisterEvent
} from '@seedcord/gateway';
import { Events } from 'discord.js';

@Gated(IgnoreBots)
@RegisterEvent([Events.MessageCreate])
export class Mentioned extends EventHandler<Events.MessageCreate> {
    public async execute(): Promise<void> {
        const [message] = this.event;

        await message.reply('hey');
    }
}
```

Every gate carries the context it reads, and `@Gated` checks that against the handler you put it on. `Nsfw()` reads an interaction's channel, so attaching it to the event handler below fails to compile. The error names the gate and the handler kind.

```ts
@Gated(Nsfw())
@RegisterEvent([Events.MessageCreate])
export class Welcome extends EventHandler<Events.MessageCreate> {
    public async execute(): Promise<void> {}
}
```

`Cooldown` has [a page of its own](/checks/cooldown).

### Who counts as an owner

`OwnerOnly` fits a command only you should run, like a reload or a shutdown. It reads `ownerIds` from your bot config, and with that key unset it refuses *everyone*.

```ts title="src/bot.ts"
import { resolve } from 'node:path';

import { Seedcord } from '@seedcord/gateway';
import { GatewayIntentBits } from 'discord.js';

export const seedcord = new Seedcord({
    ownerIds: ['221439658686939136'],
    bot: {
        clientOptions: { intents: [GatewayIntentBits.Guilds] },
        interactions: {
            path: resolve(import.meta.dirname, './handlers')
        },
        commands: {
            path: resolve(import.meta.dirname, './commands')
        },
        events: { path: null }
    },
    subscribers: { path: null }
});
```

## Rewording a refusal

Each refusal card uses seedcord's default wording. Reword it when your command has a next step to offer, like pointing someone at the server where it works.

`OwnerOnly`, `GuildOnly`, `DmOnly`, and `Nsfw` take the same [`GateNoticeOptions`](https://docs.seedcord.org/packages/core/latest/gate-notice-options). `message` swaps the text and keeps the card. `notice` replaces the whole refusal with a [`Notice`](https://docs.seedcord.org/packages/core/latest/notice) you wrote.

```ts
GuildOnly({ message: 'Try this in a server.' });

OwnerOnly({ notice: new Locked() });
```

The `Require` gates nest that shape one level deeper, since each can refuse for two different reasons. `RequirePermissions` takes a `notInGuild` and a `missing` object, each holding its own `message` or `notice`. `Cooldown` takes `message` and `notice` as functions of `resetAt`, the time in milliseconds when the caller can try again, so its refusal can print that time. Each gate in [the catalog table](#the-catalog-gates) links to its API page, which lists every option it takes.

[Permissions](/checks/permissions) takes the permission gates in turn, including what Administrator does to them. When none of the shipped gates fits, you can [write your own](/checks/your-own) or [combine several into one](/checks/combining).
