command-parser/examples/Command.ts

190 lines
4.4 KiB
TypeScript

/**
* SAMPLE CODE
* Reference for how to extend a command option and implement a custom
* command that inherits a custom superclass while being compatible with the parser by implementing the interface
*/
/* eslint-disable max-classes-per-file */
import { ICommand, OptionType, CommandOpts, SubcommandOption, SubcommandGroupOption } from '@navy.gif/commandparser';
import ExtendedCommandOption from './ExtendedCommandOption.js';
type Snowflake = string
type DiscordClient = {
//
}
type ComponentOptions = {
//
}
type CommandDefinition = {
aliases: never[];
help: string | undefined;
restricted: boolean;
disabled: boolean;
guildOnly: boolean;
dmOnly: boolean;
limited: null;
options: ExtendedCommandOption[];
}
type User = {
//
}
class Component
{
// eslint-disable-next-line no-useless-constructor
constructor (_client: DiscordClient, _options: ComponentOptions)
{
//
}
get name ()
{
return '';
}
}
class CommandError extends Error
{
constructor (_command: Command, _options: object)
{
super();
}
}
abstract class Command extends Component implements ICommand
{
#aliases: string[];
#options: ExtendedCommandOption[];
#help?: string | undefined;
#restricted: boolean; // Developer only
#disabled: boolean;
#guildOnly: boolean;
#dmOnly: boolean;
#limited: Snowflake[] | null; // Limited to specific roles
constructor (client: DiscordClient, def: CommandDefinition)
{
super(client, {
...def,
type: 'command'
});
this.#aliases = def.aliases ?? [];
this.#help = def.help;
this.#restricted = def.restricted ?? false;
this.#disabled = def.disabled ?? false;
this.#guildOnly = def.guildOnly ?? false;
this.#dmOnly = def.dmOnly ?? false;
this.#limited = def.limited ?? null;
this.#options = [];
if (def.options)
{
for (const opt of def.options)
{
if (opt instanceof ExtendedCommandOption)
this.#options.push(opt);
else
this.#options.push(new ExtendedCommandOption(opt));
}
}
if (this.options.some(opt => [ OptionType.SUB_COMMAND, OptionType.SUB_COMMAND_GROUP ].includes(opt.type))
&& this.options.some(opt => ![ OptionType.SUB_COMMAND, OptionType.SUB_COMMAND_GROUP ].includes(opt.type) && opt.name !== 'help'))
throw new Error('Cannot have subcommand(group)s on the same level as an option');
}
abstract execute(message: unknown, args: CommandOpts): Promise<unknown>;
protected timeout (user?: User)
{
throw new CommandError(this, { reason: 'Command timed out', user });
}
get restricted ()
{
return this.#restricted;
}
get limited ()
{
return this.#limited;
}
get disabled ()
{
return this.#disabled;
}
get guildOnly ()
{
return this.#guildOnly;
}
get dmOnly ()
{
return this.#dmOnly;
}
get aliases ()
{
return this.#aliases;
}
get options ()
{
return this.#options;
}
get help ()
{
return this.#help;
}
get subcommands (): SubcommandOption[]
{
return this._subcommands(this.options);
}
get subcommandGroups (): SubcommandGroupOption[]
{
return this.options.filter((opt) => opt.type === OptionType.SUB_COMMAND_GROUP);
}
subcommand (name: string): SubcommandOption | null
{
name = name.toLowerCase();
return this.subcommands.find((cmd) => cmd.name === name || cmd.aliases.includes(name)) || null;
}
subcommandGroup (name: string): SubcommandGroupOption | null
{
name = name.toLowerCase();
return this.subcommandGroups.find((grp) => grp.name === name || grp.aliases.includes(name)) || null;
}
private _subcommands (options: ExtendedCommandOption[]): SubcommandOption[]
{
const subcommands: SubcommandOption[] = [];
for (const opt of options)
{
if (opt.type === OptionType.SUB_COMMAND)
subcommands.push(opt);
else if (opt.type === OptionType.SUB_COMMAND_GROUP)
subcommands.push(...this._subcommands(opt.options));
}
return subcommands;
}
}
export default Command;