Recipes

Quick Reference

A collection of frequently used snippets and recipes for Seyfert bots.

Event Patterns

Ready Event with Command Upload

src/events/ready.ts
import {  } from 'seyfert';

export default ({
    : { : 'botReady', : true },
    async (, ) {
        await .({ : './commands.json' });
        ..(`${.} is online!`);
    },
});

Welcome DM on Member Join

src/events/welcome.ts
import {  } from 'seyfert';

export default ({
    : { : 'guildMemberAdd' },
    async (, ) {
        if (..) return;

        await .({
            : `Welcome to the server, ${..}!`,
        });
    },
});

Support Thread Auto-Reply

src/events/supportThread.ts
import {  } from 'seyfert';

const  = '123456789';

export default ({
    : { : 'channelCreate' },
    async () {
        if (!.() || . !== ) return;

        await ..({
            : 'Welcome to your support thread! A team member will assist you shortly.',
        });
    },
});

Forward raw gateway packets to external managers like Lavalink or Shoukaku:

src/events/raw.ts
import { createEvent } from 'seyfert';

export default createEvent({
    data: { name: 'raw' },
    async run(payload, client) {
        await client.manager.sendRawData(payload);
    },
});

Full Event Reference

Common events and their signatures:

// Messages
createEvent({ data: { name: 'messageCreate' }, run(message, client) {} });
createEvent({ data: { name: 'messageUpdate' }, run([newMessage, oldMessage], client) {} });
createEvent({ data: { name: 'messageDelete' }, run(message, client) {} });

// Guilds
createEvent({ data: { name: 'guildCreate' }, run(guild, client) {} });
createEvent({ data: { name: 'guildDelete' }, run(guild, client) {} });
createEvent({ data: { name: 'guildUpdate' }, run([newGuild, oldGuild], client) {} });

// Members
createEvent({ data: { name: 'guildMemberAdd' }, run(member, client) {} });
createEvent({ data: { name: 'guildMemberRemove' }, run(member, client) {} });
createEvent({ data: { name: 'guildMemberUpdate' }, run([newMember, oldMember], client) {} });

// Voice
createEvent({ data: { name: 'voiceStateUpdate' }, run([newState, oldState], client) {} });

// Channels
createEvent({ data: { name: 'channelCreate' }, run(channel, client) {} });
createEvent({ data: { name: 'channelUpdate' }, run([newChannel, oldChannel], client) {} });
createEvent({ data: { name: 'channelDelete' }, run(channel, client) {} });

// Roles
createEvent({ data: { name: 'roleCreate' }, run(role, client) {} });
createEvent({ data: { name: 'roleUpdate' }, run([newRole, oldRole], client) {} });
createEvent({ data: { name: 'roleDelete' }, run(role, client) {} });

// Threads
createEvent({ data: { name: 'threadCreate' }, run(thread, client) {} });
createEvent({ data: { name: 'threadUpdate' }, run([newThread, oldThread], client) {} });
createEvent({ data: { name: 'threadDelete' }, run(thread, client) {} });

// Interactions (raw)
createEvent({ data: { name: 'interactionCreate' }, run(interaction, client) {} });

Middleware Patterns

Developer-Only Gate

src/middlewares/onlyDev.ts
import {  } from 'seyfert';

const  = ['123456789', '987654321'];

export const  = <void>(async ({ , ,  }) => {
    if (!.(..)) {
        return ('This command is developer-only.');
    }
    return ();
});

Voice Channel Validation

Ensure the user is in a voice channel before running music commands:

src/middlewares/checkVoice.ts
import { createMiddleware } from 'seyfert';
import { MessageFlags } from 'seyfert/lib/types';

export const checkVoice = createMiddleware<void>(async ({ context, next, pass }) => {
    if (!context.guildId) return pass();

    const member = context.member;
    const voiceState = await member?.voice('cache');

    if (!voiceState?.channelId) {
        await context.editOrReply({
            content: 'You must be in a voice channel!',
            flags: MessageFlags.Ephemeral,
        });
        return pass();
    }
    return next();
});

Command Logging

src/middlewares/logCommand.ts
import {  } from 'seyfert';

export const  = <void>(async ({ ,  }) => {
    if (.()) {
        ...(
            `${..} used /${.}`
        );
    }
    return ();
});

Cooldown via ExtraProps

Define per-command ratelimits using the props field:

import { LimitedCollection, createMiddleware, Command, Declare, type CommandContext } from 'seyfert';

// Module augmentation
declare module 'seyfert' {
    interface ExtraProps {
        ratelimit?: { time: number; type: 'user' | 'channel' };
    }
}

// Middleware
const cooldowns = new LimitedCollection<string, number>();

export const cooldownMiddleware = createMiddleware<void>(async ({ context, next, stop }) => {
    if (!context.isChat()) return next();

    const ratelimit = context.resolver.parent?.props?.ratelimit;
    if (!ratelimit) return next();

    const key = ratelimit.type === 'user'
        ? `${context.author.id}-${context.fullCommandName}`
        : `${context.channelId}-${context.fullCommandName}`;

    const expiresAt = cooldowns.get(key);
    if (expiresAt && Date.now() < expiresAt) {
        return stop('You are on cooldown!');
    }

    cooldowns.set(key, Date.now() + ratelimit.time, ratelimit.time);
    return next();
});

// Usage on a command
@Declare({
    name: 'play',
    description: 'Play music',
    props: { ratelimit: { time: 7000, type: 'user' } },
})
export default class PlayCommand extends Command {
    async run(ctx: CommandContext) { /* ... */ }
}

Gateway & Shard Access

// Average gateway latency
client.gateway.latency;

// Per-shard ping
await client.gateway.get(ctx.shardId)?.ping();

// Total shard count
client.gateway.totalShards;

// Calculate shard for a guild
client.gateway.calculateShardId('guildId');

// Set presence
import { PresenceUpdateStatus, ActivityType } from 'seyfert/lib/types';

client.gateway.setPresence({
    status: PresenceUpdateStatus.Online,
    activities: [{ name: 'with Seyfert', type: ActivityType.Playing }],
});

LimitedCollection

A TTL-based key-value collection for temporary data:

import {  } from 'seyfert';

const  = new <string, { : number }>();

// Set with auto-expiry (3rd argument is TTL in ms)
.('user-123', { : 42 }, 60_000); // Expires in 60s

// Get (returns undefined if expired)
const  = .('user-123');

// Check existence
.('user-123');

Presence Rotation

Cycle through different activities:

import { PresenceUpdateStatus, ActivityType } from 'seyfert/lib/types';

const activities = [
    { name: 'with Seyfert', type: ActivityType.Playing },
    { name: 'music', type: ActivityType.Listening },
    { name: 'over the server', type: ActivityType.Watching },
];

let index = 0;
setInterval(() => {
    client.gateway.setPresence({
        status: PresenceUpdateStatus.Online,
        activities: [activities[index % activities.length]],
    });
    index++;
}, 60_000);