djs-easy
Version:
djs-easy is a javascript package for discord with the a lot stuff you need to make your bot programming easy
151 lines (122 loc) • 4.67 kB
JavaScript
const { defaults, parseTime } = require('../utils.js');
/**
* @typedef {object} definition
* @property {string} key The name of the argument
* @property {string|any[]} prompt The prompt to query if the arg is not specified in the main message.
* @property {?('string'|'number'|'time'|string[])} type The type of the argument. If an array, the argument
* will resolve to the first type that returns a non null value, in left to right order.
* @property {?boolean} infinite Whether or not the argument takes up the rest of the string. Defaults to false.
* @property {?any} default The default value of the argument if not provided (makes the argument optional).
* @property {?object} range The range of the variable. Should contain a `min` property and/or a `max` property.
* If the type is 'string', this defines the range of the length of the string.
* If 'number', the range of the number.
* If 'time', the range of the time (in milliseconds).
*/
let rangeDefault = {
min: -Infinity,
max: Infinity,
};
let defDefault = {
type: 'string',
infinite: false,
range: rangeDefault
};
let types = {
string: {
parse: (c) => c,
filter: (c, range) => c && c.length <= range.min && c.length >= range.max
},
number: {
parse: (c) => parseInt(c),
filter: (c, range) => c && c >= range.min && c <= range.max
},
time: {
parse: (c) => parseTime(c),
filter: (c, range) => c && c >= range.min && c <= range.max
},
member: {
parse: (c, call) => {
c = c.toLowerCase();
return call.message.guild &&
call.message.guild.members.cache.get(c.replace(/\D+/g, '')) ||
call.message.guild.members.cache.find((m) => m.user.tag.toLowerCase() === c) ||
call.message.guild.members.cache.find((m) => m.user.displayName === c);
},
filter: (c, range) => {
return (!Array.isArray(range.ids) && !Array.isArray(range.roles)) ||
(Array.isArray(range.ids) && range.ids.includes(c.id)) ||
(Array.isArray(range.roles) && c.roles.some((r) => range.roles.includes(r.id) || range.roles.includes(r.name.toLowerCase())));
}
},
user: {
parse: async (c, call) => {
c = c.toLowerCase();
let fetched = await call.client.fetchUser(c.replace(/\D+/g, '')).catch(() => null);
return fetched || call.client.users.cache.find((u) => u.tag.toLowerCase() === c);
},
filter: (c, range) => !Array.isArray(range.ids) || range.ids.includes(c.id)
}
};
class Arguments {
constructor(definitions, splitter) {
this.splitter = splitter || /[^\s"']+|"([^"]*)"|'([^']*)'/g;
this.definitions = definitions.map((def) => {
defaults(def, defDefault);
defaults(def.range, rangeDefault);
return def;
});
this.handler = require('../index.js');
}
validType(defType) {
return defType in types || (Array.isArray(defType) && defType.every((type) => typeof type === 'string' || Array.isArray(type)));
}
async firstType(call, def, arg) {
for (let type of Array.isArray(def.type) ? def.type.map((type) => types[type] || type) : [types[def.type] || type]) {
let oldType = type;
// Custom type where an argument matching the content in an array is valid.
if (Array.isArray(type))
type = { parse: (c) => c, filter: (c) => oldType.includes(c.toLowerCase()) };
else if (type.parse || type.filter)
type = { parse: type.parse || ((c) => c), filter: type.filter || (() => true) };
let parsed = await type.parse(arg, call);
if (parsed && type.filter(parsed, def.range))
return type;
}
}
async getAll(call) {
let split = this.splitter instanceof RegExp ? call.cut.match(this.splitter) || [] : this.splitter(call.cut) || [];
let args = {};
let pos = 0;
for (let def of this.definitions) {
if (!this.validType(def.type))
throw new TypeError(`Invalid type(s) provided for argument ${pos + 1} of ${call.command.id}.`);
let argValue = def.infinite ? split[pos] : split.slice(pos).join(' ');
let arg;
if (argValue) {
let type = await this.firstType(call, def, argValue);
if (type)
arg = type.parse(argValue, call);
}
if (!arg) {
if ('default' in def) {
arg = def.default;
} else {
if (typeof def.prompt !== 'string')
throw new TypeError(`Invalid prompt property for argument ${pos} of ${call.command.id}.`);
let type;
arg = await call.prompt(def.prompt, { filter: async (m) => type = await this.firstType(call, def, m.content) })
.then((m) => type.parse(m.content, call));
}
// Do not count this as an argument as to not offset other arguments.
pos--;
}
pos++;
args[def.key] = arg;
if (def.infinite)
break;
}
return args;
}
}
Arguments.types = types;
module.exports = Arguments;