Compare commits

..

No commits in common. "fb46e09d704abefc2c8ecda732e6298174e24895" and "57313488b4136a83e26f05f0c225f2a8a43e0124" have entirely different histories.

6 changed files with 22 additions and 66 deletions

View File

@ -1,40 +1,3 @@
A command parser initially made for quickly making discord bots, but works as a standalone parser too. A command parser for Discord bots. Should work with any JS library.
Your commands are expected to inherit from the Command class You're expected to implement a string -> abstraction resolver yourself and pass it to the parser.
**Example usage**
```js
const commands: Command[] = [
new Command({
name: 'ping',
options: [
{
name: 'respond', //
type: OptionType.BOOLEAN, // Boolean type expects the parser to have some kind of string -> boolean resolver, see the resolver interface for which methods are expected
required: true, // Will throw error if not provided
defaultValue: true, // The value that will be given back if no value is given, or if option is required but not provided
flag: true // The parser will look for the option in the "--respond value" form
}
]
})
]; // Command definitions
const parser = new Parser({commands, prefix: ''}); // Empty prefix
const parsed = await parser.parseMessage('ping --respond');
/**
* Parsed will be an object cotaining properties args, command, subcommand, and subcommandgroup
* the args property is an object of the given options mapped by their names, e.g.
* {
* respond: {
* ... properties,
* value: true
* }
* }
*
*/
```
Some of the option types have built-in parsers (e.g. numbers, strings), but others will require the resolver implementation, and in some cases CommandOption extensions.

View File

@ -1,6 +1,6 @@
{ {
"name": "@navy.gif/commandparser", "name": "@navy.gif/commandparser",
"version": "1.5.1", "version": "1.5.0",
"description": "Parser meant to parse commands and their options for discord bots", "description": "Parser meant to parse commands and their options for discord bots",
"author": "Navy.gif", "author": "Navy.gif",
"license": "MIT", "license": "MIT",

View File

@ -23,8 +23,7 @@ type ParserOptions = {
commands: Iterable<Command>, commands: Iterable<Command>,
prefix?: string, prefix?: string,
debug?: boolean, debug?: boolean,
resolver?: IResolver<unknown, unknown, unknown, unknown, unknown>, resolver?: IResolver<unknown, unknown, unknown, unknown, unknown>
allowDefaultOnRequired?: boolean
} }
type ParseOptions = { type ParseOptions = {
@ -40,12 +39,14 @@ const flagReg = /(?:^| )(?<flag>(?:--[a-z0-9]{3,})|(?:-[a-z]{1,2}))(?:$| )/iu;
class Parser extends EventEmitter { class Parser extends EventEmitter {
private commands: ExtendedMap<string, Command>; private commands: ExtendedMap<string, Command>;
private _debug = false;
prefix: string;
resolver?: IResolver<unknown, unknown, unknown, unknown, unknown>;
allowDefaultOnRequired: boolean;
constructor ({ commands, prefix, debug = false, resolver, allowDefaultOnRequired }: ParserOptions) { private _debug = false;
prefix: string;
resolver?: IResolver<unknown, unknown, unknown, unknown, unknown>;
constructor ({ commands, prefix, debug = false, resolver }: ParserOptions) {
super(); super();
@ -56,7 +57,6 @@ class Parser extends EventEmitter {
this.resolver = resolver; this.resolver = resolver;
this.prefix = prefix || ''; this.prefix = prefix || '';
this.allowDefaultOnRequired = allowDefaultOnRequired ?? true;
} }
@ -311,17 +311,10 @@ class Parser extends EventEmitter {
// } // }
this.debug(`Making sure required options were given.`); this.debug(`Making sure required options were given.`);
for (const req of options.filter((opt) => opt.required)) { for (const req of options.filter((opt) => opt.required))
if (!args[req.name]) { if (!args[req.name])
if (req.defaultValue === null || !this.allowDefaultOnRequired)
throw new ParseError(`${req.name} is a required option`); throw new ParseError(`${req.name} is a required option`);
const opt = req.clone();
opt.value = req.defaultValue;
args[req.name] = opt;
}
}
if (strings.length) if (strings.length)
throw new ParseError(`Unrecognised option(s): "${strings.join('", "')}"`); throw new ParseError(`Unrecognised option(s): "${strings.join('", "')}"`);

View File

@ -26,7 +26,7 @@ class CommandOption implements ICommandOption {
value?: unknown; value?: unknown;
aliased = false; aliased = false;
strict: boolean; strict: boolean;
private resolver?: IResolver | undefined; private resolver?: IResolver<unknown, unknown, unknown, unknown, unknown>|undefined;
constructor (def: CommandOptionDefinition|CommandOption|ICommandOption) { constructor (def: CommandOptionDefinition|CommandOption|ICommandOption) {
@ -89,7 +89,7 @@ class CommandOption implements ICommandOption {
} }
clone (rawValue?: string[], resolver?: IResolver): CommandOption { clone (rawValue?: string[], resolver?: IResolver<unknown, unknown, unknown, unknown, unknown>): CommandOption {
const opt = new CommandOption(this); const opt = new CommandOption(this);
opt.rawValue = rawValue; opt.rawValue = rawValue;
opt.resolver = resolver; opt.resolver = resolver;

View File

@ -130,7 +130,7 @@ interface ICommandOption {
// private _options?: CommandOptionDefinition // private _options?: CommandOptionDefinition
clone(rawValue?: string[], resolver?: IResolver): CommandOption clone(rawValue?: string[], resolver?: IResolver<unknown, unknown, unknown, unknown, unknown>): CommandOption
parse(guild: unknown): Promise<ParseResult> parse(guild: unknown): Promise<ParseResult>

View File

@ -1,4 +1,4 @@
interface IResolver { interface IResolver <User, Member, Channel, Role, Guild> {
/** /**
* Should resolve to a user abstraction * Should resolve to a user abstraction
@ -8,7 +8,7 @@ interface IResolver {
* @return {User} {User} * @return {User} {User}
* @memberof IResolver * @memberof IResolver
*/ */
resolveUser<User>(resolveable: string, strict?: boolean): Promise<User | null> resolveUser(resolveable: string, strict?: boolean): Promise<User | null>
/** /**
* Should resolve to a member abstraction * Should resolve to a member abstraction
@ -19,7 +19,7 @@ interface IResolver {
* @return {Member} {Member} * @return {Member} {Member}
* @memberof IResolver * @memberof IResolver
*/ */
resolveMember<Guild, Member>(resolveable: string, strict: boolean, guild: Guild): Promise<Member | null> resolveMember(resolveable: string, strict: boolean, guild: Guild): Promise<Member | null>
/** /**
* Should resolve to a role abstraction * Should resolve to a role abstraction
@ -30,7 +30,7 @@ interface IResolver {
* @return {Channel} {Channel} * @return {Channel} {Channel}
* @memberof IResolver * @memberof IResolver
*/ */
resolveChannel<Guild, Channel>(resolveable: string, strict: boolean, guild: Guild): Promise<Channel | null> resolveChannel(resolveable: string, strict: boolean, guild: Guild): Promise<Channel | null>
/** /**
* Should resolve to a role abstraction * Should resolve to a role abstraction
@ -41,7 +41,7 @@ interface IResolver {
* @return {Role} {Role} * @return {Role} {Role}
* @memberof IResolver * @memberof IResolver
*/ */
resolveRole<Guild, Role>(resolveable: string, strict: boolean, guild: Guild): Promise<Role | null> resolveRole(resolveable: string, strict: boolean, guild: Guild): Promise<Role | null>
/** /**
* Should resolve to true when a truthy resolveable is passed, i.e. yes, true, on etc * Should resolve to true when a truthy resolveable is passed, i.e. yes, true, on etc