Command Middlewares
In Seyfert middleware are functions that are called before the command is executed. You can use them to do verifications, logging, etc.
Let’s create a basic middleware that logs in the command that is being executed.
Creating a middleware
import { createMiddleware } from "seyfert";
// The generic type tells the middleware what information it'll pass to the commandexport const loggerMiddleware = createMiddleware<void>( (middle) => { // Log the command console.log( `${middle.context.author.username} (${middle.context.author.id}) ran /(${middle.context.resolver.fullCommandName}` );
// Pass to the next middleware middle.next(); });
Now let’s register the middlewares on seyfert but first we should create a file to export all our middleware
import { loggerMiddleware } from "./path/to/logger.middleware";
export const middlewares = { // The key is the name of the middleware which will be used to reference it on the command logger: loggerMiddleware}
import { Client, type ParseMiddlewares, type ParseClient } from "seyfert";import { middlewares } from "./path/to/middlewares";
const client = new Client();
// Register the middlewaresclient.setServices({ middlewares: middlewares});
declare module "seyfert" { interface UsingClient extends ParseClient<Client<true>> {}
// Register the middlewares on seyfert types interface RegisteredMiddlewares extends ParseMiddlewares<typeof middlewares> {}}
Now we can use the logger
middleware on any command.
import { Middlewares, Declare, Command, type CommandContext } from "seyfert";
@Declare({ name: "ping", description: "Ping the bot"})// Note we are using the name "logger" to reference the middleware@Middlewares(["logger"])export default class PingCommand extends Command { async run(ctx: CommandContext) { await ctx.reply("Pong!"); }}
Now every time the ping
command is executed, the logger middleware will log forward the command.
Stop middleware
As we had said you can use middlewares to do verifications, and you can stop the execution of the command if the verification fails.
Let’s take a look adding some logic to the logger middleware.
import { createMiddleware } from "seyfert";import { ChannelType } from 'seyfert/lib/types';
export const loggerMiddleware = createMiddleware<void>((middle) => { // Log the command console.log( `${middle.context.author.username} (${middle.context.author.id}) ran /(${middle.context.resolver.fullCommandName}` );
// Check if the command is being executed in a guild if (middle.context.interaction.channel?.type === ChannelType.DM) { return middle.stop("This command can only be used in a guild."); }
// Pass to the next middleware if the command is being executed in a guild middle.next();});
Now every time the ping
command is executed in a DM, the logger middleware will stop the execution of the command and send the error message to the handler. Learn how to handle errors here.
On the other hand we could skip the interaction (ignore the interaction and literally do nothing) by using middle.pass()
import { createMiddleware } from "seyfert";import { ChannelType } from 'seyfert/lib/types';
export const loggerMiddleware = createMiddleware<void>((middle) => { // Log the command console.log( `${middle.context.author.username} (${middle.context.author.id}) ran /(${middle.context.resolver.fullCommandName}` );
// Ignore the interaction if it's a DM if (middle.context.interaction.channel?.type === ChannelType.DM) { return middle.pass(); }
// Pass to the next middleware if the command is being executed in a guild middle.next();});
Passing data
The last thing we can do with middlewares is to pass data to the command. This can be useful to avoid repeating the same code in multiple commands for example fetching data from the database.
We’ll continue with the logger middleware and pass some data to the command.
import { createMiddleware } from "seyfert";
// This interface is used to let the middleware know what type of data to pass to the commandinterface LoggerData { time: number;}
export const loggerMiddleware = createMiddleware<LoggerData>((middle) => { // Log the command console.log(`${middle.context.author.username} (${middle.context.author.id}) ran /${middle.context.resolver.fullCommandName}`);
// Pass the data to the command middle.next({ time: Date.now() });});
Now let’s modify the ping
command to receive the data.
import { Middlewares, Declare, Command, type CommandContext } from "seyfert";
@Declare({ name: "ping", description: "Ping the bot"})@Middlewares(["logger"])export default class PingCommand extends Command { async run(ctx: CommandContext<never, "logger">) { const time = ctx.metadata.logger.time; console.log(time); await ctx.reply({ content: `Pong! Time: ${time}`, }); }}
Global Middlewares
Global middlewares follow the same rule and structure explained above, with the brief difference that they have a unique property in the context and are declared separately.
import { Client, type ParseMiddlewares, type ParseClient, type ParseGlobalMiddlewares} from 'seyfert';import { middlewares } from "./path/to/middlewares";import { global } from "./path/to/globals";
const globalMiddlewares: (keyof typeof global)[] = ['logger']
// Register middlewareconst client = new Client({ globalMiddlewares});
client.setServices({ middlewares: { ...global, ...middlewares },});
declare module 'seyfert' { interface RegisteredMiddlewares extends ParseMiddlewares<typeof middlewares & typeof global> {} interface GlobalMetadata extends ParseGlobalMiddlewares<typeof global> {}}