# The core object

Read your config and your plugins from any handler through this.core, and run work while your bot starts and stops.

Your `/ping` handler already read `this.core.uptime`. Other handlers read your own config, a database, or the rate limiter that a cooldown charges. You read all of it from `this.core`, one object that seedcord builds while your bot starts.

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

@SlashRoute('ping')
export class Ping extends SlashHandler<'ping'> {
    public async execute(): Promise<void> {
        const owners = this.core.config.ownerIds;

        await this.reply(
            `${owners?.length ?? 0} owners configured.`
        );
    }
}
```

`this.core.config` is the object you passed to `new Seedcord()`. The type under the `ownerIds` line marks it optional, which is why the sample guards it with `owners?.length`. [`OwnerOnly`](https://seedcord.org/docs/packages/core/latest/functions/owner-only) reads the same key.

Other code reaches the same object. A gate, which is a check that runs before a handler, reads it as `ctx.core`. A subscriber, which listens for your own events, and a plugin read `this.core` like a handler does.

## What it carries

{/* prettier-ignore-start */}

| member          | holds                                                                                                                  |
| --------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `config`        | the object you passed to `new Seedcord()`                                                                              |
| `rest`          | Discord's REST client, which is the discord.js client's own on gateway                                                 |
| `applicationId` | your bot's Discord application id                                                                                      |
| `rateLimiter`   | the counters behind [`Cooldown`](https://preview.seedcord.org/guide/checks/cooldown/), which you can also use directly |
| `bus`           | [publishing and subscribing](https://preview.seedcord.org/guide/events/bus/) for framework and application events      |
| `startup`       | `addTask`, for work that runs while your bot starts                                                                    |
| `shutdown`      | `addTask`, for work that runs while it stops                                                                           |
| `bot`           | on gateway, `bot.client` is the discord.js client and `bot.botToken` is your token. Http has no `bot`                  |

{/* prettier-ignore-end */}

[`CoreBase`](https://seedcord.org/docs/packages/core/latest/interfaces/core-base) is the part both transports share. Each one adds its own members to it, and a plugin you attach adds a key of its own, as the last section shows.

## Running work while your bot starts and stops

Suppose your bot uses a cache of its own. It has to open before the first command runs and close when the bot stops. Without seedcord, you listen for `SIGTERM` yourself to close it.

You write a function that opens the cache and another that closes it. You register them with [`startup.addTask`](https://seedcord.org/docs/packages/core/latest/classes/coordinated-startup#add-task) and [`shutdown.addTask`](https://seedcord.org/docs/packages/core/latest/classes/coordinated-shutdown#add-task), each in a phase, which is a stage of startup or shutdown. seedcord runs the phases in order. If the cache needs options of its own or its own log channel, write a [plugin](https://preview.seedcord.org/guide/plugins/) instead. A plugin starts and stops in these same phases.

```ts title="src/bot.ts"
import {
    Seedcord,
    ShutdownPhase,
    StartupPhase
} from '@seedcord/gateway';
import { GatewayIntentBits } from 'discord.js';

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

seedcord.startup.addTask(
    StartupPhase.Configuration,
    'open-cache',
    (signal) => openCache(signal)
);

seedcord.shutdown.addTask(
    ShutdownPhase.Disconnect,
    'close-cache',
    closeCache
);

export default seedcord;
```

Each call takes the phase, a name for the logs, the function to run, and an optional timeout in milliseconds. A startup task gets 10000 unless you pass a timeout. A shutdown task gets 5000.

Add every task before the bot starts, because `addTask` throws `LifecycleTaskChangeRejected` once startup is running or finished. The sample does it at the top level of `bot.ts`.

A startup task's function receives an `AbortSignal`, and the sample passes it to `openCache`. Pass it to any request the task makes, because it aborts if the task times out or startup stops. A shutdown task gets a signal too, and it aborts if that task times out.

Pick the phase by what must already be ready when your task runs. The sample registers `open-cache` in `Configuration`, before anything connects, and `close-cache` in `Disconnect`, the phase for closing external resources. [Plugin lifecycle](https://preview.seedcord.org/guide/plugins/lifecycle/) has a table of every startup and shutdown phase and what has run before it.

Tasks in the same phase run at the same time, including seedcord's own tasks. The next phase starts once all of them have finished. If one task needs another task's result, put the task that needs it in a later phase.

If a startup task throws, startup fails and no later phase runs. If a shutdown task fails, the phases after it still run.

All the shutdown phases share one deadline of 25000 milliseconds, and after them seedcord waits three more seconds to flush the logs. To finish before your platform stops the process, set `lifecycle.shutdownDeadline` lower in the object you pass to `new Seedcord()`.

> **Gateway only**
>
> `core.applicationId` comes from the discord.js client, so it resolves during the Login phase. If you read it in a `Configuration` task, it throws. Read it in a handler or in a `Ready` task. `core.rest` has no token before Login either.

> **Http only**
>
> On [Cloudflare Workers](https://preview.seedcord.org/guide/build/workers/), startup tasks run on the first POST request each worker instance receives, in the same phases and with the same timeouts. Requests that arrive meanwhile wait for them. If a task fails, the next request runs the tasks again.
>
> `shutdown.addTask` throws `CoreLifecycleUnavailable` there, because Cloudflare doesn't have a shutdown hook for a worker. Do your cleanup inside a handler. A Workers config doesn't take a `lifecycle`.

## Attach a plugin to the core

If you construct a plugin yourself, you pass it down through imports and order its setup by hand. Attaching it puts it on the core, so every handler already reads it from `this.core`.

[`attach()`](https://seedcord.org/docs/packages/gateway/latest/classes/seedcord#attach) takes a key, a plugin class, and the options that plugin's constructor expects. The key becomes a property on the same object your handlers read.

```ts
import { Seedcord } from '@seedcord/gateway';
import { KyselyPostgres } from '@seedcord/plugin-kysely-postgres';
import { GatewayIntentBits } from 'discord.js';

export default seedcord.attach('db', KyselyPostgres, {
    dir: './services',
    connectionString: 'postgres://localhost:5432/seedcord',
    migrations: {
        dir: './migrations'
    }
});
```

`attach()` sets the property at runtime. The type that makes `this.core.db` compile in a handler is defined in `seedcord-gen.d.ts`. `seedcord codegen` writes that file, and `seedcord dev` rewrites it each time the bot starts. Both `dir` values are relative to `root` in `seedcord.config.ts`, like the folders in `bot.ts`.

The scaffold's `bot.ts` attaches its `Uptime` plugin with `.attach('uptime', Uptime)`, so the `ping` handler can read `this.core.uptime`.
