Creating your own arguments
Creating an argument can be done by extending the Argument class which can be imported from the framework:
- CommonJS
- ESM
- TypeScript
const { isTextBasedChannel } = require('@sapphire/discord.js-utilities');
const { Argument, Resolvers } = require('@sapphire/framework');
const { isNullish } = require('@sapphire/utilities');
class TextBasedChannelsArgument extends Argument {
run(parameter, context) {
const { value } = Resolvers.resolveGuildChannel(parameter, context.message.guild);
if (!isNullish(value) && isTextBasedChannel(value)) {
return this.ok(value);
}
return this.error({
context,
parameter,
message: 'The provided argument could not be resolved to a text based channel.',
identifier: 'ChannelNotATextBasedChannel'
});
}
}
module.exports = {
TextBasedChannelsArgument
};
import { isTextBasedChannel } from '@sapphire/discord.js-utilities';
import { Argument, Resolvers } from '@sapphire/framework';
import { isNullish } from '@sapphire/utilities';
export class TextBasedChannelsArgument extends Argument {
run(parameter, context) {
const { value } = Resolvers.resolveGuildChannel(parameter, context.message.guild);
if (!isNullish(value) && isTextBasedChannel(value)) {
return this.ok(value);
}
return this.error({
context,
parameter,
message: 'The provided argument could not be resolved to a text based channel.',
identifier: 'ChannelNotATextBasedChannel'
});
}
}
import { isTextBasedChannel, type TextBasedChannelTypes } from '@sapphire/discord.js-utilities';
import { Argument, Resolvers } from '@sapphire/framework';
import { isNullish } from '@sapphire/utilities';
export class TextBasedChannelsArgument extends Argument<TextBasedChannelTypes> {
public run(parameter: string, context: Argument.LoaderContext) {
const { value } = Resolvers.resolveGuildChannel(parameter, context.message.guild!);
if (!isNullish(value) && isTextBasedChannel(value)) {
return this.ok(value);
}
return this.error({
context,
parameter,
message: 'The provided argument could not be resolved to a text based channel.',
identifier: 'ChannelNotATextBasedChannel'
});
}
}
Typescript
Typescript users must augment Sapphire's ArgType
interface, which is needed to increase the
security of Sapphire's types. Otherwise, you will run into type errors in the next section.
Make sure you first create an index.d.ts file in the TypeScript root
directory (that is to say, the directory you have
configured as rootDir
in your tsconfig
, generally this is src
. Note that we are NOT talking about the actual root
folder here!) of your project, then put the following code using the name of your new argument, in this case
textBasedChannels
.
declare module '@sapphire/framework' {
interface ArgType {
textBasedChannels: string;
}
}
export default undefined;
The above code creates an argument which checks if the provided parameter is a valid text based channel. It first tries to resolve the parameter to a channel in the guild, if the the parameter is a valid channel, and the channel is a text based channel, it returns the value. If the channel is not a text based channel, or the parameter could not be resolved to a channel, it returns an error.
We then augment the @sapphire/framework
module to include the new argument in the ArgType interface.
Passing custom options in one of the args
methods:
If you want to pass a custom option, like an array to the argument context, to create an argument which checks if the parameter includes one of the elements from the array. You can add a context option to the argument. In your argument file:
- CommonJS
- ESM
- TypeScript
const { Argument } = require('@sapphire/framework');
const { isNullishOrEmpty } = require('@sapphire/utilities');
class ArrayArgument extends Argument {
run(parameter, context) {
if (isNullishOrEmpty(context.array)) {
return this.error({
context,
parameter,
message: 'No array of options was provided. You should alert the bot developer.',
identifier: 'NoArrayContextProvided'
});
}
if (context.array.includes(parameter)) return this.ok(parameter);
return this.error({
context,
parameter,
message: 'The provided parameter does not include an element from the array.',
identifier: 'NotInArrayOfOptions'
});
}
}
module.exports = {
ArrayArgument
};
import { Argument } from '@sapphire/framework';
import { isNullishOrEmpty } from '@sapphire/utilities';
export class ArrayArgument extends Argument {
run(parameter, context) {
if (isNullishOrEmpty(context.array)) {
return this.error({
context,
parameter,
message: 'No array of options was provided. You should alert the bot developer.',
identifier: 'NoArrayContextProvided'
});
}
if (context.array.includes(parameter)) return this.ok(parameter);
return this.error({
context,
parameter,
message: 'The provided parameter does not include an element from the array.',
identifier: 'NotInArrayOfOptions'
});
}
}
import { Argument } from '@sapphire/framework';
import { isNullishOrEmpty } from '@sapphire/utilities';
interface ArrayArgumentContext extends Argument.LoaderContext {
array?: string[];
}
export class ArrayArgument extends Argument<string> {
public run(parameter: string, context: ArrayArgumentContext) {
if (isNullishOrEmpty(context.array)) {
return this.error({
context,
parameter,
message: 'No array of options was provided. You should alert the bot developer.',
identifier: 'NoArrayContextProvided'
});
}
if (context.array.includes(parameter)) return this.ok(parameter);
return this.error({
context,
parameter,
message: 'The provided parameter does not include an element from the array.',
identifier: 'NotInArrayOfOptions'
});
}
}
declare module '@sapphire/framework' {
interface ArgType {
array: string;
}
}
export default undefined;
This is essentially the enum
argument, one of the Built-in arguments, which was added in version 2.3.0 of
@sapphire/framework
The above code checks if the array passed in the context includes the parameter, if it does it returns the value, if
not, an error is thrown. After that, we augment the @sapphire/framework
module to include the new argument in the
ArgType interface (as above).
With all that setup done, you can use the new argument in your command file:
- CommonJS
- ESM
- TypeScript
const { Command } = require('@sapphire/framework');
class ArrayCommand extends Command {
async messageRun(message, args) {
const parameter = await args.pick('array', { array: ['yes', 'no'] });
return message.reply(`Your parameter ${parameter} is valid!`);
}
}
module.exports = {
ArrayCommand
};
import { Command } from '@sapphire/framework';
export class ArrayCommand extends Command {
async messageRun(message, args) {
const parameter = await args.pick('array', { array: ['yes', 'no'] });
return message.reply(`Your parameter ${parameter} is valid!`);
}
}
import { Args, Command } from '@sapphire/framework';
import type { Message } from 'discord.js';
export class ArrayCommand extends Command {
public async messageRun(message: Message, args: Args) {
const parameter = await args.pick('array', { array: ['yes', 'no'] });
return message.reply(`Your parameter ${parameter} is valid!`);
}
}
The above code calls the pick
method of the args class, specifies the argument name, which is 'array', and passes the
array, which is your array you want to check from, in the context.
Using Args.make
Another way to create an argument is by using the make
method of the Args class:
- CommonJS
- ESM
- TypeScript
const { Args, Command } = require('@sapphire/framework');
const { isNullishOrEmpty } = require('@sapphire/utilities');
class SampleCommand extends Command {
async messageRun(message, args) {
const parameter = await args.pick(SampleCommand.numberOrString);
return message.reply(`Your parameter ${parameter} is a valid number or a string!`);
}
static numberOrString = Args.make((parameter, { argument }) => {
if (Number(parameter) || !isNullishOrEmpty(parameter)) {
return Args.ok(parameter);
}
return Args.error({
argument,
parameter,
identifier: 'NumberOrStringError',
message: 'The provided argument was neither a number nor a string.'
});
});
}
module.exports = {
SampleCommand
};
import { Args, Command } from '@sapphire/framework';
import { isNullishOrEmpty } from '@sapphire/utilities';
export class SampleCommand extends Command {
async messageRun(message, args) {
const parameter = await args.pick(SampleCommand.numberOrString);
return message.reply(`Your parameter ${parameter} is a valid number or a string!`);
}
static numberOrString = Args.make((parameter, { argument }) => {
if (Number(parameter) || !isNullishOrEmpty(parameter)) {
return Args.ok(parameter);
}
return Args.error({
argument,
parameter,
identifier: 'NumberOrStringError',
message: 'The provided argument was neither a number nor a string.'
});
});
}
import { Args, Command } from '@sapphire/framework';
import { isNullishOrEmpty } from '@sapphire/utilities';
import type { Message } from 'discord.js';
export class SampleCommand extends Command {
public async messageRun(message: Message, args: Args) {
const parameter = await args.pick(SampleCommand.numberOrString);
return message.reply(`Your parameter ${parameter} is a valid number or a string!`);
}
private static numberOrString = Args.make<number | string>((parameter, { argument }) => {
if (Number(parameter) || !isNullishOrEmpty(parameter)) {
return Args.ok(parameter);
}
return Args.error({
argument,
parameter,
identifier: 'NumberOrStringError',
message: 'The provided argument was neither a number nor a string.'
});
});
}
The above code creates a command, which picks a numberOrString argument from the provided text, the numberOrString argument is created below using the Args.make method, which takes the parameter, checks if it is a number and is not nullish or empty and returns the value. If not, an error is returned.