diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..0f5513c --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +BOT_TOKEN= +CLIENT_ID= +OWNERS_IDS= +COMMAND_PREFIX=! \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..454757c --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,18 @@ +module.exports = { + root: true, + env: { + node: true, + es6: true, + }, + extends: ['prettier', 'plugin:vue/essential', '@vue/prettier'], + rules: { + 'prettier/prettier': 'error', + 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', + 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', + 'func-call-spacing': 'error', + 'curly': 'error' + }, + parserOptions: { + parser: 'babel-eslint', + }, +} diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000..8e5bcd3 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,8 @@ +module.exports = { + semi: false, + tabs: false, + tabWidth: 2, + singleQuote: true, + trailingComma: 'es5', + bracketSpacing: true, +} diff --git a/README.md b/README.md index c22169d..8563e54 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,115 @@ # **vue-land-bot** -**UPDATE 02/09/2019:** Aimed to be rewritten using [Commando.js](https://discord.js.org/#/docs/commando/master/general/welcome) for easiness of maintainability. +This is the official bot for the Vue Land Discord server. -API Doc: https://discord.js.org +It's written in JS and built on [Discord.js](https://github.com/discordjs/discord.js/) and [Commando](https://github.com/discordjs/Commando). -Based on guide: https://discordjs.guide/ +## Table of Contents -Not implemented: - -- [Setting a command as guild-only](https://discordjs.guide/commando/guild-only.html#setting-a-command-as-guild-only) -- [Cooldowns](https://discordjs.guide/command-handling/adding-features.html#cooldowns) -- [Command aliases](https://discordjs.guide/command-handling/adding-features.html#command-aliases) +- [Set up the bot](#setup) + - [Necessary steps](#necessary-steps) + - [Install dependencies](#install-dependencies) + - [Create Discord app and bot](#create-discord-application-and-bot-user) + - [Create the `.env` file](#create-the-.env-file) + - [Add Discord token to `.env` file](#add-discord-token-to-.env-file) + - [Optional steps](#optional-steps) + - [Configure role and user IDs](#configure-role-and-user-ids) +- [Run the bot](#running) +- [FAQ](#faq) +- [Contributors](#contributors) # Setup -To make this code work, you'll need to setup your own bot with its own token. -To do so, follow [this steps](https://discordjs.guide/preparations/setting-up-a-bot-application.html). +## Necessary steps + +### Install dependencies -# How to run? +As always: -```sh -# Install dependencies +```bash npm install +``` + +### Create Discord application and bot user + +Before you can run vue-land-bot, you'll need to setup a Discord Application and attach a bot user to it. + +Once you're done, copy the bot token to your clipboard. + +If you're not sure what to do you can [follow this guide](https://discordjs.guide/preparations/setting-up-a-bot-application.html). + +### Create the `.env` file + +You'll need to copy `.env.example` to `.env`. + +On \*nix/bsd you can run this command: + +```bash +cp .env.example .env +``` + +### Add Discord token to `.env` file + +Next you need to add the bot token to the `.env` file (as `DISCORD_TOKEN`). + +> **IMPORTANT:** You should treate the Discord bot token like a password - keep it safe! Especially if you plan on giving it permissions like `ADMINISTRATOR`! + +[Back to top](#vue-land-bot) + +## Optional steps -# Start bot + + +### Configure role and user IDs + +While not necessary per se, it's recommended to check out `src/constants/development.js` and `src/constants/production.js`. + +The relevant file is included based on the `NODE_ENV` environmental variable. + +These files contain various [Snowflakes](https://discordapp.com/developers/docs/reference#snowflakes) (basically IDs) for users and roles. + +[Back to top](#vue-land-bot) + +# Running + +To run the bot simply run: + +```bash npm run serve ``` +[Back to top](#vue-land-bot) + # FAQ -### When I run `npm run serve`, I get: `UnhandledPromiseRejectionWarning: Error: Incorrect login details were provided` +## When I run `npm run serve` I get an error + +### "The environmental variable BOT_TOKEN is required but was not present!" + +Please read the [necessary steps](#necessary-steps) section of the README. + +### Error: Incorrect login details were provided. + +Ensure you copy-pasted the token correctly. Perhaps you accidentally added a space, for instance? + +[Back to top](#vue-land-bot) -Make sure you have a `./src/config.json` file with your token properly filled. +# Contributors -To get a default `config.json`, run `npm run init`. +- Lead Developer / Maintainer + - [Elfayer](https://github.com/elfayer/), Hong Kong +- Contributors + - [sustained](https://github.com/sustained/), United Kingdom +- Ideas, Feedback & Testing + - [gusto](https://github.com/gustojs/), Poland + - [laquasicinque](https://github.com/laquasicinque) United Kingdom -To setup your token, see the [Setup](#Setup) section. \ No newline at end of file +[Back to top](#vue-land-bot) diff --git a/src/services/ban-words.txt b/data/ban-words.txt similarity index 100% rename from src/services/ban-words.txt rename to data/ban-words.txt diff --git a/data/documentation.js b/data/documentation.js new file mode 100644 index 0000000..51f4b83 --- /dev/null +++ b/data/documentation.js @@ -0,0 +1,102 @@ +export default [ + { + name: 'vue', + aliases: ['home', 'vuejs'], + value: 'https://vuejs.org/', + }, + { + name: 'get-started', + aliases: ['start', 'intro', 'introduction'], + value: 'https://vuejs.org/v2/guide/', + }, + { + name: 'installation', + value: 'https://vuejs.org/v2/guide/installation.html', + }, + { + name: 'class', + aliases: ['classes'], + value: + 'https://vuejs.org/v2/guide/class-and-style.html#Binding-HTML-Classes', + }, + { + name: 'style', + aliases: ['styles'], + value: + 'https://vuejs.org/v2/guide/class-and-style.html#Binding-Inline-Styles', + }, + { + name: 'component', + aliases: ['components'], + value: 'https://vuejs.org/v2/guide/components.html', + }, + { + name: 'slot', + aliases: ['slots'], + value: 'https://vuejs.org/v2/guide/components-slots.html', + }, + { + name: 'prop', + aliases: ['props'], + value: 'https://vuejs.org/v2/guide/components-props.html', + }, + { + name: 'event', + aliases: ['events'], + value: 'https://vuejs.org/v2/guide/components-custom-events.html', + }, + { + name: 'registration', + value: 'https://vuejs.org/v2/guide/components-registration.html', + }, + { + name: 'transition', + aliases: ['transitions'], + value: 'https://vuejs.org/v2/guide/transitions.html', + }, + { + name: 'mixin', + aliases: ['mixins'], + value: 'https://vuejs.org/v2/guide/mixins.html', + }, + { + name: 'directive', + aliases: ['directives', 'custom-directive', 'custom-directives'], + value: 'https://vuejs.org/v2/guide/custom-directive.html', + }, + { + name: 'render', + aliases: ['render-function', 'render-functions'], + value: 'https://vuejs.org/v2/guide/render-function.html', + }, + { + name: 'lifecycle', + aliases: ['cycle', 'lifecycles', 'lifecycle-hooks'], + value: 'https://vuejs.org/v2/api/#Options-Lifecycle-Hooks', + }, + { + name: 'lifecycle-diagram', + aliases: ['cycle-diag', 'cycle-diagram', 'life-cycle-diagram'], + value: 'https://vuejs.org/v2/guide/instance.html#Lifecycle-Diagram', + }, + { + name: 'cookbook', + aliases: ['cook'], + value: 'https://vuejs.org/v2/cookbook/', + }, + { + name: 'style-guide', + aliases: ['guide'], + value: 'https://vuejs.org/v2/style-guide/', + }, + { + name: 'example', + aliases: ['examples'], + value: 'https://vuejs.org/v2/examples/', + }, + { + name: 'prop-event', + aliases: ['props-events'], + value: 'https://vuejs.org/images/props-events.png', + }, +] diff --git a/package-lock.json b/package-lock.json index a6b68fb..b57a446 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,6 @@ "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "dev": true, "requires": { "@babel/highlight": "^7.0.0" } @@ -62,7 +61,6 @@ "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.5.5.tgz", "integrity": "sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==", - "dev": true, "requires": { "@babel/types": "^7.5.5", "jsesc": "^2.5.1", @@ -74,14 +72,12 @@ "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" } } }, @@ -89,7 +85,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", - "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.0.0", "@babel/template": "^7.1.0", @@ -100,7 +95,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", - "dev": true, "requires": { "@babel/types": "^7.0.0" } @@ -115,7 +109,6 @@ "version": "7.4.4", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, "requires": { "@babel/types": "^7.4.4" } @@ -135,7 +128,6 @@ "version": "7.5.0", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", - "dev": true, "requires": { "chalk": "^2.0.0", "esutils": "^2.0.2", @@ -145,16 +137,14 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" } } }, "@babel/parser": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.5.tgz", - "integrity": "sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==", - "dev": true + "integrity": "sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==" }, "@babel/plugin-syntax-object-rest-spread": { "version": "7.2.0", @@ -169,7 +159,6 @@ "version": "7.4.4", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", - "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "@babel/parser": "^7.4.4", @@ -180,7 +169,6 @@ "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.5.5.tgz", "integrity": "sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==", - "dev": true, "requires": { "@babel/code-frame": "^7.5.5", "@babel/generator": "^7.5.5", @@ -197,7 +185,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, "requires": { "ms": "^2.1.1" } @@ -205,8 +192,7 @@ "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" } } }, @@ -214,7 +200,6 @@ "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", - "dev": true, "requires": { "esutils": "^2.0.2", "lodash": "^4.17.13", @@ -224,8 +209,7 @@ "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" } } }, @@ -527,6 +511,14 @@ "integrity": "sha512-wBlsw+8n21e6eTd4yVv8YD/E3xq0O6nNnJIquutAsFGE7EyMKz7W6RNT6BRu1SmdgmlCZ9tb0X+j+D6HGr8pZw==", "dev": true }, + "@vue/eslint-config-prettier": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-5.0.0.tgz", + "integrity": "sha512-OXcH+XWevp3DIdC3BBornC1q6/MNYfca/3HY66awV6aGm+dtkR/hpfBb6fX7nsVjcox13kgG+eSUtUfJ3uxZ8A==", + "requires": { + "eslint-config-prettier": "^6.0.0" + } + }, "abab": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.1.tgz", @@ -536,8 +528,7 @@ "acorn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.5.tgz", - "integrity": "sha512-i33Zgp3XWtmZBMNvCr4azvOFeWVw1Rk6p3hfi3LUDvIFraOMywb1kAtrbi+med14m4Xfpqm3zRZMT+c0FNE7kg==", - "dev": true + "integrity": "sha512-i33Zgp3XWtmZBMNvCr4azvOFeWVw1Rk6p3hfi3LUDvIFraOMywb1kAtrbi+med14m4Xfpqm3zRZMT+c0FNE7kg==" }, "acorn-globals": { "version": "4.3.3", @@ -550,10 +541,9 @@ } }, "acorn-jsx": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", - "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==", - "dev": true + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.2.tgz", + "integrity": "sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==" }, "acorn-walk": { "version": "6.2.0", @@ -574,9 +564,9 @@ } }, "ajv-keywords": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", - "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", + "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", "dev": true }, "ansi-escapes": { @@ -586,16 +576,17 @@ "dev": true }, "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" }, "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } }, "anymatch": { "version": "2.0.0", @@ -611,7 +602,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -680,8 +670,7 @@ "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==" }, "async-limiter": { "version": "1.0.0", @@ -723,6 +712,18 @@ "js-tokens": "^3.0.2" }, "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", @@ -747,6 +748,29 @@ } } }, + "babel-eslint": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.3.tgz", + "integrity": "sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.0.0", + "@babel/traverse": "^7.0.0", + "@babel/types": "^7.0.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, "babel-jest": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", @@ -847,8 +871,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base": { "version": "0.11.2", @@ -926,7 +949,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1029,13 +1051,20 @@ "dev": true, "requires": { "callsites": "^0.2.0" + }, + "dependencies": { + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + } } }, "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", - "dev": true + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" }, "camelcase": { "version": "5.3.1", @@ -1062,7 +1091,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -1073,7 +1101,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -1082,7 +1109,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -1090,10 +1116,9 @@ } }, "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", - "dev": true + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" }, "ci-info": { "version": "2.0.0", @@ -1134,7 +1159,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, "requires": { "restore-cursor": "^2.0.0" } @@ -1142,8 +1166,7 @@ "cli-width": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" }, "cliui": { "version": "5.0.0", @@ -1204,7 +1227,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -1212,8 +1234,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "combined-stream": { "version": "1.0.8", @@ -1231,6 +1252,11 @@ "dev": true, "optional": true }, + "common-tags": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", + "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==" + }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", @@ -1240,8 +1266,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "contains-path": { "version": "0.1.0", @@ -1274,7 +1299,6 @@ "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, "requires": { "nice-try": "^1.0.4", "path-key": "^2.0.1", @@ -1332,10 +1356,9 @@ } }, "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "requires": { "ms": "^2.1.1" } @@ -1361,8 +1384,7 @@ "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" }, "define-properties": { "version": "1.1.3", @@ -1455,15 +1477,25 @@ "dev": true }, "discord.js": { - "version": "11.4.2", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-11.4.2.tgz", - "integrity": "sha512-MDwpu0lMFTjqomijDl1Ed9miMQe6kB4ifKdP28QZllmLv/HVOJXhatRgjS8urp/wBlOfx+qAYSXcdI5cKGYsfg==", + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-11.5.1.tgz", + "integrity": "sha512-tGhV5xaZXE3Z+4uXJb3hYM6gQ1NmnSxp9PClcsSAYFVRzH6AJH74040mO3afPDMWEAlj8XsoPXXTJHTxesqcGw==", "requires": { "long": "^4.0.0", "prism-media": "^0.0.3", "snekfetch": "^3.6.4", "tweetnacl": "^1.0.0", - "ws": "^4.0.0" + "ws": "^6.0.0" + } + }, + "discord.js-commando": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/discord.js-commando/-/discord.js-commando-0.10.0.tgz", + "integrity": "sha1-v6mL8zEIcDdvPHoNe3Nqaj5dCg8=", + "requires": { + "common-tags": "^1.0.0", + "escape-string-regexp": "^1.0.0", + "require-all": "^2.0.0" } }, "doctrine": { @@ -1484,6 +1516,11 @@ "webidl-conversions": "^4.0.2" } }, + "dotenv": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.1.0.tgz", + "integrity": "sha512-GUE3gqcDCaMltj2++g6bRQ5rBJWtkWTmqmD0fo1RnnMuUqHNCt2oTPeDnS9n6fKYvlhn7AeBkb38lymBtWBQdA==" + }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -1497,8 +1534,7 @@ "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" }, "end-of-stream": { "version": "1.4.1", @@ -1546,8 +1582,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "escodegen": { "version": "1.12.0", @@ -1571,49 +1606,94 @@ } }, "eslint": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.4.0.tgz", - "integrity": "sha512-UIpL91XGex3qtL6qwyCQJar2j3osKxK9e3ano3OcGEIRM4oWIpCkDg9x95AXEC2wMs7PnxzOkPZ2gq+tsMS9yg==", - "dev": true, + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.3.0.tgz", + "integrity": "sha512-ZvZTKaqDue+N8Y9g0kp6UPZtS4FSY3qARxBs7p4f0H0iof381XHduqVerFWtK8DPtKmemqbqCFENWSQgPR/Gow==", "requires": { - "ajv": "^6.5.0", - "babel-code-frame": "^6.26.0", + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", "chalk": "^2.1.0", "cross-spawn": "^6.0.5", - "debug": "^3.1.0", - "doctrine": "^2.1.0", - "eslint-scope": "^4.0.0", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^4.0.0", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.2", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.1", "esquery": "^1.0.1", "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", + "file-entry-cache": "^5.0.1", "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", + "glob-parent": "^5.0.0", "globals": "^11.7.0", - "ignore": "^4.0.2", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", - "inquirer": "^5.2.0", - "is-resolvable": "^1.1.0", - "js-yaml": "^3.11.0", + "inquirer": "^6.4.1", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.3.0", - "lodash": "^4.17.5", + "lodash": "^4.17.14", "minimatch": "^3.0.4", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", "progress": "^2.0.0", - "regexpp": "^2.0.0", - "require-uncached": "^1.0.3", - "semver": "^5.5.0", - "strip-ansi": "^4.0.0", - "strip-json-comments": "^2.0.1", - "table": "^4.0.3", - "text-table": "^0.2.0" + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "requires": { + "esutils": "^2.0.2" + } + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==" + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "eslint-config-prettier": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.1.0.tgz", + "integrity": "sha512-k9fny9sPjIBQ2ftFTesJV21Rg4R/7a7t7LCtZVrYQiHEp8Nnuk3EGaDmsKSAnsPj0BYcgB2zxzHa2NTkIxcOLg==", + "requires": { + "get-stdin": "^6.0.0" } }, "eslint-config-standard": { @@ -1751,6 +1831,14 @@ "semver": "^5.5.0" } }, + "eslint-plugin-prettier": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.0.tgz", + "integrity": "sha512-XWX2yVuwVNLOUhQijAkXz+rMPPoCr7WFiAl8ig6I7Xn+pPVhDhzg4DxHpmbeb0iqjO9UronEA3Tb09ChnFVHHA==", + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, "eslint-plugin-promise": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.0.1.tgz", @@ -1776,11 +1864,18 @@ "integrity": "sha512-OwxJkR6TQiYMmt1EsNRMe5qG3GsbjlcOhbGUBY4LtavF9DsLaTcoR+j2Tdjqi23oUwKNUqX7qcn5fPStafMdlA==", "dev": true }, + "eslint-plugin-vue": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-5.2.3.tgz", + "integrity": "sha512-mGwMqbbJf0+VvpGR5Lllq0PMxvTdrZ/ZPjmhkacrCHbubJeJOt+T6E3HUzAifa2Mxi7RSdJfC9HFpOeSYVMMIw==", + "requires": { + "vue-eslint-parser": "^5.0.0" + } + }, "eslint-scope": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", - "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", - "dev": true, + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", "requires": { "esrecurse": "^4.1.0", "estraverse": "^4.1.1" @@ -1790,7 +1885,6 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz", "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==", - "dev": true, "requires": { "eslint-visitor-keys": "^1.0.0" } @@ -1798,31 +1892,44 @@ "eslint-visitor-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", - "dev": true + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==" + }, + "esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" }, "espree": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-4.1.0.tgz", - "integrity": "sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w==", - "dev": true, + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.1.tgz", + "integrity": "sha512-EYbr8XZUhWbYCqQRW0duU5LxzL5bETN6AjKBGy1302qqzPaCH10QbRg3Wvco79Z8x9WbiE8HYB4e75xl6qUYvQ==", "requires": { - "acorn": "^6.0.2", - "acorn-jsx": "^5.0.0", - "eslint-visitor-keys": "^1.0.0" + "acorn": "^7.0.0", + "acorn-jsx": "^5.0.2", + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "acorn": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.0.0.tgz", + "integrity": "sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ==" + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==" + } } }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "esquery": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", - "dev": true, "requires": { "estraverse": "^4.0.0" } @@ -1831,7 +1938,6 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, "requires": { "estraverse": "^4.1.0" } @@ -1839,14 +1945,12 @@ "estraverse": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" }, "esutils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" }, "exec-sh": { "version": "0.3.2", @@ -1978,13 +2082,12 @@ } }, "external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", - "dev": true, + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", "tmp": "^0.0.33" } }, @@ -2062,20 +2165,22 @@ "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==" }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" }, "fb-watchman": { "version": "2.0.0", @@ -2090,19 +2195,16 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, "requires": { "escape-string-regexp": "^1.0.5" } }, "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", - "dev": true, + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" + "flat-cache": "^2.0.1" } }, "fill-range": { @@ -2145,17 +2247,20 @@ } }, "flat-cache": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", - "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", - "dev": true, + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", "requires": { - "circular-json": "^0.3.1", - "graceful-fs": "^4.1.2", - "rimraf": "~2.6.2", - "write": "^0.2.1" + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" } }, + "flatted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", + "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==" + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -2191,8 +2296,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "1.2.9", @@ -2214,7 +2318,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -2235,12 +2340,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2255,17 +2362,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -2382,7 +2492,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -2394,6 +2505,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -2408,6 +2520,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2415,12 +2528,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -2439,6 +2554,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -2519,7 +2635,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -2531,6 +2648,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -2616,7 +2734,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -2652,6 +2771,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -2671,6 +2791,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -2714,12 +2835,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -2732,8 +2855,7 @@ "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" }, "get-caller-file": { "version": "2.0.5", @@ -2744,8 +2866,7 @@ "get-stdin": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", - "dev": true + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==" }, "get-stream": { "version": "4.1.0", @@ -2775,7 +2896,6 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2785,11 +2905,18 @@ "path-is-absolute": "^1.0.0" } }, + "glob-parent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz", + "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==", + "requires": { + "is-glob": "^4.0.1" + } + }, "globals": { "version": "11.10.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.10.0.tgz", - "integrity": "sha512-0GZF1RiPKU97IHUO5TORo9w1PwrH/NBPl+fS7oMLdaTRiYmYbwK4NWoZWrAdd0/abG9R2BU+OiwyQpTpE6pdfQ==", - "dev": true + "integrity": "sha512-0GZF1RiPKU97IHUO5TORo9w1PwrH/NBPl+fS7oMLdaTRiYmYbwK4NWoZWrAdd0/abG9R2BU+OiwyQpTpE6pdfQ==" }, "graceful-fs": { "version": "4.1.15", @@ -2847,13 +2974,20 @@ "dev": true, "requires": { "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + } } }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-symbols": { "version": "1.0.0", @@ -2923,7 +3057,6 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" } @@ -2931,8 +3064,16 @@ "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" + }, + "import-fresh": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz", + "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } }, "import-local": { "version": "2.0.0", @@ -3007,14 +3148,12 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -3023,28 +3162,41 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "inquirer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", - "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", - "dev": true, + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", "cli-cursor": "^2.1.0", "cli-width": "^2.0.0", - "external-editor": "^2.1.0", + "external-editor": "^3.0.3", "figures": "^2.0.0", - "lodash": "^4.3.0", + "lodash": "^4.17.12", "mute-stream": "0.0.7", "run-async": "^2.2.0", - "rxjs": "^5.5.2", + "rxjs": "^6.4.0", "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", + "strip-ansi": "^5.1.0", "through": "^2.3.6" + }, + "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } } }, "invariant": { @@ -3163,11 +3315,15 @@ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", "dev": true }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "is-generator-fn": { "version": "2.1.0", @@ -3175,6 +3331,14 @@ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", @@ -3207,8 +3371,7 @@ "is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "dev": true + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" }, "is-regex": { "version": "1.0.4", @@ -3267,8 +3430,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isobject": { "version": "3.0.1", @@ -3810,7 +3972,6 @@ "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -3876,8 +4037,7 @@ "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" }, "json-parse-better-errors": { "version": "1.0.2", @@ -3894,14 +4054,12 @@ "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" }, "json-stringify-safe": { "version": "5.0.1", @@ -3975,7 +4133,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, "requires": { "prelude-ls": "~1.1.2", "type-check": "~0.3.2" @@ -4014,8 +4171,7 @@ "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, "lodash.sortby": { "version": "4.7.0", @@ -4124,14 +4280,12 @@ "mimic-fn": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -4139,8 +4293,7 @@ "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "mixin-deep": { "version": "1.3.2", @@ -4167,7 +4320,6 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, "requires": { "minimist": "0.0.8" } @@ -4175,14 +4327,12 @@ "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" }, "mute-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" }, "nan": { "version": "2.14.0", @@ -4213,8 +4363,7 @@ "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" }, "neo-async": { "version": "2.6.1", @@ -4225,8 +4374,7 @@ "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, "node-int64": { "version": "0.4.0", @@ -4370,7 +4518,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -4379,7 +4526,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, "requires": { "mimic-fn": "^1.0.0" } @@ -4406,7 +4552,6 @@ "version": "0.8.2", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, "requires": { "deep-is": "~0.1.3", "fast-levenshtein": "~2.0.4", @@ -4419,8 +4564,7 @@ "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, "p-each-series": { "version": "1.0.0", @@ -4467,6 +4611,14 @@ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "requires": { + "callsites": "^3.0.0" + } + }, "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", @@ -4500,8 +4652,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { "version": "1.0.2", @@ -4512,14 +4663,12 @@ "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" }, "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" }, "path-type": { "version": "2.0.0", @@ -4656,8 +4805,20 @@ "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" + }, + "prettier": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.18.2.tgz", + "integrity": "sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==" + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "requires": { + "fast-diff": "^1.1.2" + } }, "pretty-format": { "version": "24.9.0", @@ -4696,8 +4857,7 @@ "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" }, "prompts": { "version": "2.2.1", @@ -4738,8 +4898,7 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "qs": { "version": "6.5.2", @@ -4807,8 +4966,7 @@ "regexpp": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", - "dev": true + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==" }, "remove-trailing-separator": { "version": "1.1.0", @@ -4894,6 +5052,11 @@ "tough-cookie": "^2.3.3" } }, + "require-all": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/require-all/-/require-all-2.2.0.tgz", + "integrity": "sha1-tEIMIzrAKC0P9Jsnf7iAqLXeCJQ=" + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -4914,6 +5077,14 @@ "requires": { "caller-path": "^0.1.0", "resolve-from": "^1.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true + } } }, "resolve": { @@ -4943,10 +5114,9 @@ } }, "resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", - "dev": true + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" }, "resolve-url": { "version": "0.2.1", @@ -4958,7 +5128,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, "requires": { "onetime": "^2.0.0", "signal-exit": "^3.0.2" @@ -4974,7 +5143,6 @@ "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, "requires": { "glob": "^7.1.3" } @@ -4989,7 +5157,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true, "requires": { "is-promise": "^2.1.0" } @@ -5001,18 +5168,18 @@ "dev": true }, "rxjs": { - "version": "5.5.12", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", - "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", - "dev": true, + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", + "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==", "requires": { - "symbol-observable": "1.0.1" + "tslib": "^1.9.0" } }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "safe-regex": { "version": "1.1.0", @@ -5026,8 +5193,7 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sane": { "version": "4.1.0", @@ -5063,8 +5229,7 @@ "semver": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", - "dev": true + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" }, "set-blocking": { "version": "2.0.0", @@ -5099,7 +5264,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, "requires": { "shebang-regex": "^1.0.0" } @@ -5107,8 +5271,7 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, "shellwords": { "version": "0.1.1", @@ -5119,8 +5282,7 @@ "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "sisteransi": { "version": "1.0.3", @@ -5135,11 +5297,12 @@ "dev": true }, "slice-ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", - "dev": true, + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", "is-fullwidth-code-point": "^2.0.0" } }, @@ -5355,8 +5518,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sshpk": { "version": "1.16.1", @@ -5404,6 +5566,191 @@ "eslint-plugin-react": "~7.11.1", "eslint-plugin-standard": "~4.0.0", "standard-engine": "~9.0.0" + }, + "dependencies": { + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "eslint": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.4.0.tgz", + "integrity": "sha512-UIpL91XGex3qtL6qwyCQJar2j3osKxK9e3ano3OcGEIRM4oWIpCkDg9x95AXEC2wMs7PnxzOkPZ2gq+tsMS9yg==", + "dev": true, + "requires": { + "ajv": "^6.5.0", + "babel-code-frame": "^6.26.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^3.1.0", + "doctrine": "^2.1.0", + "eslint-scope": "^4.0.0", + "eslint-utils": "^1.3.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^4.0.0", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^2.0.0", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.7.0", + "ignore": "^4.0.2", + "imurmurhash": "^0.1.4", + "inquirer": "^5.2.0", + "is-resolvable": "^1.1.0", + "js-yaml": "^3.11.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.5", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "pluralize": "^7.0.0", + "progress": "^2.0.0", + "regexpp": "^2.0.0", + "require-uncached": "^1.0.3", + "semver": "^5.5.0", + "strip-ansi": "^4.0.0", + "strip-json-comments": "^2.0.1", + "table": "^4.0.3", + "text-table": "^0.2.0" + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "espree": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-4.1.0.tgz", + "integrity": "sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w==", + "dev": true, + "requires": { + "acorn": "^6.0.2", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "external-editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "dev": true, + "requires": { + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" + } + }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true, + "requires": { + "flat-cache": "^1.2.1", + "object-assign": "^4.0.1" + } + }, + "flat-cache": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", + "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", + "dev": true, + "requires": { + "circular-json": "^0.3.1", + "graceful-fs": "^4.1.2", + "rimraf": "~2.6.2", + "write": "^0.2.1" + } + }, + "inquirer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", + "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.1.0", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^5.5.2", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + } + }, + "rxjs": { + "version": "5.5.12", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", + "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", + "dev": true, + "requires": { + "symbol-observable": "1.0.1" + } + }, + "slice-ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "table": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", + "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", + "dev": true, + "requires": { + "ajv": "^6.0.1", + "ajv-keywords": "^3.0.0", + "chalk": "^2.1.0", + "lodash": "^4.17.4", + "slice-ansi": "1.0.0", + "string-width": "^2.1.1" + } + }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + } } }, "standard-engine": { @@ -5467,7 +5814,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" @@ -5477,7 +5823,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, "requires": { "ansi-regex": "^3.0.0" }, @@ -5485,8 +5830,7 @@ "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" } } }, @@ -5503,10 +5847,9 @@ "dev": true }, "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==" }, "supports-color": { "version": "2.0.0", @@ -5527,17 +5870,45 @@ "dev": true }, "table": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", - "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", - "dev": true, - "requires": { - "ajv": "^6.0.1", - "ajv-keywords": "^3.0.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", - "slice-ansi": "1.0.0", - "string-width": "^2.1.1" + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } } }, "test-exclude": { @@ -5664,8 +6035,7 @@ "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" }, "throat": { "version": "4.1.0", @@ -5676,14 +6046,12 @@ "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, "requires": { "os-tmpdir": "~1.0.2" } @@ -5697,8 +6065,7 @@ "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" }, "to-object-path": { "version": "0.3.0", @@ -5764,8 +6131,12 @@ "trim-right": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=" + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" }, "tunnel-agent": { "version": "0.6.0", @@ -5777,15 +6148,14 @@ } }, "tweetnacl": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.0.tgz", - "integrity": "sha1-cT2LgY2kIGh0C/aDhtBHnmb8ins=" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.1.tgz", + "integrity": "sha512-kcoMoKTPYnoeS50tzoqjPY3Uv9axeuuFAZY9M/9zFnhoVvRfxz9K29IMPD7jGmt2c8SW7i3gT9WqDl2+nV7p4A==" }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, "requires": { "prelude-ls": "~1.1.2" } @@ -5863,7 +6233,6 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, "requires": { "punycode": "^2.1.0" } @@ -5896,6 +6265,11 @@ "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", "dev": true }, + "v8-compile-cache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", + "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==" + }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -5917,6 +6291,40 @@ "extsprintf": "^1.2.0" } }, + "vue-eslint-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-5.0.0.tgz", + "integrity": "sha512-JlHVZwBBTNVvzmifwjpZYn0oPWH2SgWv5dojlZBsrhablDu95VFD+hriB1rQGwbD+bms6g+rAFhQHk6+NyiS6g==", + "requires": { + "debug": "^4.1.0", + "eslint-scope": "^4.0.0", + "eslint-visitor-keys": "^1.0.0", + "espree": "^4.1.0", + "esquery": "^1.0.1", + "lodash": "^4.17.11" + }, + "dependencies": { + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "espree": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-4.1.0.tgz", + "integrity": "sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w==", + "requires": { + "acorn": "^6.0.2", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + } + } + } + }, "w3c-hr-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", @@ -5971,7 +6379,6 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, "requires": { "isexe": "^2.0.0" } @@ -5985,8 +6392,7 @@ "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" }, "wrap-ansi": { "version": "5.1.0", @@ -6039,14 +6445,12 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", - "dev": true, + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", "requires": { "mkdirp": "^0.5.1" } @@ -6063,12 +6467,11 @@ } }, "ws": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz", - "integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0" + "async-limiter": "~1.0.0" } }, "xml-name-validator": { diff --git a/package.json b/package.json index 5719c6f..28dd04b 100644 --- a/package.json +++ b/package.json @@ -4,22 +4,34 @@ "description": "", "main": "index.js", "scripts": { - "serve": "npm run init && node ./src/index.js", - "init": "node ./src/scripts/init.js" + "serve": "node -r esm -r dotenv/config ./src/index.js" }, "repository": { "type": "git", "url": "git+https://github.com/Elfayer/vue-land-bot.git" }, "keywords": [], - "author": "", + "author": "Elfayer", + "contributors": [ + "Elfayer", + "sustained [https://sustained.name/]" + ], "license": "ISC", "bugs": { "url": "https://github.com/Elfayer/vue-land-bot/issues" }, "homepage": "https://github.com/Elfayer/vue-land-bot#readme", "dependencies": { - "discord.js": "^11.4.2" + "@vue/eslint-config-prettier": "^5.0.0", + "babel-eslint": "^10.0.3", + "discord.js": "^11.5.1", + "discord.js-commando": "^0.10.0", + "dotenv": "^8.1.0", + "eslint": "^6.3.0", + "eslint-plugin-prettier": "^3.1.0", + "eslint-plugin-vue": "^5.2.3", + "esm": "^3.2.25", + "prettier": "^1.18.2" }, "devDependencies": { "jest": "^24.9.0", diff --git a/src/client.js b/src/client.js index 28b796f..afc8150 100644 --- a/src/client.js +++ b/src/client.js @@ -1,3 +1,114 @@ -const Discord = require('discord.js') +import { readdirSync } from 'fs' +import { join } from 'path' +import { Collection } from 'discord.js' +import { CommandoClient } from 'discord.js-commando' -module.exports = new Discord.Client() +const { + OWNERS_IDS = '269617876036616193', // Default to @evan#9589 + COMMAND_PREFIX = '!', +} = process.env + +const PATH_JOBS = join(__dirname, 'jobs') +const PATH_TYPES = join(__dirname, 'types') +const PATH_COMMANDS = join(__dirname, 'commands') + +const client = new CommandoClient({ + owner: OWNERS_IDS, + commandPrefix: COMMAND_PREFIX, +}) + +/* + Initialise jobs. +*/ + +client.jobs = new Collection() + +const jobFiles = readdirSync(PATH_JOBS).filter(file => file.endsWith('.js')) + +for (const file of jobFiles) { + try { + const { default: jobDefinition } = require(`./jobs/${file}`) + + const jobInstance = new jobDefinition(client) + + client.jobs.set(jobInstance.name, jobInstance) + } catch (e) { + console.warn('Could not load job file: ' + file) + console.error(e) + } +} + +/* + Register command groups. + + https://discord.js.org/#/docs/commando/master/class/CommandoRegistry?scrollTo=registerGroups +*/ +client.registry.registerGroups([ + { + id: 'development', + name: 'Development', + }, + { + id: 'documentation', + name: 'Documentation', + }, + { + id: 'miscellaneous', + name: 'Miscellaneous', + }, + { + id: 'moderation', + name: 'Moderation', + }, + { + id: 'jobs', + name: 'Jobs', + }, +]) + +/* + Register default command groups, commands and argument types. + + And then register our own types and commands. + + https://discord.js.org/#/docs/commando/master/class/CommandoRegistry?scrollTo=registerDefaults + https://discord.js.org/#/docs/commando/master/class/CommandoRegistry?scrollTo=registerTypesIn + https://discord.js.org/#/docs/commando/master/class/CommandoRegistry?scrollTo=registerCommandsIn +*/ +client.registry.registerDefaults() +client.registry.registerTypesIn(PATH_TYPES) +client.registry.registerCommandsIn(PATH_COMMANDS) + +/* + Set up some global error handling and some purely informational event handlers. +*/ + +client.on('warn', console.warn) +client.on('error', console.error) + +client.on('ready', () => console.info('Client ready!')) +client.on('resume', () => console.info('Connection resumed!')) +client.on('disconnect', () => console.info('Lost connection!')) +client.on('reconnecting', () => console.info('Attempting to reconnect.')) + +process.on('unhandledRejection', console.error) + +/* + Process jobs. +*/ +client.on('message', msg => { + // Don't process own messages. + if (msg.author.id === msg.client.user.id) { + return + } + + client.jobs + .filter(job => job.enabled) + .forEach(job => { + if (job.shouldExecute(msg)) { + job.run(msg) + } + }) +}) + +export default client diff --git a/src/commands/README.md b/src/commands/README.md deleted file mode 100644 index c1bd533..0000000 --- a/src/commands/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Commands - -### Template - -```js -module.exports = { - name: String, // should match file name - description: String, // for maintainers understanding - args: Boolean, // true if the execute function uses args - isAvailable: Boolean, // if false, won't be used - usage: ' ', // if using args, to show users right args usage - execute (message, args) { - message.channel.send(String) - } -} -``` diff --git a/src/commands/add-ban-word.js b/src/commands/add-ban-word.js deleted file mode 100644 index eedb75a..0000000 --- a/src/commands/add-ban-word.js +++ /dev/null @@ -1,16 +0,0 @@ -const { banWords, saveToFile, toString } = require('../services/ban-words') - -module.exports = { - name: 'add-ban-word', - description: 'Add a word to be used by the ban job.', - args: true, - isAvailable: true, - usage: '', - execute (message, args) { - const word = args[0] - - banWords.push(word) - saveToFile() - message.channel.send(`Ban words: ${toString()}`) - } -} diff --git a/src/commands/args-info.js b/src/commands/args-info.js deleted file mode 100644 index d30bb66..0000000 --- a/src/commands/args-info.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - name: 'args-info', - description: 'Information about the arguments provided.', - args: false, - isAvailable: false, - execute (message, args) { - if (args[0] === 'foo') { - return message.channel.send('bar') - } - - message.channel.send(`Arguments: ${args}\nArguments length: ${args.length}`) - } -} diff --git a/src/commands/code.js b/src/commands/code.js deleted file mode 100644 index bc85b90..0000000 --- a/src/commands/code.js +++ /dev/null @@ -1,19 +0,0 @@ -const Discord = require('discord.js') - -module.exports = { - name: 'code', - description: 'Show code highlight tips', - args: false, - isAvailable: true, - execute (message) { - const embedMessage = new Discord.RichEmbed() - .setColor('#42b883') - .setTitle('Code highlight guide') - .addField('Inline code', '\\`code\\`') - .addField('Multiline code', '\\`\\`\\`\n// code\n\\`\\`\\`') - .addField('Multiline code coloring', '\\`\\`\\`html\n\n\n\n\\`\\`\\`') - .setFooter('Note you can use any language name for multiline coloring such as: html, js, css, sql, etc.') - - message.channel.send(embedMessage) - } -} diff --git a/src/commands/doc.js b/src/commands/doc.js deleted file mode 100644 index d92b9c7..0000000 --- a/src/commands/doc.js +++ /dev/null @@ -1,101 +0,0 @@ -const links = [{ - name: 'vue', - aliases: ['home', 'vuejs'], - value: 'https://vuejs.org/' -}, { - name: 'get-started', - aliases: ['start', 'intro', 'introduction'], - value: 'https://vuejs.org/v2/guide/' -}, { - name: 'installation', - value: 'https://vuejs.org/v2/guide/installation.html' -}, { - name: 'class', - aliases: ['classes'], - value: 'https://vuejs.org/v2/guide/class-and-style.html#Binding-HTML-Classes' -}, { - name: 'style', - aliases: ['styles'], - value: 'https://vuejs.org/v2/guide/class-and-style.html#Binding-Inline-Styles' -}, { - name: 'component', - aliases: ['components'], - value: 'https://vuejs.org/v2/guide/components.html' -}, { - name: 'slot', - aliases: ['slots'], - value: 'https://vuejs.org/v2/guide/components-slots.html' -}, { - name: 'prop', - aliases: ['props'], - value: 'https://vuejs.org/v2/guide/components-props.html' -}, { - name: 'event', - aliases: ['events'], - value: 'https://vuejs.org/v2/guide/components-custom-events.html' -}, { - name: 'registration', - value: 'https://vuejs.org/v2/guide/components-registration.html' -}, { - name: 'transition', - aliases: ['transitions'], - value: 'https://vuejs.org/v2/guide/transitions.html' -}, { - name: 'mixin', - aliases: ['mixins'], - value: 'https://vuejs.org/v2/guide/mixins.html' -}, { - name: 'directive', - aliases: ['directives', 'custom-directive', 'custom-directives'], - value: 'https://vuejs.org/v2/guide/custom-directive.html' -}, { - name: 'render', - aliases: ['render-function', 'render-functions'], - value: 'https://vuejs.org/v2/guide/render-function.html' -}, { - name: 'lifecycle', - aliases: ['cycle', 'lifecycles', 'lifecycle-hooks'], - value: 'https://vuejs.org/v2/api/#Options-Lifecycle-Hooks' -}, { - name: 'lifecycle-diagram', - aliases: ['cycle-diag', 'cycle-diagram', 'life-cycle-diagram'], - value: 'https://vuejs.org/v2/guide/instance.html#Lifecycle-Diagram' -}, { - name: 'cookbook', - aliases: ['cook'], - value: 'https://vuejs.org/v2/cookbook/' -}, { - name: 'style-guide', - aliases: ['guide'], - value: 'https://vuejs.org/v2/style-guide/' -}, { - name: 'example', - aliases: ['examples'], - value: 'https://vuejs.org/v2/examples/' -}, { - name: 'prop-event', - aliases: ['props-events'], - value: 'https://vuejs.org/images/props-events.png' -}] - -module.exports = { - name: 'doc', - description: 'Match a key word with a doc link', - args: true, - isAvailable: true, - usage: '', - execute (message, args) { - const value = args[0] - const found = links.find( - link => link.name === value || - (link.aliases && link.aliases.some(alias => alias === value)) - ) - - if (!found) { - const linksName = links.map(link => link.name).join(', ') - message.channel.send(`Documentation not found. Try: ${linksName}`) - } else { - message.channel.send(found.value) - } - } -} diff --git a/src/commands/documentation/docs.js b/src/commands/documentation/docs.js new file mode 100644 index 0000000..bf55a9d --- /dev/null +++ b/src/commands/documentation/docs.js @@ -0,0 +1,43 @@ +import { Command } from 'discord.js-commando' + +import links from '../../../data/documentation' + +module.exports = class DocsDocsCommand extends Command { + constructor(client) { + super(client, { + args: [ + { + key: 'keyword', + type: 'string', + prompt: 'keyword to search for?', + }, + ], + name: 'docs', + group: 'documentation', + aliases: ['d'], + guildOnly: false, + memberName: 'docs', + description: 'Match a keyword with a documentation link.', + }) + } + + hasPermission() { + return true + } + + async run(msg, args) { + const { keyword } = args + const found = links.find( + link => + link.name === keyword || + (link.aliases && link.aliases.some(alias => alias === keyword)) + ) + + if (!found) { + const linksName = links.map(link => link.name).join(', ') + msg.channel.send(`Documentation not found. Try: ${linksName}`) + } else { + msg.channel.send(found.value) + } + } +} diff --git a/src/commands/help.js b/src/commands/help.js deleted file mode 100644 index 6636be6..0000000 --- a/src/commands/help.js +++ /dev/null @@ -1,41 +0,0 @@ -const { prefix } = require('../config.json') - -module.exports = { - name: 'help', - description: 'List all commands or info about a specific command.', - usage: '[command name]', - isAvailable: false, - execute (message, args) { - const data = [] - const { commands } = message.client - - if (!args.length) { - data.push('Available commands:') - data.push(commands.map(command => command.name).join(', ')) - data.push(`\nYou can send \`${prefix}help [command name]\` to get info on a specific command!`) - - return message.author.send(data, { split: true }).then(() => { - if (message.channel.type === 'dm') return - message.reply('I\'ve sent you a DM with all my commands!') - }).catch(error => { - console.error(`Could not send help DM to ${message.author.tag}.\n`, error) - message.reply('It seems like I can\'t DM you! Do you have DMs disabled?') - }) - } - - const name = args[0].toLowerCase() - const command = commands.get(name)// || commands.find(c => c.aliases && c.aliases.includes(name)) - - if (!command) { - return message.reply('that\'s not a valid command!') - } - - data.push(`**Name:** ${command.name}`) - - // if (command.aliases) data.push(`**Aliases:** ${command.aliases.join(', ')}`) - if (command.description) data.push(`**Description:** ${command.description}`) - if (command.usage) data.push(`**Usage:** ${prefix}${command.name} ${command.usage}`) - - message.channel.send(data, { split: true }) - } -} diff --git a/src/commands/jobs/disable.js b/src/commands/jobs/disable.js new file mode 100644 index 0000000..07d4efb --- /dev/null +++ b/src/commands/jobs/disable.js @@ -0,0 +1,48 @@ +import { Command } from 'discord.js-commando' +import { + OWNER_IDS, + BOT_DEVELOPER_IDS, + MODERATOR_ROLE_IDS, +} from '../../utils/constants' + +const ALLOWED_ROLES = [...MODERATOR_ROLE_IDS] +const ALLOWED_USERS = [...OWNER_IDS, ...BOT_DEVELOPER_IDS] + +module.exports = class JobsDisableCommand extends Command { + constructor(client) { + super(client, { + args: [ + { + key: 'job', + type: 'job', + prompt: 'the job to disable?', + }, + ], + name: 'disable-job', + group: 'jobs', + aliases: ['jd'], + guildOnly: true, + memberName: 'disable', + description: 'Disable a job', + }) + } + + hasPermission(msg) { + if (msg.member.roles.some(role => ALLOWED_ROLES.includes(role.id))) { + return true + } + + return ALLOWED_USERS.includes(msg.author.id) + } + + async run(msg, args) { + const { job } = args + + if (job.enabled) { + job.enabled = false + return msg.channel.send(`Job "${job}" has been disabled.`) + } else { + return msg.channel.send(`Job "${job}" was already disabled.`) + } + } +} diff --git a/src/commands/jobs/enable.js b/src/commands/jobs/enable.js new file mode 100644 index 0000000..4c2187a --- /dev/null +++ b/src/commands/jobs/enable.js @@ -0,0 +1,48 @@ +import { Command } from 'discord.js-commando' +import { + OWNER_IDS, + BOT_DEVELOPER_IDS, + MODERATOR_ROLE_IDS, +} from '../../utils/constants' + +const ALLOWED_ROLES = [...MODERATOR_ROLE_IDS] +const ALLOWED_USERS = [...OWNER_IDS, ...BOT_DEVELOPER_IDS] + +module.exports = class JobsEnableCommand extends Command { + constructor(client) { + super(client, { + args: [ + { + key: 'job', + type: 'job', + prompt: 'the job to enable?', + }, + ], + name: 'enable-job', + group: 'jobs', + aliases: ['je'], + guildOnly: true, + memberName: 'enable', + description: 'Enable a job', + }) + } + + hasPermission(msg) { + if (msg.member.roles.some(role => ALLOWED_ROLES.includes(role.id))) { + return true + } + + return ALLOWED_USERS.includes(msg.author.id) + } + + async run(msg, args) { + const { job } = args + + if (!job.enabled) { + job.enabled = true + return msg.channel.send(`Job "${job}" has been enabled.`) + } else { + return msg.channel.send(`Job "${job}" was already enabled.`) + } + } +} diff --git a/src/commands/jobs/info.js b/src/commands/jobs/info.js new file mode 100644 index 0000000..42356cc --- /dev/null +++ b/src/commands/jobs/info.js @@ -0,0 +1,60 @@ +import { Command } from 'discord.js-commando' +import { RichEmbed } from 'discord.js' +import { + EMPTY_MESSAGE, + OWNER_IDS, + BOT_DEVELOPER_IDS, + MODERATOR_ROLE_IDS, +} from '../../utils/constants' + +const ALLOWED_ROLES = [...MODERATOR_ROLE_IDS] +const ALLOWED_USERS = [...OWNER_IDS, ...BOT_DEVELOPER_IDS] + +module.exports = class JobsEnableCommand extends Command { + constructor(client) { + super(client, { + args: [ + { + key: 'job', + type: 'job', + prompt: 'the job to view info about?', + }, + ], + name: 'job-info', + group: 'jobs', + aliases: ['ji'], + guildOnly: true, + memberName: 'info', + description: 'View information about a job.', + }) + } + + hasPermission(msg) { + if (msg.member.roles.some(role => ALLOWED_ROLES.includes(role.id))) { + return true + } + + return ALLOWED_USERS.includes(msg.author.id) + } + + async run(msg, args) { + const { job } = args + + let ignoredRoles = job.ignored.roles.map(roleId => { + return msg.guild.roles.get(roleId).name + }) + + if (!ignoredRoles.length) { + ignoredRoles = ['None'] + } + + const embed = new RichEmbed() + embed.setTitle('Job (' + job.name + ')') + embed.setDescription(job.description) + embed.addField('Status', job.enabled) + embed.addField('Guild Only', job.guildOnly) + embed.addField('Ignored Roles', ignoredRoles.join(', ')) + + return msg.channel.send(EMPTY_MESSAGE, { embed }) + } +} diff --git a/src/commands/jobs/list.js b/src/commands/jobs/list.js new file mode 100644 index 0000000..c6b7919 --- /dev/null +++ b/src/commands/jobs/list.js @@ -0,0 +1,46 @@ +import { Command } from 'discord.js-commando' +import { RichEmbed } from 'discord.js' +import { + EMPTY_MESSAGE, + OWNER_IDS, + BOT_DEVELOPER_IDS, + MODERATOR_ROLE_IDS, +} from '../../utils/constants' + +const ALLOWED_ROLES = [...MODERATOR_ROLE_IDS] +const ALLOWED_USERS = [...OWNER_IDS, ...BOT_DEVELOPER_IDS] + +module.exports = class JobsEnableCommand extends Command { + constructor(client) { + super(client, { + name: 'list-jobs', + group: 'jobs', + aliases: ['jl'], + guildOnly: true, + memberName: 'list', + description: 'List all jobs.', + }) + } + + hasPermission(msg) { + if (msg.member.roles.some(role => ALLOWED_ROLES.includes(role.id))) { + return true + } + + return ALLOWED_USERS.includes(msg.author.id) + } + + async run(msg) { + const embed = new RichEmbed() + embed.setTitle('Job List') + embed.setDescription( + 'Jobs are basically micro-tasks which are executed for every message.' + ) + + this.client.jobs.forEach(job => { + embed.addField(job.name, job.getStatus(), true) + }) + + return msg.channel.send(EMPTY_MESSAGE, { embed }) + } +} diff --git a/src/commands/list-ban-words.js b/src/commands/list-ban-words.js deleted file mode 100644 index a8b02f3..0000000 --- a/src/commands/list-ban-words.js +++ /dev/null @@ -1,11 +0,0 @@ -const banWords = require('../services/ban-words') - -module.exports = { - name: 'list-ban-words', // should match file name - description: 'List all ban words used by the ban job.', // for maintainers understanding - isAvailable: true, // if false, won't be used - usage: '', // if using args, to show users right args usage - execute (message) { - message.channel.send(`Ban words: ${banWords.toString()}`) - } -} diff --git a/src/commands/miscellaneous/code.js b/src/commands/miscellaneous/code.js new file mode 100644 index 0000000..4ff7bd0 --- /dev/null +++ b/src/commands/miscellaneous/code.js @@ -0,0 +1,39 @@ +import { Command } from 'discord.js-commando' +import { RichEmbed } from 'discord.js' + +module.exports = class MiscCodeCommand extends Command { + constructor(client) { + super(client, { + name: 'code', + group: 'miscellaneous', + aliases: ['hl', 'highlight', 'highlighting'], + guildOnly: false, + memberName: 'code', + description: 'Show code highlighting tips.', + }) + } + + hasPermission() { + return true + } + + async run(msg) { + /* + TODO: Extract fields and build dynamically? Makes for easier reading + modification. + */ + const embedMessage = new RichEmbed() + .setColor('#42b883') + .setTitle('Code Highlight Guide') + .addField('Inline code', '\\`code\\`') + .addField('Multiline code', '\\`\\`\\`\n// code\n\\`\\`\\`') + .addField( + 'Multiline code coloring', + '\\`\\`\\`html\n\n\n\n\\`\\`\\`' + ) + .setFooter( + 'Note you can use any language name for multiline coloring such as: html, js, css, sql, etc.' + ) + + msg.channel.send(embedMessage) + } +} diff --git a/src/commands/moderation/add-ban-word.js b/src/commands/moderation/add-ban-word.js new file mode 100644 index 0000000..f1f21e4 --- /dev/null +++ b/src/commands/moderation/add-ban-word.js @@ -0,0 +1,37 @@ +import { Command } from 'discord.js-commando' + +import { banWords, saveToFile, toString } from '../../services/ban-words' +import { MODERATOR_ROLE_IDS } from '../../utils/constants' + +module.exports = class ModerationAddBanWordCommand extends Command { + constructor(client) { + super(client, { + args: [ + { + key: 'word', + type: 'string', + prompt: 'the word to add?', + }, + ], + name: 'add-ban-word', + group: 'moderation', + aliases: ['abw'], + guildOnly: true, + memberName: 'add-ban-word', + description: 'Add a word to the ban list.', + }) + } + + hasPermission(msg) { + return msg.member.roles.some(role => MODERATOR_ROLE_IDS.includes(role.id)) + } + + async run(msg, args) { + const { word } = args + + banWords.push(word) + saveToFile() + + return msg.channel.send(`Ban words: ${toString()}`) + } +} diff --git a/src/commands/moderation/list-ban-words.js b/src/commands/moderation/list-ban-words.js new file mode 100644 index 0000000..769fb53 --- /dev/null +++ b/src/commands/moderation/list-ban-words.js @@ -0,0 +1,25 @@ +import { Command } from 'discord.js-commando' + +import { banWords } from '../../services/ban-words' +import { MODERATOR_ROLE_IDS } from '../../utils/constants' + +module.exports = class ModerationListBanWordsCommand extends Command { + constructor(client) { + super(client, { + name: 'list-ban-words', + group: 'moderation', + aliases: ['lbw'], + guildOnly: true, + memberName: 'list-ban-words', + description: 'List all banned words.', + }) + } + + hasPermission(msg) { + return msg.member.roles.some(role => MODERATOR_ROLE_IDS.includes(role.id)) + } + + async run(msg) { + return msg.channel.send(`Ban words: ${banWords.toString()}`) + } +} diff --git a/src/commands/moderation/remove-ban-word.js b/src/commands/moderation/remove-ban-word.js new file mode 100644 index 0000000..0d819ef --- /dev/null +++ b/src/commands/moderation/remove-ban-word.js @@ -0,0 +1,45 @@ +import { Command } from 'discord.js-commando' + +import { banWords, saveToFile, toString } from '../../services/ban-words' +import { MODERATOR_ROLE_IDS } from '../../utils/constants' + +module.exports = class ModerationRemoveBanWordCommand extends Command { + constructor(client) { + super(client, { + args: [ + { + key: 'word', + type: 'string', + prompt: 'the word to add?', + }, + ], + name: 'remove-ban-word', + group: 'moderation', + aliases: ['rbw', 'del-ban-word', 'rm-ban-word'], + guildOnly: true, + memberName: 'remove-ban-word', + description: 'Remove a banned word.', + }) + } + + hasPermission(msg) { + return msg.member.roles.some(role => MODERATOR_ROLE_IDS.includes(role.id)) + } + + async run(msg, args) { + const { word } = args + const foundIndex = banWords.findIndex( + w => w.toLowerCase() === word.toLowerCase() + ) + + if (foundIndex >= 0) { + banWords.splice(foundIndex, 1) + saveToFile() + msg.channel.send(`Ban words: ${toString()}`) + } else { + msg.channel.send( + `I cannot find the word "${word}" in the banned words list.` + ) + } + } +} diff --git a/src/commands/remove-ban-word.js b/src/commands/remove-ban-word.js deleted file mode 100644 index 9edf682..0000000 --- a/src/commands/remove-ban-word.js +++ /dev/null @@ -1,21 +0,0 @@ -const { banWords, saveToFile, toString } = require('../services/ban-words') - -module.exports = { - name: 'remove-ban-word', - description: 'Remove a word to be used by the ban job.', - args: true, - isAvailable: true, - usage: '', - execute (message, args) { - const word = args[0] - const foundIndex = banWords.findIndex(w => w.toLowerCase() === word.toLowerCase()) - - if (foundIndex >= 0) { - banWords.splice(foundIndex, 1) - saveToFile() - message.channel.send(`Ban words: ${toString()}`) - } else { - message.channel.send(`I cannot find the word "${word}" in the ban words list.`) - } - } -} diff --git a/src/commands/status.js b/src/commands/status.js deleted file mode 100644 index f98748c..0000000 --- a/src/commands/status.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - name: 'status', - description: 'Bot status', - args: false, - isAvailable: false, - execute (message) { - message.channel.send('Running.') - } -} diff --git a/src/index.js b/src/index.js index f22bb93..2775945 100644 --- a/src/index.js +++ b/src/index.js @@ -1,73 +1,20 @@ -const fs = require('fs') -const Discord = require('discord.js') -const client = require('./client.js') -const { prefix, token } = require('./config.json') +import client from './client.js' -// Init commands -client.commands = new Discord.Collection() +const { BOT_TOKEN } = process.env -const commandFiles = fs.readdirSync('./src/commands').filter(file => file.endsWith('.js')) - -for (const file of commandFiles) { - const command = require(`./commands/${file}`) - - if (command.isAvailable) { - client.commands.set(command.name, command) - } +if (!BOT_TOKEN) { + console.error( + 'The environmental variable BOT_TOKEN is required but was not present!' + ) + process.exit() } -// Init jobs -client.jobs = new Discord.Collection() - -const jobFiles = fs.readdirSync('./src/jobs').filter(file => file.endsWith('.js')) - -for (const file of jobFiles) { - const job = require(`./jobs/${file}`) - - if (job.isAvailable) { - client.jobs.set(job.name, job) - } +try { + client.login(BOT_TOKEN) +} catch (e) { + console.error( + 'The provided BOT_TOKEN environmental variable could not be used to login!' + ) + console.error(e) + process.exit(1) } - -client.once('ready', () => { - console.log('Ready!') -}) - -client.on('message', message => { - // If the message was sent by a bot, exit early - if (message.author.bot) { - return - } - - // If commands - if (message.content.startsWith(prefix)) { - const args = message.content.slice(prefix.length).split(/ +/) - const commandName = args.shift().toLowerCase() - const command = client.commands.get(commandName) - - if (!client.commands.has(commandName)) { - return message.channel.send(`There is no \`${prefix}${commandName}\` command!`) - } - - if (command.args && !args.length) { - let reply = `You didn't provide any arguments, ${message.author}!` - - if (command.usage) { - reply += `\nThe proper usage would be: \`${prefix}${command.name} ${command.usage}\`` - } - - return message.channel.send(reply) - } - - try { - command.execute(message, args) - } catch (error) { - console.error(error) - message.reply(`There was an error trying to execute "${commandName}" command!`) - } - } - // Read all by jobs - client.jobs.forEach(job => job.execute(message)) -}) - -client.login(token) diff --git a/src/jobs/README.md b/src/jobs/README.md deleted file mode 100644 index 537874c..0000000 --- a/src/jobs/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Jobs - -### Template - -```js -module.exports = { - name: String, // should match file name - description: String, // for maintainers understanding - isAvailable: Boolean, // if false, won't be used - execute (message) { } -} -``` diff --git a/src/jobs/ban.js b/src/jobs/ban.js index ac2601f..ca4136f 100644 --- a/src/jobs/ban.js +++ b/src/jobs/ban.js @@ -1,36 +1,95 @@ -const { banWords } = require('../services/ban-words') - -module.exports = { - name: 'ban', - description: 'Rules to ban', - isAvailable: true, - execute (message) { - if (banWords.some(word => message.content.toLowerCase().includes(word.toLowerCase()))) { - const user = message.author - const member = message.guild.member(user) - - // If the member is in the guild - if (member) { - /** - * Ban the member - * Make sure you run this on a member, not a user! - * There are big differences between a user and a member - */ - member.ban('[BOT] ad-block-ban').then(() => { - console.log(`Banned ${user.username}#${user.discriminator}`) - console.log(`Due to message: "${message.content}"`) - }).catch(err => { - // An error happened - // This is generally due to the bot not being able to ban the member, - // either due to missing permissions or role hierarchy - console.log('I was unable to kick the member') - // Log the error - console.error(err) - }) - } else { - // The mentioned user isn't in this guild - console.log('I was unable to kick the member, user isn\'t in this guild') - } +import { RichEmbed } from 'discord.js' +import Job from '../lib/job' +import { banWords } from '../services/ban-words' +import { + MODERATOR_ROLE_IDS, + PROTECTED_ROLE_IDS, + EMPTY_MESSAGE, +} from '../utils/constants' + +// Don't *actually* ban - real bans make testing hard! +const DEBUG_MODE = true + +export default class BanJob extends Job { + constructor(client) { + super(client, { + name: 'ban', + description: 'Automatically bans users who violate the banned word list.', + enabled: false, + ignored: { + roles: [...MODERATOR_ROLE_IDS, ...PROTECTED_ROLE_IDS], + }, + guildOnly: true, + config: { + logChannel: { + name: 'ban-log', + }, + }, + }) + } + + shouldExecute(msg) { + // None of the ban words were mentioned - bail. + if ( + !banWords.some(word => + msg.content.toLowerCase().includes(word.toLowerCase()) + ) + ) { + return false + } + + // We don't have permission to ban - bail. + if (!msg.channel.permissionsFor(msg.client.user).has('BAN_MEMBERS')) { + return !!console.warn('[BanJob] Cannot ban - lacking permission.') + } + + const botMember = msg.guild.member(msg.client.user) + const botHighestRole = botMember.highestRole.calculatedPosition + const userHighestRole = msg.member.highestRole.calculatedPosition + + // Our role is not high enough in the hierarchy to ban - bail. + if (botHighestRole < userHighestRole) { + return !!console.warn('[BanJob] Cannot ban - role too low.') + } + + return true + } + + run(msg) { + const logChannel = msg.client.channels.find( + channel => channel.name === this.config.logChannel.name + ) + + if (!logChannel) { + return console.warn( + `WarnJob: Could not find channel with name ${this.config.logChannel.name}` + ) } + + if (DEBUG_MODE) { + return this.log(msg, logChannel) + } + + msg.member + .ban(`[${msg.client.user.name}] Automated anti-spam measures.`) + .then(() => this.log(msg, logChannel)) + .catch(console.error) // Shouldn't happen due to shouldExecute checks but... + } + + log(msg, logChannel) { + if (!logChannel) { + return console.info( + `Banned user: ${msg.author}`, + `Due to message: ${msg.cleanContent}` + ) + } + + const embed = new RichEmbed() + embed.setTitle('Banned User') + embed.setAuthor(msg.author, msg.author.avatarURL) + embed.setTimestamp() + embed.addField('Triggering Message', msg.content) + + logChannel.send(EMPTY_MESSAGE, { embed }) } } diff --git a/src/jobs/log.js b/src/jobs/log.js new file mode 100644 index 0000000..62d147d --- /dev/null +++ b/src/jobs/log.js @@ -0,0 +1,54 @@ +import Job from '../lib/job' +import { trySend } from '../utils/messages' + +export default class LogJob extends Job { + constructor(client) { + super(client, { + name: 'log', + events: ['ready', 'resume', 'commandRun', 'unknownCommand'], + description: + 'Logs various events (connection, command invocations etc.) for debugging purposes/to aid development.', + enabled: false, + config: { + connectionChannel: { + name: 'connection', + }, + commandChannel: { + name: 'commands', + }, + }, + }) + } + + shouldExecute() { + return false + } + + ready() { + trySend( + this.config.connectionChannel, + `Successfully connected - I am now online.` + ) + } + + resume() { + trySend( + this.config.connectionChannel, + `The connection was lost but automatically resumed.` + ) + } + + commandRun(cmd, _, msg) { + trySend( + this.config.commandChannel, + `The command ${cmd.name} was ran in ${msg.channel}` + ) + } + + unknownCommand(msg) { + trySend( + this.config.commandChannel, + `Unknown command, triggered by message from ${msg.author} in ${msg.channel}.` + ) + } +} diff --git a/src/jobs/test.js b/src/jobs/test.js new file mode 100644 index 0000000..6bc43e0 --- /dev/null +++ b/src/jobs/test.js @@ -0,0 +1,15 @@ +import Job from '../lib/job' + +export default class TestJob extends Job { + constructor(client) { + super(client, { + name: 'test', + enabled: false, + description: 'Move along, move along.', + }) + } + + run() { + console.log('test job executed') + } +} diff --git a/src/jobs/warn.js b/src/jobs/warn.js index 4317e58..704d238 100644 --- a/src/jobs/warn.js +++ b/src/jobs/warn.js @@ -1,15 +1,50 @@ -const client = require('../client.js') +import Job from '../lib/job' +import { MODERATOR_ROLE_IDS, PROTECTED_ROLE_IDS } from '../utils/constants' +import { banWords } from '../services/ban-words' -module.exports = { - name: 'warn', - description: 'Rules to warn Moderators', - isAvailable: false, - execute (message) { - const notifyRole = message.guild.roles.find(role => role.name === 'Admin') - const notifyChannel = client.channels.find(channel => channel.name === 'spam') +export default class WarnJob extends Job { + constructor(client) { + super(client, { + name: 'warn', + description: 'Warn moderators when a user utters a banned word.', + enabled: false, + ignored: { + roles: [...MODERATOR_ROLE_IDS, ...PROTECTED_ROLE_IDS], + }, + guildOnly: true, + config: { + notifyRole: { + name: 'Moderators', + }, + notifyChannel: { + name: 'spam-log', + }, + }, + }) + } + + shouldExecute(msg) { + return banWords.some(word => + msg.content.toLowerCase().includes(word.toLowerCase()) + ) + } + + run(msg) { + const notifyRole = msg.guild.roles.find( + role => role.name === this.config.notifyRole.name + ) + const notifyChannel = msg.client.channels.find( + channel => channel.name === this.config.notifyChannel.name + ) - console.log(notifyRole) + if (!notifyChannel) { + return console.warn( + `WarnJob: Could not find channel with name ${this.config.notifyChannel.name}` + ) + } - notifyChannel.send(`${notifyRole} Suspicious user: ${message.author} in channel ${message.channel}`) + notifyChannel.send( + `${notifyRole} Suspicious user: ${msg.author} in channel ${msg.channel}` + ) } } diff --git a/src/lib/job.js b/src/lib/job.js new file mode 100644 index 0000000..d37d1f9 --- /dev/null +++ b/src/lib/job.js @@ -0,0 +1,305 @@ +import EventEmitter from 'events' + +/** + * A Job is a task which by default runs for every single message received so + * long as it is enabled and its shouldExecute() returns true. + * + * Example usages: + * + * - Check message contents against a banned word list (and optionally warn/kick/ban) + * - etc. + * + * A Job does not necessarily need to process messages however - there is a + * concept of event-only jobs. Such a job should simply return false in + * shouldExecute and specify a list of Discord/Commando events via JobOptions.events. + * + * When the job is enabled, listeners for those events will be attached to the + * CommandoClient and when the job is disabled they will be removed. + * + * See `src/jobs/log.js` for an example of an event-only job. + * + * @event Job#enabled + * @event Job#disabled + * @extends EventEmitter + * @abstract + */ +export default class Job extends EventEmitter { + /** + * Create a new Job. + * @param {CommandoClient} client The CommandoClient instance. + * @param {JobOptions} options The options for the Job. + */ + constructor(client, options = {}) { + super() + + this.client = client + + /* + Jobs are stored as a key-value pair in a Collection (Map) on the client. + The name is used as the key, as such it must be both provided and unique. + */ + if (!options.name) { + throw new Error('Job lacks required option - name.') + } + + if (client.jobs.has(options.name)) { + throw new Error( + `Job names must be unique, conflicting name - ${options.name}.` + ) + } + + /* + A list of user, role, channel and category IDs. + + If any of these match then the job will NEVER be executed. + */ + if (!options.ignored) { + options.ignored = {} + } + + ;['roles', 'users', 'channels'].forEach(key => { + if (!options.ignored[key]) { + options.ignored[key] = [] + } + }) + + /* + Arbitrary configuration e.g. log channels, mention roles etc. + */ + if (!options.config) { + options.config = {} + } + + /* + Discord.js/Commando events we intend to provide listeners for. + */ + if (!options.events) { + options.events = [] + } + + if (typeof options.enabled === 'undefined') { + options.enabled = false + } + + if (typeof options.guildOnly === 'undefined') { + options.guildOnly = true + } + + this.name = options.name + this.events = options.events + this.config = options.config + this.ignored = options.ignored + this.guildOnly = options.guildOnly + this.description = options.description || '' + + this.on('enabled', this.attachEventListeners) + this.on('disabled', this.removeEventListeners) + + // NOTE: Must come last because the setter triggers an event (enabled). + this.enabled = options.enabled + } + + /** + * Attach listeners to the `DiscordClient` for every event in `this.events`, + * provided that there is an instance method matching the event name. + */ + attachEventListeners() { + for (const event of this.events) { + if (!isValidEvent(event)) { + continue + } + + const eventHandler = this[event] + + if (!eventHandler) { + continue + } + + this.client.on(event, eventHandler) + } + } + + /** + * Remove event listeners from the `DiscordClient` for every event in `this.events`. + */ + removeEventListeners() { + for (const event of this.events) { + if (!isValidEvent(event)) { + continue + } + + const eventHandler = this[event] + + if (!eventHandler) { + continue + } + + this.client.removeListener(event, eventHandler) + } + } + + /** + * The job will not be ran if this returns `false` - even if the job is enabled. + * + * By default it checks `this.ignored.roles|users|channels`, returning `false` for + * any matches - if no matches are found, it returns `true`. + * + * @param {CommandoMessage} msg + * @returns {boolean} Whether to run (execute) the Job or not. + */ + shouldExecute(msg) { + if (msg.channel.type === 'dm') { + if (this.guildOnly) { + return false + } + + return true + } + + if (this.ignored.roles.length) { + return msg.member.roles.some(role => this.ignored.roles.includes(role.id)) + } + + if (this.ignored.users.length) { + return this.ignored.users.some(userId => msg.author.id === userId) + } + + if (this.ignored.channels.length) { + return this.ignored.channels.some( + channelId => msg.channel.id === channelId + ) + } + + return true + } + + /** + * The job itself - ran if `enabled` is `true` and `shouldExecute` returns `true`. + * + * @param {CommandoMessage} message + */ + /* eslint-disable no-unused-vars */ + run(msg) {} + + /** + * Returns a string representation of the Job. + * + * @return {string} The string representation (e.g. ). + */ + toString() { + return `` + } + + /** + * Returns the enabled status of the Job as a string. + * + * @return {string} Either `enabled` or `disabled`. + */ + getStatus() { + return this.enabled ? 'enabled' : 'disabled' + } + + /** + * Is the job enabled? + * + * @return {boolean} Is this job enabled? + */ + get enabled() { + return this._enabled + } + + /** + * Set a job as enabled or disabled. + * + * @param {boolean} enabled Set as enabled or disabled. + * @fires Job#enabled + * @fires Job#disabled + */ + set enabled(enabled) { + this._enabled = enabled + + if (enabled) { + this.emit('enabled') + } else { + this.emit('disabled') + } + } +} + +/* + List of valid Discord/Commando events. + + - https://discord.js.org/#/docs/main/stable/class/Client + - https://discord.js.org/#/docs/commando/master/class/CommandoClient + + TODO: This probably needs moving elsewhere. +*/ +const VALID_DISCORD_EVENTS = [ + // Discord.js Events + 'channelCreate', + 'channelDelete', + 'channelPinsUpdate', + 'channelUpdate', + 'clientUserGuildSettingsUpdate', + 'clientUserSettingsUpdate', + 'debug', + 'disconnect', + 'emojiCreate', + 'emojiDelete', + 'emojiUpdate', + 'error', + 'guildBanAdd', + 'guildBanRemove', + 'guildCreate', + 'guildDelete', + 'guildIntegrationsUpdate', + 'guildMemberAdd', + 'guildMemberAvailable', + 'guildMemberRemove', + 'guildMembersChunk', + 'guildMemberSpeaking', + 'guildMemberUpdate', + 'guildUnavailable', + 'guildUpdate', + 'message', + 'messageDelete', + 'messageDeleteBulk', + 'messageReactionAdd', + 'messageReactionRemove', + 'messageReactionRemoveAll', + 'messageUpdate', + 'presenceUpdate', + 'rateLimit', + 'ready', + 'reconnecting', + 'resume', + 'roleCreate', + 'roleDelete', + 'roleUpdate', + 'typingStart', + 'typingStop', + 'userNoteUpdate', + 'userUpdate', + 'voiceStateUpdate', + 'warn', + 'webhookUpdate', + // Commando Events + 'commandBlock', + 'commandCancel', + 'commandError', + 'commandPrefixChange', + 'commandRegister', + 'commandReregister', + 'commandRun', + 'commandStatusChange', + 'commandUnregister', + 'groupRegister', + 'groupStatusChange', + 'providerReady', + 'typeRegister', + 'unknownCommand', +] + +function isValidEvent(event) { + return VALID_DISCORD_EVENTS.includes(event) +} diff --git a/src/scripts/init.js b/src/scripts/init.js deleted file mode 100644 index 57e40a8..0000000 --- a/src/scripts/init.js +++ /dev/null @@ -1,21 +0,0 @@ -const fs = require('fs') - -const FILENAME = 'config.json' -const PATH = `./src/${FILENAME}` -const defaultConfig = { - prefix: '!', - token: 'your-token-goes-here' -} - -fs.access(PATH, (err) => { - if (!err) { - return - } - - fs.writeFile(PATH, JSON.stringify(defaultConfig), 'utf8', (err) => { - if (err) { - throw err - } - console.log(`A "${PATH}" file has been created, make sure to fill in your token properly.`) - }) -}) diff --git a/src/services/ban-words.js b/src/services/ban-words.js index dcc6bfb..5c4796e 100644 --- a/src/services/ban-words.js +++ b/src/services/ban-words.js @@ -1,48 +1,48 @@ -const fs = require('fs') -const path = require('path') +import fs from 'fs' +import { resolve } from 'path' + const FILENAME = 'ban-words.txt' -const PATH = path.join(__dirname, FILENAME) -const SEPARATOR = '\r\n' -const banWords = [] +const PATH = resolve(__dirname, '../../data/', FILENAME) +const SEPARATOR = '\n' + +export const banWords = [] -function _initFromFile () { - fs.access(PATH, (err) => { +export function saveToFile() { + fs.access(PATH, err => { if (err) { throw err } - fs.readFile(PATH, 'utf8', (err, data) => { + fs.writeFile(PATH, banWords.join(SEPARATOR), 'utf8', err => { if (err) { throw err } - banWords.push(...data.split(SEPARATOR)) + console.log(`File "${PATH}" updated.`) }) }) } -function saveToFile () { - fs.access(PATH, (err) => { +export function toString() { + return banWords.reduce( + (acc, val) => (acc ? `${acc}, \`${val}\`` : `\`${val}\``), + '' + ) +} + +function _initFromFile() { + fs.access(PATH, err => { if (err) { throw err } - fs.writeFile(PATH, banWords.join(SEPARATOR), 'utf8', (err, data) => { + fs.readFile(PATH, 'utf8', (err, data) => { if (err) { throw err } - console.log(`File "${PATH}" updated.`) + + banWords.push(...data.split(/\r?\n/)) }) }) } -function toString () { - return banWords.reduce((acc, val) => acc ? `${acc}, \`${val}\`` : `\`${val}\``, '') -} - _initFromFile() - -module.exports = { - saveToFile, - toString, - banWords -} diff --git a/src/types/job.js b/src/types/job.js new file mode 100644 index 0000000..90b837a --- /dev/null +++ b/src/types/job.js @@ -0,0 +1,15 @@ +import { ArgumentType } from 'discord.js-commando' + +module.exports = class JobArgumentType extends ArgumentType { + constructor(client, id = 'job') { + super(client, id) + } + + validate(value) { + return this.client.jobs.has(value) + } + + parse(value) { + return this.client.jobs.get(value) + } +} diff --git a/src/utils/constants/development.js b/src/utils/constants/development.js new file mode 100644 index 0000000..5831d82 --- /dev/null +++ b/src/utils/constants/development.js @@ -0,0 +1,47 @@ +/* + Various important and or noteworthy user IDs. +*/ +export const USERS = Object.freeze({ + EVAN: '269617876036616193', + GUSTO: '287377476647124992', + ELFAYER: '248017273950830593', + SUSTAINED: '136620462821081088', +}) + +/* + Various important and or noteworthy role IDs. +*/ +export const ROLES = Object.freeze({ + MVPS: '618043046055116801', + CORE_TEAM: '618042877271867413', + MODERATORS: '618042920028733461', + COMMUNITY_LEADERS: '618042942871044117', + LIBRARY_MAINTAINERS: '618043006506893322', +}) + +/* + Bot developers IDS. + + - can enable/disable/list jobs +*/ +export const BOT_DEVELOPER_IDS = Object.freeze([ + '248017273950830593', // Elfayer + '136620462821081088', // sustained +]) + +/* + Bot owner(s) + + PRODUCTION - gusto, evan + DEVELOPMENT - elfayer, sustained + + - can run commands set as ownerOnly +*/ +export const OWNER_IDS = Object.freeze(BOT_DEVELOPER_IDS) + +/* + Protected roles. + + - moderation-related commands have no effect +*/ +export const PROTECTED_USER_IDS = Object.freeze([USERS.EVAN, USERS.GUSTO]) diff --git a/src/utils/constants/index.js b/src/utils/constants/index.js new file mode 100644 index 0000000..ef59048 --- /dev/null +++ b/src/utils/constants/index.js @@ -0,0 +1,50 @@ +const { NODE_ENV = 'development' } = process.env + +const IMPORT_FILE = `./${NODE_ENV}.js` + +const { + USERS, + ROLES, + OWNER_IDS, + BOT_DEVELOPER_IDS, + PROTECTED_USER_IDS, +} = require(IMPORT_FILE) + +/* + Protected roles. + + - moderation-related commands have no effect +*/ + +const PROTECTED_ROLE_IDS = Object.freeze([ + ROLES.CORE_TEAM, + ROLES.MODERATORS, + ROLES.COMMUNITY_LEADERS, +]) + +/* + Moderators. + + - may use commands in the moderation command group +*/ +const MODERATOR_ROLE_IDS = Object.freeze([ROLES.CORE_TEAM, ROLES.MODERATORS]) + +/* + Used to send an embed-only message: + + Example: + + `channel.send(EMPTY_MESSAGE, { embed })` +*/ +const EMPTY_MESSAGE = '\u200b' + +export { + USERS, + ROLES, + OWNER_IDS, + PROTECTED_USER_IDS, + PROTECTED_ROLE_IDS, + MODERATOR_ROLE_IDS, + BOT_DEVELOPER_IDS, + EMPTY_MESSAGE, +} diff --git a/src/utils/constants/production.js b/src/utils/constants/production.js new file mode 100644 index 0000000..8341075 --- /dev/null +++ b/src/utils/constants/production.js @@ -0,0 +1,49 @@ +const { OWNERS } = process.env + +/* + Various important and or noteworthy user IDs. +*/ +export const USERS = Object.freeze({ + EVAN: '269617876036616193', + GUSTO: '287377476647124992', + ELFAYER: '248017273950830593', + SUSTAINED: '136620462821081088', +}) + +/* + Various important and or noteworthy role IDs. +*/ +export const ROLES = Object.freeze({ + MVPS: '443314906050330635', + CORE_TEAM: '361871508102053892', + MODERATORS: '336317962522722316', + COMMUNITY_LEADERS: '469085209187319808', + LIBRARY_MAINTAINERS: '359877575738130432', +}) + +/* + Bot developers IDS. + + - can enable/disable/list jobs +*/ +export const BOT_DEVELOPER_IDS = Object.freeze([ + '248017273950830593', // Elfayer + '136620462821081088', // sustained +]) + +/* + Bot owner(s) + + PRODUCTION - gusto, evan + DEVELOPMENT - elfayer, sustained + + - can run commands set as ownerOnly +*/ +export const OWNER_IDS = Object.freeze(OWNERS) + +/* + Protected roles. + + - moderation-related commands have no effect +*/ +export const PROTECTED_USER_IDS = Object.freeze([USERS.EVAN, USERS.GUSTO]) diff --git a/src/utils/embed.js b/src/utils/embed.js index 55733f6..0bccaa9 100644 --- a/src/utils/embed.js +++ b/src/utils/embed.js @@ -1,12 +1,10 @@ -const Discord = require('discord.js') +import { RichEmbed } from 'discord.js' -function embedMessage (title, description) { - return new Discord.RichEmbed() - .setColor('#42b883') +const DEFAULT_EMBED_COLOUR = '#42b883' + +export function embedMessage(title, description) { + return new RichEmbed() + .setColor(DEFAULT_EMBED_COLOUR) .setTitle(title) .setDescription(description) } - -module.exports = { - embedMessage -} diff --git a/src/utils/index.js b/src/utils/index.js index 11b2fd5..be5af8c 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1,5 +1,5 @@ -const embed = require('./embed') +import * as embed from './embed' +import * as messages from './messages' +import * as constants from './constants' -module.exports = { - embed -} +export { embed, messages, constants } diff --git a/src/utils/messages.js b/src/utils/messages.js new file mode 100644 index 0000000..95a5177 --- /dev/null +++ b/src/utils/messages.js @@ -0,0 +1,68 @@ +import client from '../client' + +/* + Delete a message safely, if possible. + + Safely means without risking permission errors. +*/ +export function tryDelete(msg, delay = 0) { + console.debug(`Attempting to delete msg #${msg.id}.`) + + if (msg.channel.type === 'dm' && msg.author.id !== msg.client.user.id) { + console.debug('Cannot delete - is DM.') + return false + } + + if (!msg.guild.me.hasPermission('MANAGE_MESSAGES')) { + console.debug('Cannot delete - lacking permissions.') + return false + } + + if (msg.member.hasPermission('ADMINISTRATOR')) { + if (!msg.guild.me.hasPermission('ADMINISTRATOR')) { + console.debug('Cannot delete - user is admin but I am not.') + } + } + + msg.delete(delay) + return true +} + +/* + Try to send a message to a channel by id or name if it exists. +*/ +export function trySend(channelResolvable, message, embed = {}) { + let channel + + if (channelResolvable.name) { + channel = client.channels.find( + channel => channel.name === channelResolvable.name + ) + + if (!channel) { + return + } + } else { + if (!channelResolvable.id) { + channelResolvable = { id: channelResolvable } + } + + channel = client.channels.get(channelResolvable.id) + + if (!channel) { + return + } + } + + channel.send(message, embed) +} + +/* + Remove emojis from a string. +*/ +export function stripEmojis(str) { + return str.replace( + /([\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2694-\u2697]|\uD83E[\uDD10-\uDD5D])/g, + '' + ) +}