• Guide
  • Core Concepts
  • Adding plugins

Adding Plugins

Plugins are a way to reuse code between multiple different commands, without writing the same code twice.

Plugins were heavily inspired by sern's system. Check them out!

It's recommended that you have a directory for plugins, and you can use that directory to import it to your commands.

For example, you could make an Owner Only plugin like this:

src/plugins/ownerOnly.ts
// This is an example plugin for a 'owner only' command plugin.
 
// Import needed types from Spark
import { CommandPlugin, CommandType } from '@spark.ts/handler';
 
// Util function to mention users with their ids
function mapUsers(ids: string[]) {
  const mention = (s: string) => `<@!${s}>`;
  return ids.map((id) => `\` - \` ${mention(id)}`).join('\n');
}
 
// The params in this function will be required when you use the plugin.
export function ownerOnly(ownerIds: string[]): CommandPlugin<CommandType.Both> {
  return {
    name: 'Owner Only',
    description: 'Only allows the owner(s) to run the command.',
    async run({
      controller, command, message, interaction,
    }) {
      // Checks if the command is a text command.
      if (command.type === CommandType.Text) {
        // You can use the `!` operator safety as message will be inside of this block.
        if (ownerIds.includes(message!.author.id)) {
          // Continues with execution, will run other plugins.
          // If all plugins are successful, the command is ran.
          return controller.next();
        }
 
        await message!.reply(`Only these people can run the command!\n${mapUsers(ownerIds)}`);
      }
 
      if (command.type === CommandType.Slash) {
        if (ownerIds.includes(interaction!.user.id)) {
          return controller.next();
        }
 
        await interaction!.reply({
          content: `Only these people can run the command!\n${mapUsers(ownerIds)}`,
          ephemeral: true,
        });
      }
 
      // Stops the controller, this stops the command & other plugins from being ran.
      return controller.stop();
    },
  };
}

This plugin works for both text commands & slash commands because of the type used in CommandPlugin.

Command Integration

To integrate this plugin to one of your commands, first check that your command and plugin are of the same type. (You cannot use text plugins with a slash command)

To add a plugin to your command, just import the function and then add it to the plugins option:

src/commands/ownerOnly.ts
import { SparkCommand } from '@spark.ts/handler';
import { ownerOnly } from '../../plugins/ownerOnly.js';
 
export default new SparkCommand({
  plugins: [ownerOnly('<your user id would go here>')],
  run({ interaction }) {
    // This would only be executed if the plugin passed.
    interaction.reply('You are permitted to use this command!');
  },
});

Init Plugins

Init plugins are plugins that run once a command is initialized/loaded.

The pattern of creating these plugins are very similar to creating any other plugin, you just have to use the InitPlugin type instead of CommandPlugin.

We can create a basic InitPlugin, one that logs ${command.name} whenever a command is loaded.

src/plugins/log.ts
// Import types from Spark
import { CommandType, InitPlugin } from '@spark.ts/handler';
 
export function logPlugin(): InitPlugin<CommandType.Both> {
  return {
    name: 'Log Plugin',
    description: 'Logs the plugin name with a success log',
    run({ client, command, controller }) {
      // Logs that the command was loaded
      client.logger.success(`${command.name} was successfully loaded.`);
 
      // Continues with execution
      return controller.next();
    },
  };
}

Controller

The controller is what controls your flow of your code. If you do controller.stop() in your command, it stops the execution of your command.

Please make sure to return this upon an error in your plugin. For example, if the user is not permitted to use the command, return controller.stop() in your plugin.

If you have a success in your plugin, return controller.next(). This will execute other plugins, if available, and then the command.