diff --git a/commands/edit-avatar/eject.js b/commands/edit-avatar/eject.js index eb86616c..68121a12 100644 --- a/commands/edit-avatar/eject.js +++ b/commands/edit-avatar/eject.js @@ -44,24 +44,17 @@ module.exports = class EjectCommand extends Command { key: 'user', type: 'user', default: msg => msg.author - }, - { - key: 'imposter', - type: 'boolean', - default: '' } ] }); } - async run(msg, { user, imposter }) { + async run(msg, { user }) { const avatarURL = user.displayAvatarURL({ extension: 'png', size: 128 }); const { body } = await request.get(avatarURL); const avatar = await loadImage(body); - if (imposter === '') { - const random = MersenneTwister19937.seed(user.id); - imposter = bool()(random); - } + const random = MersenneTwister19937.seed(user.id); + const imposter = bool()(random); const text = `${user.username} was${imposter ? ' ' : ' not '}An Imposter.`; const encoder = new GIFEncoder(320, 180); const canvas = createCanvas(320, 180); diff --git a/commands/util-public/help.js b/commands/util-public/help.js index 50ce6eca..1f75db0a 100644 --- a/commands/util-public/help.js +++ b/commands/util-public/help.js @@ -75,6 +75,7 @@ module.exports = class HelpCommand extends Command { cmdHelpText += `**Flags:**\n${flags}\n`; } cmdHelpText += `**Format:** ${command.usage()}\n`; + cmdHelpText += `**Example:** ${command.example(msg)}\n`; if (command.aliases.length) cmdHelpText += `**Aliases:** ${command.aliases.join(', ')}\n`; cmdHelpText += `**Group:** ${command.group.name}\n`; return msg.say(cmdHelpText); diff --git a/framework/Argument.js b/framework/Argument.js index 702133e0..58b57bd4 100644 --- a/framework/Argument.js +++ b/framework/Argument.js @@ -15,6 +15,7 @@ module.exports = class Argument { this.default = typeof options.default === 'undefined' ? null : options.default; this.infinite = Boolean(options.infinite); this.avatarSize = typeof options.avatarSize === 'undefined' ? 2048 : options.avatarSize; + this.examples = typeof options.examples === 'undefined' ? null : options.examples; this.validator = typeof options.validate === 'undefined' ? null : options.validate; this.parser = typeof options.parse === 'undefined' ? null : options.parse; this.emptyChecker = typeof options.isEmpty === 'undefined' ? null : options.isEmpty; @@ -40,6 +41,11 @@ module.exports = class Argument { return this.type.isEmpty(val, msg, arg); } + example(msg, arg) { + if (this.examples) return this.examples[Math.floor(Math.random() * this.examples.length)]; + return this.type.example(msg, arg); + } + get invalidText() { if (this.oneOf) { return `It must be one of the following: ${list(this.oneOf, 'or')}`; diff --git a/framework/ArgumentType.js b/framework/ArgumentType.js index 9f6b8a95..35839c5f 100644 --- a/framework/ArgumentType.js +++ b/framework/ArgumentType.js @@ -16,4 +16,8 @@ module.exports = class ArgumentType { isEmpty(val) { return !val; } + + example() { + return 'moo'; + } }; diff --git a/framework/Command.js b/framework/Command.js index 249d7d12..1bb0ea77 100644 --- a/framework/Command.js +++ b/framework/Command.js @@ -49,6 +49,17 @@ module.exports = class Command { return `\`${this.client.commandPrefix}${this.name}${args}\` or \`@${this.client.user.tag} ${this.name}${args}\``; } + example(msg) { + const args = this.args.map((arg, i) => { + const example = arg.example(msg, arg); + if (i !== args.length - 1 && example.includes(' ')) { + return `"${example}"`; + } + return example; + }); + return `${this.client.commandPrefix}${this.name} ${args.join(' ')}`; + } + disable() { this._enabled = false; } diff --git a/framework/Dispatcher.js b/framework/Dispatcher.js index 79f36f2a..d0f08510 100644 --- a/framework/Dispatcher.js +++ b/framework/Dispatcher.js @@ -59,6 +59,7 @@ module.exports = class CommandDispatcher { error: stripIndents` The "${arg.label || arg.key}" argument is required. ${arg.invalidText} + Correct Usage Example: ${command.example(msg)} ` }; } @@ -75,6 +76,7 @@ module.exports = class CommandDispatcher { error: stripIndents` An invalid value was provided for one of the "${arg.label || arg.key}" arguments. ${arg.invalidText} + Correct Usage Example: ${command.example(msg)} ` }; } @@ -91,6 +93,7 @@ module.exports = class CommandDispatcher { error: stripIndents` The "${arg.label || arg.key}" argument is required. ${arg.invalidText} + Correct Usage Example: ${command.example(msg)} ` }; } else { @@ -107,6 +110,7 @@ module.exports = class CommandDispatcher { error: stripIndents` An invalid value was provided for the "${arg.label || arg.key}" argument. ${arg.invalidText} + Correct Usage Example: ${command.example(msg)} ` }; } diff --git a/framework/types/boolean.js b/framework/types/boolean.js deleted file mode 100644 index e1d880df..00000000 --- a/framework/types/boolean.js +++ /dev/null @@ -1,21 +0,0 @@ -const ArgumentType = require('../ArgumentType'); - -module.exports = class BooleanArgumentType extends ArgumentType { - constructor(client) { - super(client, 'boolean'); - this.truthy = new Set(['true', 't', 'yes', 'y', 'on', 'enable', 'enabled', '1', '+']); - this.falsy = new Set(['false', 'f', 'no', 'n', 'off', 'disable', 'disabled', '0', '-']); - } - - validate(val) { - const lc = val.toLowerCase(); - return this.truthy.has(lc) || this.falsy.has(lc); - } - - parse(val) { - const lc = val.toLowerCase(); - if (this.truthy.has(lc)) return true; - if (this.falsy.has(lc)) return false; - throw new RangeError('Unknown boolean value.'); - } -}; diff --git a/framework/types/channel.js b/framework/types/channel.js index 344b1bd0..9b8d2627 100644 --- a/framework/types/channel.js +++ b/framework/types/channel.js @@ -28,6 +28,10 @@ module.exports = class ChannelArgumentType extends ArgumentType { if (exactChannels.size === 1) return exactChannels.first(); return null; } + + example() { + return '#general'; + } }; function nameFilterExact(search) { diff --git a/framework/types/command.js b/framework/types/command.js index a7e25160..f754e205 100644 --- a/framework/types/command.js +++ b/framework/types/command.js @@ -14,4 +14,8 @@ module.exports = class CommandArgumentType extends ArgumentType { parse(val) { return this.client.registry.findCommands(val).first(); } + + example() { + return this.client.registry.commands.random().name; + } }; diff --git a/framework/types/custom-emoji.js b/framework/types/custom-emoji.js index bb1e1257..0f596816 100644 --- a/framework/types/custom-emoji.js +++ b/framework/types/custom-emoji.js @@ -29,6 +29,11 @@ module.exports = class CustomEmojiArgumentType extends ArgumentType { if (exactEmojis.size === 1) return exactEmojis.first(); return null; } + + example(msg) { + if (msg.guild && msg.guild.emojis.cache.size) return msg.guild.emojis.cache.random().toString(); + return this.client.emojis.cache.random().toString(); + } }; function nameFilterExact(search) { diff --git a/framework/types/default-emoji.js b/framework/types/default-emoji.js index d1d08709..c67b1d65 100644 --- a/framework/types/default-emoji.js +++ b/framework/types/default-emoji.js @@ -1,5 +1,6 @@ const ArgumentType = require('../ArgumentType'); const emojiRegex = require('emoji-regex')(); +const examples = ['😄', '🏳️‍🌈', '🦑', '🦆']; module.exports = class DefaultEmojiArgumentType extends ArgumentType { constructor(client) { @@ -14,4 +15,8 @@ module.exports = class DefaultEmojiArgumentType extends ArgumentType { parse(value) { return value.match(emojiRegex)[0]; } + + example() { + return examples[Math.floor(Math.random() * examples.length)]; + } }; diff --git a/framework/types/float.js b/framework/types/float.js index a1b4afdb..45e918e3 100644 --- a/framework/types/float.js +++ b/framework/types/float.js @@ -1,4 +1,5 @@ const ArgumentType = require('../ArgumentType'); +const { randomRange } = require('../../util/Util'); module.exports = class FloatArgumentType extends ArgumentType { constructor(client) { @@ -17,4 +18,11 @@ module.exports = class FloatArgumentType extends ArgumentType { parse(val) { return Number.parseFloat(val); } + + example(msg, arg) { + if (arg.oneOf) return arg.oneOf[Math.floor(Math.random() * arg.oneOf.length)]; + const min = arg.min || 0; + const max = arg.max || 1000; + return randomRange(min, max); + } }; diff --git a/framework/types/group.js b/framework/types/group.js index 252c9ec0..ba026acd 100644 --- a/framework/types/group.js +++ b/framework/types/group.js @@ -14,4 +14,8 @@ module.exports = class GroupArgumentType extends ArgumentType { parse(val) { return this.client.registry.findGroups(val).first(); } + + example() { + return this.client.registry.groups.random().id; + } }; diff --git a/framework/types/integer.js b/framework/types/integer.js index 1745480d..fd1a4e9d 100644 --- a/framework/types/integer.js +++ b/framework/types/integer.js @@ -1,4 +1,5 @@ const ArgumentType = require('../ArgumentType'); +const { randomRange } = require('../../util/Util'); module.exports = class IntegerArgumentType extends ArgumentType { constructor(client) { @@ -17,4 +18,11 @@ module.exports = class IntegerArgumentType extends ArgumentType { parse(val) { return Number.parseInt(val, 10); } + + example(msg, arg) { + if (arg.oneOf) return arg.oneOf[Math.floor(Math.random() * arg.oneOf.length)]; + const min = arg.min || 0; + const max = arg.max || 1000; + return randomRange(min, max); + } }; diff --git a/framework/types/member.js b/framework/types/member.js index 5077b17b..e276bde7 100644 --- a/framework/types/member.js +++ b/framework/types/member.js @@ -36,6 +36,12 @@ module.exports = class MemberArgumentType extends ArgumentType { if (exactMembers.size === 1) return exactMembers.first(); return null; } + + example(msg) { + if (msg.guild) return msg.guild.members.cache.random().tag; + const members = [this.client.user, msg.channel.recipient]; + return members[Math.floor(Math.random() * members.length)].tag; + } }; function memberFilterExact(search) { diff --git a/framework/types/message.js b/framework/types/message.js index e08bc766..fe051479 100644 --- a/framework/types/message.js +++ b/framework/types/message.js @@ -13,4 +13,8 @@ module.exports = class MessageArgumentType extends ArgumentType { parse(val, msg) { return msg.channel.messages.cache.get(val); } + + example(msg) { + return msg.id; + } }; diff --git a/framework/types/role.js b/framework/types/role.js index c615a51e..3db62b49 100644 --- a/framework/types/role.js +++ b/framework/types/role.js @@ -28,6 +28,11 @@ module.exports = class RoleArgumentType extends ArgumentType { if (exactRoles.size === 1) return exactRoles.first(); return null; } + + example(msg) { + if (!msg.guild) return 'Administrator'; + return msg.guild.roles.cache.random().name; + } }; function nameFilterExact(search) { diff --git a/framework/types/string.js b/framework/types/string.js index 628c0755..d73a02ea 100644 --- a/framework/types/string.js +++ b/framework/types/string.js @@ -1,4 +1,5 @@ const ArgumentType = require('../ArgumentType'); +const words = require('../../assets/json/word-list'); module.exports = class StringArgumentType extends ArgumentType { constructor(client) { @@ -15,4 +16,15 @@ module.exports = class StringArgumentType extends ArgumentType { parse(val) { return val; } + + example(msg, arg) { + if (arg.oneOf) return arg.oneOf[Math.floor(Math.random() * arg.oneOf.length)]; + let sentence = ''; + while (sentence.length <= (arg.min || 50) || sentence.length <= (arg.max || 100)) { + const valid = words.filter(word => sentence.length + word.length + 1 <= (arg.max || 100)); + if (!valid.length) break; + sentence += ` ${valid[Math.floor(Math.random() * valid.length)]}`; + } + return sentence.trim(); + } }; diff --git a/framework/types/user.js b/framework/types/user.js index c507a036..e43aa2e4 100644 --- a/framework/types/user.js +++ b/framework/types/user.js @@ -38,6 +38,12 @@ module.exports = class UserArgumentType extends ArgumentType { if (exactMembers.size === 1) return exactMembers.first().user; return null; } + + example(msg) { + if (msg.guild) return msg.guild.members.cache.random().tag; + const members = [this.client.user, msg.channel.recipient]; + return members[Math.floor(Math.random() * members.length)].tag; + } }; function memberFilterExact(search) { diff --git a/package.json b/package.json index a7d61f2d..a164de05 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xiao", - "version": "151.2.1", + "version": "151.3.0", "description": "Your personal server companion.", "main": "Xiao.js", "private": true, diff --git a/types/font.js b/types/font.js index 3835b568..9f1ef5cf 100644 --- a/types/font.js +++ b/types/font.js @@ -34,4 +34,8 @@ module.exports = class FontArgument extends Argument { if (foundExact.size === 1) return foundExact.first(); return null; } + + example() { + return this.client.fonts.random().filenameNoExt; + } }; diff --git a/types/image-or-avatar.js b/types/image-or-avatar.js index ae6e03ad..2c6b8e23 100644 --- a/types/image-or-avatar.js +++ b/types/image-or-avatar.js @@ -22,4 +22,8 @@ module.exports = class ImageOrAvatarArgument extends Argument { return this.client.registry.types.get('image').isEmpty(value, msg, arg) && this.client.registry.types.get('user').isEmpty(value, msg, arg); } + + example(msg, arg) { + return this.client.registry.types.get('user').example(msg, arg); + } }; diff --git a/types/image.js b/types/image.js index f46f961d..7a4a3653 100644 --- a/types/image.js +++ b/types/image.js @@ -2,6 +2,8 @@ const Argument = require('../framework/ArgumentType'); const fileTypeRe = /\.(jpe?g|png|gif|jfif|bmp)(\?.+)?$/i; const request = require('node-superfetch'); const validURL = require('valid-url'); +const logos = require('../../assets/json/logos'); +const logoKeys = Object.keys(logos); module.exports = class ImageArgument extends Argument { constructor(client) { @@ -41,4 +43,8 @@ module.exports = class ImageArgument extends Argument { if (msg.attachments.size) return false; return !value; } + + example() { + return `<${logos[logoKeys[Math.floor(Math.random() * logoKeys.length)]]}>`; + } }; diff --git a/types/month.js b/types/month.js index 8fe00ab4..ba699625 100644 --- a/types/month.js +++ b/types/month.js @@ -21,4 +21,8 @@ module.exports = class MonthArgument extends Argument { if (shorthand.includes(value.toLowerCase())) return shorthand.indexOf(value.toLowerCase()) + 1; return null; } + + example() { + return months[Math.floor(Math.random() * months.length)]; + } }; diff --git a/types/pokemon.js b/types/pokemon.js index e04bfdd4..38f46cf2 100644 --- a/types/pokemon.js +++ b/types/pokemon.js @@ -1,4 +1,5 @@ const Argument = require('../framework/ArgumentType'); +const examples = ['Pikachu', 'Bulbasaur', 'Victini', 'Flygon']; module.exports = class PokemonArgument extends Argument { constructor(client) { @@ -14,4 +15,9 @@ module.exports = class PokemonArgument extends Argument { parse(value) { return this.client.pokemon.fetch(value); } + + example() { + if (this.client.pokemon.size) return this.client.pokemon.random().name; + return examples[Math.floor(Math.random() * examples.length)]; + } }; diff --git a/types/sherlock.js b/types/sherlock.js index 4cd7c082..d09a0d2d 100644 --- a/types/sherlock.js +++ b/types/sherlock.js @@ -15,4 +15,8 @@ module.exports = class SherlockType extends Argument { parse(value) { return sherlock.parse(value); } + + example() { + return 'sleep 1 hour'; + } }; diff --git a/types/timezone.js b/types/timezone.js index 99637fbb..18ad753b 100644 --- a/types/timezone.js +++ b/types/timezone.js @@ -37,4 +37,9 @@ module.exports = class TimezoneType extends Argument { } return null; } + + example() { + const zones = moment.tz.names(); + return zones[Math.floor(Math.random() * zones.length)]; + } }; diff --git a/types/url.js b/types/url.js index a3dc46ae..50b5c100 100644 --- a/types/url.js +++ b/types/url.js @@ -15,4 +15,8 @@ module.exports = class UrlType extends Argument { if (!validURL.isWebUri(value)) return new URL(`http://${value}`); return new URL(value); } + + example() { + return ''; + } };