# Writing a bot

Source: https://docs.gryt.chat/docs/build/bots

Writing and running a Gryt bot

`@gryt/bot` is an SDK for writing Gryt bots in TypeScript. A bot joins a server
the way any other client does — a key it holds, a self-signed certificate, and a
challenge-response over P-256 — so from the server's side it's a member like
any other.

```ts

const bot = new GrytBot({
  host: "chat.example.com",
  nickname: "Helper",
  wants: ["read_messages", "send_messages"],
  description: "Answers questions in #support",
});

bot.command("ping", async (ctx) => ctx.reply("pong"), {
  description: "Check I am alive",
  requires: ["send_messages"],
});

void bot.start();
```

There's no bot account type, no bot token and no bot bypass. What a bot may do
is what an admin agreed to let it do, enforced by the same checks that apply to
a person.

This page teaches the shape. [The API reference](https://docs.gryt.chat/docs/build/bot-api) is
everything `@gryt/bot` exports, generated from the source, and there are two bots
to copy in
[the SDK's `examples/`](https://github.com/Gryt-chat/bot/tree/main/examples).

## How a bot gets in

It knocks.

1. The bot starts, knowing only the server's address. It says what it is called
   and what it wants to be allowed to do.
2. It's turned away, and a request is left behind.
3. An admin opens **Server settings → Bots**, sees the ask, unticks anything
   they would rather it didn't have, and lets it in.
4. The approval reaches the bot without a restart. Leave it running.

Nothing to configure on the server, no invite, and no restart.

<Callout type="warn" title="What a bot asks for is fixed">
The first declaration is the only one. A later run asking for more gets the
answer to the question the first one asked, and the server won't record the
new list at all — a bot can't even change its own name after approval.

That isn't about you. It's about the run that isn't yours, after a published
image has been taken over. If a bot genuinely needs more, an admin makes a new
registration.
</Callout>

### Setting one up in advance

For a compose file or CI, where nobody is watching the first launch: an admin
creates the registration first, decides everything up front, and hands over a
single-use token.

```ts
const bot = new GrytBot({ host, botToken: process.env.GRYT_BOT_TOKEN });
```

The token is shown once, when it's created. The first bot to present it becomes
that registration; after that it stops working.

## A bot isn't a person, and can't pretend to be

Bots have their own identity namespace with a `BOT_` prefix, so a bot can never
hold an id a person could hold. Every surface that shows a member shows a **BOT**
tag beside the name — in the member list and on every message — derived from the
identity rather than from anything the bot sends.

People can't take bot-shaped names either: `BOT_helper` and `Bot Helper` are
refused. Robot and Botany are fine.

## Running one

A bot is a container. The [support bot
example](https://github.com/Gryt-chat/bot/tree/main/examples/support-bot) is a
folder to copy, with a Dockerfile and a compose file.

```yaml
services:
  support-bot:
    image: ghcr.io/you/support-bot:latest
    restart: unless-stopped
    environment:
      GRYT_HOST: chat.example.com
    volumes:
      - support-bot-identity:/data

volumes:
  support-bot-identity:
```

<Callout type="warn" title="The volume isn't optional">
`gryt-bot-identity.json` **is** the bot — the id the server knows it by is
derived from the key inside it. Keep it and the bot keeps its permissions across
restarts and upgrades. Lose it and the server sees a stranger knocking, holding
nothing.

Mounting the volume isn't enough on its own. By default the bot writes the file
to `./gryt-bot-identity.json`, next to the code, and only the bot can move it.
`identityPath` is an option on `GrytBot`, and the SDK reads no environment
variables of its own. The support bot passes `process.env.GRYT_IDENTITY_PATH`,
and its Dockerfile sets that to `/data/gryt-bot-identity.json`. Without that, a
bot works, survives restarts, and loses its identity the next time you rebuild
the image.
</Callout>

## Asking for the least that works

The permissions a bot declares are the list an admin has to decide about. A long
one is a list they can't check, and the ones they untick are the ones you find
out about at runtime — so check before you act:

```ts
if (bot.can("add_reactions")) {
  await bot.react(conversationId, messageId, "👋");
}
```

`bot.can()` answers from what the server said, and stays current: an admin who
widens or narrows a bot mid-run is obeyed without a restart.

Commands declare what they need and are **skipped rather than attempted** when
the bot doesn't hold it, so nobody types a command and watches nothing happen:

```ts
bot.command("purge", handler, { requires: ["manage_messages"] });
```

## For server admins

Whether bots may knock at all is a switch at the top of the Bots tab. Turn it
off and the only way in is a token you hand out yourself.

Approving a bot needs the *manage bots* permission, which by default only the
owner has — approving one is granting permissions to something nobody in the
room can vouch for. See [Roles and permissions](https://docs.gryt.chat/docs/use/roles).
