# Several events in one class

Run one handler class for several discord.js events with this.match(). Covers what each arm receives, the compile error for a missing arm, returning a value from an arm, and mixing frequencies.

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://docs.seedcord.org/packages/gateway/latest/register-event) takes as many event tuples as you pass it. [`this.match`](https://docs.seedcord.org/packages/gateway/latest/event-handler#match) runs the arm for whichever event fired.

```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}.`
        );
    }
}
```

`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 arms call the same private `post`, so you write the posting code once.

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

## What an arm receives

An arm 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 arm 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 arms against the union, so a missing arm 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 arm is missing, here `[Events.GuildMemberUpdate]`. If you write an arm for an event that the generic doesn't list, that fails to compile too.

## Arms return values

Often only a small part differs between events, like the wording of a log line. `match` returns the value from the arm that ran, so each arm 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, whichever event fired.

## Mixed frequency

Each tuple carries its own options, so one event can run once while the others keep firing.

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

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

> **Warning**
>
> seedcord records a `once` run against the whole class. If one class marks two different events as `once`, like `guildMemberAdd` and `guildMemberUpdate`, whichever fires first marks the class as run. The other event's arm then won't run until your bot restarts. Put each run-once event in its own class.
>
> An arm on that class without `once` still runs every time. Another class registered for the same event keeps its own record and follows its own `frequency`.

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