diff --git a/.gitignore b/.gitignore index bb57796..f620185 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,4 @@ node_modules .vscode yarn-error.log .eslintrc.json -logs -permissionExample.json \ No newline at end of file +logs \ No newline at end of file diff --git a/language/languages/en_us/arguments/en_us_administrator.lang b/language/languages/en_us/arguments/en_us_administrator.lang index d961b01..cd7ca6e 100644 --- a/language/languages/en_us/arguments/en_us_administrator.lang +++ b/language/languages/en_us/arguments/en_us_administrator.lang @@ -1,2 +1,15 @@ +//Grant Command + [A_CHANNEL_GRANT_DESCRIPTION] -Specify channels to grant specific permissions to. \ No newline at end of file +Specify channels to grant specific permissions to. + +//Revoke Command +[A_CHANNEL_REVOKE_DESCRIPTION] +Specify channels to revoke permissions from. + +//Permissions Command +[A_USER_PERMISSIONS_DESCRIPTION] +Enable viewing all user's permissions. + +[A_RAW_PERMISSIONS_DESCRIPTION] +Upload a raw JSON file of all of the permissions in the guild. \ No newline at end of file diff --git a/language/languages/en_us/arguments/en_us_utility.lang b/language/languages/en_us/arguments/en_us_utility.lang index e69de29..c59bdb1 100644 --- a/language/languages/en_us/arguments/en_us_utility.lang +++ b/language/languages/en_us/arguments/en_us_utility.lang @@ -0,0 +1,5 @@ +[A_USER_SETTINGS_DESCRIPTION] +View or edit user-only settings. + +[A_ALL_SETTINGS_DESCRIPTION] +View all guild, user, or restricted settings. \ No newline at end of file diff --git a/language/languages/en_us/commands/en_us_administration.lang b/language/languages/en_us/commands/en_us_administration.lang index ff72ef5..4808aeb 100644 --- a/language/languages/en_us/commands/en_us_administration.lang +++ b/language/languages/en_us/commands/en_us_administration.lang @@ -1,7 +1,8 @@ //Grant Command [C_GRANT_DESCRIPTION] -Grant roles or users permissions for commands or modules. +Grant roles or users permissions to use commands or modules. +To view all grantable permissions, use the command `{prefix}perms list`. [C_GRANT_RESOLVEERROR] Unable to find a role or member, view `{prefix}cmd grant` for more help. @@ -15,16 +16,14 @@ There was an issue pushing the permissions to the database. Contact a bot develo [C_GRANT_SUCCESS] Successfully granted **{resolveable}** the following permissions: {permissions} -[C_GRANT_SUCCESSALT]S +[C_GRANT_SUCCESSALT] in channel(s) {channels}. [C_GRANT_SUCCESSFAILED] -Some permissions have failed to add, likely because the {resolveable} already has the permissions or you did not supply valid permissions. +Some permissions failed to grant, likely because they already have the permissions or you did not supply valid permissions. [C_GRANT_FAILED] -Failed to grant the following permissions: {failed}. -This is likely because the {resolveable} already has the permissions or you did not supply valid permissions. - +Failed to grant the permissions provided, view `{prefix}cmd grant` for more help. //Revoke Command @@ -41,7 +40,52 @@ You must provide permissions to revoke, view `{prefix}cmd grant` for more help. [C_REVOKE_DATABASEERROR] There was an issue removing the permissions from the database. Contact a bot developer. +[C_REVOKE_SUCCESS] +Successfully revoked **{resolveable}** the following permissions: {removed} + +[C_REVOKE_SUCCESSALT] +in channel(s) {channels}. + +[C_REVOKE_SUCCESSFAILED] +Some permissions failed to revoke, likely because they did not have the permissions in the first place. + +[C_REVOKE_FAILED] +Failed to revoke the permissions provided, try double-checking the permissions with `{prefix}perms {resolveable}`. + //Permissions Command [C_PERMISSIONS_DESCRIPTION] -View permissions granted to roles or users. \ No newline at end of file +View permissions granted to roles or users. + +[C_PERMISSIONS_LIST] +You can **grant**/**revoke** the following permissions: {permissions}. + +[C_PERMISSIONS_SHOWTITLE] +switch({user}) { + case true: + "User Permissions" + break; + case false: + "Role Permissions" + break; +} + +[C_PERMISSIONS_SHOWDESCRIPTION] +An overview of all {resolve}'s permissions in the guild. If you would like to see an in-depth view of the role or user's permissions, use `{prefix}perms [user|role]`. + +[C_PERMISSIONS_MAXFIELDS] +:warning: **You have met the max amount of fields and you will need to use `{prefix}perms --json` to view them.** :warning: + +[C_PERMISSIONS_NOTFOUND] +Unable to find any roles or users with those arguments. + +[C_PERMISSIONS_JSON] +Attached the JSON-formatted permission file in the file below. +You may want to format this file for your viewing pleasure. + +[C_PERMISSIONS_GLOBAL] +**Global Permissions:** {permissions} +Global permissions can be used in all channels, but you can add specific channels if needed. + +[C_PERMISSIONS_GLOBALALT] +Channel-specific permissions are listed below. \ No newline at end of file diff --git a/language/languages/en_us/commands/en_us_utility.lang b/language/languages/en_us/commands/en_us_utility.lang index 0ee3c6b..4138b8a 100644 --- a/language/languages/en_us/commands/en_us_utility.lang +++ b/language/languages/en_us/commands/en_us_utility.lang @@ -1,8 +1,5 @@ //Ping Command -[C_PING_RESPONSE] -Pong! - [C_PING_DESCRIPTION] Shows the millisecond delay between the bot and the discord server. @@ -12,7 +9,7 @@ Shows the millisecond delay between the bot and the discord server. Configure your guild and user settings. [C_SETTINGS_ADMINISTRATORERROR] -You must have the `ADMINISTRATOR` permission to reset the {type} settings. +You must have the `ADMINISTRATOR` permission to run this. [C_SETTINGS_CLIENTPERMISSIONERROR] The setting **{setting}** requires __the bot__ to have permissions to use. @@ -29,6 +26,9 @@ This prompt will time out in __30 seconds__. [C_SETTINGS_RESETERROR] You provided an invalid input, please try again. +[C_SETTINGS_RESETABORT] +Successfully aborted the operation. + [C_SETTINGS_RESETSUCCESS] All {type} settings were successfully deleted and set to default. @@ -73,5 +73,18 @@ Found {matches} matches, displaying {count} To search server members with similar names use `{prefix}user search ` //Avatar Command +[C_AVATAR_DESCRIPTION] +Fetches avatars for various users in different formats and sizes. + [C_AVATAR_FORMATERROR] -Unable to find an avatar with those arguments, try a different size or format. \ No newline at end of file +Unable to find an avatar with those arguments, try a different size or format. + +//Lookup Command +[C_LOOKUP_DESCRIPTION] +Looks up a discord invite code and returns the information of the invite. + +[C_LOOKUP_FAILEDMATCH] +Couldn't find a discord invite code, try again. + +[C_LOOKUP_NONEXISTANT] +Unable to find any data about that invite code. \ No newline at end of file diff --git a/language/languages/en_us/en_us_general.lang b/language/languages/en_us/en_us_general.lang index 3ef3f15..4ab3f3c 100644 --- a/language/languages/en_us/en_us_general.lang +++ b/language/languages/en_us/en_us_general.lang @@ -19,3 +19,9 @@ Aliases [GENERAL_ARGUMENTS] Arguments + +[GENERAL_CURRENT] +Current Settings + +[GENERAL_SETTINGRESET] +Successfully reset the setting {setting} to the default value. diff --git a/language/languages/en_us/en_us_modules.lang b/language/languages/en_us/en_us_modules.lang deleted file mode 100644 index cf67afd..0000000 --- a/language/languages/en_us/en_us_modules.lang +++ /dev/null @@ -1,19 +0,0 @@ -[M_UTILITY_NAME] -Utility - -[M_MODERATION_NAME] -Moderation - -[M_DEVELOPER_NAME] -Developer - -[M_ADMINISTRATOR_NAME] -Administrator - -[M_INFORMATION_NAME] -Information - -[M_MUSIC_NAME] -Music - -[ diff --git a/language/languages/en_us/settings/en_us_utility.lang b/language/languages/en_us/settings/en_us_utility.lang index da2685c..c529f98 100644 --- a/language/languages/en_us/settings/en_us_utility.lang +++ b/language/languages/en_us/settings/en_us_utility.lang @@ -1,3 +1,8 @@ +//Guild Settings + + +//guildPrefix Setting + [S_GUILDPREFIX_DESCRIPTION] Customizes your prefix in the guild. @@ -7,5 +12,99 @@ Successfully set the guild prefix to `{prefix}`. [S_GUILDPREFIX_LENGTH] The guild prefix cannot exceed {max} characters. `[{length}/{max}]`. +[S_GUILDPREFIX_SPACES] +The guild prefix cannot include spaces. + [S_GUILDPREFIX_RESET] -Successfully reset the guild prefix to `{prefix}`. \ No newline at end of file +Successfully reset the guild prefix to `{prefix}`. + +//mute Setting + +[S_MUTE_DESCRIPTION] +Assign or create a muted role and choose mute functionality for your guild. +__Mute Types__ +**`0`:** Mutes only add/remove the muted role. *(default)* +**`1`:** Mutes remove all roles except for the muted role. +**`2`:** Mutes remove all roles, does not require a muted role. + +[S_MUTE_TYPENAN] +The argument provided is not a number! The available types are: `0`, `1`, or `2`. +View `{prefix}setting mute` for more help. + +[S_MUTE_TYPEINVALID] +The argument provided is not a valid type! The available types are: `0`, `1`, or `2`. +View `{prefix}setting mute` for more help. + +[S_MUTE_TYPESUCCESS] +Successfully set the **mute type** to `{type}`. + +[S_MUTE_TYPESWITCH] +switch({type}) { + case 0: + "Mutes will now *only add/remove* the muted role."; + break; + case 1: + "Mutes will now *remove all roles except for the muted role*, and then add them back once done."; + break; + case 2: + "Mutes will now *remove all roles* and then add them back once done."; + break; +} + +[S_MUTE_ROLEMISSINGPERMISSION] +The bot *must have* the `MANAGE_ROLES` permission to create a new mute role. + +[S_MUTE_ROLEPROMPT] +Found an existing role named **{name}** `({id})`, would you like to use it and update these roles permissions? +Answering no will create a new role with updated permissions. *(__y__es, __n__o)* +This prompt will time out in __30 seconds__. + +[S_MUTE_ROLEPROMPTERROR] +You provided an invalid input, please try again. + +[S_MUTE_ROLECREATEERROR] +The bot encountered an issue while creating a role for the guild. + +[S_MUTE_ROLEMISSING] +Cannot find a specified role with those arguments. +To create a new role, try `{prefix}setting createmute Muted` + +[S_MUTE_ROLESUCCESS] +Successfully {type} the **mute role** to `{role}`. + +[S_MUTE_GENERATEDPERMISSIONS] +**Permissions have been applied to all possible channels.** + +[S_MUTE_UNGENERATEDPERMISSIONS] +**None of the permissions have been changed.** + + + + + + + + + + + + + + + + + +[S_MUTEDROLE_MISSINGPERMISSION] +The bot must have the `MANAGE_ROLES` permission to create a new muted role in the guild. + +[S_MUTEDROLE_ERRORCREATE] +There was an issue creating the muted role for your guild. + +[S_MUTEDROLE_CANNOTFIND] +I cannot find a role name with those arguments. If you would like to create a new one, use the command `{prefix}settings mute create`. + +[S_MUTEDROLE_SUCCESS] +Successfully set the muted role to `{role}`. + + +//User Settings \ No newline at end of file diff --git a/middleware/Shard.js b/middleware/Shard.js index 13de173..3c6d794 100644 --- a/middleware/Shard.js +++ b/middleware/Shard.js @@ -1,4 +1,4 @@ -/* Adopted from Discord.js */ +/*Adopted from Discord.js */ const path = require('path'); const EventEmitter = require('events'); @@ -15,18 +15,17 @@ class Shard extends EventEmitter { super(); if(manager.mode === 'process') childProcess = require('child_process'); - else if(manager.mode === 'worker') Worker = require('worker_threads').Worker; + else if(manager.mode === 'worker') Worker = require('worker_threads').Worker; //eslint-disable-line prefer-destructuring this.manager = manager; this.id = id; this.args = manager.shardArgs || []; this.execArgv = manager.execArgv; - this.env = Object.assign({}, process.env, { - SHARDING_MANAGER: true, + this.env = { ...process.env, SHARDING_MANAGER: true, SHARDS: this.id, TOTAL_SHARD_COUNT: this.manager.totalShards, - DISCORD_TOKEN: this.manager.token - }); + DISCORD_TOKEN: this.manager.token + }; this.ready = false; this.process = null; @@ -47,13 +46,13 @@ class Shard extends EventEmitter { this.process = childProcess.fork(path.resolve(this.manager.file), this.args, { env: this.env, execArgv: this.execArgv - }) - .on('message', this._handleMessage.bind(this)) - .on('exit', this._exitListener); + }). + on('message', this._handleMessage.bind(this)). + on('exit', this._exitListener); } else if(this.manager.mode === 'worker') { - this.worker = new Worker(path.resolve(this.manager.file), { workerData: this.env }) - .on('message', this._handleMessage.bind(this)) - .on('exit', this._exitListener); + this.worker = new Worker(path.resolve(this.manager.file), { workerData: this.env }). + on('message', this._handleMessage.bind(this)). + on('exit', this._exitListener); } this.emit('spawn', this.process || this.worker); @@ -92,7 +91,7 @@ class Shard extends EventEmitter { send(message) { return new Promise((resolve, reject) => { if(this.process) { - this.process.send(message, error => { + this.process.send(message, (error) => { if(error) reject(error); else resolve(this); }); } else { @@ -108,7 +107,7 @@ class Shard extends EventEmitter { const promise = new Promise((resolve, reject) => { const child = this.process || this.worker; - const listener = message => { + const listener = (message) => { if(!message || message._fetchProp !== prop) return; child.removeListener('message', listener); this._fetches.delete(prop); @@ -116,7 +115,7 @@ class Shard extends EventEmitter { }; child.on('message', listener); - this.send({ _fetchProp: prop }).catch(err => { + this.send({ _fetchProp: prop }).catch((err) => { child.removeListener('message', listener); this._fetches.delete(prop); reject(err); @@ -135,7 +134,7 @@ class Shard extends EventEmitter { const promise = new Promise((resolve, reject) => { const child = this.process || this.worker; - const listener = message => { + const listener = (message) => { if(!message || message._eval !== script) return; child.removeListener('message', listener); this._evals.delete(script); @@ -144,7 +143,7 @@ class Shard extends EventEmitter { child.on('message', listener); const _eval = typeof script === 'function' ? `(${script})(this)` : script; - this.send({ _eval }).catch(err => { + this.send({ _eval }).catch((err) => { child.removeListener('message', listener); this._evals.delete(script); reject(err); @@ -175,21 +174,21 @@ class Shard extends EventEmitter { } if(message._sFetchProp) { //Shard requesting property fetch this.manager.fetchClientValues(message._sFetchProp).then( - results => this.send({ _sFetchProp: message._sFetchProp, _result: results }), - err => this.send({ _sFetchProp: message._sFetchProp, _error: Util.makePlainError(err) }) + (results) => this.send({ _sFetchProp: message._sFetchProp, _result: results }), + (err) => this.send({ _sFetchProp: message._sFetchProp, _error: Util.makePlainError(err) }) ); return; } if(message._sEval) { //Shard requesting eval broadcast this.manager.broadcastEval(message._sEval).then( - results => this.send({ _sEval: message._sEval, _result: results }), - err => this.send({ _sEval: message._sEval, _error: Util.makePlainError(err) }) + (results) => this.send({ _sEval: message._sEval, _result: results }), + (err) => this.send({ _sEval: message._sEval, _error: Util.makePlainError(err) }) ); return; } if(message._sRespawnAll) { //Shard requesting to respawn all shards. const { shardDelay, respawnDelay, waitForReady } = message._sRespawnAll; - this.manager.respawnAll(shardDelay, respawnDelay, waitForReady).catch(() => { + this.manager.respawnAll(shardDelay, respawnDelay, waitForReady).catch(() => { //eslint-disable-line no-empty-function }); return; } @@ -208,7 +207,7 @@ class Shard extends EventEmitter { this._evals.clear(); this._fetches.clear(); - if(respawn) this.spawn().catch(err => this.emit('error', err)); + if(respawn) this.spawn().catch((err) => this.emit('error', err)); } diff --git a/middleware/ShardManager.js b/middleware/ShardManager.js index 04b369a..9aea6fe 100644 --- a/middleware/ShardManager.js +++ b/middleware/ShardManager.js @@ -1,4 +1,4 @@ -/* Adopted from Discord.js */ +/*Adopted from Discord.js */ const path = require('path'); const fs = require('fs'); @@ -36,10 +36,10 @@ class ShardManager extends EventEmitter { } this.shardList = [...new Set(this.shardList)]; if(this.shardList.length < 1) throw new RangeError('[shardmanager] ShardList must have one ID.'); - if(this.shardList.some(shardID => typeof shardID !== 'number' - || isNaN(shardID) - || !Number.isInteger(shardID) - || shardID < 0) + if(this.shardList.some((shardID) => typeof shardID !== 'number' || + isNaN(shardID) || + !Number.isInteger(shardID) || + shardID < 0) ) { throw new TypeError('[shardmanager] ShardList must be an array of positive integers.'); } @@ -102,7 +102,7 @@ class ShardManager extends EventEmitter { if(this.totalShards === 'auto' || this.totalShards !== amount) { this.totalShards = amount; } - if(this.shardList.some(id => id >= amount)) { + if(this.shardList.some((id) => id >= amount)) { throw new RangeError('[shardmanager] Amount of shards cannot be larger than the highest shard ID.'); } diff --git a/middleware/logger/transports/DiscordWebhook.js b/middleware/logger/transports/DiscordWebhook.js index a715909..8a2d3bb 100644 --- a/middleware/logger/transports/DiscordWebhook.js +++ b/middleware/logger/transports/DiscordWebhook.js @@ -1,12 +1,11 @@ const Transport = require('winston-transport'); const { WebhookClient } = require('discord.js'); const { username } = require('os').userInfo(); -const { inspect } = require('util'); const options = require('../../../options.json'); -// eslint-disable-next-line no-control-regex -const regex = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g +//eslint-disable-next-line no-control-regex +const regex = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/gu; class DiscordWebhook extends Transport { constructor(opts) { @@ -24,11 +23,14 @@ class DiscordWebhook extends Transport { }); const message = info.message.replace(regex, '') - .replace(new RegExp(options.bot.token, 'g'), '') - .replace(new RegExp(username, 'g'), ''); + .replace(new RegExp(options.bot.token, 'gu'), '') + .replace(new RegExp(username, 'gu'), ''); - const developers = ['nolan', 'navy']; - const random = developers[Math.floor(Math.random()*developers.length)]; + const developers = [ + 'nolan', + 'navy' + ]; + const random = developers[Math.floor(Math.random() * developers.length)]; const embed = { color: 0xe88388, @@ -43,6 +45,6 @@ class DiscordWebhook extends Transport { callback(); } -}; +} module.exports = DiscordWebhook; \ No newline at end of file diff --git a/middleware/logger/transports/FileExtension.js b/middleware/logger/transports/FileExtension.js index b4b0b51..835d1f8 100644 --- a/middleware/logger/transports/FileExtension.js +++ b/middleware/logger/transports/FileExtension.js @@ -1,4 +1,4 @@ -/* eslint-disable no-control-regex */ +/* eslint-disable */ const { transports: { File }} = require('winston'); const debug = require('diagnostics')('winston:file'); const { MESSAGE } = require('triple-beam'); diff --git a/middleware/logger/transports/index.js b/middleware/logger/transports/index.js index 9602b2f..77a8577 100644 --- a/middleware/logger/transports/index.js +++ b/middleware/logger/transports/index.js @@ -1,4 +1,3 @@ -/* eslint-disable linebreak-style */ module.exports = { DiscordWebhook: require('./DiscordWebhook.js'), FileExtension: require('./FileExtension.js') diff --git a/package.json b/package.json index 065fd82..28acf54 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "common-tags": "^1.8.0", "discord.js": "discordjs/discord.js", "escape-string-regexp": "^3.0.0", - "eslint": "^6.8.0", + "eslint": "^7.0.0", "moment": "^2.24.0", "mongodb": "^3.5.7", "mysql": "^2.18.1", diff --git a/storage/providers/Mariadb.js b/storage/providers/Mariadb.js index bfdd8dc..47e9da8 100644 --- a/storage/providers/Mariadb.js +++ b/storage/providers/Mariadb.js @@ -18,15 +18,15 @@ class MariaDBProvider extends Provider { this.db = MySQL.createPool(this.config); - this.db.on('connection', async connection => { + this.db.on('connection', async (connection) => { // this.manager.logger.log('MariaDB connected.'); - connection.on('error', err => { + connection.on('error', (err) => { // this.manager.logger.error('MariaDB errored.', err); }); - connection.on('close', data => { + connection.on('close', (data) => { // this.manager.logger.log('MariaDB connection closed.', data); }); diff --git a/storage/providers/Mongodb.js b/storage/providers/Mongodb.js index f80655f..53fb89e 100644 --- a/storage/providers/Mongodb.js +++ b/storage/providers/Mongodb.js @@ -59,37 +59,22 @@ class MongoDBProvider extends Provider { stats({ collection, options = { } }) { return new Promise((resolve, reject) => { - if (!collection || collection.length) { - - this.db.stats(options, (error, stats) => { + this.db.stats(options, (err, stats) => { + if (err) return reject(err); - if (error) return reject(error); - else { - - let { db, collections, objects, averageSize: avgObjSize, dataSize } = stats; - return resolve({ db, collections, objects, averageSize: avgObjSize, dataSize }); - - } - + const { db, collections, objects, avgObjSize, dataSize } = stats; + return resolve({ db, collections, objects, averageSize: avgObjSize, dataSize }); }); } else { - this.db.collection(collection).stats(options, (err, stats) => { - if (err) return reject(err); - else { - - let { ns, size, count, averageSize: avgObjSize } = stats; - return resolve({ index: ns, size, count, averageSize: avgObjSize }); - - } + const { ns, size, count, avgObjSize } = stats; + return resolve({ index: ns, size, count, averageSize: avgObjSize }); }); - } - }); } @@ -109,10 +94,8 @@ class MongoDBProvider extends Provider { this.db.collection(collection).countDocuments(query, options, (error, result) => { - if (error) - return reject(error); - else - return resolve(result); + if (error) return reject(error); + return resolve(result); }); @@ -239,14 +222,14 @@ class MongoDBProvider extends Provider { if(!this._initialized) reject(new Error('MongoDB not connected')); - this.db.collection(collection).updateOne(query, { $set: data }, { upsert: upsert }, async (error, result) => { + this.db.collection(collection).updateOne(query, { $set: data }, { upsert }, async (error, result) => { if(error) return reject(error); - else { - //return resolve(result) - let { matchedCount, upsertedCount, modifiedCount } = result; - return resolve({ matched: matchedCount, upserted: upsertedCount, modified: modifiedCount }); - } + + //return resolve(result) + const { matchedCount, upsertedCount, modifiedCount } = result; + return resolve({ matched: matchedCount, upserted: upsertedCount, modified: modifiedCount }); + }); }); @@ -265,17 +248,16 @@ class MongoDBProvider extends Provider { removeProperty({ collection, query, data }) { return new Promise((resolve, reject) => { - let unset = {}; - for (let field in data) unset[field] = ''; + const unset = {}; + for (const field of data) unset[field] = ''; this.db.collection(collection).updateOne(query, { $unset: unset }, async (error, result) => { if(error) return reject(error); - else { - - let { matchedCount, modifiedCount } = result; - return resolve({ matched: matchedCount, modified: modifiedCount }); - } + + const { matchedCount, modifiedCount } = result; + return resolve({ matched: matchedCount, modified: modifiedCount }); + }); @@ -300,10 +282,10 @@ class MongoDBProvider extends Provider { if(!this._initialized) reject(new Error('MongoDB not connected')); - this.db.collection(collection).updateOne(query, { $push: data }, { upsert: upsert }, async (error, result) => { + this.db.collection(collection).updateOne(query, { $push: data }, { upsert }, async (error, result) => { if(error) return reject(error); - else return resolve(result); + return resolve(result); }); @@ -325,15 +307,13 @@ class MongoDBProvider extends Provider { //if(this.manager.debug) this.manager.logger.debug(`Incoming random query for ${db} with parameters ${JSON.stringify(filter)} and amount ${amount}`); if(amount > 100) amount = 100; - return new Promise((resolve, reject)=>{ + return new Promise((resolve, reject) => { if(!this._initialized) reject(new Error('MongoDB not connected')); - this.db.collection(collection).aggregate([{ $match: query }, { $sample: {size: amount}}], function(err, item) { - + this.db.collection(collection).aggregate([{ $match: query }, { $sample: { size: amount } }], (err, item) => { if(err) return reject(err); - resolve(item); - + return resolve(item); }); }); diff --git a/structure/client/DiscordClient.js b/structure/client/DiscordClient.js index 6ec771d..b0eb7f4 100644 --- a/structure/client/DiscordClient.js +++ b/structure/client/DiscordClient.js @@ -57,12 +57,13 @@ class DiscordClient extends Client { await this.dispatcher.dispatch(); this._built = true; + return this._built; } get defaultConfig() { if(this._defaultConfig) return this._defaultConfig; - const settings = this.registry.components.filter(c=>c.type === 'setting' && c.resolve === 'GUILD'); + const settings = this.registry.components.filter((c) => c.type === 'setting' && c.resolve === 'GUILD'); let def = {}; for(const setting of settings.values()) { if(setting.default !== null) { diff --git a/structure/client/Dispatcher.js b/structure/client/Dispatcher.js index c71f1f4..4e766ab 100644 --- a/structure/client/Dispatcher.js +++ b/structure/client/Dispatcher.js @@ -9,11 +9,11 @@ class Dispatcher { async dispatch() { const observers = this.client.registry.components - .filter(c=>c.type === 'observer' && !c.disabled) + .filter((c) => c.type === 'observer' && !c.disabled) .sort((a, b) => b.priority - a.priority); for(const observer of observers.values()) { - for(let [hook, func] of observer.hooks) { + for(const [hook, func] of observer.hooks) { this.client.eventHooker.hook(hook, func); } } diff --git a/structure/client/EventHooker.js b/structure/client/EventHooker.js index d5d6937..c230b8b 100644 --- a/structure/client/EventHooker.js +++ b/structure/client/EventHooker.js @@ -3,7 +3,7 @@ const EventEmitter = require('events'); class EventHooker { constructor(target) { - if(!(target instanceof EventEmitter)) return new TypeError('Invalid EventEmitter passed to EventHooker.'); + if(!(target instanceof EventEmitter)) TypeError('Invalid EventEmitter passed to EventHooker.'); this.target = target; this.events = new Map(); @@ -22,7 +22,7 @@ class EventHooker { unhook(eventName, func) { if(this.events.has(eventName)) { - let funcs = this.events.get(eventName); + const funcs = this.events.get(eventName); const index = funcs.indexOf(func); if(index > -1) { funcs.splice(index, 1); @@ -42,4 +42,4 @@ class EventHooker { } -module.exports = EventHooker; +module.exports = EventHooker; \ No newline at end of file diff --git a/structure/client/Logger.js b/structure/client/Logger.js index 5deba47..c16d1d9 100644 --- a/structure/client/Logger.js +++ b/structure/client/Logger.js @@ -12,7 +12,7 @@ class Logger { }); this.client.eventHooker.hook('componentUpdate', ({ component, type }) => { - this.info(`Component ${chalk.bold(component.resolveable)} was ${chalk.bold(Constants.ComponentTypes[type])}.`); + this.info(`Component ${chalk.bold(component.resolveable)} was ${chalk.bold(Constants.ComponentTypes[type])}.`); //eslint-disable-line no-use-before-define }); this.client.eventHooker.hook('reconnect', () => { diff --git a/structure/client/Resolver.js b/structure/client/Resolver.js index 985b2de..50d5128 100644 --- a/structure/client/Resolver.js +++ b/structure/client/Resolver.js @@ -5,13 +5,13 @@ class Resolver { this.client = client; } - components(str = '', type, exact = true) { //used for CommandHandler + components(str = '', type, exact = true) { const string = str.toLowerCase(); const components = this.client.registry.components - .filter(c => c.type === type) - .filter(exact ? filterExact(string) : filterInexact(string)) + .filter((c) => c.type === type) + .filter(exact ? filterExact(string) : filterInexact(string)) //eslint-disable-line no-use-before-define .array(); return components || []; @@ -27,9 +27,9 @@ class Resolver { return true; } else if (falsey.includes(input)) { return false; - } else { - return null; - } + } + return null; + } // resolveTrue(input) { @@ -46,8 +46,8 @@ class Resolver { const index = guild ? guild.members.cache : this.client.users.cache; let member = null; - if(/<@!?(\d{17,21})>/iy.test(str)) { //mentions - const matches = /<@!?(\d{17,21})>/iy.exec(str); + if((/<@!?(\d{17,21})>/iyu).test(str)) { //mentions + const matches = (/<@!?(\d{17,21})>/iuy).exec(str); member = index.get(matches[1]); if(!member) { try { @@ -58,8 +58,8 @@ class Resolver { } catch(e) {} //eslint-disable-line no-empty } //eslint-disable-line no-empty } - } else if(/\d{17,21}/iy.test(str)) { //id - const matches = /(\d{17,21})/iy.exec(str); + } else if((/\d{17,21}/iuy).test(str)) { //id + const matches = (/(\d{17,21})/iuy).exec(str); member = index.get(matches[1]); if(!member) { try { @@ -70,11 +70,11 @@ class Resolver { } catch(e) {} //eslint-disable-line no-empty } //eslint-disable-line no-empty } - } else if(/(.{2,32})#(\d{4})/iy.test(str)) { //username#discrim - const matches = /(.{2,32})#(\d{4})/iy.exec(str); + } else if((/(.{2,32})#(\d{4})/iuy).test(str)) { //username#discrim + const matches = (/(.{2,32})#(\d{4})/iuy).exec(str); member = guild - ? guild.members.cache.filter(m=>m.user.username === matches[1] && m.user.discriminator === matches[2]).first() - : this.client.users.cache.filter(u=>u.username === matches[1] && u.discriminator === matches[2]).first(); + ? guild.members.cache.filter((m) => m.user.username === matches[1] && m.user.discriminator === matches[2]).first() + : this.client.users.cache.filter((u) => u.username === matches[1] && u.discriminator === matches[2]).first(); } return member || null; @@ -93,39 +93,43 @@ class Resolver { if(typeof resolveables === 'string') resolveables = [ resolveables ]; if(resolveables.length === 0) return false; - let users = this.client.users; - let resolved = []; + const { users } = this.client; + const resolved = []; - for(let resolveable of resolveables) { + for(const resolveable of resolveables) { - if(/<@!?([0-9]{17,21})>/.test(resolveable)) { + if((/<@!?([0-9]{17,21})>/u).test(resolveable)) { - let id = resolveable.match(/<@!?([0-9]{17,21})>/)[1]; - let user = await users.fetch(id).catch(err => { if(err.code === 10013) return false; else { this.client.logger.warn(err); return false; } }); + const [, id] = resolveable.match(/<@!?([0-9]{17,21})>/u); + const user = await users.fetch(id).catch((err) => { + if(err.code === 10013) return false; + this.client.logger.warn(err); return false; + + }); if(user) resolved.push(user); - } else if(/(id\:)?([0-9]{17,21})/.test(resolveable)) { + } else if((/(id:)?([0-9]{17,21})/u).test(resolveable)) { - let id = resolveable.match(/(id\:)?([0-9]{17,21})/)[2]; - let user = await users.fetch(id).catch(err => { if(err.code === 10013) return false; else { this.client.logger.warn(err); return false; } }); + const [,, id] = resolveable.match(/(id:)?([0-9]{17,21})/u); + const user = await users.fetch(id).catch((err) => { + if(err.code === 10013) return false; + this.client.logger.warn(err); return false; + + }); if(user) resolved.push(user); - } else if(/^\@?([\S\s]{1,32})\#([0-9]{4})/.test(resolveable)) { + } else if((/^@?([\S\s]{1,32})#([0-9]{4})/u).test(resolveable)) { - let m = resolveable.match(/^\@?([\S\s]{1,32})\#([0-9]{4})/); - let username = m[1].toLowerCase(); - let discrim = m[2].toLowerCase(); - let user = users.cache.filter(u => { - return u.username.toLowerCase() === username && u.discriminator === discrim; - }).first(); + const m = resolveable.match(/^@?([\S\s]{1,32})#([0-9]{4})/u); + const username = m[1].toLowerCase(); + const discrim = m[2].toLowerCase(); + const user = users.cache.filter((u) => u.username.toLowerCase() === username && u.discriminator === discrim).first(); if(user) resolved.push(user); } else if(!strict) { - let name = resolveable.toLowerCase(); - let user = users.cache.filter(u => { - return u.username.toLowerCase().includes(name); - }).first(); + const name = resolveable.toLowerCase(); + const user = users.cache.filter((u) => u.username.toLowerCase().includes(name)).first(); if(user) resolved.push(user); } @@ -137,7 +141,7 @@ class Resolver { } async resolveUser(resolveable, strict) { - let result = await this.resolveUsers([ resolveable ], strict); + const result = await this.resolveUsers([ resolveable ], strict); return result ? result[0] : false; } @@ -154,43 +158,47 @@ class Resolver { if(typeof resolveables === 'string') resolveables = [ resolveables ]; if(resolveables.length === 0) return false; - let members = guild.members; - let resolved = []; + const { members } = guild; + const resolved = []; - for(let resolveable of resolveables) { + for(const resolveable of resolveables) { - if(/<@!?([0-9]{17,21})>/.test(resolveable)) { + if((/<@!?([0-9]{17,21})>/u).test(resolveable)) { - let id = resolveable.match(/<@!?([0-9]{17,21})>/)[1]; - let member = await members.fetch(id).catch(err => { if(err.code === 10007) return false; else { this.client.logger.warn(err); return false; } }); + const [, id] = resolveable.match(/<@!?([0-9]{17,21})>/u); + const member = await members.fetch(id).catch((err) => { + if(err.code === 10007) return false; + this.client.logger.warn(err); return false; + + }); if(member) resolved.push(member); - } else if(/(id\:)?([0-9]{17,21})/.test(resolveable)) { + } else if((/(id:)?([0-9]{17,21})/u).test(resolveable)) { - let id = resolveable.match(/(id\:)?([0-9]{17,21})/)[2]; - let member = await members.fetch(id).catch(err => { if(err.code === 10007) return false; else { this.client.logger.warn(err); return false; } }); + const [,, id] = resolveable.match(/(id:)?([0-9]{17,21})/u); + const member = await members.fetch(id).catch((err) => { + if(err.code === 10007) return false; + this.client.logger.warn(err); return false; + + }); if(member) resolved.push(member); - } else if(/^\@?([\S\s]{1,32})\#([0-9]{4})/.test(resolveable)) { + } else if((/^@?([\S\s]{1,32})#([0-9]{4})/u).test(resolveable)) { - let m = resolveable.match(/^\@?([\S\s]{1,32})\#([0-9]{4})/); - let username = m[1].toLowerCase(); - let discrim = m[2].toLowerCase(); - let member = members.cache.filter(m => { - return m.user.username.toLowerCase() === username && m.user.discriminator === discrim; - }).first(); + const m = resolveable.match(/^@?([\S\s]{1,32})#([0-9]{4})/u); + const username = m[1].toLowerCase(); + const discrim = m[2].toLowerCase(); + const member = members.cache.filter((m) => m.user.username.toLowerCase() === username && m.user.discriminator === discrim).first(); if(member) resolved.push(member); - } else if(/^\@?([\S\s]{1,32})/.test(resolveable) && guild && !strict) { + } else if((/^@?([\S\s]{1,32})/u).test(resolveable) && guild && !strict) { - let nickname = resolveable.match(/^\@?([\S\s]{1,32})/)[0].toLowerCase(); - let member = members.cache.filter((m) => { - return (m && m.user) && - ((!m.nickname ? false : m.nickname.toLowerCase() == nickname ) || + const nickname = resolveable.match(/^@?([\S\s]{1,32})/u)[0].toLowerCase(); + const member = members.cache.filter((m) => m && m.user && + ((!m.nickname ? false : m.nickname.toLowerCase() === nickname) || (!m.nickname ? false : m.nickname.toLowerCase().includes(nickname)) || m.user.username.toLowerCase().includes(nickname) || - m.user.username.toLowerCase() == nickname); - }).first(); + m.user.username.toLowerCase() === nickname)).first(); if(member) resolved.push(member); } @@ -203,7 +211,7 @@ class Resolver { async resolveMember(resolveable, guild, strict) { - let result = await this.resolveMembers([ resolveable ], guild, strict); + const result = await this.resolveMembers([ resolveable ], guild, strict); return result ? result[0] : false; } @@ -221,35 +229,35 @@ class Resolver { if(typeof resolveables === 'string') resolveables = [ resolveables ]; if(resolveables.length === 0) return false; - let channels = guild.channels; - let resolved = []; + const { channels } = guild; + const resolved = []; - for(let resolveable of resolveables) { + for(const resolveable of resolveables) { - let channel = channels.resolve(resolveable); + const channel = channels.resolve(resolveable); if(channel) { resolved.push(channel); continue; } - let name = /^\#?([a-z0-9\-\_0]*)/i; - let id = /^\<\#([0-9]*)\>/i; + const name = /^#?([a-z0-9\-_0]*)/iu; + const id = /^<#([0-9]*)>/iu; if (id.test(resolveable)) { - let match = resolveable.match(id); - let ch = match[1]; + const match = resolveable.match(id); + const [, ch] = match; - let channel = channels.resolve(ch); + const channel = channels.resolve(ch); if (channel) resolved.push(channel); } else if (name.test(resolveable)) { - let match = resolveable.match(name); - let ch = match[1].toLowerCase(); + const match = resolveable.match(name); + const ch = match[1].toLowerCase(); - let [ channel ] = channels.cache.filter(c => { + const [ channel ] = channels.cache.filter((c) => { if(!strict) return c.name.toLowerCase().includes(ch); return c.name.toLowerCase() === ch; }).first(1); @@ -266,7 +274,7 @@ class Resolver { resolveChannel(resolveable, guild, strict) { - let result = this.resolveChannels([resolveable], guild, strict); + const result = this.resolveChannels([resolveable], guild, strict); return result ? result[0] : false; } @@ -284,25 +292,25 @@ class Resolver { if(typeof resolveables === 'string') resolveables = [ resolveables ]; if(resolveables.length === 0) return false; - let roles = guild.roles; - let resolved = []; + const { roles } = guild; + const resolved = []; - for(let resolveable of resolveables) { + for(const resolveable of resolveables) { - let id = /^(<@&)?([0-9]{16,22})>?/i; + const id = /^(<@&)?([0-9]{16,22})>?/iu; if(id.test(resolveable)) { - let match = resolveable.match(id); - let r_id = match[2]; + const match = resolveable.match(id); + const [,, rId] = match; - let role = await roles.fetch(r_id).catch(this.client.logger.error); + const role = await roles.fetch(rId).catch(this.client.logger.error); if(role) resolved.push(role); } else { - let role = roles.cache.filter(r => { + const role = roles.cache.filter((r) => { if(!strict) return r.name.toLowerCase().includes(resolveable.toLowerCase()); return r.name.toLowerCase() === resolveable.toLowerCase(); }).first(); @@ -319,7 +327,7 @@ class Resolver { async resolveRole(resolveable, guild, strict) { - let result = await this.resolveRoles([resolveable], guild, strict); + const result = await this.resolveRoles([resolveable], guild, strict); return result ? result[0] : false; } @@ -328,16 +336,12 @@ class Resolver { module.exports = Resolver; -const filterExact = (search) => { - return comp => comp.id.toLowerCase() === search || +const filterExact = (search) => (comp) => comp.id.toLowerCase() === search || comp.resolveable.toLowerCase() === search || - (comp.aliases && (comp.aliases.some(ali => `${comp.type}:${ali}`.toLowerCase() === search) || - comp.aliases.some(ali => ali.toLowerCase() === search))); -}; + comp.aliases && (comp.aliases.some((ali) => `${comp.type}:${ali}`.toLowerCase() === search) || + comp.aliases.some((ali) => ali.toLowerCase() === search)); -const filterInexact = (search) => { - return comp => comp.id.toLowerCase().includes(search) || +const filterInexact = (search) => (comp) => comp.id.toLowerCase().includes(search) || comp.resolveable.toLowerCase().includes(search) || - (comp.aliases && (comp.aliases.some(ali => `${comp.type}:${ali}`.toLowerCase().includes(search)) || - comp.aliases.some(ali => ali.toLowerCase().includes(search)))); -}; \ No newline at end of file + comp.aliases && (comp.aliases.some((ali) => `${comp.type}:${ali}`.toLowerCase().includes(search)) || + comp.aliases.some((ali) => ali.toLowerCase().includes(search))); \ No newline at end of file diff --git a/structure/client/TransactionHandler.js b/structure/client/TransactionHandler.js index 24c1713..7e5a964 100644 --- a/structure/client/TransactionHandler.js +++ b/structure/client/TransactionHandler.js @@ -26,7 +26,7 @@ class TransactionHandler { return new Promise((resolve, reject) => { const id = this.transactionID; - this.transactions.set(id, { id, resolve, reject } ); + this.transactions.set(id, { id, resolve, reject }); process.send({ _storage: true, transactionID: id, ...message, ...options }); @@ -44,7 +44,7 @@ class TransactionHandler { if(message.error) transaction.reject(message.message); else transaction.resolve(message.result); - this.transactions.delete(message.transactionID); + return this.transactions.delete(message.transactionID); } diff --git a/structure/client/components/commands/administration/Grant.js b/structure/client/components/commands/administration/Grant.js index da2d3d0..fd3bc28 100644 --- a/structure/client/components/commands/administration/Grant.js +++ b/structure/client/components/commands/administration/Grant.js @@ -14,17 +14,19 @@ class GrantCommand extends Command { "\"Server Moderators\" module:moderation", "@nolan#2887 command:kick" ], - memberPermissions: ['ADMINISTRATOR', 'MANAGE_SERVER'], + //memberPermissions: ['ADMINISTRATOR', 'MANAGE_GUILD'], showUsage: true, guildOnly: true, + grantable: true, arguments: [ { name: 'channel', - aliases: [ - 'channels' - ], + aliases: ['channels'], type: 'CHANNEL', - types: ['FLAG', 'VERBAL'], + types: [ + 'FLAG', + 'VERBAL' + ], infinite: true } ] @@ -34,11 +36,12 @@ class GrantCommand extends Command { async execute(message, { params, args }) { - // console.log(args.channel); - const _permissions = await message.guild.permissions(); - const [ parse, ...perms ] = params; + const [ + parse, + ...perms + ] = params; const resolveable = await this._parseResolveable(message, parse); if(!resolveable) return undefined; @@ -47,25 +50,21 @@ class GrantCommand extends Command { return undefined; } - const permissions = this.client.registry.components.filter(c=> - c.type === 'command' - || c.type === 'module' - ); + const permissions = this.client.registry.components.filter((c) => c.type === 'command' || + c.type === 'module'); let parsed = []; if(perms.join(' ') === 'all') { - parsed = this.client.registry.components.filter(c=>c.grantable && c.type === 'command').map(c=>c.resolveable); + parsed = this.client.registry.components.filter((c) => c.type === 'command').map((c) => c.resolveable); //filter for grantable } else { for(const perm of perms) { - const search = permissions.filter(filterInexact(perm)).first(); - if(!search) failed.push(perm); + const search = permissions.filter(filterInexact(perm)).first(); //eslint-disable-line no-use-before-define + if(!search) continue; if(search.type === 'module') { for(const component of search.components.values()) { if(component.type === 'command') parsed.push(component.resolveable); - //add check for grantable } } else { - //add check for grantable parsed.push(search.resolveable); } } @@ -77,34 +76,36 @@ class GrantCommand extends Command { channels: {} }; } - let existing = _permissions[resolveable.id]; + const existing = _permissions[resolveable.id]; - let pushed = []; - let failed = []; + let granted = []; if(args.channel) { for(const channel of args.channel.value) { const existingChannel = existing.channels[channel.id]; if(existingChannel) { for(const perm of parsed) { if(existingChannel.includes(perm)) { - failed.push(perm); + continue; } else { existingChannel.push(perm); - pushed.push(perm); + granted.push(perm); } } } else { existing.channels[channel.id] = parsed; - pushed.concat(parsed) + granted = [ + ...granted, + ...parsed + ]; } } } else { for(const perm of parsed) { if(existing.global.includes(perm)) { - failed.push(perm); + continue; } else { existing.global.push(perm); - pushed.push(perm); + granted.push(perm); } } } @@ -124,17 +125,16 @@ class GrantCommand extends Command { } }); } catch(error) { - await message.respond(message.format('C_GRANT_DATABASEERROR'), { emoji: 'failure' }); - return undefined; - } - - if(pushed.length > 0) { - return await message.respond(stripIndents`${message.format('C_GRANT_SUCCESS', { resolveable: resolveable.name || resolveable.user?.tag, permissions: pushed.map(p=>`\`${p}\``).join(', ') })}${args.channel ? ` ${message.format('C_GRANT_SUCCESSALT', { channels: args.channel.value.map(c=>`\`#${c.name}\``).join(', ')})}`: '.'} - ${failed.length > 0 ? message.format('C_GRANT_SUCCESSFAILED', { resolveable: resolveable.user ? 'user' : 'role' }) : ''}`, { emoji: 'success' }); - } else { - return await message.respond(message.format('C_GRANT_FAILED', { failed: failed.map(f=>`\`${f}\``).join(', '), resolveable: resolveable.user ? 'user' : 'role' }), { emoji: 'failure' }) + await message.respond(message.format('C_GRANT_DATABASEERROR'), { emoji: 'failure' }); + return undefined; } + if(granted.length > 0) { + return message.respond(stripIndents`${message.format('C_GRANT_SUCCESS', { permissions: granted.map((g) => `\`${g}\``).join(', '), resolveable: resolveable.user ? resolveable.user.tag : resolveable.name })}${args.channel ? ` ${message.format('C_GRANT_SUCCESSALT', { channels: args.channel.value.map((c) => `\`#${c.name}\``).join(', ') })}` : '.'} + ${granted.length < parsed.length ? message.format('C_GRANT_SUCCESSFAILED') : ''}`, { emoji: 'success' }); + } + return message.respond(message.format('C_GRANT_FAILED', { resolveable: resolveable.user ? resolveable.user.tag : resolveable.name }), { emoji: 'failure' }); + } @@ -154,9 +154,7 @@ class GrantCommand extends Command { module.exports = GrantCommand; -const filterInexact = (search) => { - return comp => comp.id.toLowerCase().includes(search) || +const filterInexact = (search) => (comp) => comp.id.toLowerCase().includes(search) || comp.resolveable.toLowerCase().includes(search) || - (comp.aliases && (comp.aliases.some(ali => `${comp.type}:${ali}`.toLowerCase().includes(search)) || - comp.aliases.some(ali => ali.toLowerCase().includes(search)))); -}; \ No newline at end of file + comp.aliases && (comp.aliases.some((ali) => `${comp.type}:${ali}`.toLowerCase().includes(search)) || + comp.aliases.some((ali) => ali.toLowerCase().includes(search))); \ No newline at end of file diff --git a/structure/client/components/commands/administration/Permissions.js b/structure/client/components/commands/administration/Permissions.js index de8556d..0bb01e5 100644 --- a/structure/client/components/commands/administration/Permissions.js +++ b/structure/client/components/commands/administration/Permissions.js @@ -1,40 +1,211 @@ +const { Role, MessageAttachment } = require('discord.js'); const { Command } = require('../../../../interfaces/'); const { stripIndents } = require('common-tags'); -class GrantCommand extends Command { +class PermissionsCommand extends Command { constructor(client) { super(client, { name: 'permissions', module: 'administration', - usage: '', + usage: "", aliases: [ 'perms', 'permission', 'perm' ], examples: [ - 'Server Moderators', - '@nolan#2887' + "list", + "Server Moderators", + "@nolan#2887" ], - memberPermissions: ['ADMINISTRATOR', 'MANAGE_SERVER'], + arguments: [ + { + name: 'user', + aliases: ['users'], + type: 'BOOLEAN', + types: ['VERBAL', 'FLAG'], + default: true + }, + { + name: 'raw', + aliases: ['json'], + type: 'BOOLEAN', + types: ['VERBAL', 'FLAG'], + default: true + } + ], + // memberPermissions: ['ADMINISTRATOR', 'MANAGE_GUILD'], guildOnly: true }); } - async execute(message) { + async execute(message, { params, args }) { - await message.guild.permissions(); + const permissions = await message.guild.permissions(); + if(args.json) { + await this._displayRaw(message, permissions); + return undefined; + } - message.respond(`\`\`\`js -${JSON.stringify(message.guild._permissions)}\`\`\``); + if(params.length === 0) { + await this._showPermissions(message, Boolean(args.user)); + return undefined; + } + + if(params[0] === 'list') { + await this._listAvailablePermissions(message); + return undefined; + } else { + const parameters = params.join(' '); + const resolveable = await this._parseResolveable(message, parameters); + const permission = permissions[resolveable?.id || parameters]; + if(!permission) { + await message.respond(message.format('C_PERMISSIONS_NOTFOUND'), { emoji: 'failure' }); + return undefined; + } + + const embed = { + author: { + name: `${resolveable?.user?.tag || resolveable?.tag || resolveable?.name || parameters} Permissions`, + // icon_url: resolveable?.user?.displayAvatarURL() || resolveable?.displayAvatarURL() || message.guild.iconURL() + }, + description: `${message.format('C_PERMISSIONS_GLOBAL', { permissions: permission.global.length > 0 ? this._displayNames(permission.global).map(p=>`\`${p}\``).join(', ') : "`N/A`" })} ${Object.values(permission.channels).length > 0 ? message.format('C_PERMISSIONS_GLOBALALT') : ''}`, + fields: [] + }; + + let update = false; + for(const [channelId, perms] of Object.entries(permission.channels)) { + const channel = this.client.resolver.resolveChannels(channelId, message.guild, true)[0]; + if(!channel) { + delete permission.channels[channelId]; + update = true; + continue; + } else { + if(embed.fields.length === 25) { + embed.description += `\n${message.format('C_PERMISSIONS_MAXFIELDS')}`; + break; + } + embed.fields.push({ + name: `#${channel.name}`, + value: this._displayNames(perms).map(p=>`\`${p}\``).join(', ') + }); + } + } + if(update) { + delete permissions._id + try { + await this.client.transactionHandler.send({ + provider: 'mongodb', + request: { + type: 'updateOne', + collection: 'permissions', + query: { + guildId: message.guild.id + }, + data: permissions + } + }); + } catch(error) { + this.client.logger.error(`Error removing channel permissions to ${message.guild.id}:\n${error.stack || error}`); + } + } + + return await message.embed(embed); + + } + + } + + async _showPermissions(message, user = false) { + const embed = { + author: { + name: message.format('C_PERMISSIONS_SHOWTITLE', { user }, true), + icon_url: message.guild.iconURL() + }, + description: message.format('C_PERMISSIONS_SHOWDESCRIPTION', { resolve: user ? 'user' : 'role' }), + fields: [] + }; + + const permissions = message.guild._permissions; + for(const [id, value] of Object.entries(permissions)) { + if(id === '_id' || id === 'guildId') continue; + const item = await this.client.resolver[user ? 'resolveMemberAndUser' : 'resolveRole'](id, message.guild); //dont kill me + if(item instanceof Role && user) continue; + else if(!user && !(item instanceof Role)) continue; + if(embed.fields.length === 25) { + embed.description += `\n${message.format('C_PERMISSIONS_MAXFIELDS')}`; + break; + } + const name = item?.user?.tag || item?.tag || item?.name || id; //please dont kill me again + const channels = Object.values(value.channels).length; + embed.fields.push({ + name, + value: stripIndents`${this._displayNames(value.global).map(n=>`\`${n}\``).join('\n')} + ${channels > 0 ? `\`..${channels} channel${channels === 1 ? '' : 's'}\`` : ''}` + }); + } + + return await message.embed(embed); + + } + + async _listAvailablePermissions(message) { + + const components = this.client.registry.components.filter(c=>(c.type === 'command' && c.grantable) || (c.type === 'module' && c.components.some(c=>c.type === 'command' && c.grantable))) + .sort((a, b) => a - b); + + return await message.respond(message.format('C_PERMISSIONS_LIST', { permissions: components.map(c => `\`${c.resolveable}\``).join(', ') }), { emoji: 'success' }); + + } + + async _parseResolveable(message, resolveable) { + let parsed = await this.client.resolver.resolveRoles(resolveable, message.guild); + if(!parsed) { + parsed = await this.client.resolver.resolveMembers(resolveable, message.guild); + if(!parsed) { + parsed = await this.client.resolver.resolveUsers(resolveable); + if(!parsed) return null; + } + } + return parsed[0]; + } + + async _displayRaw(message, permissions) { + + const string = JSON.stringify(permissions); + const attachment = new MessageAttachment(Buffer.from(string), "permissions.json"); + + return await message.respond(message.format('C_PERMISSIONS_JSON'), { emoji: 'success', attachments: [ attachment ] }) + + } + + _displayNames(permissions) { + + const modules = this.client.registry.components.filter(c=>c.type === 'module'); + let names = []; + + let temp = []; + for(const module of modules.values()) { + for(const component of module.components.filter(c=>c.type === 'command').values()) { + if(permissions.includes(component.resolveable)) { + temp.push(component.resolveable); + } + } + temp.length === module.components.filter(c=>c.type === 'command').size + ? names.push(module.resolveable) + : names = names.concat(temp); + temp = []; + } + + return names; } } -module.exports = GrantCommand; \ No newline at end of file +module.exports = PermissionsCommand; \ No newline at end of file diff --git a/structure/client/components/commands/administration/Revoke.js b/structure/client/components/commands/administration/Revoke.js index c520dca..fe00dc9 100644 --- a/structure/client/components/commands/administration/Revoke.js +++ b/structure/client/components/commands/administration/Revoke.js @@ -1,3 +1,6 @@ +const { User, GuildMember } = require('discord.js'); +const { stripIndents } = require('common-tags') + const { Command } = require('../../../../interfaces/'); class RevokeCommand extends Command { @@ -13,7 +16,7 @@ class RevokeCommand extends Command { "@nolan#2887 command:kick", "132620781791346688 moderation" ], - memberPermissions: ['ADMINISTRATOR'], + // memberPermissions: ['ADMINISTRATOR', 'MANAGE_GUILD'], showUsage: true, guildOnly: true, arguments: [ @@ -107,30 +110,10 @@ class RevokeCommand extends Command { } } - //check for deletion, saves DB space. - //NOTE: DO NOT REMOVE THE _PERMISSIONS VARIABLE. - console.log(permission.global.length, Object.keys(permission.channels).length); - if(permission.global.length === 0 - && Object.keys(permission.channels).length === 0) { - try { - const blah = await this.client.transactionHandler.send({ - provider: 'mongodb', - request: { - type: 'remove', - collection: 'permissions', - query: { - guildId: message.guild.id - } - } - }); - console.log(blah); - } catch(error) { - this.client.logger.warn(`Attempted to delete collection permissions:${message.guild.id} but failed.`); - } - } - delete _permissions._id; //some bullshit.. + //check for deletion, saves DB space. + //NOTE: DO NOT REMOVE THE _PERMISSIONS VARIABLE. if(permission.global.length === 0 && Object.keys(permission.channels).length === 0) { try { await this.client.transactionHandler.send({ @@ -165,8 +148,19 @@ class RevokeCommand extends Command { } } - + const name = resolveable instanceof GuildMember + ? resolveable?.user?.tag + : resolveable instanceof User + ? resolveable?.tag + : resolveable?.name; + if(removed.length > 0) { + await message.respond(stripIndents`${message.format('C_REVOKE_SUCCESS', { removed: removed.map(r=>`\`${r}\``).join(', '), resolveable: name || parsed })}${args.channel ? ` ${message.format('C_REVOKE_SUCCESSALT', { channels: args.channel.value.map(c=>`\`#${c.name}\``).join(', ')})}` : '.'} + ${removed.length < parsed.length ? message.format('C_REVOKE_SUCCESSFAILED'): ''}`, { emoji: 'success' }); + } else { + await message.respond(message.format('C_REVOKE_FAILED', { resolveable: name || parsed }), { emoji: 'failure' }); + } + } async _parseResolveable(message, resolveable) { diff --git a/structure/client/components/commands/developer/Component.js b/structure/client/components/commands/developer/Component.js index 4a4f5ca..80af4e1 100644 --- a/structure/client/components/commands/developer/Component.js +++ b/structure/client/components/commands/developer/Component.js @@ -24,7 +24,7 @@ class ComponentCommand extends Command { // description: "Reloads the language library", // default: 'all' // } - ], + ] }); this.client = client; @@ -36,21 +36,16 @@ class ComponentCommand extends Command { // const method = params.shift().toLowerCase(); - let response; + let response = null; - if (method === 'reload' || method === 'r') - response = this._handleReload(params); - else if (method === 'enable') - response = this._handleDisableEnable(params.shift().toLowerCase(), true); - else if (method === 'disable') - response = this._handleDisableEnable(params.shift().toLowerCase(), false); - else if (method === 'load') - response = this._handleLoadUnload(params.shift().toLowerCase(), true); - else if (method === 'unload') - response = this._handleLoadUnload(params.shift().toLowerCase(), false); - else return await message.respond('Invalid method. Can only be `reload`, `enable`, `disable`, `load`, `unload`', 'failure'); + if (method === 'reload' || method === 'r') response = this._handleReload(params); + else if (method === 'enable') response = this._handleDisableEnable(params.shift().toLowerCase(), true); + else if (method === 'disable') response = this._handleDisableEnable(params.shift().toLowerCase(), false); + else if (method === 'load') response = this._handleLoadUnload(params.shift().toLowerCase(), true); + else if (method === 'unload') response = this._handleLoadUnload(params.shift().toLowerCase(), false); + else return message.respond('Invalid method. Can only be `reload`, `enable`, `disable`, `load`, `unload`', 'failure'); - return await message.respond(response.msg, { emoji: response.error ? 'failure' : 'success' }); + return message.respond(response.msg, { emoji: response.error ? 'failure' : 'success' }); } @@ -64,38 +59,36 @@ class ComponentCommand extends Command { if (value === 'all') { this.client.localeLoader.loadLanguages(); return { msg: 'Reloaded all languages' }; - } else { - try { - this.client.localeLoader.loadLanguage(value); - return { msg: `Reloaded locale \`${value}\`` }; - } catch (err) { - return { error: true, msg: err.message }; - } - + } + try { + this.client.localeLoader.loadLanguage(value); + return { msg: `Reloaded locale \`${value}\`` }; + } catch (err) { + return { error: true, msg: err.message }; } + } else { if (name === 'all') { const errors = []; - const components = this.client.registry.components; - for (let component of components.values()) { + const { components } = this.client.registry; + for (const component of components.values()) { const result = component.reload(); if (result.error) errors.push(`Component ${component.id} errored while reloading with code \`${result.code}\``); } if (errors.length) return { error: true, msg: `The following errors occurred during reload:\n${errors.join('\n')}` }; return { msg: `Successfully reloaded all components` }; - } else { + } - const component = this.client.registry.components.get(name); - if (!component) return { error: true, msg: `Component ${name} doesn't exist.` }; - const result = component.reload(); - if (result.error) return { error: true, msg: `Component ${name} errored while reloading with code \`${result.code}\`` }; - else return { msg: `Successfully reloaded ${name}` }; - - } + const component = this.client.registry.components.get(name); + if (!component) return { error: true, msg: `Component ${name} doesn't exist.` }; + const result = component.reload(); + if (result.error) return { error: true, msg: `Component ${name} errored while reloading with code \`${result.code}\`` }; + return { msg: `Successfully reloaded ${name}` }; + } @@ -104,19 +97,19 @@ class ComponentCommand extends Command { _handleDisableEnable(name, enable) { const component = this.client.registry.components.get(name); - let result; + let result = null; if (!component) return { error: true, msg: `Component ${name} doesn't exist.` }; if (enable) result = component.enable(); else result = component.disable(); if (result.error) return { error: true, msg: `Cannot ${enable ? 'enable' : 'disable'} ${name} due to ${result.code}` }; - else return { msg: `Successfully ${enable ? 'enabled' : 'disabled'} component ${name}` }; + return { msg: `Successfully ${enable ? 'enabled' : 'disabled'} component ${name}` }; } _handleLoadUnload(name, load) { - let result; + let result = null; if (load) { const directory = path.join(process.cwd(), 'structure/client/components', name); try { @@ -135,14 +128,14 @@ class ComponentCommand extends Command { if (!result) return { error: true, msg: `Failed to load component ${name}, see console.` }; return { msg: `Successfully loaded component: ${component.resolveable}` }; - } else { - const component = this.client.registry.components.filter(filterInexact(name)).first(); - if (!component) return { error: true, msg: `Component ${name} doesn't exist.` }; - result = component.unload(); - } + } + const component = this.client.registry.components.filter(filterInexact(name)).first(); //eslint-disable-line no-use-before-define + if (!component) return { error: true, msg: `Component ${name} doesn't exist.` }; + result = component.unload(); + if (result.error) return { error: true, msg: `Cannot ${load ? 'load' : 'unload'} ${name} due to ${result.code}` }; - else return { msg: `Successfully ${load ? 'loaded' : 'unloaded'} component ${name}` }; + return { msg: `Successfully ${load ? 'loaded' : 'unloaded'} component ${name}` }; } @@ -150,9 +143,7 @@ class ComponentCommand extends Command { module.exports = ComponentCommand; -const filterInexact = (search) => { - return comp => comp.id.toLowerCase().includes(search) || +const filterInexact = (search) => (comp) => comp.id.toLowerCase().includes(search) || comp.resolveable.toLowerCase().includes(search) || - (comp.aliases && (comp.aliases.some(ali => `${comp.type}:${ali}`.toLowerCase().includes(search)) || - comp.aliases.some(ali => ali.toLowerCase().includes(search)))); -}; \ No newline at end of file + comp.aliases && (comp.aliases.some((ali) => `${comp.type}:${ali}`.toLowerCase().includes(search)) || + comp.aliases.some((ali) => ali.toLowerCase().includes(search))); \ No newline at end of file diff --git a/structure/client/components/commands/developer/Evaluate.js b/structure/client/components/commands/developer/Evaluate.js index abdf517..0718a69 100644 --- a/structure/client/components/commands/developer/Evaluate.js +++ b/structure/client/components/commands/developer/Evaluate.js @@ -1,12 +1,10 @@ const { inspect } = require('util'); const { username } = require('os').userInfo(); -let _storage = null; //eslint-disable-line no-unused-vars +let _storage = null; //eslint-disable-line const { Command } = require('../../../../interfaces/'); - - class Evaluate extends Command { constructor(client) { @@ -44,21 +42,21 @@ class Evaluate extends Command { params = params.join(' '); try { - let evaled = eval(params); + let evaled = eval(params); //eslint-disable-line no-eval if(evaled instanceof Promise) await evaled; if(typeof evaled !== 'string') evaled = inspect(evaled); evaled = evaled - .replace(new RegExp(this.client.token, 'g'), '') - .replace(new RegExp(username, 'g'), ''); + .replace(new RegExp(this.client.token, 'gu'), '') + .replace(new RegExp(username, 'gu'), ''); if(args.log) this.client.logger.debug(`[${message.author.tag}] Evaluation Result: ${evaled}`); if (evaled.length > 1850) { - console.log(evaled); evaled = `${evaled.substring(0, 1850)}...`; } - await message.respond(`Evaluation was successful.\`\`\`js\n${evaled}\`\`\``, + await message.respond( + `Evaluation was successful.\`\`\`js\n${evaled}\`\`\``, { emoji: 'success' } ); @@ -70,7 +68,8 @@ class Evaluate extends Command { if(args.log) this.client.logger.debug(`[${message.author.tag}] Evaluation Failed: ${msg}`); if(msg.length > 2000) msg = `${msg.substring(0, 1900)}...`; - await message.respond(`Evaluation failed.\`\`\`js\n${msg}\`\`\``, + await message.respond( + `Evaluation failed.\`\`\`js\n${msg}\`\`\``, { emoji: 'failure' } ); diff --git a/structure/client/components/commands/information/Commands.js b/structure/client/components/commands/information/Commands.js index 76a1a3b..68999b3 100644 --- a/structure/client/components/commands/information/Commands.js +++ b/structure/client/components/commands/information/Commands.js @@ -29,8 +29,7 @@ class CommandsCommand extends Command { async execute(message, { params }) { - if (!params.length) // list all commands - return this._listCommands(message); + if (!params.length) return this._listCommands(message); params = params.join(' '); @@ -38,23 +37,23 @@ class CommandsCommand extends Command { if(!mod) { const [ command ] = this.client.resolver.components(params, 'command', false); if (!command) return message.format('C_COMMAND_INVALID'); - return await message._showUsage(command); + return message._showUsage(command); } //list module's commands - const commands = mod.components.filter(c=>c.type === 'command'); + const commands = mod.components.filter((c) => c.type === 'command'); let text = ''; - for(let command of commands.values()) { + for(const command of commands.values()) { text += command.disabled ? `~~${command.name}~~\n` : `${command.name}`; //TODO: Denote disabled commands somehow } const embed = { author: { name: message.format('C_COMMANDS_TITLE'), - icon_url: this.client.user.avatarURL() + icon_url: this.client.user.avatarURL() //eslint-disable-line camelcase }, - description: message.format('C_COMMANDS') + '\n' + message.format('C_COMMANDS_TEMPLATE', { mod: mod.name, text }), + description: `${message.format('C_COMMANDS')}\n${message.format('C_COMMANDS_TEMPLATE', { mod: mod.name, text })}`, footer: { text: message.format('C_COMMANDS_FOOTER') } @@ -66,24 +65,24 @@ class CommandsCommand extends Command { _listCommands(message) { - let fields = []; + const fields = []; const sortedModules = this.client.registry.components - .filter(c => c.type === 'module') + .filter((c) => c.type === 'module') .sort((a, b) => { - const filter = c => c.type === 'command'; + const filter = (c) => c.type === 'command'; return b.components.filter(filter) - a.components.filter(filter); }); for (const mod of sortedModules.values()) { - let field = { + const field = { name: mod.id, value: '', inline: true }; for (const command of mod.components.values()) { - if (command.type !== 'command' - || (command.restricted && !this.client._options.bot.owners.includes(message.author.id))) continue; + if (command.type !== 'command' || + command.restricted && !this.client._options.bot.owners.includes(message.author.id)) continue; field.value += `${command.name}\n`; } if (field.value) fields.push(field); @@ -92,7 +91,7 @@ class CommandsCommand extends Command { const embed = { author: { name: message.format('C_COMMANDS_TITLE'), - icon_url: this.client.user.avatarURL() + icon_url: this.client.user.avatarURL() //eslint-disable-line camelcase }, description: message.format('C_COMMANDS'), fields, diff --git a/structure/client/components/commands/information/Help.js b/structure/client/components/commands/information/Help.js index b1b89d8..9c359d8 100644 --- a/structure/client/components/commands/information/Help.js +++ b/structure/client/components/commands/information/Help.js @@ -15,21 +15,19 @@ class HelpCommand extends Command { async execute(message, { params }) { - if (!params.length) - return await message.embed({ - description: message.format('C_HELP') - }); + if (!params.length) return message.embed({ + description: message.format('C_HELP') + }); const [ key ] = params; let [ result ] = this.client.resolver.components(key, 'command'); if (!result) [ result ] = this.client.resolver.components(key, 'setting'); - if (!result) - return await message.embed({ - description: message.format('C_HELP_404', { component: key }) - }); + if (!result) return message.embed({ + description: message.format('C_HELP_404', { component: key }) + }); //let index = `${result.type.slice(0, 1).toUpperCase()}_${result.name.toUpperCase()}_HELP`; - return await message.embed({ + return message.embed({ description: message.format('C_HELP_TEMPLATE', { desc: message.format(result.description), component: result.name.toUpperCase(), diff --git a/structure/client/components/commands/utility/Arguments.js b/structure/client/components/commands/utility/Arguments.js index 559359a..a504785 100644 --- a/structure/client/components/commands/utility/Arguments.js +++ b/structure/client/components/commands/utility/Arguments.js @@ -50,7 +50,8 @@ class PingCommand extends Command { } async execute(message, { args, params }) { - await message.respond(stripIndents`**arguments:** ${Object.values(args).map(a=>`${a.name}: ${a.value}`).join(' | ')} + await message.respond(stripIndents`**arguments:** ${Object.values(args).map((a) => `${a.name}: ${a.value}`) + .join(' | ')} **words:** ${params.join(', ')}`, { emoji: 'success' }); } diff --git a/structure/client/components/commands/utility/Avatar.js b/structure/client/components/commands/utility/Avatar.js index 9477e01..ec73c82 100644 --- a/structure/client/components/commands/utility/Avatar.js +++ b/structure/client/components/commands/utility/Avatar.js @@ -41,7 +41,7 @@ class AvatarCommand extends Command { if (!user) user = message.author; let avatar = null; try { - avatar = user.displayAvatarURL({ format: args.format?.value || 'webp', size: args.size?.value || 512, dynamic: true }); + avatar = user.displayAvatarURL({ format: args.format?.value || 'webp', size: args.size?.value || 128, dynamic: true }); } catch(error) { message.respond(message.format('C_AVATAR_FORMATERROR'), { emoji: 'failure' }); return undefined; diff --git a/structure/client/components/commands/utility/Lookup.js b/structure/client/components/commands/utility/Lookup.js new file mode 100644 index 0000000..ecc2e9a --- /dev/null +++ b/structure/client/components/commands/utility/Lookup.js @@ -0,0 +1,87 @@ +const fetch = require('node-fetch'); + +const { Command } = require('../../../../interfaces/'); + +//Apparently hit ratelimits pretty damn quick and doesn't expire often. +class LookupCommand extends Command { + + constructor(client) { + + super(client, { + name: 'lookup', + module: 'utility', + usage: "", + showUsage: true, + parameterType: 'PLAIN', + examples: [ + "SvJgtEj", + "discord.gg/SvJgtEj" + ], + restricted: true, //For now + // throttling: { + // usages: 1, + // duration: 30 + // } + }); + + this.client = client; + + + } + + async execute(message, { params }) { + + + const invite = await this.client.fetchInvite(params) + .catch((error) => { + console.error(error); + }); + + let fields = []; + + if(invite.inviter) { + fields.push({ + name: "Inviter", + value: `${invite.inviter.username}#${invite.inviter.discriminator} \`(${invite.inviter.id})\``, + inline: true + }); + } + + if(invite.channel) { + fields.push({ + name: "Default Channel", + value: `#${invite.channel.name} \`(${invite.channel.id})\``, + inline: true + }); + } + + if(invite?.guild?.features.length > 0) { + fields.push({ + name: "Features", + value: invite.guild.features.map(f=>`\`${f}\``).join(', '), + inline: true, + }); + } + + const embed = { + author: { + name: `${invite.guild.name} (${invite.guild.id})`, + icon_url: invite.guild.iconURL() + }, + thumbnail: { + url: invite.guild.splash ? invite.guild.splashURL() : null + }, + description: invite.guild.description || null, + fields, + footer: { + text: ` https://discord.gg/${invite.code}` + } + }; + + return message.embed(embed); + + } + +} + +module.exports = LookupCommand; \ No newline at end of file diff --git a/structure/client/components/commands/utility/Ping.js b/structure/client/components/commands/utility/Ping.js index 88cdba2..a9ee54c 100644 --- a/structure/client/components/commands/utility/Ping.js +++ b/structure/client/components/commands/utility/Ping.js @@ -16,7 +16,9 @@ class PingCommand extends Command { async execute(message) { const ping = this.client.ws.ping.toFixed(0); - return message.respond(`${message.format('C_PING_RESPONSE')} \`${ping}ms\``, { emoji: 'success' }); + const number = (ping/40).toFixed(0); + const repeat = number > 1 ? number : 1; + return message.respond(`P${'o'.repeat(repeat)}ng! \`${ping}ms\``, { emoji: 'success' }); } } diff --git a/structure/client/components/commands/utility/Settings.js b/structure/client/components/commands/utility/Settings.js index b6dd932..cfce326 100644 --- a/structure/client/components/commands/utility/Settings.js +++ b/structure/client/components/commands/utility/Settings.js @@ -2,18 +2,18 @@ const { Command } = require('../../../../interfaces/'); const { stripIndents } = require('common-tags'); -class SettingCommand extends Command { +class SettingsCommand extends Command { constructor(client) { super(client, { - name: 'setting', + name: 'settings', module: 'utility', - description: "Sets user or guild settings.", aliases: [ - 'settings', + 'setting', 'set' ], + usage: "[list|reset|setting-name] ", arguments: [ { name: 'user', @@ -45,10 +45,7 @@ class SettingCommand extends Command { this._listSettings(message, type, Boolean(args.all)); return undefined; } else if(target === 'reset') { - if(message.channel.permissionsFor(message.member).missing('ADMINISTRATOR').length > 0) { - await message.respond(message.format('C_SETTINGS_ADMINISTRATORERROR', { type: type.toLowerCase() }), { emoji: 'failure' }); - return undefined; - } + if(type === 'GUILD' && !this._checkAdministrator(message)) return undefined; //does not have admin const prompt = await message.prompt(message.format('C_SETTINGS_RESET', { type: type.toLowerCase() }), { emoji: 'warning' }); return await this._handleReset(prompt, message, type); } else if(target === 'walkthrough') { @@ -64,7 +61,7 @@ class SettingCommand extends Command { } //Setting permission handling - if(setting.clientPermissions.length > 0) { + if(setting.clientPermissions.length > 0 && setting.resolve === 'GUILD') { const missing = message.channel.permissionsFor(message.guild.me).missing(setting.clientPermissions); if(missing.length > 0) { await message.respond(message.format('C_SETTINGS_CLIENTPERMISSIONERROR', { setting: setting.moduleResolveable, missing: missing.join(', ')}), { emoji: 'failure' }); @@ -72,12 +69,20 @@ class SettingCommand extends Command { } } - if(message.channel.permissionsFor(message.member).missing('ADMINISTRATOR').length > 0 && setting.resolve === 'GUILD') { - await message.respond(message.format('C_SETTINGS_ADMINISTRATORERROR', { type: type.toLowerCase() }), { emoji: 'failure' }); + setting._caller = target; + if(setting.resolve === 'GUILD' && !this._checkAdministrator(message)) return undefined; //does not have admin + + const parameters = params.splice(1); + if(!parameters || parameters.length === 0) { + await this._showSetting(message, setting) return undefined; } - const response = await setting.handle(message, params.splice(1)); + if(parameters.join(' ').toLowerCase() === 'reset') { + return await setting._handleReset(message); + } + + const response = await setting.handle(message, parameters); message.respond(response.msg, { emoji: response.error ? 'failure' : 'success' }); } @@ -85,11 +90,11 @@ class SettingCommand extends Command { _listSettings(message, type, all) { if(!message.guild && type === 'GUILD') type = 'USER'; - const prefix = message.guild?.prefix || this.client._options.bot.prefix; + const prefix = message.guild?.prefix || this.client._options.bot.prefix; //eslint-disable-line parsing-error let fields = []; const sorted = this.client.registry.components - .filter(c=>c.type === 'module') + .filter(c=>c.type == 'module') .sort((a, b) => { const filter = c=>c.type === 'setting'; return b.components.filter(filter) - a.components.filter(filter); @@ -126,6 +131,35 @@ class SettingCommand extends Command { } + async _showSetting(message, setting) { + + const prefix = message.guild.prefix; + let description = stripIndents`\`${prefix}setting ${setting.name}${setting.usage ? ` ${setting.usage}` : ''}\` ${setting.resolve === 'GUILD' ? ' *(guild-only)*' : '*(user-only)*'} + ${message.format(setting.description)}`; + + if(setting.examples.length > 0) { + description += `\n\n**${message.format('GENERAL_EXAMPLES')}**`; + for(let example of setting.examples) { + description += `\n\`${prefix}setting ${example}\``; + } + } + + let fields = setting.fields(message.guild); + if(fields instanceof Promise) fields = await fields; + + const embed = { + author: { + name: `${setting.name} (${setting.module.resolveable})`, + icon_url: this.client.user.displayAvatarURL() + }, + description, + fields + } + + return await message.embed(embed); + + } + async _handleReset(prompt, message, type) { if(!prompt) return; @@ -135,13 +169,23 @@ class SettingCommand extends Command { if(!bool) return message.respond(message.format('C_SETTINGS_RESETABORT'), { emoji: 'success' }); type === 'USER' - ? await message.author._delete() - : await message.guild._delete('guilds'); + ? await message.author._deleteSettings() + : await message.guild._deleteSettings(); return message.respond(message.format('C_SETTINGS_RESETSUCCESS', { type: type.toLowerCase() }), { emoji: 'success' }) } + _checkAdministrator(message) { + const missing = message.channel.permissionsFor(message.member).missing(['ADMINISTRATOR']); + if(missing > 0) { + message.respond(message.format('C_SETTINGS_ADMINISTRATORERROR'), { emoji: 'failure' }) + return false; + } else { + return true; + } + } + } -module.exports = SettingCommand; +module.exports = SettingsCommand; diff --git a/structure/client/components/commands/utility/User.js b/structure/client/components/commands/utility/User.js index fa65a31..a6bfdd9 100644 --- a/structure/client/components/commands/utility/User.js +++ b/structure/client/components/commands/utility/User.js @@ -27,19 +27,17 @@ class UserCommand extends Command { async execute(message, { params, args }) { - let response; + let response = null; if(args.search && args.search.value.length > 1) { - let key = args.search.value, - count = 0; + const key = args.search.value; + let count = 0; - let members = message.guild.members.cache.filter(m => { - return (m.nickname && (m.nickname.toLowerCase().includes(key) || (similarity(m.nickname.toLowerCase(), key) > 0.75 && Math.abs(m.nickname.length - key.length) < 3) )) || - m.user.username.toLowerCase().includes(key) || (similarity(m.user.username.toLowerCase(), key) > 0.75 && Math.abs(m.user.username.length - key.length) < 3); - }); + const members = message.guild.members.cache.filter((m) => m.nickname && (m.nickname.toLowerCase().includes(key) || similarity(m.nickname.toLowerCase(), key) > 0.75 && Math.abs(m.nickname.length - key.length) < 3) || + m.user.username.toLowerCase().includes(key) || similarity(m.user.username.toLowerCase(), key) > 0.75 && Math.abs(m.user.username.length - key.length) < 3); - for(let [ id, member ] of members) { - response += `${member.user.tag} ${member.nickname ? `- ${member.nickname}` : '' } [${id}]\n`; + for(const [ id, member ] of members) { + response += `${member.user.tag} ${member.nickname ? `- ${member.nickname}` : ''} [${id}]\n`; count++; if(response.length > 1900) break; } @@ -47,7 +45,7 @@ class UserCommand extends Command { response = { description: response, title: message.format('C_USER_SEARCH_TITLE', { - key: key + key }), color: 0x0088cc, footer: { @@ -60,13 +58,13 @@ class UserCommand extends Command { } else { - let user; + let user = null; if (params.length > 0) { - user = await this.client.resolver.resolveUser(params.join(' ')).catch(console.error); + user = await this.client.resolver.resolveUser(params.join(' ')); if (!user) return message.respond('No user found.'); } else user = message.author; - let member = await message.guild.members.fetch(user.id).catch(console.error); + const member = await message.guild.members.fetch(user.id); response = message.format('C_USER', { nickname: member && member.nickname ? member.nickname : 'N/A', @@ -77,7 +75,8 @@ class UserCommand extends Command { activity: user.presence.activities.length > 0 ? user.presence.activities[0].name : 'N/A', serverActivity: member && member.lastMessage ? member.lastMessage.createdAt.toDateString() : 'N/A', globalActivity: user.lastMessage ? user.lastMessage.createdAt.toDateString() : 'N/A', - roles: member ? member.roles.cache.filter(r => r.name !== '@everyone').map(r => r.name).join(', ') : 'N/A' + roles: member ? member.roles.cache.filter((r) => r.name !== '@everyone').map((r) => r.name) + .join(', ') : 'N/A' }); response = { diff --git a/structure/client/components/inhibitors/ClientPermissions.js b/structure/client/components/inhibitors/ClientPermissions.js index 9d6c0e3..05052ff 100644 --- a/structure/client/components/inhibitors/ClientPermissions.js +++ b/structure/client/components/inhibitors/ClientPermissions.js @@ -19,9 +19,8 @@ class ClientPermissions extends Inhibitor { if(missing.length > 0) { return super._fail(stripIndents`The command **${command.resolveable}** requires the bot to have permissions to use. *Missing: ${missing.join(', ')}*`); - } else { - return super._succeed(); - } + } + return super._succeed(); } } diff --git a/structure/client/components/inhibitors/Disabled.js b/structure/client/components/inhibitors/Disabled.js index f5a07f0..53a09fd 100644 --- a/structure/client/components/inhibitors/Disabled.js +++ b/structure/client/components/inhibitors/Disabled.js @@ -16,10 +16,10 @@ class Disabled extends Inhibitor { execute(message, command) { if(command.disabled && message.author.id !== this.client._options.bot.owner) return super._fail(`The command **${command.resolveable}** is currently disabled.`); - else return super._succeed(); + return super._succeed(); } } -module.exports = Disabled; +module.exports = Disabled; \ No newline at end of file diff --git a/structure/client/components/inhibitors/GuildOnly.js b/structure/client/components/inhibitors/GuildOnly.js index 87069ca..535b93e 100644 --- a/structure/client/components/inhibitors/GuildOnly.js +++ b/structure/client/components/inhibitors/GuildOnly.js @@ -15,9 +15,9 @@ class GuildOnly extends Inhibitor { execute(message, command) { if(command.guildOnly && !message.guild) { return super._fail(`The command **${command.moduleResolveable}** can only be run in servers.`); - } else { - return super._succeed(); - } + } + return super._succeed(); + } } diff --git a/structure/client/components/inhibitors/MemberPermissions.js b/structure/client/components/inhibitors/MemberPermissions.js index e177a08..7a1c1ec 100644 --- a/structure/client/components/inhibitors/MemberPermissions.js +++ b/structure/client/components/inhibitors/MemberPermissions.js @@ -19,9 +19,9 @@ class MemberPermissions extends Inhibitor { if(missing.length > 0) { return super._fail(stripIndents`The command **${command.resolveable}** requires you to have permissions to use. *Missing: ${missing.join(', ')}*`); - } else { - return super._succeed(); - } + } + return super._succeed(); + } } diff --git a/structure/client/components/inhibitors/Restricted.js b/structure/client/components/inhibitors/Restricted.js index 702fd01..3019240 100644 --- a/structure/client/components/inhibitors/Restricted.js +++ b/structure/client/components/inhibitors/Restricted.js @@ -15,9 +15,9 @@ class Restricted extends Inhibitor { execute(message, command) { if(command.restricted && !this.client._options.bot.owners.includes(message.author.id)) { return super._fail(`The command **${command.moduleResolveable}** can only be run by developers.`); - } else { - return super._succeed(); - } + } + return super._succeed(); + } } diff --git a/structure/client/components/inhibitors/Throttle.js b/structure/client/components/inhibitors/Throttle.js index f52ae18..d67bfe3 100644 --- a/structure/client/components/inhibitors/Throttle.js +++ b/structure/client/components/inhibitors/Throttle.js @@ -19,7 +19,7 @@ class Throttle extends Inhibitor { if(throttle) { throttle.usages++; if(throttle.usages > command.throttling.usages) { - const remaining = (throttle.start + (command.throttling.duration*1000) - Date.now()) / 1000; + const remaining = (throttle.start + (command.throttling.duration*1000) - Date.now()) / 1000; //eslint-disable-line no-extra-parens return super._fail(stripIndents`The command **${command.moduleResolveable}** is currently throttled. *You can use this command again in* *\`${remaining.toFixed(2)}\`* *seconds.*`); } diff --git a/structure/client/components/observers/CommandHandler.js b/structure/client/components/observers/CommandHandler.js index c83b76d..f69bbd5 100644 --- a/structure/client/components/observers/CommandHandler.js +++ b/structure/client/components/observers/CommandHandler.js @@ -93,9 +93,16 @@ class CommandHandler extends Observer { const inhibitor = await this._handleInhibitors(message); if(inhibitor.error) return this._handleError({ type: 'inhibitor', info: inhibitor , message }); - const { parsedArguments, newArgs } = await this._parseArguments(message, args); - message.parameters = newArgs; - message.args = parsedArguments; + const response = await this._parseArguments(args, message.command.arguments, message.guild); + if(response.error) { + return this._handleError({ + ...response, + message + }); + } else { + message.parameters = message.command.parameterType === 'PLAIN' ? response.newArgs.join(' ') : response.newArgs; + message.args = response.parsedArguments; + } const resolved = await message.resolve(); if(resolved.error) { @@ -147,13 +154,11 @@ class CommandHandler extends Observer { } - async _parseArguments(message, args = []) { + async _parseArguments(args = [], passedArguments = [], guild = null) { //Only need guild parameter if using a resolver type in your arguments e.g. channel, user, member, role args = this._getWords(args.join(' ')).map(w=>w[0]); - const command = message.command; - - const { shortFlags, longFlags, keys } = await this._createFlags(command.arguments); + const { shortFlags, longFlags, keys } = await this._createFlags(passedArguments); const regex = new RegExp(`([0-9]*)(${Object.keys(longFlags).map(k=>escapeRegex(k)).join('|')})([0-9]*)`, 'i'); let parsedArguments = []; @@ -178,7 +183,12 @@ class CommandHandler extends Observer { continue; } if(currentArgument.required && !args[i+1]) { - return this._handleError({ type: 'argument', info: { argument: currentArgument, word, missing: true }, message }); + return { + error: true, + type: 'argument', + info: { argument: currentArgument, word, missing: true } + } + // return this._handleError({ type: 'argument', info: { argument: currentArgument, word, missing: true }, message }); } continue; } else if((one === '-' && two === '-') || (one === '—')) { //Handling for "long dash" on mobile phones x_x @@ -197,7 +207,12 @@ class CommandHandler extends Observer { continue; } if(currentArgument.required && !args[i+1]) { - return this._handleError({ type: 'argument', info: { argument: currentArgument, word, missing: true }, message }); + return { + error: true, + type: 'argument', + info: { argument: currentArgument, word, missing: true } + } + // return this._handleError({ type: 'argument', info: { argument: currentArgument, word, missing: true }, message }); } continue; } else { @@ -206,7 +221,7 @@ class CommandHandler extends Observer { currentArgument = longFlags[match[2]]; if(params.length > 0 && ['INTEGER', 'FLOAT'].includes(currentArgument.type)) { //15 pts const lastItem = params[params.length-1]; - const beforeError = await this._handleTypeParsing(currentArgument, lastItem, message.guild); + const beforeError = await this._handleTypeParsing(currentArgument, lastItem, guild); if(beforeError) { continue; } else { @@ -218,11 +233,16 @@ class CommandHandler extends Observer { } } const value = match[1] || match[3]; - const error = await this._handleTypeParsing(currentArgument, value, message.guild); + const error = await this._handleTypeParsing(currentArgument, value, guild); if(value) { if(error) { if(currentArgument.required) { - return this._handleError({ type: 'argument', info: { argument: currentArgument, word, missing: false }, message }); + return { + error: true, + type: 'argument', + info: { argument: currentArgument, word, missing: false } + } + // return this._handleError({ type: 'argument', info: { argument: currentArgument, word, missing: false }, message }); } else { parsedArguments.push(currentArgument); currentArgument = null; @@ -239,7 +259,7 @@ class CommandHandler extends Observer { } } else { if(currentArgument) { - const error = await this._handleTypeParsing(currentArgument, word, message.guild); + const error = await this._handleTypeParsing(currentArgument, word, guild); if(error) { if(currentArgument.default !== null) { params.push(word); @@ -251,7 +271,12 @@ class CommandHandler extends Observer { if(currentArgument.required) { if(currentArgument.infinite) { if(currentArgument.value.length === 0) { - return this._handleError({ type: 'argument', info: { argument: currentArgument, word, missing: false }, message }); + return { + error: true, + type: 'argument', + info: { argument: currentArgument, word, missing: false } + } + // return this._handleError({ type: 'argument', info: { argument: currentArgument, word, missing: false }, message }); } else { parsedArguments.push(currentArgument); currentArgument = null; @@ -259,7 +284,12 @@ class CommandHandler extends Observer { continue; } } else { - return this._handleError({ type: 'argument', info: { argument: currentArgument, word, missing: false }, message }); + return { + error: true, + type: 'argument', + info: { argument: currentArgument, word, missing: false } + } + // return this._handleError({ type: 'argument', info: { argument: currentArgument, word, missing: false }, message }); } } else { currentArgument = null; @@ -275,7 +305,7 @@ class CommandHandler extends Observer { } else { const lastArgument = parsedArguments[parsedArguments.length-1]; if(lastArgument && lastArgument.type === 'BOOLEAN' && lastArgument.value === lastArgument.default) { - const error = await this._handleTypeParsing(lastArgument, word, message.guild); + const error = await this._handleTypeParsing(lastArgument, word, guild); if(!error) continue; } params.push(word); @@ -289,7 +319,11 @@ class CommandHandler extends Observer { const blah = parsedArguments.filter(a=>a.requiredArgument && !a.value); const missingArgument = blah[0]; - if(missingArgument) return this._handleError({ type: 'argument', info: { argument: missingArgument, missing: true }, message }); + if(missingArgument) return { + error: true, + type: 'argument', + info: { argument: missingArgument, missing: true } + }; //fucking kill me @@ -451,7 +485,7 @@ class CommandHandler extends Observer { } - static async parseType(type, str, resolver, guild) { //this is in the class for a reason, will soon reference to a user resolver etc. + static async parseType(type, str, resolver, guild) { //INTEGER AND FLOAT ARE SAME FUNCTION @@ -462,12 +496,12 @@ class CommandHandler extends Observer { INTEGER: (str) => { const int = parseInt(str); if(Math.round(int) !== int) return { error: true }; - if(Number.isNaN(int)) return { error: true }; + if(isNaN(int)) return { error: true }; return { error: false, value: int }; }, FLOAT: (str) => { const float = parseInt(str); - if(Number.isNaN(float)) return { error: true }; + if(isNaN(float)) return { error: true }; return { error: false, value: float }; }, BOOLEAN: (str) => { diff --git a/structure/client/components/settings/moderation/Mute.js b/structure/client/components/settings/moderation/Mute.js new file mode 100644 index 0000000..ea1dfcf --- /dev/null +++ b/structure/client/components/settings/moderation/Mute.js @@ -0,0 +1,186 @@ +const { Setting } = require('../../../../interfaces/'); +const maxCharacters = 98; + +class MuteSetting extends Setting { + + constructor(client) { + + super(client, { + name: 'mute', + module: 'moderation', + aliases: [ + 'muted', + 'muteType', + 'mutedType', + 'muteRole', + 'mutedRole', + 'createMute' + ], + arguments: [ + { + name: 'create', + type: 'BOOLEAN', + types: ['VERBAL', 'FLAG'], + default: true + }, + { + name: 'type', + type: 'BOOLEAN', + types: ['VERBAL', 'FLAG'], + default: true + } + ], + usage: "[test] ", + examples: [ + 'muterole Muted', + 'mutetype 1', + 'createmute galacticbot-mute' + ], + resolve: 'GUILD', + default: { + mute: { + role: null, + type: 0 + } + } + }); + + this.client = client; + + } + + async handle(message, args) { + + const { params, parsedArguments } = await this._parseArguments(args, message.guild); + args = params; + + if(['mutetype', 'mutedtype'].includes(this._caller) || parsedArguments.type) { + const num = args[0].toLowerCase() === 'type' ? args[1] || 0 : args[0]; + const number = parseInt(num); + if(isNaN(number)) return { + msg: message.format('S_MUTE_TYPENAN'), + error: true + + }; + + if(![0, 1, 2].includes(number)) return { + msg: message.format('S_MUTE_TYPEINVALID'), + error: true + }; + + await message.guild._updateSettings({ + [this.index]: { + ...message.guild._settings[this.index], + type: number + } + }); + return { + msg: `${message.format('S_MUTE_TYPESUCCESS', { type: number })} ${message.format('S_MUTE_TYPESWITCH', { type: number }, true)}`, + error: false + }; + } + + let role = null; + let updatedPermissions = false; + let created = false; + if(parsedArguments.create || this._caller === 'createmute') { + const missing = message.channel.permissionsFor(message.guild.me).missing('MANAGE_ROLES'); + if(missing.length > 0) return { + msg: message.format('S_MUTE_ROLEMISSINGPERMISSION'), + error: true + }; + + const foundRole = await this.client.resolver.resolveRole(args.join(' '), message.guild); + if(foundRole) { + const prompt = await message.prompt(message.format('S_MUTE_ROLEPROMPT', { name: foundRole.name, id: foundRole.id }), { emoji: 'loading' }); + const response = prompt.content.toLowerCase(); + const bool = this.client.resolver.resolveBoolean(response); + if(bool === null) return { msg: message.format('S_MUTE_ROLEPROMPTERROR'), error: true }; + if(!bool) { + role = await this._createRole(message, args); + if(role.error) return role; + created = true; + } else { + role = foundRole; + } + } else { + role = await this._createRole(message, args); + if(role.error) return role; + created = true; + } + const channels = message.guild.channels.cache.filter((c) => c.type === 'text'); + for(const channel of channels.values()) { + try { + await channel.createOverwrite(role, { + SEND_MESSAGES: false, + ADD_REACTIONS: false + }, super.reason(message.author)); + } catch(err) {} //eslint-disable-line no-empty + } + updatedPermissions = true; + } else { + const search = args.join(' '); + role = await this.client.resolver.resolveRole(search, message.guild); + } + + if(!role) return { + msg: message.format('S_MUTE_ROLEMISSING'), + error: true + }; + + await message.guild._updateSettings({ + [this.index]: { + ...message.guild._settings[this.index], + role: role.id + } + }); + + return { + msg: `${message.format('S_MUTE_ROLESUCCESS', { role: role.name, type: created ? 'created' : 'set' })} ${updatedPermissions ? message.format('S_MUTE_GENERATEDPERMISSIONS') : message.format('S_MUTE_UNGENERATEDPERMISSIONS')}`, + error: false + }; + + } + + async _createRole(message, args) { + let role = null; + let name = args.join(' ') || 'Muted'; + if(name.length > maxCharacters) name = name.slice(0, maxCharacters); + try { + role = await message.guild.roles.create({ + data: { + name + }, + reason: super.reason(message.author) + }); + } catch(error) { + return { + msg: message.format('S_MUTE_ROLECREATEERROR'), + error: true + }; + } + return role; + } + + data(guild) { + return `**Muted Role:** ${guild._settings[this.index].role ? `<@&${guild._settings[this.index].role}>` : 'N/A'}\n**Mute Type:** \`${guild._setting[this.index].type}\``; + } + + fields(guild) { + return [ + { + name: "Muted Role", + value: guild._settings[this.index].role ? `<@&${guild._settings[this.index].role}>` : '`N/A`', + inline: true + }, + { + name: "Mute Type", + value: `\`${guild._settings[this.index].type}\``, + inline: true + } + ]; + } + +} + +module.exports = MuteSetting; \ No newline at end of file diff --git a/structure/client/components/settings/utility/GuildPrefix.js b/structure/client/components/settings/utility/GuildPrefix.js index 0b87fd2..27bb262 100644 --- a/structure/client/components/settings/utility/GuildPrefix.js +++ b/structure/client/components/settings/utility/GuildPrefix.js @@ -24,30 +24,33 @@ class GuildPrefixSetting extends Setting { } - async handle(message, params, operator) { - - if (operator === 'RESET') { - return await super._handleReset(message, params); - } - - // let { params, parsedArguments } = await this._parseArguments(params); + async handle(message, params) { let [ prefix ] = params; const MaxCharacters = 6; if(prefix.length > MaxCharacters) return { - msg: message.format('S_GPREFIX_LENGTH', { length: prefix.length, max: MaxCharacters }), + msg: message.format('S_GUILDPREFIX_LENGTH', { length: prefix.length, max: MaxCharacters }), error: true }; - await message.guild._updateSettings({ prefix }); + if(prefix.includes(' ')) return { + msg: message.format('S_GUILDPREFIX_SPACES'), + error: true + }; + + await message.guild._updateSettings({ [this.index]: prefix }); return { - msg: message.format(`S_${this.name.toUpperCase()}_SUCCESS`, { prefix }), + msg: message.format(`S_GUILDPREFIX_SUCCESS`, { prefix }), error: false }; } + data(guild) { + return `**Prefix:** \`${guild.prefix}\``; + } + } diff --git a/structure/client/components/settings/utility/PermissionType.js b/structure/client/components/settings/utility/PermissionType.js new file mode 100644 index 0000000..d277daf --- /dev/null +++ b/structure/client/components/settings/utility/PermissionType.js @@ -0,0 +1,65 @@ +const { Setting } = require('../../../../interfaces/'); + +class PermissionTypeSetting extends Setting { + + constructor(client) { + + super(client, { + name: 'permissionType', + module: 'utility', + aliases: ['permission'], + guarded: true, + resolve: 'GUILD', + default: { + permissionType: 2 + }, + custom: true + }); + + this.client = client; + + } + + async handle(message, params) { + + const [prefix] = params; + + const MaxCharacters = 6; + if(prefix.length > MaxCharacters) return { + msg: message.format('S_GUILDPREFIX_LENGTH', { length: prefix.length, max: MaxCharacters }), + error: true + }; + + if(prefix.includes(' ')) return { + msg: message.format('S_GUILDPREFIX_SPACES'), + error: true + }; + + await message.guild._updateSettings({ [this.index]: prefix }); + return { + msg: message.format(`S_GUILDPREFIX_SUCCESS`, { prefix }), + error: false + }; + + } + + fields(guild) { + return [ + { + name: "》Prefix", + value: `\`${guild.prefix}\`` + } + ]; + } + +} + +module.exports = PermissionTypeSetting; + +const Constants = { //eslint-disable-line no-unused-vars + Types: { + 'discord': 0, + 'grants': 1, + 'both': 2 + } +}; \ No newline at end of file diff --git a/structure/extensions/Guild.js b/structure/extensions/Guild.js index 9292bfa..fc3bf85 100644 --- a/structure/extensions/Guild.js +++ b/structure/extensions/Guild.js @@ -18,7 +18,7 @@ const Guild = Structures.extend('Guild', (Guild) => { async settings() { if(!this._settings) this._settings = this.client.transactionHandler.send({ provider: 'mongodb', request: { collection: 'guilds', type: 'findOne', query: { guildId: this.id } } }); if(this._settings instanceof Promise) this._settings = await this._settings || null; - if(!this._settings) this._settings = { guildId: this.id, ...this.client.defaultConfig } ; + if(!this._settings) this._settings = { guildId: this.id, ...this.client.defaultConfig }; return this._settings; } @@ -32,13 +32,13 @@ const Guild = Structures.extend('Guild', (Guild) => { /* Settings Wrapper */ - async _dbDelete(collection) { //Delete whole entry - remove + async _deleteSettings() { //Delete whole entry - remove try { await this.client.transactionHandler.send({ provider: 'mongodb', request: { type: 'remove', - collection, + collection: 'guilds', query: { guildId: this.id } @@ -49,35 +49,35 @@ const Guild = Structures.extend('Guild', (Guild) => { } catch(error) { this._storageError(error); } + return true; } - async _dbUpdateOne(data, collection) { //Update property (upsert true) - updateOne - var index = this.dbIndex(collection); //eslint-disable-line no-unused-vars + async _updateSettings(data) { //Update property (upsert true) - updateOne try { await this.client.transactionHandler.send({ provider: 'mongodb', request: { type: 'updateOne', - collection, + collection: 'guilds', query: { guildId: this.id }, data } }); - index = { - ...index, + this._settings = { + ...this._settings, ...data }; this._storageLog(`Database Update (guild:${this.id}).`); } catch(error) { this._storageError(error); } + return true; } - async _dbRemoveProperty(value, collection) { //Remove property - const index = this.dbIndex(collection); - if(collection === 'guild' && this.client.defaultConfig[value]) { + async _removeSettings(value) { //Remove property + if(this.client.defaultConfig[value]) { await this._updateSettings(this.client.defaultConfig[value]); return undefined; } @@ -86,7 +86,7 @@ const Guild = Structures.extend('Guild', (Guild) => { provider: 'mongodb', request: { type: 'removeProperty', - collection, + collection: 'guilds', query: { guildId: this.id }, @@ -95,23 +95,13 @@ const Guild = Structures.extend('Guild', (Guild) => { ] } }); - delete index[value]; this._storageLog(`Database Remove (guild:${this.id}).`); + delete this._settings[value]; } catch(error) { this._storageError(error); } - } - - _dbIndex(collection) { - return { - 'guilds': this._settings, - 'permissions': this._permissions - }[collection]; - } - - /* Permissions Wrapper */ - - + return true; + } /* Language Formatting */ @@ -121,7 +111,7 @@ const Guild = Structures.extend('Guild', (Guild) => { let value = this.client.localeLoader.languages[language][key]; for(const [param, val] of Object.entries(parameters)) { - value = value.replace(new RegExp(`{${escapeRegex(param.toLowerCase())}}`, 'gi'), val); + value = value.replace(new RegExp(`{${escapeRegex(param.toLowerCase())}}`, 'giu'), val); } return value; @@ -131,45 +121,44 @@ const Guild = Structures.extend('Guild', (Guild) => { async resolveMembers(members, strict = false) { - return await this.client.resolver.resolveMembers(members, this, strict); + return this.client.resolver.resolveMembers(members, this, strict); } async resolveMember(member, strict) { - let [ result ] = await this.resolveMembers([ member ], strict); + const [ result ] = await this.resolveMembers([ member ], strict); return result; } async resolveChannels(channels, strict = false) { - return await this.client.resolver.resolveChannels(channels, this, strict); + return this.client.resolver.resolveChannels(channels, this, strict); } async resolveChannel(channel, strict) { - let [ result ] = await this.resolveMembers([ channel ], strict); + const [ result ] = await this.resolveMembers([ channel ], strict); return result; } async resolveRoles(roles, strict = false) { - return await this.client.resolver.resolveRoles(roles, this, strict); + return this.client.resolver.resolveRoles(roles, this, strict); } async resolveRole(role, strict) { - let [ result ] = await this.resolveRoles([ role ], strict); + const [ result ] = await this.resolveRoles([ role ], strict); return result; } - /* Logging */ _storageLog(log) { diff --git a/structure/extensions/Message.js b/structure/extensions/Message.js index e05ffa9..efcbbb6 100644 --- a/structure/extensions/Message.js +++ b/structure/extensions/Message.js @@ -78,7 +78,7 @@ const Message = Structures.extend('Message', (Message) => { } - async respond(str, opts = {}) { + async respond(str, opts = { attachments: [] }) { if(typeof str === 'string') { if(opts.emoji) { @@ -90,7 +90,7 @@ const Message = Structures.extend('Message', (Message) => { } //console.log(str) - this._pending = await this.channel.send(str); + this._pending = await this.channel.send(str, { files: opts.attachments }); return this._pending; } @@ -151,7 +151,7 @@ const Message = Structures.extend('Message', (Message) => { let embed = { author: { name: `${component.name}${component.module ? ` (${component.module.resolveable})` : ''}`, - icon_url: this.client.user.avatarURL() + icon_url: this.client.user.displayAvatarURL() }, description: stripIndents`\`${prefix}${component.name}${component.usage ? ` ${component.usage}` : ''}\` ${this.format(component.description)}${component.guildOnly ? ' *(guild-only)*' : ''}`, diff --git a/structure/interfaces/Command.js b/structure/interfaces/Command.js index 32b9e3b..3119f71 100644 --- a/structure/interfaces/Command.js +++ b/structure/interfaces/Command.js @@ -28,6 +28,9 @@ class Command extends Component { this.archivable = opts.archivable === undefined ? false : Boolean(opts.archivable); this.arguments = opts.arguments || []; + this.parameterType = opts.parameterType || 'SPLIT'; //SPLIT or PLAIN, PLAIN = string, SPLIT = split into array, includes quotes + + this.grantable = Boolean(opts.grantable); this.clientPermissions = opts.clientPermissions || []; this.memberPermissions = opts.memberPermissions || []; diff --git a/structure/interfaces/Setting.js b/structure/interfaces/Setting.js index 00c3545..bd24d83 100644 --- a/structure/interfaces/Setting.js +++ b/structure/interfaces/Setting.js @@ -17,13 +17,14 @@ class Setting extends Component { this.module = opts.module; this.restricted = Boolean(opts.restricted); - this.description = `S_${opts.name}_DESCRIPTION`; - this.examples = `S_${opts.name}_EXAMPLES`; + this.description = `S_${opts.name.toUpperCase()}_DESCRIPTION`; + this.examples = opts.examples || []; + this.usage = opts.usage || ''; this.archiveable = Boolean(opts.archiveable); this.index = opts.index || opts.name; this.aliases = opts.aliases || []; - this.resolve = (opts.resolve && Constants.Resolves.includes(opts.resolve)) ? opts.resolve : 'GUILD'; + this.resolve = opts.resolve && Constants.Resolves.includes(opts.resolve) ? opts.resolve : 'GUILD'; //eslint-disable-line no-use-before-define this.default = opts.default; this.arguments = opts.arguments || []; this.custom = Boolean(opts.custom); @@ -40,17 +41,31 @@ class Setting extends Component { this.client.logger.error(`No handle function found in ${this.moduleResolveable}.`); } - async _parseArguments(params) { - const { parsedArguments, newArgs } = await this.commandHandler._parseArguments(this.arguments, params); - return { parsedArguments, params: newArgs }; + async _parseArguments(params, guild) { + const response = await this.commandHandler._parseArguments(params, this.arguments, guild); + if(response.error) return response; + return { parsedArguments: response.parsedArguments, params: response.newArgs, error: false }; + } + + reason(executor) { + return `[${this.moduleResolveable}] Executed by ${executor.tag} (${executor.id}).`; + } + + async _handleReset(message) { + await message.guild._removeSettings(this.index); + const msg = message.format('GENERAL_SETTINGRESET', { setting: this.resolveable }); + return { + error: false, + msg + }; } get commandHandler() { if(this._commandHandler) return this._commandHandler; - else return this._commandHandler = this.client.registry.components.get('observer:commandHandler'); + this._commandHandler = this.client.registry.components.get('observer:commandHandler'); + return this._commandHandler; } - get moduleResolveable() { return `${this.module.id}:${this.id}`; } diff --git a/yarn.lock b/yarn.lock index 185791b..fbf7d47 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28,6 +28,15 @@ resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-0.1.5.tgz#1781c620b4c88d619bd0373a1548e5a6025e3d3a" integrity sha512-CU1q0UXQUpFNzNB7gufgoisDHP7n+T3tkqTsp3MNUkVJ5+hS3BCvME8uCXAUFlz+6T2FbTCu75A+yQ7HMKqRKw== +"@discordjs/form-data@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@discordjs/form-data/-/form-data-3.0.1.tgz#5c9e6be992e2e57d0dfa0e39979a850225fb4697" + integrity sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" @@ -46,14 +55,14 @@ acorn-jsx@^5.2.0: integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== acorn@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" - integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.2.0.tgz#17ea7e40d7c8640ff54a694c889c26f31704effe" + integrity sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ== ajv@^6.10.0, ajv@^6.10.2: - version "6.12.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7" - integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw== + version "6.12.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" + integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== dependencies: fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" @@ -152,7 +161,7 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -chalk@^2.0.0, chalk@^2.1.0: +chalk@^2.0.0: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -190,9 +199,9 @@ cli-cursor@^3.1.0: restore-cursor "^3.1.0" cli-width@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" - integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== color-convert@^1.9.0, color-convert@^1.9.1: version "1.9.3" @@ -274,16 +283,14 @@ core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== +cross-spawn@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.2.tgz#d0d7dcfa74e89115c7619f4f721a94e1fdb716d6" + integrity sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw== dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" debug@^4.0.1: version "4.1.1" @@ -292,7 +299,7 @@ debug@^4.0.1: dependencies: ms "^2.1.1" -deep-is@~0.1.3: +deep-is@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= @@ -317,12 +324,12 @@ diagnostics@^1.1.1: kuler "1.0.x" discord.js@discordjs/discord.js: - version "12.1.1" - resolved "https://codeload.github.com/discordjs/discord.js/tar.gz/828640ca263db2c95ed21e7353a2746fe6ac9fb8" + version "12.2.0" + resolved "https://codeload.github.com/discordjs/discord.js/tar.gz/153a030c1fc04fd2a144108680dbf7bb1d5b9cc9" dependencies: "@discordjs/collection" "^0.1.5" + "@discordjs/form-data" "^3.0.1" abort-controller "^3.0.0" - form-data "^3.0.0" node-fetch "^2.6.0" prism-media "^1.2.0" setimmediate "^1.0.5" @@ -376,10 +383,10 @@ eslint-scope@^5.0.0: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-utils@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" - integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== +eslint-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.0.0.tgz#7be1cc70f27a72a76cd14aa698bcabed6890e1cd" + integrity sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA== dependencies: eslint-visitor-keys "^1.1.0" @@ -388,22 +395,22 @@ eslint-visitor-keys@^1.1.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== -eslint@^6.8.0: - version "6.8.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" - integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== +eslint@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.0.0.tgz#c35dfd04a4372110bd78c69a8d79864273919a08" + integrity sha512-qY1cwdOxMONHJfGqw52UOpZDeqXy8xmD0u8CT6jIstil72jkhURC704W8CFyTPDPllz4z4lu0Ql1+07PG/XdIg== dependencies: "@babel/code-frame" "^7.0.0" ajv "^6.10.0" - chalk "^2.1.0" - cross-spawn "^6.0.5" + chalk "^4.0.0" + cross-spawn "^7.0.2" debug "^4.0.1" doctrine "^3.0.0" eslint-scope "^5.0.0" - eslint-utils "^1.4.3" + eslint-utils "^2.0.0" eslint-visitor-keys "^1.1.0" - espree "^6.1.2" - esquery "^1.0.1" + espree "^7.0.0" + esquery "^1.2.0" esutils "^2.0.2" file-entry-cache "^5.0.1" functional-red-black-tree "^1.0.1" @@ -416,25 +423,24 @@ eslint@^6.8.0: is-glob "^4.0.0" js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" + levn "^0.4.1" lodash "^4.17.14" minimatch "^3.0.4" - mkdirp "^0.5.1" natural-compare "^1.4.0" - optionator "^0.8.3" + optionator "^0.9.1" progress "^2.0.0" - regexpp "^2.0.1" - semver "^6.1.2" - strip-ansi "^5.2.0" - strip-json-comments "^3.0.1" + regexpp "^3.1.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" table "^5.2.3" text-table "^0.2.0" v8-compile-cache "^2.0.3" -espree@^6.1.2: - version "6.2.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" - integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== +espree@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.0.0.tgz#8a7a60f218e69f120a842dc24c5a88aa7748a74e" + integrity sha512-/r2XEx5Mw4pgKdyb7GNLQNsu++asx/dltf/CI8RFi9oGHxmQFgvLbc5Op4U6i8Oaj+kdslhJtVlEZeAqH5qOTw== dependencies: acorn "^7.1.1" acorn-jsx "^5.2.0" @@ -445,12 +451,12 @@ esprima@^4.0.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.2.0.tgz#a010a519c0288f2530b3404124bfb5f02e9797fe" - integrity sha512-weltsSqdeWIX9G2qQZz7KlTRJdkkOCTPgLYJUz1Hacf48R4YOwGPHO3+ORfWedqJKbq5WQmsgK90n+pFLIKt/Q== +esquery@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" + integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== dependencies: - estraverse "^5.0.0" + estraverse "^5.1.0" esrecurse@^4.1.0: version "4.2.1" @@ -464,10 +470,10 @@ estraverse@^4.1.0, estraverse@^4.1.1: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estraverse@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.0.0.tgz#ac81750b482c11cca26e4b07e83ed8f75fbcdc22" - integrity sha512-j3acdrMzqrxmJTNj5dbr1YbjacrYgAxVMeF0gK16E3j494mOe7xygM/ZLIguEQ0ETwAg2hlJCtHRGav+y0Ny5A== +estraverse@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642" + integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw== esutils@^2.0.2: version "2.0.3" @@ -498,7 +504,7 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= @@ -541,15 +547,6 @@ flatted@^2.0.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== -form-data@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" - integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -680,11 +677,6 @@ is-glob@^4.0.0, is-glob@^4.0.1: dependencies: is-extglob "^2.1.1" -is-promise@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" - integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= - is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -735,13 +727,13 @@ levenshtein-edit-distance@^2.0.0: resolved "https://registry.yarnpkg.com/levenshtein-edit-distance/-/levenshtein-edit-distance-2.0.5.tgz#a066eca8afb350e4d9054aed9ffeef66e78ffc83" integrity sha512-Yuraz7QnMX/JENJU1HA6UtdsbhRzoSFnGpVGVryjQgHtl2s/YmVgmNYkVs5yzVZ9aAvQR9wPBUH3lG755ylxGA== -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" + prelude-ls "^1.2.1" + type-check "~0.4.0" lodash@^4.17.14, lodash@^4.17.15: version "4.17.15" @@ -764,17 +756,17 @@ memory-pager@^1.0.2: resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5" integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg== -mime-db@1.43.0: - version "1.43.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" - integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== +mime-db@1.44.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== mime-types@^2.1.12: - version "2.1.26" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" - integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ== + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== dependencies: - mime-db "1.43.0" + mime-db "1.44.0" mimic-fn@^2.1.0: version "2.1.0" @@ -801,9 +793,9 @@ mkdirp@^0.5.1: minimist "^1.2.5" moment@^2.24.0: - version "2.24.0" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" - integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== + version "2.26.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.26.0.tgz#5e1f82c6bafca6e83e808b30c8705eed0dcbd39a" + integrity sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw== mongodb@^3.5.7: version "3.5.7" @@ -843,11 +835,6 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - node-fetch@^2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" @@ -872,17 +859,17 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" -optionator@^0.8.3: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" os-tmpdir@~1.0.2: version "1.0.2" @@ -901,20 +888,20 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prism-media@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/prism-media/-/prism-media-1.2.1.tgz#168f323712bcaacb1d70ae613bf9d9dc44cf43d4" - integrity sha512-R3EbKwJiYlTvGwcG1DpUt+06DsxOGS5W4AMEHT7oVOjG93MjpdhGX1whHyjnqknylLMupKAsKMEXcTNRbPe6Vw== + version "1.2.2" + resolved "https://registry.yarnpkg.com/prism-media/-/prism-media-1.2.2.tgz#4f1c841f248b67d325a24b4e6b1a491b8f50a24f" + integrity sha512-I+nkWY212lJ500jLe4tN9tWO7nRiBAVdMv76P9kffZjYhw20raMlW1HSSvS+MLXC9MmbNZCazMrAr+5jEEgTuw== process-nextick-args@~2.0.0: version "2.0.1" @@ -953,10 +940,10 @@ readable-stream@^3.1.1: string_decoder "^1.1.1" util-deprecate "^1.0.1" -regexpp@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" - integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== +regexpp@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" + integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== require_optional@^1.0.1: version "1.0.1" @@ -992,11 +979,9 @@ rimraf@2.6.3: glob "^7.1.3" run-async@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.0.tgz#e59054a5b86876cfae07f431d18cbaddc594f1e8" - integrity sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg== - dependencies: - is-promise "^2.1.0" + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== rxjs@^6.5.3: version "6.5.5" @@ -1011,9 +996,9 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" - integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== "safer-buffer@>= 2.1.2 < 3": version "2.1.2" @@ -1027,32 +1012,32 @@ saslprep@^1.0.0: dependencies: sparse-bitfield "^3.0.3" -semver@^5.1.0, semver@^5.5.0: +semver@^5.1.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.1.2: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.2.1: + version "7.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: - shebang-regex "^1.0.0" + shebang-regex "^3.0.0" -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== signal-exit@^3.0.2: version "3.0.3" @@ -1136,7 +1121,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -strip-ansi@^5.1.0, strip-ansi@^5.2.0: +strip-ansi@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== @@ -1150,7 +1135,7 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" -strip-json-comments@^3.0.1: +strip-json-comments@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180" integrity sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w== @@ -1207,21 +1192,21 @@ triple-beam@^1.2.0, triple-beam@^1.3.0: integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== tslib@^1.9.0: - version "1.11.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" - integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== + version "1.13.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" + integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== tweetnacl@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: - prelude-ls "~1.1.2" + prelude-ls "^1.2.1" type-fest@^0.11.0: version "0.11.0" @@ -1250,10 +1235,10 @@ v8-compile-cache@^2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== -which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" @@ -1280,7 +1265,7 @@ winston@^3.2.1: triple-beam "^1.3.0" winston-transport "^4.3.0" -word-wrap@~1.2.3: +word-wrap@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== @@ -1298,6 +1283,6 @@ write@1.0.3: mkdirp "^0.5.1" ws@^7.2.1: - version "7.2.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.3.tgz#a5411e1fb04d5ed0efee76d26d5c46d830c39b46" - integrity sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ== + version "7.3.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.0.tgz#4b2f7f219b3d3737bc1a2fbf145d825b94d38ffd" + integrity sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w==