# Your first bot

Create a seedcord project with the scaffolder and get it answering in Discord. Covers what it asks, running it, what the scaffolder made, changing the ping command, and a file that fails to import.

You need the token from the last page.

```sh
pnpm create seedcord
```

## What it asks

* **Where the project goes.** The folder name becomes the package name, so it takes lowercase letters, digits, and `. _ -`.
* **TypeScript or JavaScript.**
* **How Discord reaches your bot.** Gateway or http, the choice from two pages ago.
* **What your bot should react to.** Gateway only. A checklist covering messages in servers, reactions, member joins, polls, and more. Each one says which intents and partials it needs, and the privileged ones are marked. The scaffolder writes the set into your `bot.ts`.
* **Your bot token.**
* **Your app public key.** Http only.
* **Your bot color.** Every embed and container you build takes it, unless you set a color on that one yourself.

Then the scaffolder installs your dependencies, formats the project, runs `seedcord codegen`, and makes a git commit.

## Run it

```sh
cd my-bot
pnpm run dev
```

Your bot logs in and sends its commands to Discord. The terminal turns into a dev console.

### Run the ping command

The scaffold ships a `ping` command. Type `/ping` in a server your bot is in.

The command probably won't be there yet. Global commands take time to appear, and Discord doesn't publish how long that takes.

> **Tip**
>
> Restart your Discord client with Ctrl+R or Cmd+R. That fetches a
> fresh command list and usually ends the wait.

## What the scaffolder made

The `ping` command came from a file the scaffolder wrote, along with everything else in this tree.

```txt title="my-bot" output
my-bot/
├─ .vscode/
│  └─ extensions.json
├─ src/
│  ├─ commands/
│  │  └─ Ping.ts
│  ├─ events/
│  │  └─ Ready.ts
│  ├─ handlers/
│  │  └─ Ping.ts
│  ├─ bot.ts
│  ├─ index.ts
│  └─ seedcord-gen.d.ts
├─ .env
├─ .gitignore
├─ .prettierignore
├─ eslint.config.ts
├─ package.json
├─ prettier.config.ts
├─ README.md
├─ seedcord.config.ts
├─ tsconfig.build.json
└─ tsconfig.json
```

* **`src/bot.ts`*&#x2A; builds your bot and exports it. &#x2A;*`src/index.ts`** imports that and starts it. They are separate so the CLI can import `bot.ts` and read your commands without logging in.
* **`seedcord.config.ts`** configures the CLI. Your bot's own settings go in `bot.ts`, which is the file below.
* **`src/seedcord-gen.d.ts`** carries the types generated from your config and your commands. `seedcord codegen` writes it and you commit it.
* **`tsconfig.json`*&#x2A; type-checks the project. &#x2A;*`tsconfig.build.json`** compiles `src` into `dist`.
* **`.env`** holds your token. `bot.ts` sets `Envapter.baseDir` to the project root, which is where [envapt](https://envapt.materwelon.dev) then looks for the file.
* **`src/events/`** holds `Ready.ts`. Picking any of the message capabilities swaps that for `Mentioned.ts`, which answers when someone mentions your bot.

> **Http only**
>
> Discord posts only interactions to an http bot, so the
> scaffolder writes no `src/events` folder.

### Your bot's settings

Every answer you gave shows up in one object.

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

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

Envapter.baseDir = resolve(import.meta.dirname, '..');

export const seedcord = new Seedcord({
    bot: {
        // passed straight to the discord.js Client constructor
        clientOptions: {
            intents: [GatewayIntentBits.Guilds]
        },
        interactions: {
            path: resolve(import.meta.dirname, './handlers')
        },
        commands: {
            path: resolve(import.meta.dirname, './commands')
        },
        events: {
            path: resolve(import.meta.dirname, './events')
        }
    },
    subscribers: {
        path: null
    },
    botColor: '#4ade80',
    notifications: {
        developerUsername: 'you'
    }
});

export default seedcord;
```

The `path` keys name the folders seedcord scans at startup. The tab that teaches each folder covers its key.

> **Http only**
>
> An http config takes a top-level `port`, which defaults to 3000. `clientOptions` and `events` belong to gateway alone.

### The ping command

Two files make `/ping` work.

```ts title="src/commands/Ping.ts"
import {
    BuilderComponent,
    RegisterCommand
} from '@seedcord/gateway';

@RegisterCommand('global')
export class Ping extends BuilderComponent<'command'> {
    constructor() {
        super('command');

        this.instance
            .setName('ping')
            .setDescription('Check that the bot is answering')
            .addBooleanOption((option) =>
                option
                    .setName('detailed')
                    .setDescription('Include process uptime')
            );
    }
}
```

Discord gets a name, a description, and one optional boolean.

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

@SlashRoute('ping')
export class Ping extends SlashHandler<'ping'> {
    public async execute(): Promise<void> {
        const roundtrip =
            Date.now() - timestampFromSnowflake(this.event.id);
        const lines = ['### Pong', `Roundtrip **${roundtrip}ms**`];

        if (this.options.getBoolean('detailed')) {
            lines.push(
                `Uptime **${Math.round(process.uptime())}s**`
            );
        }

        await this.reply(lines.join('\n'));
    }
}
```

That runs when someone uses it. `'ping'` appears twice, on the decorator and on the generic, and both have to match the name the command sets. The compiler catches a mismatch. [`this.options`](https://docs.seedcord.org/packages/gateway/latest/slash-handler#options) carries `detailed` because `seedcord codegen` recorded it off the command file.

> **Http only**
>
> An http scaffold writes the same two files and imports from
> `@seedcord/http`.

## Change something

Open `src/handlers/Ping.ts` and change what it replies with. Save.

The dev server reloads the handler while the bot stays connected. Run `/ping` again and the new reply comes back.

Now open `src/commands/Ping.ts` and change the command itself, its description for example. Save, then look at the terminal. The dev server asks whether to re-register.

```txt output
Commands updated:
- src/commands/Ping.ts
Refresh commands? (y/n)
```

A handler edit needs no confirmation because nothing leaves your machine. A command edit does, since the new definition has to go to Discord before anyone sees it.

Press `y`. That sends the new command to Discord and runs `seedcord codegen`, which rewrites `src/seedcord-gen.d.ts` so your handler's types match what the command now declares.

Press `n` if you want Discord to keep the old command.

## When a file fails to import

seedcord imports every file under `src/commands`, `src/handlers`, and `src/events` at startup. A syntax error in any one of them stops the whole scan. A half-typed file saved mid-edit does it too, so the restart below is the normal way back.

```txt output
src/handlers/Ping.ts threw while importing.
```

Fix the file and press `r` to restart.
