Skip to main content

Creating your own preconditions

Just as we did in both Creating Commands and Creating Listeners, we will start by creating a preconditions subdirectory in your project's entry point directory. For this guide, we'll be building out an OwnerOnly precondition, that prevents anyone but the application owner from running the command.

Your directory should now look something like this:

├── node_modules
├── package.json
└── src
├── commands
│ └── ping.js
├── index.js
├── listeners
│ └── ready.js
└── preconditions
└── OwnerOnly.js

The purpose of our OwnerOnly precondition is just as the name suggests: to check if the user is the bot owner. It can be used for developer commands, such as commands that evaluate expressions or present internal debugging information.

Creating a Precondition class

Preconditions are made by extending the Sapphire Precondition class and exporting it.

import { Precondition } from '@sapphire/framework';

export class OwnerOnlyPrecondition extends Precondition {}

Next, we can create a run function to execute our logic. This function should either return this.ok() to signify the condition has passed, or this.error(...) to signify the command should be denied.

danger

Clicking on run above will take you to a method instead called messageRun. This is because this documentation reflects the latest commit to the main branch of @sapphire/framework which is has a breaking change included that changed the name of the method. For the purposes of this guide however, you can use run as long as you are using the latest NPM published version of @sapphire/framework.

import { Precondition } from '@sapphire/framework';
import type { Message } from 'discord.js';

export class OwnerOnlyPrecondition extends Precondition {
public run(message: Message) {
return message.author.id === 'YOUR_ID'
? this.ok()
: this.error({ message: 'Only the bot owner can use this command!' });
}
}

Using Preconditions in Commands

To attach a precondition to a command, you simply have to input its name in an array in the command's preconditions option.

import { Command } from '@sapphire/framework';

export class PingCommand extends Command {
public constructor(context: Command.Context) {
super(context, {
// ...
preconditions: ['OwnerOnly']
});
}
}

Now, if someone who is not the bot owner executes the ping command, nothing will happen!

caution

For TypeScript users, there's an extra step to make this work. To increase the security of Sapphire's types, you'll need to augment Sapphire's Preconditions interface. Please see an official example here.

By default, no error message will be sent or logged when a command is denied because of a precondition. To learn how to configure this, please read Reporting Precondition Failures.

Advanced Usage

Sapphire also has a builtin system for advanced conditional precondition logic through nested arrays. By default, all preconditions in the given array must pass for the command to be run. However, you can use nested arrays to create OR functionality. This could be useful if you'd like a command to be run if the user is either a moderator or an admin.

Furthermore, if you create a nested array within a nested array, you'll receive AND functionality once more. Arrays can be nested infinitely with the same pattern for optimal control over your preconditions.

Consider the following array of preconditions:

warning

None of the following preconditions are bundled with Sapphire; as such you'd have to create them yourself!

[['AdminOnly', ['ModOnly', 'OwnerOnly']], 'InVoiceChannel'];

For a command with these preconditions to pass the denial checks, the InVoiceChannel precondition must pass, as well as AdminOnly or both OwnerOnly and ModOnly.