diff --git a/README.md b/README.md index c35c1a4e..35c1e2f5 100644 --- a/README.md +++ b/README.md @@ -8,5 +8,11 @@ - `DISCORD_TOKEN` with the token of the bot - `APP_ID` with the ID of the bot application - Install dependencies with `npm install` -- Register slash commands with `node deploy-commands.js` +- Register slash commands with `npm run deploy` or `node deploy-commands.js` +- Ensure a PostgreSQL database is setup according to "config/database.yml" - Start the bot with `node index.js` + +## Running the bot with Nodemon + +- Nodemon has been installed, this addition allows for continuous integration with and hot reloads the bot upon saving. +- Run the bot with Nodemon using `npm run server` \ No newline at end of file diff --git a/config/database.yml b/config/database.yml index eac5e48b..1fa06e4f 100644 --- a/config/database.yml +++ b/config/database.yml @@ -6,4 +6,4 @@ user: user dbname: bot password: pass host: 0.0.0.0 -port: 40041 \ No newline at end of file +port: 40041 diff --git a/config/lunch_buddy.json b/config/lunch_buddy.json new file mode 100644 index 00000000..53e72a9b --- /dev/null +++ b/config/lunch_buddy.json @@ -0,0 +1,6 @@ +{ + "voteOriginId": "959995388289495050", + "threadDestinationId": "959995388289495050", + "interactionTimeout": 360000, + "cronString": "" +} \ No newline at end of file diff --git a/data/createvc.json b/data/createvc.json index 1b3f33f3..b8daef27 100644 --- a/data/createvc.json +++ b/data/createvc.json @@ -1,4 +1,4 @@ { "users": [], "channels": [] -} +} \ No newline at end of file diff --git a/data/lunch_buddy_locations.js b/data/lunch_buddy_locations.js new file mode 100644 index 00000000..f7cbd7e8 --- /dev/null +++ b/data/lunch_buddy_locations.js @@ -0,0 +1,121 @@ +const locations = [ + { + value: "Upper Campus Food Court", + sub: [ + { + name: "Tropical Green Pho", + }, + { + name: "Pho House", + }, + { + name: "Classic Kebab", + }, + { + name: "Chinese Takeaway", + }, + { + name: "Tori Sushi", + }, + { + name: "Gradu-eat", + }, + { + name: "The Little Marionette Cafe", + }, + { + name: "Lhaksa Delight", + }, + { + name: "Bioscience building Cafe (XS Espresso)", + }, + ], + }, + { + value: "Subway Zone", + sub: [ + { + name: "Subway", + }, + { + name: "Boost", + }, + { + name: "Southern Wok", + }, + { + name: "Cafe Brioso", + }, + { + name: "Penny Lane", + }, + ], + }, + { + value: "Quadrangle Food Court", + sub: [ + { + name: "Soul Origin", + }, + { + name: "PappaRich", + }, + { + name: "Nene Chicken", + }, + { + name: "Plume Cafe", + }, + ], + }, + { + value: "Lower Campus", + sub: [ + { + name: "Stellinis Pasta Bar", + }, + { + name: "Guzman Y Gomez", + }, + { + name: "Mamak Village", + }, + { + name: "Yallah Eats Kebab and Shawarma", + }, + { + name: "Sharetea", + }, + { + name: "Maze Coffee & Food", + }, + { + name: "Campus Village Cafe", + }, + { + name: "Home Ground Kiosk", + }, + ], + }, + { + value: "J17 Ainsworth", + sub: [ + { + name: "Coffee on Campus Cafe", + }, + ], + }, + { + value: "Other Options", + sub: [ + { + name: "Sport", + }, + { + name: "On Campus Study", + }, + ], + }, +]; + +exports.locations = locations; diff --git a/events/lunch_buddy.js b/events/lunch_buddy.js new file mode 100644 index 00000000..1063a4bd --- /dev/null +++ b/events/lunch_buddy.js @@ -0,0 +1,424 @@ +const { MessageEmbed, MessageButton, MessageActionRow } = require("discord.js"); +const cron = require("node-cron"); +const lunchBuddyLocations = require("../data/lunch_buddy_locations"); +const config = require("../config/lunch_buddy.json"); + +const maxRowButtons = 4; +const areaButtonCustomId = "AreaButton"; +const locationButtonCustomId = "LocationButton"; + +const voteOriginId = config.voteOriginId; +const threadDestinationId = config.threadDestinationId; +const interactionTimeout = config.interactionTimeout; +const cronString = config.cronString; + +const generalAreaInfo = + "This lunch buddy vote commenced at 10am, you must vote for the area by 11am. A location vote will run afterwards until 12pm."; +const generalLocationInfo = + "This lunch buddy vote commenced at 10am, you must vote for the location by 12pm, when one will be chosen."; + +const getLocations = (area) => { + for (const object of lunchBuddyLocations.locations) { + if (object.value === area) { + return object; + } + } + return undefined; +}; + +const generateAreasEmbed = (areaVotes) => { + const areas = lunchBuddyLocations.locations.map( + (element) => `${element.value}: ${areaVotes ? areaVotes[element.value].length : 0}`, + ); + areas.push(`Any: ${areaVotes ? areaVotes["Any"].length : 0}`); + return new MessageEmbed() + .setTitle("Meetup Area Selection") + .setColor(0x0099ff) + .setDescription("Please select an option below to vote for that area!") + .setFields( + { + name: "Info", + value: generalAreaInfo, + }, + { + name: "Options", + value: areas.join("\n"), + }, + ); +}; + +const generateLocationsEmbed = (area, votes) => { + const locationData = getLocations(area); + const locations = locationData.sub.map( + (element) => `${element.name}: ${votes ? votes[element.name].length : 0}`, + ); + locations.push(`Any: ${votes ? votes["Any"].length : 0}`); + return new MessageEmbed() + .setTitle(`Meetup Location Selection - ${area}`) + .setColor(0x0099ff) + .setDescription("Please select an option below to vote for that location!") + .setFields( + { + name: "Info", + value: generalLocationInfo, + }, + { + name: "Options", + value: locations.join("\n"), + }, + ); +}; + +const areasList = lunchBuddyLocations.locations.map((element) => element.value); +areasList.push("Any"); +const areasButtons = lunchBuddyLocations.locations.map( + (element) => + new MessageButton({ + style: "PRIMARY", + label: element.value, + customId: `${element.value}${areaButtonCustomId}`, + }), +); +areasButtons.push( + new MessageButton({ + style: "PRIMARY", + label: "Surprise Me!", + customId: `Any${areaButtonCustomId}`, + }), +); +areasButtons.push( + new MessageButton({ + style: "DANGER", + label: "Remove Vote", + customId: `Remove${areaButtonCustomId}`, + }), +); +const areasButtonsIds = lunchBuddyLocations.locations.map( + (element) => `${element.value}${areaButtonCustomId}`, +); +areasButtonsIds.push(`Any${areaButtonCustomId}`); +areasButtonsIds.push(`Remove${areaButtonCustomId}`); +const areasActionRows = []; +for (let i = 0; i < areasButtons.length; i += maxRowButtons) { + areasActionRows.push( + new MessageActionRow({ components: areasButtons.slice(i, i + maxRowButtons) }), + ); +} + +const areasButtonsFilter = (resInteraction) => { + return areasButtonsIds.includes(resInteraction.customId); +}; + +const getVoteOption = (userId, votes) => { + for (const option of Object.keys(votes)) { + if (votes[option].includes(userId)) { + return option; + } + } + return undefined; +}; + +const getMostVoted = (votes) => { + let maxValue = 0; + let results = []; + + // Where only "Any" votes are present, all options will be returned as their + // vote length equals the maxValue variable of 0 + for (const option of Object.keys(votes)) { + if (option == "Any") continue; + + const optionVotes = votes[option].length; + if (optionVotes > maxValue) { + maxValue = optionVotes; + results = [option]; + } else if (optionVotes === maxValue) { + results.push(option); + } + } + + return results; +}; + +module.exports = { + name: "ready", + once: true, + execute(client) { + // Quick return if any config is not setup + if (!voteOriginId || !threadDestinationId || !interactionTimeout || !cronString) { + return; + } + + cron.schedule(cronString, async () => { + let locationData; + let selectedArea; + let selectedLocation; + const areaVotes = {}; + const locationVotes = {}; + + const conductAreaVote = async () => { + // Setup for voting + areasList.forEach((area) => (areaVotes[area] = [])); + + // Fetch channel object and send voting message + const voteChannel = await client.channels.fetch(voteOriginId); + + const areaMessage = await voteChannel.send({ + embeds: [generateAreasEmbed()], + components: areasActionRows, + }); + + // Setup receiving message interactions + const areaCollector = areaMessage.createMessageComponentCollector({ + filter: areasButtonsFilter, + time: interactionTimeout, + idle: interactionTimeout, + }); + + areaCollector.on("collect", async (interaction) => { + const interactorId = String(interaction.user.id); + const priorVoteOption = getVoteOption(interactorId, areaVotes); + const newOption = interaction.customId.replace(areaButtonCustomId, ""); + + let newVoteString = ` voted for ${newOption}!`; + let oldVoteString = ""; + + // Checks whether voter has previously cast vote and edits accordingly + if (priorVoteOption) { + if (priorVoteOption === newOption) { + interaction.reply({ + content: "You have already voted for this option.", + ephemeral: true, + }); + return; + } + // Removes previously cast vote + const location = areaVotes[priorVoteOption].indexOf(interactorId); + areaVotes[priorVoteOption].splice(location, 1); + + if (newOption === "Remove") { + newVoteString = " removed your vote."; + } else { + oldVoteString = ` removed your vote for ${priorVoteOption} and`; + } + } else if (newOption === "Remove") { + newVoteString = " no vote to remove."; + } + + const voteString = `You have${oldVoteString}${newVoteString}`; + + // Appends new vote if cast + if (newOption !== "Remove") { + areaVotes[newOption].push(interactorId); + } + + interaction.reply({ content: voteString, ephemeral: true }); + areaMessage.edit({ embeds: [generateAreasEmbed(areaVotes)] }); + }); + + areaCollector.on("end", async () => { + // Removes options to vote for area + await areaMessage.edit({ components: [] }); + + let areaInfo; + + // Finds the highest voted option, and randomises for ties + const mostVoted = getMostVoted(areaVotes); + + if (mostVoted.length !== 1) { + selectedArea = mostVoted[Math.floor(Math.random() * mostVoted.length)]; + areaInfo = `Several options had the highest votes, and the area ${selectedArea} was randomly selected from the tied options.`; + } else { + selectedArea = mostVoted[0]; + areaInfo = `The area ${selectedArea} had the highest votes.`; + } + + if (areaVotes[selectedArea].length || areaVotes["Any"].length) { + await areaMessage.reply(areaInfo); + locationData = getLocations(selectedArea); + await conductLocationVote(); + } else { + await areaMessage.reply("No votes were cast for the area."); + } + }); + }; + + const conductLocationVote = async () => { + const locationsList = locationData.sub.map((element) => element.name); + locationsList.push("Any"); + + // Fetch channel and prepare voting message + + const voteChannel = await client.channels.fetch(voteOriginId); + + // client.channels.fetch(voteOriginId).then(async (voteChannel) => { + const locationsButtons = locationData.sub.map( + (element) => + new MessageButton({ + style: "PRIMARY", + label: element.name, + customId: `${element.name}${locationButtonCustomId}`, + }), + ); + locationsButtons.push( + new MessageButton({ + style: "PRIMARY", + label: "Surprise Me!", + customId: `Any${locationButtonCustomId}`, + }), + ); + locationsButtons.push( + new MessageButton({ + style: "DANGER", + label: "Remove Vote", + customId: `Remove${locationButtonCustomId}`, + }), + ); + const locationsButtonsIds = locationData.sub.map( + (element) => `${element.name}${locationButtonCustomId}`, + ); + locationsButtonsIds.push(`Any${locationButtonCustomId}`); + locationsButtonsIds.push(`Remove${locationButtonCustomId}`); + const locationsActionRows = []; + for (let i = 0; i < locationsButtons.length; i += maxRowButtons) { + locationsActionRows.push( + new MessageActionRow({ + components: locationsButtons.slice(i, i + maxRowButtons), + }), + ); + } + + locationsList.forEach((location) => (locationVotes[location] = [])); + + const toPing = []; + Object.values(areaVotes).forEach((area) => { + area.forEach((id) => { + toPing.push(id); + }); + }); + + const pingStr = toPing.reduce((str, id) => str + `<@${id}>`, ""); + + const locationMessage = await voteChannel.send({ + content: pingStr, + embeds: [generateLocationsEmbed(selectedArea)], + components: locationsActionRows, + }); + + const locationsButtonsFilter = (resInteraction) => { + return locationsButtonsIds.includes(resInteraction.customId); + }; + + // Setup receiving message interactions + const locationCollector = locationMessage.createMessageComponentCollector({ + filter: locationsButtonsFilter, + time: interactionTimeout, + idle: interactionTimeout, + }); + + locationCollector.on("collect", async (interaction) => { + const interactorId = String(interaction.user.id); + const priorVoteOption = getVoteOption(interactorId, locationVotes); + const newOption = interaction.customId.replace(locationButtonCustomId, ""); + + let newVoteString = ` voted for ${newOption}!`; + let oldVoteString = ""; + + // Checks whether voter has previously cast vote and edits accordingly + if (priorVoteOption) { + if (priorVoteOption === newOption) { + interaction.reply({ + content: "You have already voted for this option.", + ephemeral: true, + }); + return; + } + // Removes previously cast vote + const location = locationVotes[priorVoteOption].indexOf(interactorId); + locationVotes[priorVoteOption].splice(location, 1); + + if (newOption === "Remove") { + newVoteString = " removed your vote."; + } else { + oldVoteString = ` removed your vote for ${priorVoteOption} and`; + } + } else if (newOption === "Remove") { + newVoteString = " no vote to remove."; + } + + const voteString = `You have${oldVoteString}${newVoteString}`; + + // Appends new vote if cast + if (newOption !== "Remove") { + locationVotes[newOption].push(interactorId); + } + + interaction.reply({ content: voteString, ephemeral: true }); + locationMessage.edit({ + embeds: [generateLocationsEmbed(selectedArea, locationVotes)], + }); + }); + + locationCollector.on("end", async () => { + // Removes options to vote for location + locationMessage.edit({ components: [] }); + + let locationInfo; + + // Finds the highest voted option, and randomises for ties + const mostVotedLocation = getMostVoted(locationVotes); + if (mostVotedLocation.length !== 1) { + selectedLocation = + mostVotedLocation[Math.floor(Math.random() * mostVotedLocation.length)]; + locationInfo = `Several options had the highest votes, and the location ${selectedLocation} was randomly selected from the tied options.`; + } else { + selectedLocation = mostVotedLocation[0]; + locationInfo = `The location ${selectedLocation} had the highest votes.`; + } + + if (locationVotes[selectedLocation].length || locationVotes["Any"].length) { + await locationMessage.reply(locationInfo); + createMeetupThread(); + } else { + await locationMessage.reply("No votes were cast for the location."); + } + }); + }; + + const createMeetupThread = () => { + const dateString = new Date().toLocaleDateString("en-AU"); + + client.channels.fetch(threadDestinationId).then(async (channel) => { + // Creates thread which expires after 1 day + const thread = await channel.threads.create({ + name: `${dateString} - ${selectedLocation}`, + autoArchiveDuration: 1440, + }); + + const toAdd = []; + Object.values(areaVotes).forEach((area) => { + area.forEach((id) => { + if (!toAdd.includes(id)) toAdd.push(id); + }); + }); + Object.values(locationVotes).forEach((location) => { + location.forEach((id) => { + if (!toAdd.includes(id)) toAdd.push(id); + }); + }); + + toAdd.forEach(async (id) => { + await thread.members.add(id); + }); + + client.channels.fetch(voteOriginId).then(async (threadId) => { + await threadId.send( + `Created a thread for today's lunch buddy meet: ${thread}`, + ); + }); + }); + }; + + await conductAreaVote(); + }); + }, +}; diff --git a/events/tictactoeButton.js b/events/tictactoeButton.js index 43511d57..9b42f89c 100644 --- a/events/tictactoeButton.js +++ b/events/tictactoeButton.js @@ -1,13 +1,13 @@ -const { handleGameButton } = require("../lib/tictactoe/tttHelper"); +// const { handleGameButton } = require("../lib/tictactoe/tttHelper"); -module.exports = { - once: false, - name: "interactionCreate", - execute(interaction) { - if (interaction.isButton() && interaction.message.interaction.commandName == "tictactoe") { - handleGameButton(interaction); - } else { - return; - } - }, -}; +// module.exports = { +// once: false, +// name: "interactionCreate", +// execute(interaction) { +// if (interaction.isButton() && interaction.message.interaction.commandName == "tictactoe") { +// handleGameButton(interaction); +// } else { +// return; +// } +// }, +// }; diff --git a/package-lock.json b/package-lock.json index c738b18d..13a6bd14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,9 @@ "dotenv": "10.0.0", "js-yaml": "4.1.0", "mathjs": "^11.3.3", + "node-cron": "^3.0.2", "nodemailer": "6.7.5", + "nodemon": "^2.0.20", "pg": "8.7.3", "textversionjs": "1.1.3", "voucher-code-generator": "1.1.1", @@ -899,6 +901,18 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -995,6 +1009,14 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -1013,7 +1035,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -1152,6 +1173,43 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -1985,7 +2043,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -2107,6 +2164,19 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/gauge": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", @@ -2351,6 +2421,11 @@ "node": ">= 4" } }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==" + }, "node_modules/ignore-walk": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz", @@ -2405,11 +2480,21 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, + "node_modules/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==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -2426,7 +2511,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -2438,7 +2522,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "engines": { "node": ">=0.12.0" } @@ -2540,9 +2623,9 @@ "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" }, "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "bin": { "json5": "lib/cli.js" @@ -2867,6 +2950,25 @@ "ms": "^2.1.1" } }, + "node_modules/node-cron": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.2.tgz", + "integrity": "sha512-iP8l0yGlNpE0e6q1o185yOApANRe47UPbLf4YxfbiNHt/RU5eBcGB/e0oudruheSf+LQeDMezqC5BVAb5wwRcQ==", + "dependencies": { + "uuid": "8.3.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/node-cron/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -3165,6 +3267,68 @@ "node": ">=6.0.0" } }, + "node_modules/nodemon": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz", + "integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -3179,6 +3343,14 @@ "node": ">=6" } }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/npm-bundled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", @@ -3518,7 +3690,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -3633,6 +3804,11 @@ "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" }, + "node_modules/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==" + }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -3706,6 +3882,17 @@ "node": ">= 6" } }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/regenerator-runtime": { "version": "0.13.10", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", @@ -3937,6 +4124,25 @@ "simple-concat": "^1.0.0" } }, + "node_modules/simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "dependencies": { + "semver": "~7.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -4084,7 +4290,6 @@ "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==", - "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -4092,6 +4297,31 @@ "node": ">=8.0" } }, + "node_modules/touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dependencies": { + "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/touch/node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", @@ -4171,6 +4401,11 @@ "node": ">= 14" } }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" + }, "node_modules/update-browserslist-db": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz", @@ -5010,6 +5245,15 @@ "color-convert": "^2.0.1" } }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, "aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -5096,6 +5340,11 @@ } } }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + }, "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -5114,7 +5363,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "requires": { "fill-range": "^7.0.1" } @@ -5199,6 +5447,31 @@ "domutils": "^3.0.1" } }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, "chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -5821,7 +6094,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "requires": { "to-regex-range": "^5.0.1" } @@ -5900,6 +6172,12 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "optional": true + }, "gauge": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", @@ -6079,6 +6357,11 @@ "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "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": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==" + }, "ignore-walk": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz", @@ -6124,11 +6407,18 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, + "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-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" }, "is-fullwidth-code-point": { "version": "3.0.0", @@ -6139,7 +6429,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "requires": { "is-extglob": "^2.1.1" } @@ -6147,8 +6436,7 @@ "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, "is-obj": { "version": "2.0.0", @@ -6235,9 +6523,9 @@ "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, "jsprim": { @@ -6482,6 +6770,21 @@ } } }, + "node-cron": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.2.tgz", + "integrity": "sha512-iP8l0yGlNpE0e6q1o185yOApANRe47UPbLf4YxfbiNHt/RU5eBcGB/e0oudruheSf+LQeDMezqC5BVAb5wwRcQ==", + "requires": { + "uuid": "8.3.2" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + }, "node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -6735,6 +7038,51 @@ "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.5.tgz", "integrity": "sha512-6VtMpwhsrixq1HDYSBBHvW0GwiWawE75dS3oal48VqRhUvKJNnKnJo2RI/bCVQubj1vgrgscMNW4DHaD6xtMCg==" }, + "nodemon": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz", + "integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==", + "requires": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "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" + } + } + } + }, "nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -6743,6 +7091,11 @@ "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==" + }, "npm-bundled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", @@ -7002,8 +7355,7 @@ "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, "postgres-array": { "version": "2.0.0", @@ -7066,6 +7418,11 @@ "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" }, + "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==" + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -7112,6 +7469,14 @@ "util-deprecate": "^1.0.1" } }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "requires": { + "picomatch": "^2.2.1" + } + }, "regenerator-runtime": { "version": "0.13.10", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", @@ -7265,6 +7630,21 @@ "simple-concat": "^1.0.0" } }, + "simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "requires": { + "semver": "~7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" + } + } + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -7379,11 +7759,28 @@ "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==", - "dev": true, "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" + }, + "dependencies": { + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "requires": { + "abbrev": "1" + } + } + } + }, "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", @@ -7447,6 +7844,11 @@ "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.1.0.tgz", "integrity": "sha512-DGwUl6cioBW5gw2L+6SMupGwH/kZOqivy17E4nsh1JI9fKF87orMmlQx3KISQPmg3sfnOUGlwVkroosvgddrlg==" }, + "undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" + }, "update-browserslist-db": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz", diff --git a/package.json b/package.json index 55e35eeb..1711f0a3 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,10 @@ "format:check": "prettier --check './**/*.js'", "lint": "eslint . --ext .js", "lint:fix": "eslint --fix . --ext .js", - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "deploy": "node deploy-commands.js", + "start": "node index.js", + "server": "nodemon index.js" }, "repository": { "type": "git", @@ -37,7 +40,9 @@ "dotenv": "10.0.0", "js-yaml": "4.1.0", "mathjs": "^11.3.3", + "node-cron": "^3.0.2", "nodemailer": "6.7.5", + "nodemon": "^2.0.20", "pg": "8.7.3", "textversionjs": "1.1.3", "voucher-code-generator": "1.1.1",