Plugins
Installation
Chances are, you just want your bot to work. Plugins can preprocess and create reusable conditions for modules.
To install plugins, you can use the CLI:
sern plugins
- Install your favorite(s) (or the ones that look the coolest). I installed the
ownerOnly
plugin. - Thank the creator of the plugin. (mandatory)
- Add the plugin to your module in the
plugins
field.
1import { commandModule, CommandType } from '@sern/handler'2import { ownerOnly } from '../plugins'3
4export default commandModule({5 type: CommandType.Both,6 plugins: [ownerOnly(['182326315813306368'])],7 description: 'ping command',8 execute: (ctx) => {9 ctx.reply('hello, owner');10 }11})
┗|` O′|┛ perfect, your first plugin!
Creating Plugins
Plugins are essentially functions that use the controller object to determine whether to continue or stop the execution of a command.
Init Plugins
Init plugins modify how commands are loaded or do preprocessing.
1import { CommandInitPlugin } from "@sern/handler";2
3export const updateDescription = (description: string) => {4 return CommandInitPlugin(({ deps }) => {5 if(description.length > 100) {6 deps.logger?.info({ message: "Invalid description" })7 return controller.stop("From updateDescription: description is invalid");8 }9 module.description = description;10 return controller.next(); // continue to next plugin11 });12};
You may modify any of the fields of the module, but please careful! This also includes double checking any plugins which may modify fields of modules.
User Data
Each module has a locals
object which you are free to add custom user data. For example, the localizer AND publisher
take advantage of this to attach metadata.
1import { CommandInitPlugin } from "@sern/handler";2
3export const dateRegistered = (description: string) => {4 return CommandInitPlugin(({ module, deps }) => {5 module.locals.registered = Date.now() // save module registration date6 return controller.next(); // continue to next plugin7 });8};
Control Plugins
1import { CommandControlPlugin } from "@sern/handler";2
3export const inGuild = (guildId: string) => {4 return CommandInitPlugin(ctx, sdt) => {5 if(ctx.guild.id !== guildId) {6 return controller.stop();7 }8 return controller.next();9 });10};
- An event is emitted by
discord.js
. - This event is passed to all control plugins in order!!,
- If all are successful, the command is executed.
Controller Object
The controller object is passed into every plugin. It has two methods: next
and stop
.
Plugins use the controller to control the flow of the command. For example, if a plugin fails, it calls controller.stop()
to prevent the command from executing.
1// Reference object, import this from @sern/handler2const controller = {3 next: (val?: Record<string,unknown>) => Ok(val),4 stop: (val?: string) => Err(val),5};
State
SDT = state, dependencies, type (very creative)
Plugins can recieve data from previous plugins. State is created when controller.next
is called with a record.
If all control plugins are successful, the final state is passed to the module execute
.
1import { commandModule, CommandControlPlugin, CommandType } from '@sern/handler'2
3const plugin = CommandControlPlugin((ctx, sdt) => {4 return controller.next({ a: 'from plugin1' });5});6const plugin2 = CommandControlPlugin((ctx, sdt) => {7 return controller.next({ b: ctx.user.id + "from plugin2" });8})9
10export default commandModule({11 type: CommandType.Slash,12 plugins: [plugin, plugin2],13 execute: (ctx, sdt) => {14 sdt.state // { a: 'from plugin1', b: '182326315813306368 from plugin2' }15 }16})
Caveats
Passing data with the same key will get overridden by the latest plugin. It is recommended to namespace data keys if you have multiple plugins, or you can ensure no keys are overridden by the plugin chain.
1return controller.next({ 'cheese-plugin/data' : "From cheese-plugin" })
Dependencies
SDT = state, dependencies, type (very creative)
Plugins also carry an instance of all of your dependencies. Use them and use them as you please! For example, creating a plugin which logs which user uses your command
1import { commandModule, CommandControlPlugin, CommandType } from '@sern/handler'2export const log = CommandControlPlugin((ctx, sdt) => {3 sdt4 .deps['@sern/logger']5 .info({ message: `${ctx.user.id} used this command from ${ctx.guild.id}` });6})