diff --git a/package.json b/package.json index 612bac4..113fbb1 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "dependencies": { "@eartharoid/i18n": "^1.0.0", "boxen": "^5.0.0", + "command-line-args": "^5.1.1", "cryptr": "^6.0.2", "discord.js": "^12.5.1", "dotenv": "^8.2.0", @@ -38,6 +39,7 @@ "node-fetch": "^2.6.1", "semver": "^7.3.4", "sequelize": "^6.5.0", + "string-argv": "^0.3.1", "terminal-link": "^2.1.1" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4fcd4f3..b997a0d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3,6 +3,7 @@ lockfileVersion: 5.3 specifiers: '@eartharoid/i18n': ^1.0.0 boxen: ^5.0.0 + command-line-args: ^5.1.1 cryptr: ^6.0.2 discord.js: ^12.5.1 dotenv: ^8.2.0 @@ -18,12 +19,14 @@ specifiers: semver: ^7.3.4 sequelize: ^6.5.0 sqlite3: ^5.0.2 + string-argv: ^0.3.1 tedious: ^11.0.3 terminal-link: ^2.1.1 dependencies: '@eartharoid/i18n': 1.0.0 boxen: 5.0.0 + command-line-args: 5.1.1 cryptr: 6.0.2 discord.js: 12.5.1 dotenv: 8.2.0 @@ -32,6 +35,7 @@ dependencies: node-fetch: 2.6.1 semver: 7.3.4 sequelize: 6.5.0_fb66e8c649bde1be622cc06164c9e22d + string-argv: 0.3.1 terminal-link: 2.1.1 optionalDependencies: @@ -338,6 +342,11 @@ packages: sprintf-js: 1.0.3 dev: true + /array-back/3.1.0: + resolution: {integrity: sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==} + engines: {node: '>=6'} + dev: false + /asn1/0.2.4: resolution: {integrity: sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==} dependencies: @@ -589,6 +598,16 @@ packages: dependencies: delayed-stream: 1.0.0 + /command-line-args/5.1.1: + resolution: {integrity: sha512-hL/eG8lrll1Qy1ezvkant+trihbGnaKaeEjj6Scyr3DN+RC7iQ5Rz84IeLERfAWDGo0HBSNAakczwgCilDXnWg==} + engines: {node: '>=4.0.0'} + dependencies: + array-back: 3.1.0 + find-replace: 3.0.0 + lodash.camelcase: 4.3.0 + typical: 4.0.0 + dev: false + /concat-map/0.0.1: resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} @@ -955,6 +974,13 @@ packages: to-regex-range: 5.0.1 dev: true + /find-replace/3.0.0: + resolution: {integrity: sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==} + engines: {node: '>=4.0.0'} + dependencies: + array-back: 3.1.0 + dev: false + /flat-cache/3.0.4: resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -1451,6 +1477,10 @@ packages: type-check: 0.4.0 dev: true + /lodash.camelcase/4.3.0: + resolution: {integrity: sha1-soqmKIorn8ZRA1x3EfZathkDMaY=} + dev: false + /lodash/4.17.20: resolution: {integrity: sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==} @@ -2311,6 +2341,11 @@ packages: safer-buffer: 2.1.2 tweetnacl: 0.14.5 + /string-argv/0.3.1: + resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==} + engines: {node: '>=0.6.19'} + dev: false + /string-width/1.0.2: resolution: {integrity: sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=} engines: {node: '>=0.10.0'} @@ -2571,6 +2606,11 @@ packages: is-typedarray: 1.0.0 dev: true + /typical/4.0.0: + resolution: {integrity: sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==} + engines: {node: '>=8'} + dev: false + /undefsafe/2.0.3: resolution: {integrity: sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==} dependencies: diff --git a/src/modules/commands/command.js b/src/modules/commands/command.js index e4422cc..bbc1296 100644 --- a/src/modules/commands/command.js +++ b/src/modules/commands/command.js @@ -21,7 +21,7 @@ module.exports = class Command { * @param {boolean} [data.staff_only] - Only allow staff to use this command? * @param {string[]} [data.permissions] - Array of permissions needed for a user to use this command * @param {boolean} [data.process_args] - Should the command handler process named arguments? - * @param {CommandArgument[]} [data.args] - The command's arguments + * @param {CommandArgument[]} [data.args] - The command's arguments (see [docs](https://github.com/75lb/command-line-args/blob/master/doc/option-definition.md) if using processed args) */ constructor(client, data) { @@ -132,7 +132,7 @@ module.exports = class Command { const addArgs = (embed, arg) => { let required = arg.required ? '`❗` ' : ''; - embed.addField(required + arg.name, `» ${i18n('cmd_usage.args.description', arg.description)}}\n» ${i18n('cmd_usage.args.example', arg.example)}`); + embed.addField(required + arg.name, `» ${i18n('cmd_usage.args.description', arg.description)}\n» ${i18n('cmd_usage.args.example', arg.example)}`); }; let usage, @@ -140,8 +140,8 @@ module.exports = class Command { embed; if (this.process_args) { - usage = `${prefix + cmd_name} ${this.args.map(arg => arg.required ? `<${arg.name};>` : `[${arg.name};]`).join(' ')}`; - example = `${prefix + cmd_name} ${this.args.map(arg => `${arg.name}: ${arg.example};`).join(' ')}`; + usage = `${prefix + cmd_name} ${this.args.map(arg => arg.required ? `<${arg.name}>` : `[${arg.name}]`).join(' ')}`; + example = `${prefix + cmd_name} \n${this.args.map(arg => `--${arg.name} ${arg.example}`).join('\n')}`; embed = new MessageEmbed() .setColor(settings.error_colour) .setTitle(i18n('cmd_usage.title', cmd_name)) diff --git a/src/modules/commands/manager.js b/src/modules/commands/manager.js index 59936e4..f1c7458 100644 --- a/src/modules/commands/manager.js +++ b/src/modules/commands/manager.js @@ -4,6 +4,9 @@ const { Collection, Client, Message, MessageEmbed } = require('discord.js'); const fs = require('fs'); const { path } = require('../../utils/fs'); +const { parseArgsStringToArgv: argv } = require('string-argv'); +const parseArgs = require('command-line-args'); + /** * Manages the loading and execution of commands */ @@ -153,11 +156,9 @@ module.exports = class CommandManager { let args = raw_args; if (cmd.process_args) { - args = {}; - let data = [...raw_args.matchAll(/(?\w+)\??\s?:\s?(?([^;]|;{2})*);/gmi)]; // an array of argument objects - data.forEach(arg => args[arg.groups.key] = arg.groups.value.replace(/;{2}/gm, ';')); // put the data into a useful format + args = parseArgs(cmd.args, { argv: argv(raw_args) }); for (let arg of cmd.args) { - if (arg.required && !args[arg]) { + if (arg.required && args[arg.name] === undefined) { return await cmd.sendUsage(message.channel, cmd_name); // send usage if any required arg is missing } }