# Several events in one class

Run one handler class for several discord.js events with this.match(), and get a compile error for any event you forget.

A member log might post when someone joins and again when someone changes their profile. Two handler classes would each need the posting code, so you'd pull it into a shared helper that both files import.

One class can answer both instead. [`@RegisterEvent`](https://seedcord.org/docs/packages/gateway/latest/functions/register-event) takes as many event tuples as you pass it. [`this.match`](https://seedcord.org/docs/packages/gateway/latest/classes/event-handler#match) runs the case for the current event.

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

@RegisterEvent([Events.GuildMemberAdd], [Events.GuildMemberUpdate])
export class MemberLog extends EventHandler<
    Events.GuildMemberAdd | Events.GuildMemberUpdate
> {
    public async execute(): Promise<void> {
        await this.match({
            [Events.GuildMemberAdd]: (member) =>
                this.post(member, 'joined'),
            [Events.GuildMemberUpdate]: (_before, after) =>
                this.post(after, 'updated their profile')
        });
    }

    private async post(
        member: GuildMember,
        what: string
    ): Promise<void> {
        await member.guild.systemChannel?.send(
            `${member} ${what}.`
        );
    }
}
```

You write the events twice. `MemberLog` passes `@RegisterEvent` one tuple for each event, then lists the same two events as a union in the `EventHandler` generic. If the decorator and the generic list different events, the class fails to compile. Both cases call the same private `post`, so you write the posting code once.

> **Warning**
>
> [`this.event`](https://seedcord.org/docs/packages/gateway/latest/classes/event-handler#event) is `never` on a handler registered for several events, since the payload depends on the event's name. Read the payload through `this.match` there.

## What a case receives

A case takes that event's own payload, spread into parameters. That's one member for `guildMemberAdd` and two for `guildMemberUpdate`, under the names discord.js uses.

```ts
await this.match({
    [Events.GuildMemberAdd]: (member) =>
        this.logger.info(`${member.displayName} joined`),
    [Events.GuildMemberUpdate]: (before, after) =>
        this.logger.info(
            `${before.displayName} is now ${after.displayName}`
        )
});
```

`before` is `GuildMember | PartialGuildMember`. discord.js builds `before` from its cache. If discord.js hadn't cached the member, `before` is a partial whose `joinedAt`, `joinedTimestamp`, and `pending` are `null`.

## Every case is required

Without `match`, you'd branch with a `switch` on the event name. Then you add a third event to the decorator and forget its case. That event then silently does nothing. `match` checks the cases against the union, so a missing case fails to compile.

```ts
@RegisterEvent([Events.GuildMemberAdd], [Events.GuildMemberUpdate])
export class MemberLog extends EventHandler<
    Events.GuildMemberAdd | Events.GuildMemberUpdate
> {
    public async execute(): Promise<void> {
        await this.match({
            [Events.GuildMemberAdd]: (member) =>
                post(member, 'joined')
        });
    }
}
```

The second line of that error says which case is missing, here `[Events.GuildMemberUpdate]`. If you write a case for an event that the generic doesn't list, that fails to compile too.

## Cases return values

Often only a small part differs between events, like the wording of a log line. `match` returns the value from the case that ran, so each case can return only the part that differs. The shared work runs once after `match`.

```ts
const what = await this.match({
    [Events.GuildMemberAdd]: () => 'joined',
    [Events.GuildMemberUpdate]: () =>
        'updated their profile'
});

await post(what);
```

`what` is a `string`. `post(what)` runs once for either event.

## Mixed frequency

Each tuple carries its own options, so one event's handler can run once while the others run on every dispatch.

```ts
@RegisterEvent(
    [Events.GuildMemberAdd, { frequency: 'once' }],
    [Events.GuildMemberUpdate]
)
```

Here the join case runs for the first member and never again. The update case runs every time.

When several handlers need the same check before they run, like skipping muted members, an [event middleware](https://next.seedcord.org/guide/events/middleware/) runs it once ahead of all of them.
