Recipes
Quick Reference
A collection of frequently used snippets and recipes for Seyfert bots.
Event Patterns
Ready Event with Command Upload
import { } from 'seyfert';
export default ({
: { : 'botReady', : true },
async (, ) {
await .({ : './commands.json' });
..(`${.} is online!`);
},
});Welcome DM on Member Join
import { } from 'seyfert';
export default ({
: { : 'guildMemberAdd' },
async (, ) {
if (..) return;
await .({
: `Welcome to the server, ${..}!`,
});
},
});Support Thread Auto-Reply
import { } from 'seyfert';
const = '123456789';
export default ({
: { : 'channelCreate' },
async () {
if (!.() || . !== ) return;
await ..({
: 'Welcome to your support thread! A team member will assist you shortly.',
});
},
});Raw Gateway Event (for Lavalink)
Forward raw gateway packets to external managers like Lavalink or Shoukaku:
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
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:
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
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);