From 428fa3feaa08b24a4fa5c2b302996543da857713 Mon Sep 17 00:00:00 2001 From: benmcmahon100 Date: Sun, 28 Jun 2020 18:28:45 +0100 Subject: [PATCH 01/10] meta: Add jetbrains idea files to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c2658d7..9cf6fff 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules/ +.idea From d1208bd8dec18f90ff7be5311fecf754e2baf62c Mon Sep 17 00:00:00 2001 From: benmcmahon100 Date: Sun, 28 Jun 2020 22:46:49 +0100 Subject: [PATCH 02/10] struct: Restructure brickbot and mark old commands as deprecated --- .eslintrc.json | 6 + .gitignore | 1 + Command.js | 33 ++++ bot.js | 128 ++++++++------- commands/bus.deprecated.js | 63 +++++++ commands/bus.js | 51 ------ commands/coinflip.js | 37 +++-- commands/help.deprecated.js | 57 +++++++ commands/help.js | 53 ------ commands/{info.js => info.deprecated.js} | 0 commands/{isitup.js => isitup.deprecated.js} | 0 commands/{luas.js => luas.deprecated.js} | 0 .../{nslookup.js => nslookup.deprecated.js} | 0 commands/{pwgen.js => pwgen.deprecated.js} | 0 commands/{pwned.js => pwned.deprecated.js} | 0 .../{register.js => register.deprecated.js} | 0 commands/{room.js => room.deprecated.js} | 0 commands/{ssl.js => ssl.deprecated.js} | 0 commands/{uptime.js => uptime.deprecated.js} | 0 commands/{verify.js => verify.deprecated.js} | 6 +- .../{weather.js => weather.deprecated.js} | 0 commands/{wiki.js => wiki.deprecated.js} | 0 helpers/helpers.js | 52 +++--- package.json | 1 + yarn.lock | 154 +++++++++++++++++- 25 files changed, 430 insertions(+), 212 deletions(-) create mode 100644 Command.js create mode 100644 commands/bus.deprecated.js delete mode 100644 commands/bus.js create mode 100644 commands/help.deprecated.js delete mode 100644 commands/help.js rename commands/{info.js => info.deprecated.js} (100%) rename commands/{isitup.js => isitup.deprecated.js} (100%) rename commands/{luas.js => luas.deprecated.js} (100%) rename commands/{nslookup.js => nslookup.deprecated.js} (100%) rename commands/{pwgen.js => pwgen.deprecated.js} (100%) rename commands/{pwned.js => pwned.deprecated.js} (100%) rename commands/{register.js => register.deprecated.js} (100%) rename commands/{room.js => room.deprecated.js} (100%) rename commands/{ssl.js => ssl.deprecated.js} (100%) rename commands/{uptime.js => uptime.deprecated.js} (100%) rename commands/{verify.js => verify.deprecated.js} (89%) rename commands/{weather.js => weather.deprecated.js} (100%) rename commands/{wiki.js => wiki.deprecated.js} (100%) diff --git a/.eslintrc.json b/.eslintrc.json index 67e52af..f3b3680 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -6,6 +6,8 @@ }, "extends": "eslint:recommended", "globals": { + "__dirname": "readonly", + "process": "readonly", "Atomics": "readonly", "SharedArrayBuffer": "readonly" }, @@ -13,6 +15,10 @@ "ecmaVersion": 2018 }, "rules": { + "max-len": [ + "error", + 120 + ], "linebreak-style": [ "error", "unix" diff --git a/.gitignore b/.gitignore index 9cf6fff..893c4f7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules/ .idea +.env diff --git a/Command.js b/Command.js new file mode 100644 index 0000000..57e288b --- /dev/null +++ b/Command.js @@ -0,0 +1,33 @@ +const helpers = require("./helpers/helpers.js"); + +class Command { + constructor(bot, opts) { + this.bot = bot; + this.opts = { + args: { + required: 0, + optional: 0 + }, + help: { + blurb: "Command placeholder", + example: "!command" + }, + name: "command", + ...opts + }; + } + + initCommand() { + // Connect to service, create database, etc. + return true; + } + + execute() { + return helpers.embedify( + this.bot, + "Some command output" + ); + } +} + +module.exports = Command; diff --git a/bot.js b/bot.js index 695f5cb..fdca9a6 100644 --- a/bot.js +++ b/bot.js @@ -1,19 +1,31 @@ -var Discord = require("discord.js"); -var fs = require("fs"); +const fs = require("fs"); +const path = require("path"); -var path = require("path"); -var __dirname = "/home/brickbot/brickbot/commands"; +const Discord = require("discord.js"); +const commandDir = `${__dirname}/commands`; -fs.readdirSync(__dirname).forEach(function (file) { - module.exports[path.basename(file, ".js")] = require(path.join(__dirname, file)); -}); +const { + argumentsUsedExample, + embedify, + tooManyArgs +} = require("./helpers/helpers.js"); + +require("dotenv").config(); -var commands = module.exports; -var helpers = require("./helpers/helpers.js"); -var bot = new Discord.Client(); +const bot = new Discord.Client(); +let commands = {}; + +fs.readdirSync(commandDir) + .filter(file => !(RegExp("deprecated", "gi")).test(file)) + .forEach(function (file) { + let command = require(path.join(commandDir, file))(bot); + if(command.initCommand && command.initCommand()) { + commands[command.opts.name] = command; + } +}); bot.on("message", (receivedMessage) => { - if (receivedMessage.author == bot.user) { + if (receivedMessage.author === bot.user) { return; } if (receivedMessage.content.startsWith("!")) { @@ -21,6 +33,29 @@ bot.on("message", (receivedMessage) => { } }); +function testAndExecute(command, args) { + const commandOpts = command.opts; + const {args: commandArgs} = commandOpts; + const {help: commandHelp} = commandOpts; + + if ( + args.length >= commandArgs.required && + args.length <= commandArgs.required + commandArgs.optional + ) { + return command.execute(); + } + else { + switch (false) { + case !args.length < commandArgs.required: + return argumentsUsedExample(bot, commandHelp); + case !args.length > commandArgs.require + commandArgs.optional: + return tooManyArgs(bot, commandHelp); + default: + return `command: ${commandOpts.name} - ${commandHelp.blurb}\nusage: ${commandOpts.help.example}`; + } + } +} + function processCommand(receivedMessage) { let fullCommand = receivedMessage.content.substr(1); let splitCommand = fullCommand.split(" "); @@ -30,59 +65,26 @@ function processCommand(receivedMessage) { console.log("Command received: " + primaryCommand); console.log("args: " + args); - switch (primaryCommand) { - case "help": - commands.help.helpCommand(bot, args, receivedMessage); - break; - case "bus": - commands.bus.busCommand(bot, args, receivedMessage); - break; - case "coinflip": - commands.coinflip.coinflipCommand(bot, args, receivedMessage); - break; - case "info": - commands.info.infoCommand(bot, args, receivedMessage); - break; - case "isitup": - commands.isitup.isitupCommand(bot, args, receivedMessage); - break; - case "luas": - commands.luas.luasCommand(bot, args, receivedMessage); - break; - case "nslookup": - commands.nslookup.nslookupCommand(bot, args, receivedMessage); - break; - case "pwgen": - commands.pwgen.pwgenCommand(bot, args, receivedMessage); - break; - case "pwned": - commands.pwned.pwnedCommand(bot, args, receivedMessage); - break; - case "register": - commands.register.registerCommand(bot, args, receivedMessage); - break; - case "room": - commands.room.roomCommand(bot, args, receivedMessage); - break; - case "ssl": - commands.ssl.sslCommand(bot, args, receivedMessage); - break; - case "uptime": - commands.uptime.uptimeCommand(bot, args, receivedMessage); - break; - case "verify": - commands.verify.verifyCommand(bot, args, receivedMessage); - break; - case "weather": - commands.weather.command(bot, args, receivedMessage); - break; - case "wiki": - commands.wiki.wikiCommand(bot, args, receivedMessage); - break; - default: - receivedMessage.channel.send(helpers.embedify(bot, "I don't understand the command. Try `!help [command]`")); + const {channel} = receivedMessage; + + console.log(commands[primaryCommand]); + + if (commands[primaryCommand]) { + channel.send(testAndExecute(commands[primaryCommand], args)); + } + else { + channel.send( + embedify( + bot, + `I don't understand the command. + Try \`!help [command]\`` + ) + ); } } -var bot_secret_token = fs.readFileSync("/etc/brickbot.token", "utf-8").replace(/\n$/, ""); +const bot_secret_token = typeof process.env.DISCORD_SECRET_FILE !== "undefined" + ? fs.readFileSync(process.env.DISCORD_SECRET_FILE, "utf-8").replace(/\n$/, "") + : process.env.DISCORD_SECRET; + bot.login(bot_secret_token); diff --git a/commands/bus.deprecated.js b/commands/bus.deprecated.js new file mode 100644 index 0000000..432acc0 --- /dev/null +++ b/commands/bus.deprecated.js @@ -0,0 +1,63 @@ +const request = require("request"); +const helpers = require("../helpers/helpers.js"); + +function busCommand (bot, args, receivedMessage) { + if (args.length === 0) { + helpers.argumentsUsedExample(bot, receivedMessage, "stop", "!bus 7571"); + } + else if (args.length > 0) { + request.post({ + url: "https://faas.jamesmcdermott.ie/function/transport", + body: "127.0.0.1:8000/bus/stop/" + args + }, + function(error, response, body) { + const {departures: buses} = JSON.parse(body); + let schedule = ""; + for(const n in buses){ + const parsed = busParseTime(buses[n].MonitoredCall_ExpectedArrivalTime + .substring(11, 16) + .split(":")); + const timeTo = busGetTimeTo(parsed[0], parsed[1], parsed[2], parsed[3]); + + const { + MonitoredVehicleJourney_PublishedLineName: LineName, + MonitoredVehicleJourney_DestinationName: DestinationName + } = buses[n]; + + schedule += + `${LineName} (${DestinationName}) - ${timeTo}\n`; + } + + receivedMessage.author.send(helpers.embedify(bot, schedule)) + .catch(err => console.trace(`Exception occurred in sending message ${err}`)); + }); + } +} + +function busParseTime (arr) { + const date = new Date(); const h = date.getHours(); const m = date.getMinutes(); + return [parseInt(arr[0]), h, parseInt(arr[1]), m]; +} + +function busGetTimeTo(hr, h, min, m) { + var hour = hr - h - 2; + var minute; + var timeTo; + if (min - m < 0){ + minute = 60 + (min - m); + } else { + minute = min - m; + } + if (hour <= 0 && minute == 0) { + timeTo = "Due"; + } else if (hour <= 0) { + timeTo = minute + " mins"; + } else { + timeTo = hour + " hr " + minute + " mins"; + } + return timeTo; +} + +module.exports = { + busCommand: function() { } +}; diff --git a/commands/bus.js b/commands/bus.js deleted file mode 100644 index 3f1e0e6..0000000 --- a/commands/bus.js +++ /dev/null @@ -1,51 +0,0 @@ -var request = require("request"); -var helpers = require("../helpers/helpers.js"); - -module.exports = { - busCommand: function (bot, args, receivedMessage) { - if (args.length == 0) { - helpers.argumentsUsedExample(bot, receivedMessage, "stop", "!bus 7571"); - return; - } - else if (args.length > 0) { - request.post({ - url: "https://faas.jamesmcdermott.ie/function/transport", - body: "127.0.0.1:8000/bus/stop/" + args - }, - function(error, response, body) { - var buses = JSON.parse(body).departures; - var schedule = ""; - for(var n in buses){ - var parsed = busParseTime(buses[n].MonitoredCall_ExpectedArrivalTime.substring(11, 16).split(":")); - var timeTo = busGetTimeTo(parsed[0], parsed[1], parsed[2], parsed[3]); - schedule += (buses[n].MonitoredVehicleJourney_PublishedLineName + " (" + buses[n].MonitoredVehicleJourney_DestinationName + ") - " + timeTo + "\n"); - } - receivedMessage.author.send(helpers.embedify(bot, schedule)); - }); - } - }, -}; - -function busParseTime (arr) { - var date = new Date(); var h = date.getHours(); var m = date.getMinutes(); - return [parseInt(arr[0]), h, parseInt(arr[1]), m]; -} - -function busGetTimeTo(hr, h, min, m) { - var hour = hr - h - 2; - var minute; - var timeTo; - if (min - m < 0){ - minute = 60 + (min - m); - } else { - minute = min - m; - } - if (hour <= 0 && minute == 0) { - timeTo = "Due"; - } else if (hour <= 0) { - timeTo = minute + " mins"; - } else { - timeTo = hour + " hr " + minute + " mins"; - } - return timeTo; -} diff --git a/commands/coinflip.js b/commands/coinflip.js index 848f0c0..e20ed14 100644 --- a/commands/coinflip.js +++ b/commands/coinflip.js @@ -1,18 +1,23 @@ -var request = require("request"); -var helpers = require("../helpers/helpers.js"); +const helpers = require("../helpers/helpers.js"); +const Command = require("../Command"); -module.exports = { - coinflipCommand: function (bot, args, receivedMessage) { - if (args.length > 0) { - helpers.noArgumentsUsedExample(bot, receivedMessage, "!coinflip"); - } - else if (args.length == 0) { - request.get({ - url: "https://faas.jamesmcdermott.ie/function/coinflip", - }, - function(error, response, body) { - receivedMessage.channel.send(helpers.embedify(bot, "Came up " + body)); - }); - } +class CoinFlip extends Command { + constructor(bot) { + super(bot, { + help: { + blurb: "This command performs a coinflip in the cloud for all you indecisive people", + example: "!coinflip" + }, + name: "coinflip" + }); } -}; + + execute() { + return helpers.embedify( + this.bot, + "Came up " + `${Math.random() >= 0.5 ? "heads" : "tails"}` + ); + } +} + +module.exports = (bot) => new CoinFlip(bot); diff --git a/commands/help.deprecated.js b/commands/help.deprecated.js new file mode 100644 index 0000000..cf80eab --- /dev/null +++ b/commands/help.deprecated.js @@ -0,0 +1,57 @@ +const helpers = require("../helpers/helpers.js"); + +// module.exports = { +// helpCommand: function (bot, args, receivedMessage) { +// if (args.length > 1) { +// receivedMessage.channel.send(helpers.embedify(bot, "Please specify one single command. Try `!help [command]`")); +// } else if (args.length == 1) { +// switch (args[0]) { +// case "bus": +// receivedMessage.channel.send(helpers.embedify(bot, "bus - check the schedule of a Dublin Bus stop.\n\nExample: '!bus 1635'\n\nThe nearest bus stops to DCU are 7516 (The Helix) and 37 (Ballymun Road)")); +// break; +// case "coinflip": +// receivedMessage.channel.send(helpers.embedify(bot, "coinflip - toss a coin.\n\nExample: '!coinflip'")); +// break; +// case "info": +// receivedMessage.channel.send(helpers.embedify(bot, "info - show brickbot info.\n\nExample: '!info'")); +// break; +// case "isitup": +// receivedMessage.channel.send(helpers.embedify(bot, "isitup - check if a site is up or down.\n\nExample: '!isitup redbrick.dcu.ie'")); +// break; +// case "luas": +// receivedMessage.channel.send(helpers.embedify(bot, "luas - check the schedule of a Luas stop.\n\nExample: '!luas harcourt'")); +// break; +// case "nslookup": +// receivedMessage.channel.send(helpers.embedify(bot, "nslookup - uses nslookup to return any IP address info on domains.\n\nExample: '!nslookup redbrick.dcu.ie'")); +// break; +// case "pwgen": +// receivedMessage.channel.send(helpers.embedify(bot, "pwgen - uses pwgen to generate a password and privately send it to you.\n\nExample: '!pwgen'`")); +// break; +// case "pwned": +// receivedMessage.channel.send(helpers.embedify(bot, "pwned - check if an email has been pwned.\n\nExample: '!pwned bertie@redbrick.dcu.ie'")); +// break; +// case "room": +// receivedMessage.channel.send(helpers.embedify(bot, "room - provides timetable information for a specified room or building in DCU.\n\nExample: '!room GLA.LG26'")); +// break; +// case "ssl": +// receivedMessage.channel.send(helpers.embedify(bot, "ssl - check the certificate info of a website.\n\nExample: '!ssl redbrick.dcu.ie'")); +// break; +// case "uptime": +// receivedMessage.channel.send(helpers.embedify(bot, "uptime - check the uptime of brickbot.\n\nExample '!uptime'")); +// break; +// case "weather": +// receivedMessage.channel.send(helpers.embedify(bot, "weather - check the weather forecast by location.\n\nExample '!weather DCU'")); +// break; +// case "wiki": +// receivedMessage.channel.send(helpers.embedify(bot, "wiki - return a random page from wiki.redbrick.dcu.ie.\n\nExample: '!wiki'")); +// break; +// } +// } else { +// receivedMessage.channel.send(helpers.embedify(bot, "Here is the list of brickbot commands:\n • bus \n • coinflip\n • info\n • isitup\n • luas\n • nslookup\n • pwgen\n • pwned\n • room\n • ssl\n • uptime\n • wiki\n • help\n")); +// } +// } +// }; + +const opts = { + +} diff --git a/commands/help.js b/commands/help.js deleted file mode 100644 index 26094d5..0000000 --- a/commands/help.js +++ /dev/null @@ -1,53 +0,0 @@ -var helpers = require("../helpers/helpers.js"); - -module.exports = { - helpCommand: function (bot, args, receivedMessage) { - if (args.length > 1) { - receivedMessage.channel.send(helpers.embedify(bot, "Please specify one single command. Try `!help [command]`")); - } else if (args.length == 1) { - switch (args[0]) { - case "bus": - receivedMessage.channel.send(helpers.embedify(bot, "bus - check the schedule of a Dublin Bus stop.\n\nExample: '!bus 1635'\n\nThe nearest bus stops to DCU are 7516 (The Helix) and 37 (Ballymun Road)")); - break; - case "coinflip": - receivedMessage.channel.send(helpers.embedify(bot, "coinflip - toss a coin.\n\nExample: '!coinflip'")); - break; - case "info": - receivedMessage.channel.send(helpers.embedify(bot, "info - show brickbot info.\n\nExample: '!info'")); - break; - case "isitup": - receivedMessage.channel.send(helpers.embedify(bot, "isitup - check if a site is up or down.\n\nExample: '!isitup redbrick.dcu.ie'")); - break; - case "luas": - receivedMessage.channel.send(helpers.embedify(bot, "luas - check the schedule of a Luas stop.\n\nExample: '!luas harcourt'")); - break; - case "nslookup": - receivedMessage.channel.send(helpers.embedify(bot, "nslookup - uses nslookup to return any IP address info on domains.\n\nExample: '!nslookup redbrick.dcu.ie'")); - break; - case "pwgen": - receivedMessage.channel.send(helpers.embedify(bot, "pwgen - uses pwgen to generate a password and privately send it to you.\n\nExample: '!pwgen'`")); - break; - case "pwned": - receivedMessage.channel.send(helpers.embedify(bot, "pwned - check if an email has been pwned.\n\nExample: '!pwned bertie@redbrick.dcu.ie'")); - break; - case "room": - receivedMessage.channel.send(helpers.embedify(bot, "room - provides timetable information for a specified room or building in DCU.\n\nExample: '!room GLA.LG26'")); - break; - case "ssl": - receivedMessage.channel.send(helpers.embedify(bot, "ssl - check the certificate info of a website.\n\nExample: '!ssl redbrick.dcu.ie'")); - break; - case "uptime": - receivedMessage.channel.send(helpers.embedify(bot, "uptime - check the uptime of brickbot.\n\nExample '!uptime'")); - break; - case "weather": - receivedMessage.channel.send(helpers.embedify(bot, "weather - check the weather forecast by location.\n\nExample '!weather DCU'")); - break; - case "wiki": - receivedMessage.channel.send(helpers.embedify(bot, "wiki - return a random page from wiki.redbrick.dcu.ie.\n\nExample: '!wiki'")); - break; - } - } else { - receivedMessage.channel.send(helpers.embedify(bot, "Here is the list of brickbot commands:\n • bus \n • coinflip\n • info\n • isitup\n • luas\n • nslookup\n • pwgen\n • pwned\n • room\n • ssl\n • uptime\n • wiki\n • help\n")); - } - } -}; diff --git a/commands/info.js b/commands/info.deprecated.js similarity index 100% rename from commands/info.js rename to commands/info.deprecated.js diff --git a/commands/isitup.js b/commands/isitup.deprecated.js similarity index 100% rename from commands/isitup.js rename to commands/isitup.deprecated.js diff --git a/commands/luas.js b/commands/luas.deprecated.js similarity index 100% rename from commands/luas.js rename to commands/luas.deprecated.js diff --git a/commands/nslookup.js b/commands/nslookup.deprecated.js similarity index 100% rename from commands/nslookup.js rename to commands/nslookup.deprecated.js diff --git a/commands/pwgen.js b/commands/pwgen.deprecated.js similarity index 100% rename from commands/pwgen.js rename to commands/pwgen.deprecated.js diff --git a/commands/pwned.js b/commands/pwned.deprecated.js similarity index 100% rename from commands/pwned.js rename to commands/pwned.deprecated.js diff --git a/commands/register.js b/commands/register.deprecated.js similarity index 100% rename from commands/register.js rename to commands/register.deprecated.js diff --git a/commands/room.js b/commands/room.deprecated.js similarity index 100% rename from commands/room.js rename to commands/room.deprecated.js diff --git a/commands/ssl.js b/commands/ssl.deprecated.js similarity index 100% rename from commands/ssl.js rename to commands/ssl.deprecated.js diff --git a/commands/uptime.js b/commands/uptime.deprecated.js similarity index 100% rename from commands/uptime.js rename to commands/uptime.deprecated.js diff --git a/commands/verify.js b/commands/verify.deprecated.js similarity index 89% rename from commands/verify.js rename to commands/verify.deprecated.js index b4df387..caa5361 100644 --- a/commands/verify.js +++ b/commands/verify.deprecated.js @@ -4,10 +4,12 @@ var ldap = require("ldapjs"); var helpers = require("../helpers/helpers.js"); const ldapClient = ldap.createClient({ - url: "ldap://ldap.internal" + url: process.env.LDAP_HOST }); -const ldapSecret = fs.readFileSync("/etc/ldap.secret", "utf-8"); +const ldapSecret = typeof process.env.LDAP_SECRET_FILE !== "undefined" + ? fs.readFileSync(process.env.LDAP_SECRET_FILE, "utf-8") + : process.env.LDAP_SECRET; module.exports = { verifyCommand: function(bot, args, receivedMessage) { diff --git a/commands/weather.js b/commands/weather.deprecated.js similarity index 100% rename from commands/weather.js rename to commands/weather.deprecated.js diff --git a/commands/wiki.js b/commands/wiki.deprecated.js similarity index 100% rename from commands/wiki.js rename to commands/wiki.deprecated.js diff --git a/helpers/helpers.js b/helpers/helpers.js index 428e9b9..1110f04 100644 --- a/helpers/helpers.js +++ b/helpers/helpers.js @@ -1,25 +1,31 @@ -module.exports = { - argumentsUsedExample: function(bot, receivedMessage, required, example) { - receivedMessage.channel.send(module.exports.embedify(bot, `No ${required} supplied. Try ${example}`)); - }, - noArgumentsUsedExample: function(bot, receivedMessage, example) { - receivedMessage.channel.send(module.exports.embedify(bot, `Too many arguments supplied. Try ${example}`)); - }, - embedify: function(bot, contents) { - return {embed: - { - color: 0xAA0202, - author: { - name: bot.user.username, - icon_url: bot.user.avatarURL - }, - description: contents, - timestamp: new Date(), - footer: { - icon_url: bot.user.avatarURL, - text: "© Redbrick" - } +function argumentsUsedExample(bot, receivedMessage, required, example) { + return embedify(bot, `No ${required} supplied. Try ${example}`); +} + +function embedify(bot, contents) { + return { + embed: { + color: 0xAA0202, + author: { + name: bot.user.username, + icon_url: bot.user.avatarURL + }, + description: contents, + timestamp: new Date(), + footer: { + icon_url: bot.user.avatarURL, + text: "© Redbrick" } - }; - } + } + }; +} + +function tooManyArgs(bot, example) { + return embedify(bot, `Too many arguments supplied. Try ${example}`); +} + +module.exports = { + argumentsUsedExample, + embedify, + tooManyArgs, }; diff --git a/package.json b/package.json index 05b0c56..3b2b12b 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ }, "dependencies": { "discord.js": "^11.5.1", + "dotenv": "^8.2.0", "fs": "^0.0.1-security", "ldapjs": "^1.0.2", "request": "^2.88.0" diff --git a/yarn.lock b/yarn.lock index 5084b3d..fc46f6b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -67,6 +67,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +asn1@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + integrity sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y= + asn1@~0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" @@ -74,6 +79,11 @@ asn1@~0.2.3: dependencies: safer-buffer "~2.1.0" +assert-plus@0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.1.5.tgz#ee74009413002d84cec7219c6ac811812e723160" + integrity sha1-7nQAlBMALYTOxyGcasgRgS5yMWA= + assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" @@ -104,6 +114,13 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== +backoff@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.5.0.tgz#f616eda9d3e4b66b8ca7fca79f695722c5f8e26f" + integrity sha1-9hbtqdPktmuMp/ynn2lXIsX44m8= + dependencies: + precond "0.2" + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -124,6 +141,16 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +bunyan@^1.8.3: + version "1.8.13" + resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.13.tgz#dde6bacd9ebccaedb110f1319f88db3f415ccfeb" + integrity sha512-4zO4iMxZeCpf+95ERsr83nwQr11o1KY2FLhX4wZ6kPXieIVYL3k9eX+N6vbHhFEK5h5O/qCQpfXt7N9VBAIvCA== + optionalDependencies: + dtrace-provider "~0.8" + moment "^2.10.6" + mv "~2" + safe-json-stringify "~1" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -200,7 +227,7 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -dashdash@^1.12.0: +dashdash@^1.12.0, dashdash@^1.14.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= @@ -242,6 +269,18 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dotenv@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" + integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== + +dtrace-provider@~0.8: + version "0.8.8" + resolved "https://registry.yarnpkg.com/dtrace-provider/-/dtrace-provider-0.8.8.tgz#2996d5490c37e1347be263b423ed7b297fb0d97e" + integrity sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg== + dependencies: + nan "^2.14.0" + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -375,6 +414,11 @@ external-editor@^3.0.3: iconv-lite "^0.4.24" tmp "^0.0.33" +extsprintf@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.2.0.tgz#5ad946c22f5b32ba7f8cd7426711c6e8a3fc2529" + integrity sha1-WtlGwi9bMrp/jNdCZxHG6KP8JSk= + extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -471,6 +515,17 @@ glob-parent@^5.0.0: dependencies: is-glob "^4.0.1" +glob@^6.0.1: + version "6.0.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + integrity sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI= + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^7.1.3: version "7.1.4" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" @@ -657,6 +712,30 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" +ldap-filter@0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/ldap-filter/-/ldap-filter-0.2.2.tgz#f2b842be0b86da3352798505b31ebcae590d77d0" + integrity sha1-8rhCvguG2jNSeYUFsx68rlkNd9A= + dependencies: + assert-plus "0.1.5" + +ldapjs@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/ldapjs/-/ldapjs-1.0.2.tgz#544ff7032b7b83c68f0701328d9297aa694340f9" + integrity sha1-VE/3Ayt7g8aPBwEyjZKXqmlDQPk= + dependencies: + asn1 "0.2.3" + assert-plus "^1.0.0" + backoff "^2.5.0" + bunyan "^1.8.3" + dashdash "^1.14.0" + ldap-filter "0.2.2" + once "^1.4.0" + vasync "^1.6.4" + verror "^1.8.1" + optionalDependencies: + dtrace-provider "~0.8" + levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" @@ -692,7 +771,7 @@ mimic-fn@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== -minimatch@^3.0.4: +"minimatch@2 || 3", minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -704,6 +783,11 @@ minimist@0.0.8: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + mkdirp@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" @@ -711,6 +795,18 @@ mkdirp@^0.5.1: dependencies: minimist "0.0.8" +mkdirp@~0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +moment@^2.10.6: + version "2.27.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d" + integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ== + ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -721,11 +817,30 @@ mute-stream@0.0.7: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= +mv@~2: + version "2.1.1" + resolved "https://registry.yarnpkg.com/mv/-/mv-2.1.1.tgz#ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2" + integrity sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI= + dependencies: + mkdirp "~0.5.1" + ncp "~2.0.0" + rimraf "~2.4.0" + +nan@^2.14.0: + version "2.14.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" + integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +ncp@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" + integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M= + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -736,7 +851,7 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -once@^1.3.0: +once@^1.3.0, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= @@ -789,6 +904,11 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +precond@0.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" + integrity sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw= + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -875,6 +995,13 @@ rimraf@2.6.3: dependencies: glob "^7.1.3" +rimraf@~2.4.0: + version "2.4.5" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da" + integrity sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto= + dependencies: + glob "^6.0.1" + run-async@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" @@ -894,6 +1021,11 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.2: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== +safe-json-stringify@~1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz#356e44bc98f1f93ce45df14bcd7c01cda86e0afd" + integrity sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg== + "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -1084,7 +1216,14 @@ v8-compile-cache@^2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== -verror@1.10.0: +vasync@^1.6.4: + version "1.6.4" + resolved "https://registry.yarnpkg.com/vasync/-/vasync-1.6.4.tgz#dfe93616ad0e7ae801b332a9d88bfc5cdc8e1d1f" + integrity sha1-3+k2Fq0OeugBszKp2Iv8XNyOHR8= + dependencies: + verror "1.6.0" + +verror@1.10.0, verror@^1.8.1: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= @@ -1093,6 +1232,13 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +verror@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.6.0.tgz#7d13b27b1facc2e2da90405eb5ea6e5bdd252ea5" + integrity sha1-fROyex+swuLakEBetepuW90lLqU= + dependencies: + extsprintf "1.2.0" + which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" From 63b1dfcf18cdf784cf4737c90ec8503cc0111905 Mon Sep 17 00:00:00 2001 From: benmcmahon100 Date: Sun, 28 Jun 2020 23:26:27 +0100 Subject: [PATCH 03/10] meta: Add new contributing guide and a template for the ServiceConsumerCommand --- .github/ISSUE_TEMPLATE/bug.md | 15 +++++++ CONTRIBUTING.md | 70 ++++++++++++++++++++++----------- ServiceConsumerCommand.js | 42 ++++++++++++++++++++ commands/bus.deprecated.js | 2 +- commands/help.deprecated.js | 57 --------------------------- commands/register.deprecated.js | 6 ++- commands/verify.deprecated.js | 16 ++++++-- 7 files changed, 122 insertions(+), 86 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug.md create mode 100644 ServiceConsumerCommand.js diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md new file mode 100644 index 0000000..a98233a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -0,0 +1,15 @@ +--- +name: Bug +about: Bug found in brickbot +title: 'BUG: ' +labels: bug +--- + +**Title of bug found** + + +**Description of Issue/Unexpected behaviour** + + +**Screenshots if possible** +Please add some screenshots if you have any diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5088237..1385f21 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,43 +18,65 @@ Clone the project with: > git clone https://github.com/theycallmemac/brickbot.git -From here you can install the dependencies (don't worry there aren't many) like so: +Next you should install the dependencies using: -> yarn +> yarn install -You can run the bot by using: +the bot can be ran locally using > node bot.js -You can also run it as a systemd service if you want, checkout the `brickbot.service` file and make changes where appropriate. +If you're doing development - it would be possible to also use `nodemon`, this will watch your fs for changes to the codebase and automatically start the bot -If you don't want to run on bare-metal, then Docker is your friend and there's docker-compose to utilise by running: +### Environment variables -> docker-compose up -d - -### Getting your command into production +Starting the bot requires some environment variables to be set, to get started, it's best to copy the `.env.sample` to `.env` and modify from there -Let's say you have a command you want to add, well this is the section which clarifies how you go about doing that. +> `cp .env.sample .env` -All the code for the commands is hosted at 'faas.jamesmcdermott.ie' as a cluster of serverless functions. Each one of these functions can be found on a given http endpoint. You can try this one out with just `curl`, it should return a link to a random page on the Redbrick wiki: +The only mandatory variables are, and should only ever be, `DISCORD_SECRET` or `DISCORD_SECRET_FILE`, all other vars should fail softly in their given commands -> curl faas.jamesmcdermott.ie/function/wiki -As said in the README, you can write this in any language! The hosted wiki command is written in Python, the hosted 'isitup' command is written in Bash, and the 'bus' and 'luas' commands actually use a Node.js api to query, which is a little anti-serverless, but also not really. We're digressing. No matter what language you prefer, it can be used to extend brickbot functionality! +### The anatomy of a command: -So, let's say you have code, in a repo or a gist for example, all you need to do is tell me where that code lives. We'll take care of it from there, we'll just need the location. +For an update version see `Command.js` -Once this part is done it's pretty trivial. Let's run through an example of adding a command called "coinflip". +This is the base version of a command: -1. Add a `coinflip.js` file in the `commands` directory where the function is inside `module.exports`. There's a few examples of this. -2. Your command needs to make http request to the endpoint at which the code is hosted, in this case it's at `faas.jamesmcdermott.ie/function/coinflip`. -3. Based on the response you get back from the function, add your logic to handle it. there's a function called `receivedMessage.channel.send()` that is used to send your finalised output back to the channel. -4. If you need to write any command specific helper functions, you can just add them outside of `module.exports` in the same file. -5. If you need to write a non specific help function, you can add it to `helpers/helpers/js`. -6. Once it's all done, make sure to add your command to the big switch statement in `bot.js`, otherwise all your work won't get evaluated! While doing this make sure you also add a like help section to the switch statement in `commands/help.js`. -7. Add your command to the function list in `get_test_set_one` ([in this file](https://github.com/theycallmemac/brickbot/blob/master/tests/endpoints.py)) along with it's expected result of 0 to its respective list. -8. Add the command and its creator to the list of functions in the `README.md` file. +```js +class Command { + constructor(bot, opts) { + this.bot = bot; + this.opts = { + args: { + required: 0, + optional: 0 + }, + help: { + blurb: "Command placeholder", + example: "!command" + }, + name: "command", + ...opts + }; + } -### Adding to my OpenFaaS functions + initCommand() { + // Connect to service, create database, etc. + return true; + } -If you want some configs to be added to the instance of OpenFaaS We're running, you can make a PR [here](https://github.com/theycallmemac/brickbot-faas). + execute() { + return helpers.embedify( + this.bot, + "Some command output" + ); + } +} +``` + +Any services that a command uses should be connected to in the `initCommand` function. The command should return true if the services are available and connection succeeds, and false if not. + +If a service becomes unreachable, the command response should reflect this. All commands shall have their status checked periodically, if a command depends on a services it should use the `ServiceConsumerCommand`, which will have a flag for failing connections. these commands shall be reinitialized on a scaling interval of no less than 30 seconds, and no more than daily + +Services consumed by brickbot should be soft failures and self healing to minimize downtime. diff --git a/ServiceConsumerCommand.js b/ServiceConsumerCommand.js new file mode 100644 index 0000000..1b72e2e --- /dev/null +++ b/ServiceConsumerCommand.js @@ -0,0 +1,42 @@ +const Command = require("Command"); +const helpers = require("./helpers/helpers"); + +const timeoutMax = 86400; + +class ServiceConsumerCommand extends Command { + constructor(bot, opts, serviceOpts) { + super(bot, { + ...opts + }); + + this.opts.service = { + retries: 0, + timeout: 60, + ...serviceOpts + }; + } + + // To be used by execute in the event that execution fails due to a service being down. + handleDisconnect() { + const {timeout} = this.opts.service; + setTimeout(this.initCommand, timeout); + } + + initCommand() { + // Connect to service, create database, etc. + this.opts.service.retries++; + this.opts.service.timeout = this.opts.service.timeout * 2; + + // If successful please reset the timeout + return true; + } + + execute() { + return helpers.embedify( + this.bot, + "Some command output" + ); + } +} + +module.exports = ServiceConsumerCommand; diff --git a/commands/bus.deprecated.js b/commands/bus.deprecated.js index 432acc0..39ac607 100644 --- a/commands/bus.deprecated.js +++ b/commands/bus.deprecated.js @@ -59,5 +59,5 @@ function busGetTimeTo(hr, h, min, m) { } module.exports = { - busCommand: function() { } + busCommand }; diff --git a/commands/help.deprecated.js b/commands/help.deprecated.js index cf80eab..e69de29 100644 --- a/commands/help.deprecated.js +++ b/commands/help.deprecated.js @@ -1,57 +0,0 @@ -const helpers = require("../helpers/helpers.js"); - -// module.exports = { -// helpCommand: function (bot, args, receivedMessage) { -// if (args.length > 1) { -// receivedMessage.channel.send(helpers.embedify(bot, "Please specify one single command. Try `!help [command]`")); -// } else if (args.length == 1) { -// switch (args[0]) { -// case "bus": -// receivedMessage.channel.send(helpers.embedify(bot, "bus - check the schedule of a Dublin Bus stop.\n\nExample: '!bus 1635'\n\nThe nearest bus stops to DCU are 7516 (The Helix) and 37 (Ballymun Road)")); -// break; -// case "coinflip": -// receivedMessage.channel.send(helpers.embedify(bot, "coinflip - toss a coin.\n\nExample: '!coinflip'")); -// break; -// case "info": -// receivedMessage.channel.send(helpers.embedify(bot, "info - show brickbot info.\n\nExample: '!info'")); -// break; -// case "isitup": -// receivedMessage.channel.send(helpers.embedify(bot, "isitup - check if a site is up or down.\n\nExample: '!isitup redbrick.dcu.ie'")); -// break; -// case "luas": -// receivedMessage.channel.send(helpers.embedify(bot, "luas - check the schedule of a Luas stop.\n\nExample: '!luas harcourt'")); -// break; -// case "nslookup": -// receivedMessage.channel.send(helpers.embedify(bot, "nslookup - uses nslookup to return any IP address info on domains.\n\nExample: '!nslookup redbrick.dcu.ie'")); -// break; -// case "pwgen": -// receivedMessage.channel.send(helpers.embedify(bot, "pwgen - uses pwgen to generate a password and privately send it to you.\n\nExample: '!pwgen'`")); -// break; -// case "pwned": -// receivedMessage.channel.send(helpers.embedify(bot, "pwned - check if an email has been pwned.\n\nExample: '!pwned bertie@redbrick.dcu.ie'")); -// break; -// case "room": -// receivedMessage.channel.send(helpers.embedify(bot, "room - provides timetable information for a specified room or building in DCU.\n\nExample: '!room GLA.LG26'")); -// break; -// case "ssl": -// receivedMessage.channel.send(helpers.embedify(bot, "ssl - check the certificate info of a website.\n\nExample: '!ssl redbrick.dcu.ie'")); -// break; -// case "uptime": -// receivedMessage.channel.send(helpers.embedify(bot, "uptime - check the uptime of brickbot.\n\nExample '!uptime'")); -// break; -// case "weather": -// receivedMessage.channel.send(helpers.embedify(bot, "weather - check the weather forecast by location.\n\nExample '!weather DCU'")); -// break; -// case "wiki": -// receivedMessage.channel.send(helpers.embedify(bot, "wiki - return a random page from wiki.redbrick.dcu.ie.\n\nExample: '!wiki'")); -// break; -// } -// } else { -// receivedMessage.channel.send(helpers.embedify(bot, "Here is the list of brickbot commands:\n • bus \n • coinflip\n • info\n • isitup\n • luas\n • nslookup\n • pwgen\n • pwned\n • room\n • ssl\n • uptime\n • wiki\n • help\n")); -// } -// } -// }; - -const opts = { - -} diff --git a/commands/register.deprecated.js b/commands/register.deprecated.js index 1d67e12..b016353 100644 --- a/commands/register.deprecated.js +++ b/commands/register.deprecated.js @@ -2,6 +2,10 @@ var helpers = require("../helpers/helpers.js"); module.exports = { registerCommand: function(bot, args, receivedMessage) { - receivedMessage.author.send(helpers.embedify(bot, "To verify, run the following command:\n\n '!verify your-redbrick-username your-dcu-email-address")); + receivedMessage.author.send( + helpers.embedify( + bot, + "To verify, run the following command:\n\n '!verify your-redbrick-username your-dcu-email-address") + ); } }; diff --git a/commands/verify.deprecated.js b/commands/verify.deprecated.js index caa5361..1f8f41d 100644 --- a/commands/verify.deprecated.js +++ b/commands/verify.deprecated.js @@ -25,17 +25,27 @@ module.exports = { ldapClient.bind("cn=root,ou=ldap,o=redbrick", ldapSecret, function(err) { if (err) { - receivedMessage.channel.send(helpers.embedify(bot,"Could not connect to Redbrick LDAP server, please contact a member of Committee.")); + receivedMessage.channel.send( + helpers.embedify( + bot, + "Could not connect to Redbrick LDAP server, please contact a member of Committee." + ) + ); } else { ldapClient.search("o=redbrick", searchOptions, function(error, result) { result.on("searchEntry", function(entry) { if (entry.object.altmail == args[1]) { - const serverNumber = fs.readFileSync("/etc/discord.server.number", "utf-8").replace(/\n$/, ""); + const serverNumber = fs.readFileSync( + "/etc/discord.server.number", + "utf-8" + ).replace(/\n$/, ""); const memberRole = fs.readFileSync("/etc/discord.member.role", "utf-8").replace(/\n$/, ""); bot.guilds.get(serverNumber).members.get(receivedMessage.author.id).addRole(memberRole); receivedMessage.channel.send(helpers.embedify(bot, "Verified!")); } else { - receivedMessage.channel.send(helpers.embedify(bot,"Username and Email Address do not match.")); + receivedMessage.channel.send( + helpers.embedify(bot,"Username and Email Address do not match.") + ); } }); }); From d3822fc3a69a739be356fb2d277c072e7c5f853f Mon Sep 17 00:00:00 2001 From: benmcmahon100 Date: Sat, 4 Jul 2020 20:06:09 +0100 Subject: [PATCH 04/10] layout: Moved bot to it's own class and differentiated between the actualy bot and the discord client/user Signed-off-by: benmcmahon100 --- ServiceConsumerCommand.js | 4 +- bot.js | 158 ++++++++++++++++++++++-------------- commands/coinflip.js | 2 +- commands/help.deprecated.js | 0 commands/help.js | 50 ++++++++++++ helpers/helpers.js | 9 +- 6 files changed, 154 insertions(+), 69 deletions(-) delete mode 100644 commands/help.deprecated.js create mode 100644 commands/help.js diff --git a/ServiceConsumerCommand.js b/ServiceConsumerCommand.js index 1b72e2e..02f3fed 100644 --- a/ServiceConsumerCommand.js +++ b/ServiceConsumerCommand.js @@ -25,7 +25,9 @@ class ServiceConsumerCommand extends Command { initCommand() { // Connect to service, create database, etc. this.opts.service.retries++; - this.opts.service.timeout = this.opts.service.timeout * 2; + this.opts.service.timeout = this.opts.service.timeout * 2 <= timeoutMax + ? this.opts.service.timeout * 2 + : timeoutMax; // If successful please reset the timeout return true; diff --git a/bot.js b/bot.js index fdca9a6..cf11b87 100644 --- a/bot.js +++ b/bot.js @@ -12,79 +12,111 @@ const { require("dotenv").config(); -const bot = new Discord.Client(); -let commands = {}; - -fs.readdirSync(commandDir) - .filter(file => !(RegExp("deprecated", "gi")).test(file)) - .forEach(function (file) { - let command = require(path.join(commandDir, file))(bot); - if(command.initCommand && command.initCommand()) { - commands[command.opts.name] = command; +class Log { + static error(str) { + const dt = new Date(); + process.env.debug + ? console.trace(`${dt.toLocaleDateString()}[${dt.toLocaleTimeString()}]: ${str}`) + : console.trace(str); } -}); - -bot.on("message", (receivedMessage) => { - if (receivedMessage.author === bot.user) { - return; - } - if (receivedMessage.content.startsWith("!")) { - processCommand(receivedMessage); - } -}); - -function testAndExecute(command, args) { - const commandOpts = command.opts; - const {args: commandArgs} = commandOpts; - const {help: commandHelp} = commandOpts; - - if ( - args.length >= commandArgs.required && - args.length <= commandArgs.required + commandArgs.optional - ) { - return command.execute(); - } - else { - switch (false) { - case !args.length < commandArgs.required: - return argumentsUsedExample(bot, commandHelp); - case !args.length > commandArgs.require + commandArgs.optional: - return tooManyArgs(bot, commandHelp); - default: - return `command: ${commandOpts.name} - ${commandHelp.blurb}\nusage: ${commandOpts.help.example}`; - } + static log(str) { + const dt = new Date(); + process.env.debug + ? console.info(`${dt.toLocaleDateString()}[${dt.toLocaleTimeString()}]: ${str}`) + : console.info(str); } } -function processCommand(receivedMessage) { - let fullCommand = receivedMessage.content.substr(1); - let splitCommand = fullCommand.split(" "); - let primaryCommand = splitCommand[0]; - let args = splitCommand.slice(1); +const client_secret_token = typeof process.env.DISCORD_SECRET_FILE !== "undefined" + ? fs.readFileSync(process.env.DISCORD_SECRET_FILE, "utf-8").replace(/\n$/, "") + : process.env.DISCORD_SECRET; + +class BrickBot { + constructor() { + this.client = new Discord.Client(); + this.commands = {}; + } - console.log("Command received: " + primaryCommand); - console.log("args: " + args); + async build() { + const commandReadPromise = fs.promises.readdir(commandDir) + .then(files => { + let commands = {}; + files.filter(file => !(RegExp("deprecated", "gi")).test(file)) + .forEach((file) => { + let commandClass = require(path.join(commandDir, file)); + let command = new commandClass(this); + if(command.initCommand && command.initCommand()) { + commands[command.opts.name] = command; + } + }); + this.commands = commands; + }) + .catch(err => Log.error(`Error reading command dir: ${err}`)) + .finally(() => Log.log("Commands loaded")); - const {channel} = receivedMessage; + const clientLoginPromise = this.client.login(client_secret_token) + .catch(err => Log.error(`Error logging into discord: ${err}`)) + .finally(() => Log.log("Logged into discord")); + + return Promise.all([commandReadPromise, clientLoginPromise]) + .then(() => Log.log("Bot ready")) + .catch(err => Log.error(err)) + .finally(() => this.bindListeners()); + } - console.log(commands[primaryCommand]); + testAndExecute(command, args) { + const commandOpts = command.opts; + const {args: commandArgs} = commandOpts; + const {help: commandHelp} = commandOpts; - if (commands[primaryCommand]) { - channel.send(testAndExecute(commands[primaryCommand], args)); + if (args.length < commandArgs.required) + return argumentsUsedExample(this, commandHelp); + else if (args.length > commandArgs.required + commandArgs.optional) + return tooManyArgs(this, commandHelp.example); + else + return command.execute(args); } - else { - channel.send( - embedify( - bot, - `I don't understand the command. + + processCommand(receivedMessage) { + let fullCommand = receivedMessage.content.substr(1); + let splitCommand = fullCommand.split(" "); + let primaryCommand = splitCommand[0]; + let args = splitCommand.slice(1); + + const {author, channel} = receivedMessage; + Log.log(`${author.username}#${author.discriminator} - ID: ${author.id}`); + Log.log(` Command received: ${primaryCommand}`); + Log.log(` args: ${args.length ? args : "None Supplied"}`); + + if (this.commands[primaryCommand]) { + return channel.send(this.testAndExecute( + this.commands[primaryCommand], + args + )); + } + else { + return channel.send( + embedify( + this, + `I don't understand the command. Try \`!help [command]\`` - ) - ); + ) + ); + } } -} -const bot_secret_token = typeof process.env.DISCORD_SECRET_FILE !== "undefined" - ? fs.readFileSync(process.env.DISCORD_SECRET_FILE, "utf-8").replace(/\n$/, "") - : process.env.DISCORD_SECRET; + bindListeners() { + this.client.on("message", (receivedMessage) => { + if (receivedMessage.author === this.client.user) { + return; + } + if (receivedMessage.content.startsWith("!")) { + this.processCommand(receivedMessage) + .catch(err => Log.error(`Error processing message: ${receivedMessage}\n${err}`)); + } + }); + } +} -bot.login(bot_secret_token); +new BrickBot().build() + .catch(err => `Error constructing brickbot: \n${err}`); diff --git a/commands/coinflip.js b/commands/coinflip.js index e20ed14..3f7c57d 100644 --- a/commands/coinflip.js +++ b/commands/coinflip.js @@ -20,4 +20,4 @@ class CoinFlip extends Command { } } -module.exports = (bot) => new CoinFlip(bot); +module.exports = CoinFlip; diff --git a/commands/help.deprecated.js b/commands/help.deprecated.js deleted file mode 100644 index e69de29..0000000 diff --git a/commands/help.js b/commands/help.js new file mode 100644 index 0000000..2127a8e --- /dev/null +++ b/commands/help.js @@ -0,0 +1,50 @@ +const helpers = require("../helpers/helpers.js"); +const Command = require("../Command"); + +class Help extends Command { + constructor(bot) { + super(bot, { + args: { + optional: 1 + }, + help: { + // eslint-disable-next-line max-len + blurb: "This command can be used to get the full list of commands, or given and argument, the help information for that command", + example: "!help " + }, + name: "help" + }); + } + + execute(args) { + const commands = this.bot.commands; + let ret = Object.keys(commands) + .sort() + .map((key) => { + const commandBlurb = commands[key].opts.help.blurb; + const commandName = commands[key].opts.name; + return `\`${commandName}\`\n└ ${commandBlurb}`; + }) + .join("\n"); + + if (args.length === 1) { + const command = commands[args[0]]; + if(command) { + const {name, help} = command.opts; + ret = `\`${name}\`: ${help.blurb}\n\n**Example**:\n\`\`\`${help.example}\`\`\``; + } + else { + ret = "command not found"; + } + } + + ret = helpers.embedify( + this.bot, + ret + ); + + return ret; + } +} + +module.exports = Help; diff --git a/helpers/helpers.js b/helpers/helpers.js index 1110f04..dcc8caa 100644 --- a/helpers/helpers.js +++ b/helpers/helpers.js @@ -3,17 +3,18 @@ function argumentsUsedExample(bot, receivedMessage, required, example) { } function embedify(bot, contents) { + const {client} = bot; return { embed: { color: 0xAA0202, author: { - name: bot.user.username, - icon_url: bot.user.avatarURL + name: client.user.username, + icon_url: client.user.avatarURL }, description: contents, timestamp: new Date(), footer: { - icon_url: bot.user.avatarURL, + icon_url: client.user.avatarURL, text: "© Redbrick" } } @@ -21,7 +22,7 @@ function embedify(bot, contents) { } function tooManyArgs(bot, example) { - return embedify(bot, `Too many arguments supplied. Try ${example}`); + return embedify(bot, `Too many arguments supplied, try:\n\`${example}\``); } module.exports = { From 633f2b0414b0b85213d4c27962688ec45492639c Mon Sep 17 00:00:00 2001 From: benmcmahon100 Date: Sat, 4 Jul 2020 20:41:01 +0100 Subject: [PATCH 05/10] meta: Bump node version in tests and remove python tests --- .circleci/config.yml | 23 +++----------- package.json | 10 +++--- tests/endpoints.py | 76 -------------------------------------------- 3 files changed, 9 insertions(+), 100 deletions(-) delete mode 100644 tests/endpoints.py diff --git a/.circleci/config.yml b/.circleci/config.yml index 9f751f6..c3d44c7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ version: 2 jobs: build: docker: - - image: circleci/node:8.10 + - image: circleci/node:11 working_directory: ~/brickbot steps: - checkout @@ -20,7 +20,7 @@ jobs: key: v1-dependencies-{{ checksum "package.json" }} lint: docker: - - image: circleci/node:8.10 + - image: circleci/node:11 environment: ENV: CI working_directory: ~/brickbot @@ -28,30 +28,15 @@ jobs: - checkout - run: name: Install the latest eslint - command: sudo npm install -g eslint + command: npm install - run: name: eslint test command: yarn lint prefix: tests - endpoints: - docker: - - image: circleci/python:3.6.4 - environment: - ENV: CI - working_directory: ~/brickbot - steps: - - checkout - - run: - name: install python requests - command: sudo pip3 install requests - - run: - name: endpoints test - command: python3 tests/endpoints.py - prefix: tests + workflows: version: 2 workflow: jobs: - build - lint - - endpoints diff --git a/package.json b/package.json index 3b2b12b..3557643 100644 --- a/package.json +++ b/package.json @@ -4,17 +4,17 @@ "description": "A bot for the Redbrick Discord server, made extendible by OpenFaaS.", "main": "bot.js", "scripts": { - "lint": "eslint --ignore-path .gitignore --fix .", + "lint": "npx eslint --ignore-path .gitignore --fix .", "start": "echo '> Running brickbot ...' && node bot.js" }, "repository": { "type": "git", - "url": "git+https://github.com/theycallmemac/brickbot.git" + "url": "git+https://github.com/redbrick/brickbot.git" }, - "author": "James McDermott (theycallmemac)", + "author": "Redbrick DCU", "license": "ISC", "bugs": { - "url": "https://github.com/theycallmemac/brickbot/issues" + "url": "https://github.com/redbrick/brickbot/issues" }, "dependencies": { "discord.js": "^11.5.1", @@ -23,7 +23,7 @@ "ldapjs": "^1.0.2", "request": "^2.88.0" }, - "homepage": "github.com/theycallmemac/brickbot", + "homepage": "github.com/redbrick/brickbot", "devDependencies": { "eslint": "^6.1.0" } diff --git a/tests/endpoints.py b/tests/endpoints.py deleted file mode 100644 index 3944dc9..0000000 --- a/tests/endpoints.py +++ /dev/null @@ -1,76 +0,0 @@ -import requests -import ssl -import socket -def check_base_url(): - response = requests.get("https://faas.jamesmcdermott.ie") - if response.status_code == 401: - print("Passed") - else: - print("Failed") - -def check_ssl_cert(): - try: - hostname = 'faas.jamesmcdermott.ie' - ctx = ssl.create_default_context() - s = ctx.wrap_socket(socket.socket(), server_hostname=hostname) - s.connect((hostname, 443)) - cert = s.getpeercert() - subject = dict(x[0] for x in cert['subject']) - issued_to = subject['commonName'] - if issued_to == "faas.jamesmcdermott.ie": - print("Passed") - else: - print(Failed) - except ssl.CertificateError: - print("Failed") - -def get_test_set_one(): - functions = ["coinflip", "isitup", "transport", "nslookup", "pwgen", "certinfo", "haveibeenpwned", "wiki", "uptime", "dcurooms", "weather", "info"] - expected_results = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - return functions, expected_results - -def get_test_set_two(): - functions = ["notacommand", "30-50_feral_hogs", "transport", "nslookup", "randomCommand", "pwgen", "haveibeenpwned", "wikipedia_command"] - expected_results = [1, 1, 0, 0, 1, 0, 0, 1] - return functions, expected_results - -def get_test_set_three(): - functions = ["commandoesntexist", "thisshouldfail", "nothing_to_see_here", "command"] - expected_results = [1, 1, 1, 1] - return functions, expected_results - -def run_test_set(functions, expected_results): - actual_results = [] - for function_name in functions: - response = requests.get(f"https://faas.jamesmcdermott.ie/function/{function_name.strip()}") - check_endpoint(function_name, response, actual_results) - print(check_results(expected_results, actual_results)) - -def check_endpoint(function_name, response, actual_results): - if f"error finding function {function_name}: server returned non-200 status code (404) for function, {function_name}" == response.text: - actual_results.append(1) - else: - actual_results.append(0) - -def check_results(expected_results, actual_results): - if expected_results != actual_results: - print(f"Expected: {expected_results}") - print(f"Actually got: {actual_results}") - return "Failed" - else: - return "Passed" - -def main(): - check_base_url() - check_ssl_cert() - - functions, expected_results = get_test_set_one() - run_test_set(functions, expected_results) - functions, expected_results = get_test_set_two() - run_test_set(functions, expected_results) - functions, expected_results = get_test_set_three() - run_test_set(functions, expected_results) - -if __name__ == "__main__": - main() - From 287ab5d52f0c15f9c42aaafb3cee0efc3d7003b9 Mon Sep 17 00:00:00 2001 From: benmcmahon100 Date: Sat, 4 Jul 2020 22:14:31 +0100 Subject: [PATCH 06/10] struct: Add src file for neatness and start adding mail and registration --- brickbot.service | 13 --- commands/register.deprecated.js | 11 --- package.json | 10 +- Command.js => src/Command.js | 1 + .../ServiceConsumerCommand.js | 2 +- bot.js => src/bot.js | 94 +++++++++++++------ {commands => src/commands}/bus.deprecated.js | 0 {commands => src/commands}/coinflip.js | 0 {commands => src/commands}/help.js | 0 {commands => src/commands}/info.deprecated.js | 0 .../commands}/isitup.deprecated.js | 0 {commands => src/commands}/luas.deprecated.js | 0 .../commands}/nslookup.deprecated.js | 0 .../commands}/pwgen.deprecated.js | 0 .../commands}/pwned.deprecated.js | 0 src/commands/register.js | 27 ++++++ {commands => src/commands}/room.deprecated.js | 0 {commands => src/commands}/ssl.deprecated.js | 0 .../commands}/uptime.deprecated.js | 0 .../commands}/verify.deprecated.js | 6 +- .../commands}/weather.deprecated.js | 0 {commands => src/commands}/wiki.deprecated.js | 0 {helpers => src/helpers}/helpers.js | 8 +- yarn.lock | 87 ++++++++++++++++- 24 files changed, 195 insertions(+), 64 deletions(-) delete mode 100644 brickbot.service delete mode 100644 commands/register.deprecated.js rename Command.js => src/Command.js (96%) rename ServiceConsumerCommand.js => src/ServiceConsumerCommand.js (96%) rename bot.js => src/bot.js (65%) rename {commands => src/commands}/bus.deprecated.js (100%) rename {commands => src/commands}/coinflip.js (100%) rename {commands => src/commands}/help.js (100%) rename {commands => src/commands}/info.deprecated.js (100%) rename {commands => src/commands}/isitup.deprecated.js (100%) rename {commands => src/commands}/luas.deprecated.js (100%) rename {commands => src/commands}/nslookup.deprecated.js (100%) rename {commands => src/commands}/pwgen.deprecated.js (100%) rename {commands => src/commands}/pwned.deprecated.js (100%) create mode 100644 src/commands/register.js rename {commands => src/commands}/room.deprecated.js (100%) rename {commands => src/commands}/ssl.deprecated.js (100%) rename {commands => src/commands}/uptime.deprecated.js (100%) rename {commands => src/commands}/verify.deprecated.js (89%) rename {commands => src/commands}/weather.deprecated.js (100%) rename {commands => src/commands}/wiki.deprecated.js (100%) rename {helpers => src/helpers}/helpers.js (76%) diff --git a/brickbot.service b/brickbot.service deleted file mode 100644 index 2fa5198..0000000 --- a/brickbot.service +++ /dev/null @@ -1,13 +0,0 @@ -[Unit] -Description=brickbot -After=multi-user.target - -[Service] -ExecStart=/usr/bin/node /home/brickbot/brickbot/bot.js -Restart=always -RestartSec=15 -RestartPreventExitStatus=0 -TimeoutStopSec=10 - -[Install] -WantedBy=multi-user.target diff --git a/commands/register.deprecated.js b/commands/register.deprecated.js deleted file mode 100644 index b016353..0000000 --- a/commands/register.deprecated.js +++ /dev/null @@ -1,11 +0,0 @@ -var helpers = require("../helpers/helpers.js"); - -module.exports = { - registerCommand: function(bot, args, receivedMessage) { - receivedMessage.author.send( - helpers.embedify( - bot, - "To verify, run the following command:\n\n '!verify your-redbrick-username your-dcu-email-address") - ); - } -}; diff --git a/package.json b/package.json index 3557643..72cb7e8 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name": "brickbot", - "version": "1.0.0", + "version": "2.0.0", "description": "A bot for the Redbrick Discord server, made extendible by OpenFaaS.", - "main": "bot.js", + "main": "src/bot.js", "scripts": { - "lint": "npx eslint --ignore-path .gitignore --fix .", - "start": "echo '> Running brickbot ...' && node bot.js" + "lint": "npx eslint --ignore-path .gitignore --fix src/.", + "start": "echo '> Running brickbot ...' && node src/bot.js" }, "repository": { "type": "git", @@ -20,7 +20,9 @@ "discord.js": "^11.5.1", "dotenv": "^8.2.0", "fs": "^0.0.1-security", + "jsonwebtoken": "^8.5.1", "ldapjs": "^1.0.2", + "nodemailer": "^6.4.10", "request": "^2.88.0" }, "homepage": "github.com/redbrick/brickbot", diff --git a/Command.js b/src/Command.js similarity index 96% rename from Command.js rename to src/Command.js index 57e288b..3266ad5 100644 --- a/Command.js +++ b/src/Command.js @@ -12,6 +12,7 @@ class Command { blurb: "Command placeholder", example: "!command" }, + isDM: false, name: "command", ...opts }; diff --git a/ServiceConsumerCommand.js b/src/ServiceConsumerCommand.js similarity index 96% rename from ServiceConsumerCommand.js rename to src/ServiceConsumerCommand.js index 02f3fed..7595b67 100644 --- a/ServiceConsumerCommand.js +++ b/src/ServiceConsumerCommand.js @@ -1,4 +1,4 @@ -const Command = require("Command"); +const Command = require("src/Command"); const helpers = require("./helpers/helpers"); const timeoutMax = 86400; diff --git a/bot.js b/src/bot.js similarity index 65% rename from bot.js rename to src/bot.js index cf11b87..cd110fc 100644 --- a/bot.js +++ b/src/bot.js @@ -1,16 +1,24 @@ +// Std lib packages const fs = require("fs"); const path = require("path"); - +// External packages const Discord = require("discord.js"); -const commandDir = `${__dirname}/commands`; - +const JWT = require("jsonwebtoken"); +const NodeMailer = require("nodemailer"); +// Our libs const { argumentsUsedExample, embedify, tooManyArgs } = require("./helpers/helpers.js"); - +// Load .env file info into process.env +// will clobber anything set in the parent environment require("dotenv").config(); +// Constants +const commandDir = `${__dirname}/commands`; +const client_secret_token = typeof process.env.DISCORD_SECRET_FILE !== "undefined" + ? fs.readFileSync(process.env.DISCORD_SECRET_FILE, "utf-8").replace(/\n$/, "") + : process.env.DISCORD_SECRET; class Log { static error(str) { @@ -27,13 +35,20 @@ class Log { } } -const client_secret_token = typeof process.env.DISCORD_SECRET_FILE !== "undefined" - ? fs.readFileSync(process.env.DISCORD_SECRET_FILE, "utf-8").replace(/\n$/, "") - : process.env.DISCORD_SECRET; - class BrickBot { constructor() { - this.client = new Discord.Client(); + this.discordClient = new Discord.Client(); + // Unlike the discord client - the mail client only auths on send. + // email is transactional like that + this.mailClient = NodeMailer.createTransport({ + host: process.env.MAIL_HOST, + port: parseInt(process.env.MAIL_PORT, 10), + secure: Boolean(process.env.MAIL_SECURE), // true for 465, false for other ports + auth: { + user: process.env.MAIL_USER, // generated ethereal user + pass: process.env.MAIL_PASS, // generated ethereal password + }, + }); this.commands = {}; } @@ -54,7 +69,7 @@ class BrickBot { .catch(err => Log.error(`Error reading command dir: ${err}`)) .finally(() => Log.log("Commands loaded")); - const clientLoginPromise = this.client.login(client_secret_token) + const clientLoginPromise = this.discordClient.login(client_secret_token) .catch(err => Log.error(`Error logging into discord: ${err}`)) .finally(() => Log.log("Logged into discord")); @@ -64,19 +79,6 @@ class BrickBot { .finally(() => this.bindListeners()); } - testAndExecute(command, args) { - const commandOpts = command.opts; - const {args: commandArgs} = commandOpts; - const {help: commandHelp} = commandOpts; - - if (args.length < commandArgs.required) - return argumentsUsedExample(this, commandHelp); - else if (args.length > commandArgs.required + commandArgs.optional) - return tooManyArgs(this, commandHelp.example); - else - return command.execute(args); - } - processCommand(receivedMessage) { let fullCommand = receivedMessage.content.substr(1); let splitCommand = fullCommand.split(" "); @@ -89,10 +91,13 @@ class BrickBot { Log.log(` args: ${args.length ? args : "None Supplied"}`); if (this.commands[primaryCommand]) { - return channel.send(this.testAndExecute( + let ret = this.testAndExecute( this.commands[primaryCommand], args - )); + ); + return this.commands[primaryCommand].isDM + ? author.send(ret) + : channel.send(ret); } else { return channel.send( @@ -105,9 +110,44 @@ class BrickBot { } } + testAndExecute(command, args) { + const commandOpts = command.opts; + const {args: commandArgs} = commandOpts; + const {help: commandHelp} = commandOpts; + + if (args.length < commandArgs.required) + return argumentsUsedExample(this, commandHelp); + else if (args.length > commandArgs.required + commandArgs.optional) + return tooManyArgs(this, commandHelp.example); + else + return command.execute(args); + } + + async tokenFactory(discordUsername, claimedUsername, claimedEmail) { + return new Promise(function (resolve, reject) { + return JWT.sign( + { + discordUsername, + claimedUsername, + claimedEmail, + claimedDate: (new Date()).toUTCString() + }, + process.env.JWT_SECRET, + { + // End of the current academic year can be defined as after May + expiresIn: `${(new Date()).getUTCFullYear()}-06-01T00:00:00Z` + }, + function (err, token) { + if(err) reject(err); + resolve(token); + } + ); + }); + } + bindListeners() { - this.client.on("message", (receivedMessage) => { - if (receivedMessage.author === this.client.user) { + this.discordClient.on("message", (receivedMessage) => { + if (receivedMessage.author === this.discordClient.user) { return; } if (receivedMessage.content.startsWith("!")) { diff --git a/commands/bus.deprecated.js b/src/commands/bus.deprecated.js similarity index 100% rename from commands/bus.deprecated.js rename to src/commands/bus.deprecated.js diff --git a/commands/coinflip.js b/src/commands/coinflip.js similarity index 100% rename from commands/coinflip.js rename to src/commands/coinflip.js diff --git a/commands/help.js b/src/commands/help.js similarity index 100% rename from commands/help.js rename to src/commands/help.js diff --git a/commands/info.deprecated.js b/src/commands/info.deprecated.js similarity index 100% rename from commands/info.deprecated.js rename to src/commands/info.deprecated.js diff --git a/commands/isitup.deprecated.js b/src/commands/isitup.deprecated.js similarity index 100% rename from commands/isitup.deprecated.js rename to src/commands/isitup.deprecated.js diff --git a/commands/luas.deprecated.js b/src/commands/luas.deprecated.js similarity index 100% rename from commands/luas.deprecated.js rename to src/commands/luas.deprecated.js diff --git a/commands/nslookup.deprecated.js b/src/commands/nslookup.deprecated.js similarity index 100% rename from commands/nslookup.deprecated.js rename to src/commands/nslookup.deprecated.js diff --git a/commands/pwgen.deprecated.js b/src/commands/pwgen.deprecated.js similarity index 100% rename from commands/pwgen.deprecated.js rename to src/commands/pwgen.deprecated.js diff --git a/commands/pwned.deprecated.js b/src/commands/pwned.deprecated.js similarity index 100% rename from commands/pwned.deprecated.js rename to src/commands/pwned.deprecated.js diff --git a/src/commands/register.js b/src/commands/register.js new file mode 100644 index 0000000..ee8376e --- /dev/null +++ b/src/commands/register.js @@ -0,0 +1,27 @@ +const helpers = require("../helpers/helpers.js"); +const Command = require("../Command"); + +class CoinFlip extends Command { + constructor(bot) { + super(bot, { + help: { + blurb: "This command acts as your key to the discord server", + example: "!register" + }, + isDM: true, + name: "register" + }); + } + + execute() { + return helpers.embedify( + this.bot, + `To verify, run the following command: + \`\`\`!verify \`\`\` + If you are not a member and would like to register, please run this instead + \`\`\`!signup \`\`\` ` + ); + } +} + +module.exports = CoinFlip; diff --git a/commands/room.deprecated.js b/src/commands/room.deprecated.js similarity index 100% rename from commands/room.deprecated.js rename to src/commands/room.deprecated.js diff --git a/commands/ssl.deprecated.js b/src/commands/ssl.deprecated.js similarity index 100% rename from commands/ssl.deprecated.js rename to src/commands/ssl.deprecated.js diff --git a/commands/uptime.deprecated.js b/src/commands/uptime.deprecated.js similarity index 100% rename from commands/uptime.deprecated.js rename to src/commands/uptime.deprecated.js diff --git a/commands/verify.deprecated.js b/src/commands/verify.deprecated.js similarity index 89% rename from commands/verify.deprecated.js rename to src/commands/verify.deprecated.js index 1f8f41d..eae7b6f 100644 --- a/commands/verify.deprecated.js +++ b/src/commands/verify.deprecated.js @@ -3,7 +3,7 @@ var ldap = require("ldapjs"); var helpers = require("../helpers/helpers.js"); -const ldapClient = ldap.createClient({ +const ldapdiscordClient = ldap.creatediscordClient({ url: process.env.LDAP_HOST }); @@ -23,7 +23,7 @@ module.exports = { filter: (`uid=${args[0]}`) }; - ldapClient.bind("cn=root,ou=ldap,o=redbrick", ldapSecret, function(err) { + ldapdiscordClient.bind("cn=root,ou=ldap,o=redbrick", ldapSecret, function(err) { if (err) { receivedMessage.channel.send( helpers.embedify( @@ -32,7 +32,7 @@ module.exports = { ) ); } else { - ldapClient.search("o=redbrick", searchOptions, function(error, result) { + ldapdiscordClient.search("o=redbrick", searchOptions, function(error, result) { result.on("searchEntry", function(entry) { if (entry.object.altmail == args[1]) { const serverNumber = fs.readFileSync( diff --git a/commands/weather.deprecated.js b/src/commands/weather.deprecated.js similarity index 100% rename from commands/weather.deprecated.js rename to src/commands/weather.deprecated.js diff --git a/commands/wiki.deprecated.js b/src/commands/wiki.deprecated.js similarity index 100% rename from commands/wiki.deprecated.js rename to src/commands/wiki.deprecated.js diff --git a/helpers/helpers.js b/src/helpers/helpers.js similarity index 76% rename from helpers/helpers.js rename to src/helpers/helpers.js index dcc8caa..59831be 100644 --- a/helpers/helpers.js +++ b/src/helpers/helpers.js @@ -3,18 +3,18 @@ function argumentsUsedExample(bot, receivedMessage, required, example) { } function embedify(bot, contents) { - const {client} = bot; + const {discordClient} = bot; return { embed: { color: 0xAA0202, author: { - name: client.user.username, - icon_url: client.user.avatarURL + name: discordClient.user.username, + icon_url: discordClient.user.avatarURL }, description: contents, timestamp: new Date(), footer: { - icon_url: client.user.avatarURL, + icon_url: discordClient.user.avatarURL, text: "© Redbrick" } } diff --git a/yarn.lock b/yarn.lock index fc46f6b..e6c680f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -141,6 +141,11 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= + bunyan@^1.8.3: version "1.8.13" resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.13.tgz#dde6bacd9ebccaedb110f1319f88db3f415ccfeb" @@ -289,6 +294,13 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -702,6 +714,22 @@ json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= +jsonwebtoken@^8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -712,6 +740,23 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + ldap-filter@0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/ldap-filter/-/ldap-filter-0.2.2.tgz#f2b842be0b86da3352798505b31ebcae590d77d0" @@ -744,6 +789,41 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + lodash@^4.17.12, lodash@^4.17.14: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" @@ -846,6 +926,11 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +nodemailer@^6.4.10: + version "6.4.10" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.4.10.tgz#f4c8dc7991c57f41fd081bef224ef01f7065143d" + integrity sha512-j+pS9CURhPgk6r0ENr7dji+As2xZiHSvZeVnzKniLOw1eRAyM/7flP0u65tCnsapV8JFu+t0l/5VeHsCZEeh9g== + oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" @@ -1031,7 +1116,7 @@ safe-json-stringify@~1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -semver@^5.5.0: +semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== From f41897ba610214589eda0c22fccaf8f0e08fd84a Mon Sep 17 00:00:00 2001 From: benmcmahon100 Date: Sat, 4 Jul 2020 23:49:23 +0100 Subject: [PATCH 07/10] meta: Change user for systemd unit to unprivileged account --- brickbot.service | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 brickbot.service diff --git a/brickbot.service b/brickbot.service new file mode 100644 index 0000000..d899c1a --- /dev/null +++ b/brickbot.service @@ -0,0 +1,15 @@ +[Unit] +Description=brickbot +After=multi-user.target + +[Service] +ExecStart=/usr/bin/node /home/brickbot/brickbot/src/bot.js +Restart=always +RestartSec=15 +RestartPreventExitStatus=0 +TimeoutStopSec=10 +User=brickbot +Group=brickbot + +[Install] +WantedBy=multi-user.target From ab45498fcc44832f02b1a097f8fac5f62283cf12 Mon Sep 17 00:00:00 2001 From: benmcmahon100 Date: Wed, 15 Jul 2020 22:25:21 +0100 Subject: [PATCH 08/10] refac: Fixup helpers and args system --- .eslintignore | 3 + .eslintrc.json | 4 + CONTRIBUTING.md | 2 +- package.json | 1 + src/Command.js | 17 +++-- src/ServiceConsumerCommand.js | 6 +- src/bot.js | 44 ++++++----- src/commands/bus.deprecated.js | 5 +- src/commands/coinflip.js | 4 +- src/commands/help.js | 13 ++-- src/commands/info.deprecated.js | 5 +- src/commands/isitup.deprecated.js | 5 +- src/commands/luas.deprecated.js | 5 +- src/commands/nslookup.deprecated.js | 5 +- src/commands/pwgen.deprecated.js | 5 +- src/commands/pwned.deprecated.js | 4 +- src/commands/register.js | 12 +-- src/commands/room.deprecated.js | 5 +- src/commands/signup.js | 41 +++++++++++ src/commands/ssl.deprecated.js | 5 +- src/commands/uptime.deprecated.js | 5 +- src/commands/verify.deprecated.js | 6 +- src/commands/weather.deprecated.js | 2 +- src/commands/wiki.deprecated.js | 5 +- src/helpers/helpers.js | 110 ++++++++++++++++++++++------ 25 files changed, 220 insertions(+), 99 deletions(-) create mode 100644 .eslintignore create mode 100644 src/commands/signup.js diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..db0046e --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +*.deprecated.js +node_modules/ +.env diff --git a/.eslintrc.json b/.eslintrc.json index f3b3680..05788d3 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -30,6 +30,10 @@ "semi": [ "error", "always" + ], + "brace-style": [ + "error", + "1tbs" ] } } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1385f21..fe9e9f4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -67,7 +67,7 @@ class Command { } execute() { - return helpers.embedify( + return Utils.embed( this.bot, "Some command output" ); diff --git a/package.json b/package.json index 72cb7e8..927dee7 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "jsonwebtoken": "^8.5.1", "ldapjs": "^1.0.2", "nodemailer": "^6.4.10", + "nodemon": "^2.0.4", "request": "^2.88.0" }, "homepage": "github.com/redbrick/brickbot", diff --git a/src/Command.js b/src/Command.js index 3266ad5..1e8a182 100644 --- a/src/Command.js +++ b/src/Command.js @@ -1,21 +1,22 @@ -const helpers = require("./helpers/helpers.js"); +const { + Utils +} = require("./helpers/helpers.js"); class Command { constructor(bot, opts) { this.bot = bot; - this.opts = { + this.opts = Utils.mergeObj({ args: { - required: 0, - optional: 0 + required: [], + optional: [] }, help: { blurb: "Command placeholder", example: "!command" }, isDM: false, - name: "command", - ...opts - }; + name: "command" + }, opts); } initCommand() { @@ -24,7 +25,7 @@ class Command { } execute() { - return helpers.embedify( + return Utils.embed( this.bot, "Some command output" ); diff --git a/src/ServiceConsumerCommand.js b/src/ServiceConsumerCommand.js index 7595b67..bb16931 100644 --- a/src/ServiceConsumerCommand.js +++ b/src/ServiceConsumerCommand.js @@ -1,5 +1,7 @@ const Command = require("src/Command"); -const helpers = require("./helpers/helpers"); +const { + Utils +} = require("./helpers/helpers"); const timeoutMax = 86400; @@ -34,7 +36,7 @@ class ServiceConsumerCommand extends Command { } execute() { - return helpers.embedify( + return Utils.embed( this.bot, "Some command output" ); diff --git a/src/bot.js b/src/bot.js index cd110fc..570baa5 100644 --- a/src/bot.js +++ b/src/bot.js @@ -7,9 +7,8 @@ const JWT = require("jsonwebtoken"); const NodeMailer = require("nodemailer"); // Our libs const { - argumentsUsedExample, - embedify, - tooManyArgs + Utils, + Responses, } = require("./helpers/helpers.js"); // Load .env file info into process.env // will clobber anything set in the parent environment @@ -23,13 +22,15 @@ const client_secret_token = typeof process.env.DISCORD_SECRET_FILE !== "undefine class Log { static error(str) { const dt = new Date(); - process.env.debug + // eslint-disable-next-line no-extra-boolean-cast + Boolean(process.env.DEBUG) ? console.trace(`${dt.toLocaleDateString()}[${dt.toLocaleTimeString()}]: ${str}`) : console.trace(str); } static log(str) { const dt = new Date(); - process.env.debug + // eslint-disable-next-line no-extra-boolean-cast + Boolean(process.env.DEBUG) ? console.info(`${dt.toLocaleDateString()}[${dt.toLocaleTimeString()}]: ${str}`) : console.info(str); } @@ -45,8 +46,8 @@ class BrickBot { port: parseInt(process.env.MAIL_PORT, 10), secure: Boolean(process.env.MAIL_SECURE), // true for 465, false for other ports auth: { - user: process.env.MAIL_USER, // generated ethereal user - pass: process.env.MAIL_PASS, // generated ethereal password + user: process.env.MAIL_USER, + pass: process.env.MAIL_PASS, }, }); this.commands = {}; @@ -67,7 +68,10 @@ class BrickBot { this.commands = commands; }) .catch(err => Log.error(`Error reading command dir: ${err}`)) - .finally(() => Log.log("Commands loaded")); + .finally(() => { + const cmds = Object.keys(this.commands).map(cmd => ` - ${cmd}\n`).join(""); + Log.log(`Commands loaded:\n${cmds}`); + }); const clientLoginPromise = this.discordClient.login(client_secret_token) .catch(err => Log.error(`Error logging into discord: ${err}`)) @@ -98,13 +102,19 @@ class BrickBot { return this.commands[primaryCommand].isDM ? author.send(ret) : channel.send(ret); - } - else { + } else { + // Command not found or successfully loaded + // TODO: [v2.1] - differentiate between: + // - command failed to construct and init + // - service dependancy + // - is rate limit/black listing us + // - is down + // - command doesn't exist + return channel.send( - embedify( + Utils.embed( this, - `I don't understand the command. - Try \`!help [command]\`` + "I don't understand the command.\n\nTry:\n```!help```\nOr:\n```!help [command]```" ) ); } @@ -115,10 +125,10 @@ class BrickBot { const {args: commandArgs} = commandOpts; const {help: commandHelp} = commandOpts; - if (args.length < commandArgs.required) - return argumentsUsedExample(this, commandHelp); - else if (args.length > commandArgs.required + commandArgs.optional) - return tooManyArgs(this, commandHelp.example); + if (args.length < commandArgs.required.length) + return Responses.argumentsUsedExample(this, commandHelp, commandArgs.required, commandHelp.example); + else if (args.length > commandArgs.required.length + commandArgs.optional.length) + return Responses.tooManyArgs(this, commandHelp.example); else return command.execute(args); } diff --git a/src/commands/bus.deprecated.js b/src/commands/bus.deprecated.js index 39ac607..03d83eb 100644 --- a/src/commands/bus.deprecated.js +++ b/src/commands/bus.deprecated.js @@ -4,8 +4,7 @@ const helpers = require("../helpers/helpers.js"); function busCommand (bot, args, receivedMessage) { if (args.length === 0) { helpers.argumentsUsedExample(bot, receivedMessage, "stop", "!bus 7571"); - } - else if (args.length > 0) { + } else if (args.length > 0) { request.post({ url: "https://faas.jamesmcdermott.ie/function/transport", body: "127.0.0.1:8000/bus/stop/" + args @@ -28,7 +27,7 @@ function busCommand (bot, args, receivedMessage) { `${LineName} (${DestinationName}) - ${timeTo}\n`; } - receivedMessage.author.send(helpers.embedify(bot, schedule)) + receivedMessage.author.send(Utils.embed(bot, schedule)) .catch(err => console.trace(`Exception occurred in sending message ${err}`)); }); } diff --git a/src/commands/coinflip.js b/src/commands/coinflip.js index 3f7c57d..849a7dc 100644 --- a/src/commands/coinflip.js +++ b/src/commands/coinflip.js @@ -1,4 +1,4 @@ -const helpers = require("../helpers/helpers.js"); +const { Utils } = require("../helpers/helpers.js"); const Command = require("../Command"); class CoinFlip extends Command { @@ -13,7 +13,7 @@ class CoinFlip extends Command { } execute() { - return helpers.embedify( + return Utils.embed( this.bot, "Came up " + `${Math.random() >= 0.5 ? "heads" : "tails"}` ); diff --git a/src/commands/help.js b/src/commands/help.js index 2127a8e..16d2fb7 100644 --- a/src/commands/help.js +++ b/src/commands/help.js @@ -1,11 +1,15 @@ -const helpers = require("../helpers/helpers.js"); +const { + Utils +} = require("../helpers/helpers.js"); const Command = require("../Command"); class Help extends Command { constructor(bot) { super(bot, { args: { - optional: 1 + optional: [ + "command name" + ] }, help: { // eslint-disable-next-line max-len @@ -32,13 +36,12 @@ class Help extends Command { if(command) { const {name, help} = command.opts; ret = `\`${name}\`: ${help.blurb}\n\n**Example**:\n\`\`\`${help.example}\`\`\``; - } - else { + } else { ret = "command not found"; } } - ret = helpers.embedify( + ret = Utils.embed( this.bot, ret ); diff --git a/src/commands/info.deprecated.js b/src/commands/info.deprecated.js index 6008b94..2ab37e7 100644 --- a/src/commands/info.deprecated.js +++ b/src/commands/info.deprecated.js @@ -5,13 +5,12 @@ module.exports = { infoCommand: function(bot, args, receivedMessage) { if (args.length > 0) { helpers.noArgumentsUsedExample(bot, receivedMessage, "!info"); - } - else if (args.length == 0) { + } else if (args.length == 0) { request.get({ url: "https://faas.jamesmcdermott.ie/function/info", }, function(error, response, body) { - receivedMessage.channel.send(helpers.embedify(bot, body)); + receivedMessage.channel.send(Utils.embed(bot, body)); }); } } diff --git a/src/commands/isitup.deprecated.js b/src/commands/isitup.deprecated.js index fa1e336..d8045a4 100644 --- a/src/commands/isitup.deprecated.js +++ b/src/commands/isitup.deprecated.js @@ -6,14 +6,13 @@ module.exports = { if (args.length == 0) { helpers.argumentsUsedExample(bot, receivedMessage, "URL", "!isitup redbrick.dcu.ie"); return; - } - else if (args.length > 0) { + } else if (args.length > 0) { request.post({ url: "https://faas.jamesmcdermott.ie/function/isitup", body: args }, function(error, response, body) { - receivedMessage.channel.send(helpers.embedify(bot, body)); + receivedMessage.channel.send(Utils.embed(bot, body)); }); } } diff --git a/src/commands/luas.deprecated.js b/src/commands/luas.deprecated.js index e08a39c..c91c73f 100644 --- a/src/commands/luas.deprecated.js +++ b/src/commands/luas.deprecated.js @@ -6,15 +6,14 @@ module.exports = { if (args.length == 0) { helpers.argumentsUsedExample(bot, receivedMessage, "stop", "!luas harcourt"); return; - } - else if (args.length > 0) { + } else if (args.length > 0) { request.post({ url: "https://faas.jamesmcdermott.ie/function/transport", body: "127.0.0.1:8000/luas/stop/" + args }, function(error, response, body) { var schedule = luasScheduleBuilder(body); - receivedMessage.author.send(helpers.embedify(bot, schedule)); + receivedMessage.author.send(Utils.embed(bot, schedule)); }); } } diff --git a/src/commands/nslookup.deprecated.js b/src/commands/nslookup.deprecated.js index c628d94..a56882a 100644 --- a/src/commands/nslookup.deprecated.js +++ b/src/commands/nslookup.deprecated.js @@ -6,14 +6,13 @@ module.exports = { if (args.length == 0) { helpers.argumentsUsedExample(bot, receivedMessage, "URL", "!nslookup redbrick.dcu.ie"); return; - } - else if (args.length > 0) { + } else if (args.length > 0) { request.post({ url: "https://faas.jamesmcdermott.ie/function/nslookup", body: args }, function(error, response, body) { - receivedMessage.channel.send(helpers.embedify(bot, body)); + receivedMessage.channel.send(Utils.embed(bot, body)); }); } } diff --git a/src/commands/pwgen.deprecated.js b/src/commands/pwgen.deprecated.js index db8fdb4..d2ef888 100644 --- a/src/commands/pwgen.deprecated.js +++ b/src/commands/pwgen.deprecated.js @@ -5,13 +5,12 @@ module.exports = { pwgenCommand: function(bot, args, receivedMessage) { if (args.length > 0) { helpers.noArgumentsUsedExample(bot, receivedMessage, "!pwgen"); - } - else if (args.length == 0) { + } else if (args.length == 0) { request.get({ url: "https://faas.jamesmcdermott.ie/function/pwgen", }, function(error, response, body) { - receivedMessage.author.send(helpers.embedify(bot, "Generated Password: " + body)); + receivedMessage.author.send(Utils.embed(bot, "Generated Password: " + body)); }); } } diff --git a/src/commands/pwned.deprecated.js b/src/commands/pwned.deprecated.js index 9842e8a..b9d3047 100644 --- a/src/commands/pwned.deprecated.js +++ b/src/commands/pwned.deprecated.js @@ -15,9 +15,9 @@ module.exports = { function(error, response, body) { var n = JSON.parse(body).found; if (n == 0) { - receivedMessage.channel.send(helpers.embedify(bot, email + " has not been pwned")); + receivedMessage.channel.send(Utils.embed(bot, email + " has not been pwned")); } else { - receivedMessage.channel.send(helpers.embedify(bot, email + " has been pwned")); + receivedMessage.channel.send(Utils.embed(bot, email + " has been pwned")); } }); } diff --git a/src/commands/register.js b/src/commands/register.js index ee8376e..1f0ea05 100644 --- a/src/commands/register.js +++ b/src/commands/register.js @@ -1,7 +1,9 @@ -const helpers = require("../helpers/helpers.js"); +const { + Utils +} = require("../helpers/helpers.js"); const Command = require("../Command"); -class CoinFlip extends Command { +class Register extends Command { constructor(bot) { super(bot, { help: { @@ -14,14 +16,14 @@ class CoinFlip extends Command { } execute() { - return helpers.embedify( + return Utils.embed( this.bot, `To verify, run the following command: \`\`\`!verify \`\`\` If you are not a member and would like to register, please run this instead - \`\`\`!signup \`\`\` ` + \`\`\`!signup \`\`\` ` ); } } -module.exports = CoinFlip; +module.exports = Register; diff --git a/src/commands/room.deprecated.js b/src/commands/room.deprecated.js index 7dc09d0..cad28dd 100644 --- a/src/commands/room.deprecated.js +++ b/src/commands/room.deprecated.js @@ -6,14 +6,13 @@ module.exports = { if (args.length == 0) { helpers.argumentsUsedExample(bot, receivedMessage, "room", "!room GLA.LG26"); return; - } - else if (args.length > 0) { + } else if (args.length > 0) { request.post({ url: "https://faas.jamesmcdermott.ie/function/dcurooms", body: args }, function(error, response, body) { - receivedMessage.channel.send(helpers.embedify(bot, body)); + receivedMessage.channel.send(Utils.embed(bot, body)); }); } } diff --git a/src/commands/signup.js b/src/commands/signup.js new file mode 100644 index 0000000..7b2176f --- /dev/null +++ b/src/commands/signup.js @@ -0,0 +1,41 @@ +const { + Sanitize, + Utils +} = require("../helpers/helpers.js"); +const Command = require("../Command"); + +class SignUp extends Command { + constructor(bot) { + super(bot, { + args: { + required: [ + "email", + "username" + ] + }, + help: { + blurb: "This command provides a registration path to redbrick DCU", + example: "!signup myuser firstname.lastname@mail.dcu.ie" + }, + isDM: true, + name: "signup" + }); + } + + execute(args) { + const username = args.pop(); + Sanitize.username(username); + const email = args.pop(); + Sanitize.email(email); + + return Utils.embed( + this.bot, + ` + \`\`\`!verify \`\`\` + If you are not a member and would like to register, please run this instead + \`\`\`!signup \`\`\` ` + ); + } +} + +module.exports = SignUp; diff --git a/src/commands/ssl.deprecated.js b/src/commands/ssl.deprecated.js index 072846e..7ad3199 100644 --- a/src/commands/ssl.deprecated.js +++ b/src/commands/ssl.deprecated.js @@ -6,14 +6,13 @@ module.exports = { if (args.length == 0) { helpers.argumentsUsedExample(bot, receivedMessage, "ssl", "!ssl redbrick.dcu.ie"); return; - } - else if (args.length > 0) { + } else if (args.length > 0) { request.post({ url: "https://faas.jamesmcdermott.ie/function/certinfo", body: args }, function(error, response, body) { - receivedMessage.channel.send(helpers.embedify(bot, body)); + receivedMessage.channel.send(Utils.embed(bot, body)); }); } } diff --git a/src/commands/uptime.deprecated.js b/src/commands/uptime.deprecated.js index 5539c80..6cac337 100644 --- a/src/commands/uptime.deprecated.js +++ b/src/commands/uptime.deprecated.js @@ -6,14 +6,13 @@ module.exports = { if (args.length > 0) { helpers.noArgumentsUsedExample(bot, receivedMessage, "!uptime"); return; - } - else if (args.length == 0) { + } else if (args.length == 0) { request.post({ url: "https://faas.jamesmcdermott.ie/function/uptime", body: String(bot.uptime / 1000) }, function(error, response, body) { - receivedMessage.channel.send(helpers.embedify(bot, body)); + receivedMessage.channel.send(Utils.embed(bot, body)); }); } } diff --git a/src/commands/verify.deprecated.js b/src/commands/verify.deprecated.js index eae7b6f..6051132 100644 --- a/src/commands/verify.deprecated.js +++ b/src/commands/verify.deprecated.js @@ -26,7 +26,7 @@ module.exports = { ldapdiscordClient.bind("cn=root,ou=ldap,o=redbrick", ldapSecret, function(err) { if (err) { receivedMessage.channel.send( - helpers.embedify( + Utils.embed( bot, "Could not connect to Redbrick LDAP server, please contact a member of Committee." ) @@ -41,10 +41,10 @@ module.exports = { ).replace(/\n$/, ""); const memberRole = fs.readFileSync("/etc/discord.member.role", "utf-8").replace(/\n$/, ""); bot.guilds.get(serverNumber).members.get(receivedMessage.author.id).addRole(memberRole); - receivedMessage.channel.send(helpers.embedify(bot, "Verified!")); + receivedMessage.channel.send(Utils.embed(bot, "Verified!")); } else { receivedMessage.channel.send( - helpers.embedify(bot,"Username and Email Address do not match.") + Utils.embed(bot,"Username and Email Address do not match.") ); } }); diff --git a/src/commands/weather.deprecated.js b/src/commands/weather.deprecated.js index 4c1a2da..7e72796 100644 --- a/src/commands/weather.deprecated.js +++ b/src/commands/weather.deprecated.js @@ -12,7 +12,7 @@ module.exports = { body: args }, function(error, response, body) { - receivedMessage.channel.send(helpers.embedify(bot, body)); + receivedMessage.channel.send(Utils.embed(bot, body)); }); } } diff --git a/src/commands/wiki.deprecated.js b/src/commands/wiki.deprecated.js index 840a83b..81a69e3 100644 --- a/src/commands/wiki.deprecated.js +++ b/src/commands/wiki.deprecated.js @@ -6,12 +6,11 @@ module.exports = { if (args.length > 0) { helpers.noArgumentsUsedExample(bot, receivedMessage, "!wiki"); return; - } - else if (args.length == 0) { + } else if (args.length == 0) { request.get({ url: "https://faas.jamesmcdermott.ie/function/wiki", }, function(error, response, body) { - receivedMessage.channel.send(helpers.embedify(bot, body)); + receivedMessage.channel.send(Utils.embed(bot, body)); }); } } diff --git a/src/helpers/helpers.js b/src/helpers/helpers.js index 59831be..82b4725 100644 --- a/src/helpers/helpers.js +++ b/src/helpers/helpers.js @@ -1,32 +1,96 @@ -function argumentsUsedExample(bot, receivedMessage, required, example) { - return embedify(bot, `No ${required} supplied. Try ${example}`); -} +const assert = require("assert").strict; -function embedify(bot, contents) { - const {discordClient} = bot; - return { - embed: { - color: 0xAA0202, - author: { - name: discordClient.user.username, - icon_url: discordClient.user.avatarURL - }, - description: contents, - timestamp: new Date(), - footer: { - icon_url: discordClient.user.avatarURL, - text: "© Redbrick" +class Utils { + static embed(bot, contents) { + const {discordClient} = bot; + return { + embed: { + color: 0xAA0202, + author: { + name: discordClient.user.username, + icon_url: discordClient.user.avatarURL + }, + description: contents, + timestamp: new Date(), + footer: { + icon_url: discordClient.user.avatarURL, + text: "© Redbrick" + } } + }; + } + + static humanList(lst, finalConjunction) { + assert(Array.isArray(lst), "passed list was not an array"); + assert(typeof finalConjunction === "string", "finalConjunction passed was not a string"); + + lst = lst.map(item => `\`<${item}>\``); + + const last = lst.pop(); + + if(lst.length === 0) { + return last; + } + + return `${lst.join(",")}, ${finalConjunction} ${last}`; + } + + // Right to left clobbering merge + static mergeObj() { + if (arguments.length === 1) { + return arguments[0]; } - }; + let args = Array.from(arguments).slice(); + args.forEach(arg => { + if(typeof arg !== "object") { + throw (new Error("Non object passed to mergeObj")); + } + }); + // Merge next object into this one. + // pop will copy the values so no need to manually make + // this a new copy + let src = args.pop(); + let dest = args.pop(); + + Object.keys(src).forEach((key) => { + if(typeof src[key] !== "object") { + // Fill from right side + // right immutable value clobbers left + dest[key] = src[key]; + } else { + if (Array.isArray(dest[key]) && Array.isArray(src[key])) { + dest[key] = dest[key].concat(src[key]); + } else { + dest[key] = Utils.mergeObj(dest[key], src[key]); + } + } + }); + + args = args.concat(dest); + return Utils.mergeObj(...args); + } } -function tooManyArgs(bot, example) { - return embedify(bot, `Too many arguments supplied, try:\n\`${example}\``); +class Responses { + static tooManyArgs(bot, example) { + return Utils.embed(bot, `Too many arguments supplied!\n\nTry:\n\`${example}\``); + } + static argumentsUsedExample(bot, receivedMessage, required, example) { + return Utils.embed(bot, `No ${Utils.humanList(required, "or")} supplied! \n\nTry:\n\`${example}\``); + } +} + +class Sanitize { + static email() { + + } + static username() { + + } } module.exports = { - argumentsUsedExample, - embedify, - tooManyArgs, + Utils, + Responses, + Sanitize }; From a51150887a5ba149807535350584170d6928d33d Mon Sep 17 00:00:00 2001 From: benmcmahon100 Date: Wed, 15 Jul 2020 22:31:22 +0100 Subject: [PATCH 09/10] meta: add package lock and fix lint command --- .circleci/config.yml | 42 - package-lock.json | 2395 ++++++++++++++++++++++++++++++++++++++++++ package.json | 5 +- 3 files changed, 2398 insertions(+), 44 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 package-lock.json diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index c3d44c7..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,42 +0,0 @@ ---- -version: 2 -jobs: - build: - docker: - - image: circleci/node:11 - working_directory: ~/brickbot - steps: - - checkout - - restore_cache: - keys: - - v1-dependencies-{{ checksum "package.json" }} - - v1-dependencies- - - run: - name: Install Dependencies - command: yarn install - - save_cache: - paths: - - node_modules - key: v1-dependencies-{{ checksum "package.json" }} - lint: - docker: - - image: circleci/node:11 - environment: - ENV: CI - working_directory: ~/brickbot - steps: - - checkout - - run: - name: Install the latest eslint - command: npm install - - run: - name: eslint test - command: yarn lint - prefix: tests - -workflows: - version: 2 - workflow: - jobs: - - build - - lint diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d68abaa --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2395 @@ +{ + "name": "brickbot", + "version": "2.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "acorn": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", + "dev": true + }, + "acorn-jsx": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "dev": true + }, + "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" + } + }, + "ansi-align": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "requires": { + "string-width": "^3.0.0" + }, + "dependencies": { + "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" + } + } + } + }, + "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==", + "dev": true + }, + "ansi-regex": { + "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": "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" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "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" + } + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "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 + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", + "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==" + }, + "backoff": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", + "integrity": "sha1-9hbtqdPktmuMp/ynn2lXIsX44m8=", + "requires": { + "precond": "0.2" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + }, + "dependencies": { + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + } + } + }, + "binary-extensions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==" + }, + "boxen": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", + "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "cli-boxes": "^2.2.0", + "string-width": "^4.1.0", + "term-size": "^2.1.0", + "type-fest": "^0.8.1", + "widest-line": "^3.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, + "bunyan": { + "version": "1.8.14", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.14.tgz", + "integrity": "sha512-LlahJUxXzZLuw/hetUQJmRgZ1LF6+cr5TPpRj6jf327AsiIq2jhYEH4oqUUkVKTor+9w2BT3oxVwhzE5lw9tcg==", + "requires": { + "dtrace-provider": "~0.8", + "moment": "^2.19.3", + "mv": "~2", + "safe-json-stringify": "~1" + } + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + } + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chalk": { + "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", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "chokidar": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz", + "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==", + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.4.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + }, + "cli-boxes": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz", + "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==" + }, + "cli-cursor": { + "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" + } + }, + "cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "color-convert": { + "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" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "requires": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cross-spawn": { + "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", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "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" + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "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 + }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "discord.js": { + "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": "^6.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dot-prop": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", + "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", + "requires": { + "is-obj": "^2.0.0" + } + }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, + "dtrace-provider": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", + "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", + "optional": true, + "requires": { + "nan": "^2.14.0" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==" + }, + "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 + }, + "eslint": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.2.1.tgz", + "integrity": "sha512-ES7BzEzr0Q6m5TK9i+/iTpKjclXitOdDK4vT07OqbkBT2/VcN/gO9EL1C4HlK3TAOXYv2ItcmbVR9jO1MR0fJg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "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.0", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "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.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "progress": "^2.0.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": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-scope": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", + "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, + "espree": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", + "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.1.0" + } + }, + "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 + }, + "esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", + "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", + "dev": true + } + } + }, + "esrecurse": { + "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" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extsprintf": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.2.0.tgz", + "integrity": "sha1-WtlGwi9bMrp/jNdCZxHG6KP8JSk=" + }, + "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=" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "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 + }, + "figures": { + "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": "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==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "optional": true + }, + "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 + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "optional": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "global-dirs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", + "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", + "requires": { + "ini": "^1.3.5" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "iconv-lite": { + "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" + } + }, + "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 + }, + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=" + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "inquirer": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.12", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "requires": { + "ci-info": "^2.0.0" + } + }, + "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=" + }, + "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-installed-globally": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", + "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", + "requires": { + "global-dirs": "^2.0.1", + "is-path-inside": "^3.0.1" + } + }, + "is-npm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", + "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==" + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" + }, + "is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "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 + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "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==" + }, + "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 + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + } + } + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "requires": { + "json-buffer": "3.0.0" + } + }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "requires": { + "package-json": "^6.3.0" + } + }, + "ldap-filter": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.2.2.tgz", + "integrity": "sha1-8rhCvguG2jNSeYUFsx68rlkNd9A=", + "requires": { + "assert-plus": "0.1.5" + }, + "dependencies": { + "assert-plus": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", + "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=" + } + } + }, + "ldapjs": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-1.0.2.tgz", + "integrity": "sha1-VE/3Ayt7g8aPBwEyjZKXqmlDQPk=", + "requires": { + "asn1": "0.2.3", + "assert-plus": "^1.0.0", + "backoff": "^2.5.0", + "bunyan": "^1.8.3", + "dashdash": "^1.14.0", + "dtrace-provider": "~0.8", + "ldap-filter": "0.2.2", + "once": "^1.4.0", + "vasync": "^1.6.4", + "verror": "^1.8.1" + } + }, + "levn": { + "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" + } + }, + "lodash": { + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", + "dev": true + }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, + "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 + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "moment": { + "version": "2.27.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz", + "integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==", + "optional": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "optional": true, + "requires": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + }, + "dependencies": { + "rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "optional": true, + "requires": { + "glob": "^6.0.1" + } + } + } + }, + "nan": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", + "optional": true + }, + "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 + }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "optional": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "nodemailer": { + "version": "6.4.10", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.10.tgz", + "integrity": "sha512-j+pS9CURhPgk6r0ENr7dji+As2xZiHSvZeVnzKniLOw1eRAyM/7flP0u65tCnsapV8JFu+t0l/5VeHsCZEeh9g==" + }, + "nodemon": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.4.tgz", + "integrity": "sha512-Ltced+hIfTmaS28Zjv1BM552oQ3dbwPqI4+zI0SLgq+wpJhSyqgYude/aZa/3i31VCQWMfXJVxvu86abcam3uQ==", + "requires": { + "chokidar": "^3.2.2", + "debug": "^3.2.6", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.0.4", + "pstree.remy": "^1.1.7", + "semver": "^5.7.1", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.2", + "update-notifier": "^4.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "requires": { + "abbrev": "1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "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" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "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 + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + }, + "package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "requires": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "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==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "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=" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" + }, + "precond": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", + "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=" + }, + "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 + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + }, + "prism-media": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-0.0.3.tgz", + "integrity": "sha512-c9KkNifSMU/iXT8FFTaBwBMr+rdVcN+H/uNv1o+CuFeTThNZNTOrQ+RgXA1yL/DeLk098duAeRPP3QNPNbhxYQ==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "pupa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.0.1.tgz", + "integrity": "sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==", + "requires": { + "escape-goat": "^2.0.0" + } + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "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=" + } + } + }, + "readdirp": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", + "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "requires": { + "picomatch": "^2.2.1" + } + }, + "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 + }, + "registry-auth-token": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.1.1.tgz", + "integrity": "sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA==", + "requires": { + "rc": "^1.2.8" + } + }, + "registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "requires": { + "rc": "^1.2.8" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "restore-cursor": { + "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" + } + }, + "rimraf": { + "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" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "rxjs": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.0.tgz", + "integrity": "sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "requires": { + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "shebang-command": { + "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" + } + }, + "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 + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, + "snekfetch": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/snekfetch/-/snekfetch-3.6.4.tgz", + "integrity": "sha512-NjxjITIj04Ffqid5lqr7XdgwM7X61c/Dns073Ly170bPQHLm6jkmelye/eglS++1nfTWktpP6Y2bFXjdPlQqdw==" + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "dependencies": { + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + } + } + }, + "string-width": { + "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" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "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" + } + } + } + }, + "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" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "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==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "term-size": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz", + "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==" + }, + "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 + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "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" + } + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "requires": { + "nopt": "~1.0.10" + } + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "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" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "undefsafe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", + "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==", + "requires": { + "debug": "^2.2.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "requires": { + "crypto-random-string": "^2.0.0" + } + }, + "update-notifier": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.0.tgz", + "integrity": "sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew==", + "requires": { + "boxen": "^4.2.0", + "chalk": "^3.0.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.3.1", + "is-npm": "^4.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.0.0", + "pupa": "^2.0.1", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + } + } + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "v8-compile-cache": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", + "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", + "dev": true + }, + "vasync": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/vasync/-/vasync-1.6.4.tgz", + "integrity": "sha1-3+k2Fq0OeugBszKp2Iv8XNyOHR8=", + "requires": { + "verror": "1.6.0" + }, + "dependencies": { + "verror": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.6.0.tgz", + "integrity": "sha1-fROyex+swuLakEBetepuW90lLqU=", + "requires": { + "extsprintf": "1.2.0" + } + } + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "which": { + "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" + } + }, + "widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "requires": { + "string-width": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "ws": { + "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" + } + }, + "xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" + } + } +} diff --git a/package.json b/package.json index 927dee7..45f7805 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,9 @@ "description": "A bot for the Redbrick Discord server, made extendible by OpenFaaS.", "main": "src/bot.js", "scripts": { - "lint": "npx eslint --ignore-path .gitignore --fix src/.", - "start": "echo '> Running brickbot ...' && node src/bot.js" + "lint": "npx eslint --fix src/.", + "start": "echo '> Running brickbot ...' && node src/bot.js", + "test": "npx eslint src/." }, "repository": { "type": "git", From 65683c80c997236a4551a91b9c18a64d47e718ba Mon Sep 17 00:00:00 2001 From: benmcmahon100 Date: Wed, 15 Jul 2020 23:38:48 +0100 Subject: [PATCH 10/10] feature: add sanitization flow for signup --- src/commands/signup.js | 29 ++++++++++++++++++++++------- src/helpers/helpers.js | 31 +++++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/commands/signup.js b/src/commands/signup.js index 7b2176f..afba0a0 100644 --- a/src/commands/signup.js +++ b/src/commands/signup.js @@ -23,17 +23,32 @@ class SignUp extends Command { } execute(args) { + const emailOrStudentID = args.pop(); const username = args.pop(); - Sanitize.username(username); - const email = args.pop(); - Sanitize.email(email); + + // Sanitize.username(username); + // Sanitize.emailOrStudentID(emailOrStudentID); + + if(!Sanitize.username(username)) { + return Utils.embed( + this.bot, + "Username not valid: must be between 2 and 8 characters inclusive" + + `and only include alphanumeric characters '${username}'` + ); + } + + if(!Sanitize.emailOrStudentID(emailOrStudentID)) { + return Utils.embed( + this.bot, + Sanitize.studentID(emailOrStudentID) + ? "Student ID not valid. Should be all numbers and longer than 2 digits" + : "Email not valid: Should be a valid DCU email" + ); + } return Utils.embed( this.bot, - ` - \`\`\`!verify \`\`\` - If you are not a member and would like to register, please run this instead - \`\`\`!signup \`\`\` ` + "No: not yet" ); } } diff --git a/src/helpers/helpers.js b/src/helpers/helpers.js index 82b4725..914dfb8 100644 --- a/src/helpers/helpers.js +++ b/src/helpers/helpers.js @@ -69,23 +69,50 @@ class Utils { args = args.concat(dest); return Utils.mergeObj(...args); } + + static testAll(lst) { + return lst.reduce((l, r) => l && r, true); + } } class Responses { static tooManyArgs(bot, example) { return Utils.embed(bot, `Too many arguments supplied!\n\nTry:\n\`${example}\``); } + static argumentsUsedExample(bot, receivedMessage, required, example) { return Utils.embed(bot, `No ${Utils.humanList(required, "or")} supplied! \n\nTry:\n\`${example}\``); } } class Sanitize { - static email() { + static email(candidate) { + // This will test for valid dcu student email in the expected form: + // first.last123@mail.dcu.ie + // first.last@mail.dcu.ie + let studentEmailRegExp = RegExp("[a-zA-Z]+\\.[a-zA-Z]+[0-9]+@mail\\.dcu\\.ie", "gi"); + // This will test for a valid dcu other/staff email in expected form: + // entity@dcu.ie + let staffEmailRegExp = RegExp(".+@(?:.+\\.)*dcu\\.ie", "gi"); + // Other formats can be added the same way + return studentEmailRegExp.test(candidate) + || staffEmailRegExp.test(candidate); + } + + static emailOrStudentID(candidate) { + return Sanitize.email(candidate) + || Sanitize.studentID(candidate); + } + static studentID(candidate) { + // Should be a little speed op but there is a chance I guess that this student IDs may some day be longer + const studentIDRegExp = RegExp("^[0-9]{2,32}$", "gi"); + return studentIDRegExp.test(candidate); } - static username() { + static username(candidate) { + const usernameRegExp = RegExp("^[a-zA-Z0-9]{2,9}$", "gm"); + return usernameRegExp.test(candidate); } }