commit 960e3d1f3c036837f21abf38c8378226c76ee026 Author: cheetah Date: Tue Nov 5 18:30:29 2019 +0100 First Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1a99c29 --- /dev/null +++ b/.gitignore @@ -0,0 +1,193 @@ + + +# Logs + +logs + +*.log + +npm-debug.log* + +yarn-debug.log* + +yarn-error.log* + +lerna-debug.log* + + + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + + + +# Runtime data + +pids + +*.pid + +*.seed + +*.pid.lock + + + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + + + +# Coverage directory used by tools like istanbul + +coverage + +*.lcov + + + +# nyc test coverage + +.nyc_output + + + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + + + +# Bower dependency directory (https://bower.io/) + +bower_components + + + +# node-waf configuration + +.lock-wscript + + + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + + + +# Dependency directories + +node_modules/ + +jspm_packages/ + + + +# TypeScript v1 declaration files + +typings/ + + + +# TypeScript cache + +*.tsbuildinfo + + + +# Optional npm cache directory + +.npm + + + +# Optional eslint cache + +.eslintcache + + + +# Optional REPL history + +.node_repl_history + + + +# Output of 'npm pack' + +*.tgz + + + +# Yarn Integrity file + +.yarn-integrity + + + +# dotenv environment variables file + +.env + +.env.test + + + +# parcel-bundler cache (https://parceljs.org/) + +.cache + + + +# next.js build output + +.next + + + +# nuxt.js build output + +.nuxt + + + +# vuepress build output + +.vuepress/dist + + + +# Serverless directories + +.serverless/ + + + +# FuseBox cache + +.fusebox/ + + + +# DynamoDB Local files + +.dynamodb/ + + + +stickers/ + +$gifs/ + +searchgifs/ + +searchstickers/ + +exports/ + +dl/ + +dl2/ diff --git a/accountmanager/index.js b/accountmanager/index.js new file mode 100644 index 0000000..b19706b --- /dev/null +++ b/accountmanager/index.js @@ -0,0 +1,284 @@ +const config = require('../config') +const every = require('every') + + +// MongoDB +const { mongoose, Models } = require('../database') + + +// MCD API +const UUID = require('uuid') +const rqt = require('../node_modules/rqt') +const MCDSession = new rqt.Session({ + host: config.mcdEndpointLegacy, + headers: config.mcdHeadersLegacy, +}) + + +const imapAuth = config.accountManager.imapAuth +const MailListener = require('mail-listener2') +const mailListener = new MailListener({ + ...imapAuth, + connTimeout: 10000, + authTimeout: 10000, + // debug: console.log, + tlsOptions: { rejectUnauthorized: false }, + mailbox: config.accountManager.imapMailbox, + searchFilter: ['UNSEEN'], + markSeen: true, + fetchUnreadOnStart: true, +}) + +function generatePass(plength){ + var keylistalpha="abcdefghijklmnopqrstuvwxyz"; + var keylistint="123456789"; + var keylistspec="!@#_"; + var temp=''; + var len = plength/2; + var len = len - 1; + var lenspec = plength-len-len; + + for (i=0;i { + const BOTID = mongoose.Types.ObjectId() + const MAIL = BOTID + config.accountManager.mailSuffix + const PW = generatePass(8) + 'aA.' + const deviceId = UUID.v4() + console.log('creating account', MAIL) + const res = await MCDSession.aqt('/v2/customer/security/account?type=traditional', { + method: 'POST', + type: 'json', + data: { + "password": PW, + "profile": { + "base": { + "address": [ + { "activeInd": "Y", "allowPromotions": "Y", "details": [{ "addressLineDetails": { "zipCode": config.accountManager.mcdRegZipCode }, "addressLocale": "de-DE" }], "primaryInd": "Y", "addressType": "other" }, + ], + "email": [ + { "activeInd": "Y", "emailAddress": MAIL, "primaryInd": "Y", "type": "personal" }, + ], + "firstName": config.accountManager.mcdRegFirstName, + "lastName": config.accountManager.mcdRegLastName, + "username": MAIL + }, + "extended": { + "devices": [ + { + "brand": "privacymatters", + "deviceId": deviceId, + "deviceIdType": "AndroidId", + "isActive": "Y", + "language": "de-DE", + "manufacturer": "privacymatters", + "model": "privacymatters", + "os": "android", + "osVersion": "9", + "personalName": "PersonalMobile", + "sourceId": "MOT", + "timezone": "Europe/Berlin", + "token": "00000-0000:APA000000000000-00000000000000--0000000000000000000000000000000000000000000000000000000000000-000000000000000000000000000000000000000000000" + } + ], + "policies": { + "acceptancePolicies": [ + { "acceptanceInd": "Y", "channelId": "M", "deviceId": deviceId, "isExpired": false, "name": "TermsOfUseAcceptanceType","sourceId": "MOT", "type": "1" } + ], + "accessPolicy": [ + { "acceptanceInd": "Y", "channelId": "M", "deviceId": deviceId, "isExpired": false, "name": "PrivacyPolicyAcceptanceType", "sourceId": "MOT", "type": "2" } + ] + }, + "preferences": [ + { "details": { "Email": "de-DE", "legacyId": "1", "MobileApp": "de-DE" }, "isActive": "Y", "preferenceDesc": "PreferredLanguage", "preferenceId": "1", "label": "PreferredLanguage", "sourceId": "MOT", "type": "ecpLegacy" }, + { "details": { "Email": "False", "legacyId": "2", "MobileApp": "False" }, "isActive": "N", "preferenceDesc": "DoesAcceptPromotion", "preferenceId": "2", "label": "DoesAcceptPromotion", "sourceId": "MOT", "type": "ecpLegacy" }, + { "details": { "Email": "ByEmail", "legacyId": "3", "MobileApp": "ByEmail" }, "isActive": "Y", "preferenceDesc": "PreferredNotification", "preferenceId": "3", "label": "PreferredNotification", "sourceId": "MOT", "type": "ecpLegacy" }, + { "details": { "Email": [], "legacyId": "18", "MobileApp": [] }, "isActive": "Y", "preferenceDesc": "PreferredOfferCategory", "preferenceId": "11", "label": "PreferredOfferCategory", "sourceId": "MOT", "type": "ecpLegacy" }, + { "details": { "enabled": "Y" }, "isActive": "Y", "preferenceDesc": "FoodPreferenceFry", "preferenceId": "16", "sourceId": "MOT", "type": "FoodPreference" }, + { "details": { "Email": "False", "legacyId": "6", "MobileApp": "True" }, "isActive": "Y", "preferenceDesc": "YourOffers", "preferenceId": "6", "label": "YourOffers", "sourceId": "MOT", "type": "ecpLegacy" }, + { "details": { "Email": "False", "legacyId": "7", "MobileApp": "True" }, "isActive": "Y", "preferenceDesc": "LimitedTimeOffers", "preferenceId": "7", "label": "LimitedTimeOffers", "sourceId": "MOT", "type": "ecpLegacy" }, + { "details": { "enabled": "Y" }, "isActive": "Y", "preferenceDesc": "FoodPreferenceBreakfast", "preferenceId": "12", "sourceId": "MOT", "type": "FoodPreference" }, + { "details": { "enabled": "Y" }, "isActive": "Y", "preferenceDesc": "FoodPreferenceSandwich", "preferenceId": "13", "sourceId": "MOT", "type": "FoodPreference" }, + { "details": { "enabled": "Y" }, "isActive": "Y", "preferenceDesc": "FoodPreferenceHappymeal", "preferenceId": "17", "sourceId": "MOT", "type": "FoodPreference" }, + { "details": { "enabled": "Y" }, "isActive": "Y", "preferenceDesc": "FoodPreferenceDrink", "preferenceId": "15", "sourceId": "MOT", "type": "FoodPreference" }, + { "details": { "enabled": "Y" }, "isActive": "Y", "preferenceDesc": "FoodPreferenceSalad", "preferenceId": "19", "sourceId": "MOT", "type": "FoodPreference" }, + { "details": { "enabled": "Y" }, "isActive": "Y", "preferenceDesc": "FoodPreferenceChicken", "preferenceId": "14", "sourceId": "MOT", "type": "FoodPreference" }, + { "details": { "Email": "False", "legacyId": "8", "MobileApp": "True" }, "isActive": "Y", "preferenceDesc": "PunchcardOffers", "preferenceId": "8", "label": "PunchcardOffers", "sourceId": "MOT", "type": "ecpLegacy" }, + { "details": { "Email": "False", "legacyId": "9", "MobileApp": "True" }, "isActive": "Y", "preferenceDesc": "EverydayOffers", "preferenceId": "9", "label": "EverydayOffers", "sourceId": "MOT", "type": "ecpLegacy" }, + { "details": { "enabled": "Y" }, "isActive": "Y", "preferenceDesc": "FoodPreferenceWrap", "preferenceId": "21", "sourceId": "MOT", "type": "FoodPreference" } + ], + "subscriptions": [ + { "legacyId": "1", "legacyType": "optin", "optInStatus": "N", "sourceId": "MOT", "subscriptionDesc": "CommunicationChannel", "subscriptionId": "1" }, + { "legacyId": "2", "legacyType": "optin", "optInStatus": "N", "sourceId": "MOT", "subscriptionDesc": "Surveys", "subscriptionId": "2" }, + { "legacyId": "3", "legacyType": "optin", "optInStatus": "N", "sourceId": "MOT", "subscriptionDesc": "ProgramChanges", "subscriptionId": "3" }, + { "legacyId": "4", "legacyType": "optin", "optInStatus": "N", "sourceId": "MOT", "subscriptionDesc": "Contests", "subscriptionId": "4" }, + { "legacyId": "5", "legacyType": "optin", "optInStatus": "N", "sourceId": "MOT", "subscriptionDesc": "OtherMarketingMessages", "subscriptionId": "5" }, + { "legacyId": "2", "legacyType": "sub", "optInStatus": "Y", "sourceId": "MOT", "subscriptionDesc": "OfferProgram", "subscriptionId": "7" }, + { "legacyId": "5", "legacyType": "pref", "optInStatus": "Y", "sourceId": "MOT", "subscriptionDesc": "MobileNotificationEnabled", "subscriptionId": "11" }, + { "legacyId": "5", "legacyType": "pref", "optInStatus": "N", "sourceId": "MOT", "subscriptionDesc": "EmailNotificationEnabled", "subscriptionId": "10" }, + { "optInStatus": "Y", "sourceId": "MOT", "subscriptionDesc": "GeneralMarketing", "subscriptionId": "22" }, + { "optInStatus": "N", "sourceId": "MOT", "subscriptionDesc": "PersonalMarketing", "subscriptionId": "23" } + ] + } + } + }, + }) + //console.log(JSON.stringify(res.body, null, '\n')) + await Models.Account.create({ + _id: BOTID, + email: MAIL, + deviceId: deviceId, + password: PW, + }) + return BOTID +} +const removeAccount = async(botId) => { + let dbId = mongoose.Types.ObjectId(botId) + console.log('removeAccount', dbId) + let $user = await Models.Account.findById(dbId) + const res = await MCDSession.jqt('/v2/customer/security/account', { + method: 'DELETE', + headers: { + 'Authorization': `Bearer ${ $user.token }`, + 'Content-Type': 'application/json', + }, + }) + if (res.status == "ok") { + await Models.Account.findByIdAndRemove(dbId) + } else { + console.log(res) + return false + } +} +const login = async (botId) => { + let dbId = mongoose.Types.ObjectId(botId) + let $user = await Models.Account.findById(dbId) + console.log('logging in', $user._id) + const res = await MCDSession.jqt('/v2/customer/security/authentication?type=traditional', { + method: 'POST', + type: 'json', + data: { + password: $user.password, + type: "email", + loginUsername: $user.email, + } + }) + if (res.status == "ok") { + await Models.Account.findByIdAndUpdate(dbId, { + token: res.details.token, + lastLogin: new Date(), + }) + return true + } else + console.error('logging in', $user._id, 'failed', res) + return false +} + +async function handleMail(html, subject, toward, seqno) { + try { + let toMail = toward.replace('<', '').replace('>', '') + if (subject == config.accountManager.mcdSubjectVerified) { // remove verified-mails + mailListener.imap.seq.addFlags(seqno, '\\Seen', null) + mailListener.imap.seq.addFlags(seqno, '\\Deleted', null) + return + } + if (subject !== config.accountManager.mcdSubjectVerify) return console.error('not an register email') + if (toMail.indexOf(config.accountManager.mailSuffix) == -1) return false + let botId = toMail.substring(0, toMail.indexOf(config.accountManager.mailSuffix)) + console.log(botId) + let dbId = mongoose.Types.ObjectId(botId) + let activateAccountURL = html + if (!activateAccountURL) return false + if (activateAccountURL.indexOf(config.accountManager.mcdVerifyLink) == -1) return false + // console.log(toMail, botId) + if (await Models.Account.count({ _id: dbId }) !== 1) return console.error('not in db') + let userEntryForMail = await Models.Account.findById(dbId) + + // https://www.mcdonalds.com/de/de-de/gmaredirect.html?&ac=XXXXXXXXXX + activateAccountURL = activateAccountURL.substring(activateAccountURL.indexOf(config.accountManager.mcdVerifyLink)) + activateAccountURL = activateAccountURL.substring(0, activateAccountURL.indexOf('"')) + let verificationCode = activateAccountURL.substring(activateAccountURL.indexOf('ac=') + 'ac='.length) + console.log(toMail, toMail == userEntryForMail.email, activateAccountURL, verificationCode) + + let res = await MCDSession.jqt('/v2/customer/security/account/verification?type=email', { + method: 'POST', + type: 'json', + data: { + "username": userEntryForMail.email, + "verificationCode": verificationCode + } + }) + // console.log(userEntryForMail , JSON.stringify(res, null,'\n')) + console.log(res) + if (res['status'] == "ok") { + await Models.Account.findByIdAndUpdate(dbId, { + active: true, + link: activateAccountURL, + }) + mailListener.imap.seq.addFlags(seqno, '\\Seen', null); + mailListener.imap.seq.addFlags(seqno, '\\Deleted', null); + } else { + console.log(res) + } + } catch (ex) { + console.error(ex) + } +} + + +async function Init() { + // Schedule to fill up the Accounts + every(5000) + .on('data', async () => { + let freeAccounts = await Models.Account.count({ + free: true, + }) + if (freeAccounts < config.accountManager.freeAccountsReserve) { + makeAccount() + } + + let oldAccounts = await Models.Account.find({ + active: true, + free: false, + lastLogin: { $lt: (new Date(new Date() - (1000 * 60 * 5))) } + }) + for (let oldAccount of oldAccounts) { + if (await login(oldAccount._id) === true) { // if login works + await removeAccount(oldAccount._id) + } + } + }) + // mail stuff + mailListener.on('server:connected', () => { + console.log("imapConnected") + }) + mailListener.on('server:disconnected', () => { + console.log("imapDisconnected") + mailListener.stop() + mailListener.start() + }) + mailListener.on('error', (err) => { + console.log(err) + }) + mailListener.on('mail', async (mail, seqno, attributes) => { + if (mail.headers.from.indexOf('mobile.mcdonalds.de') > -1) { + // console.log("emailParsed", mail.headers.subject, mail.headers.to) + if (mail.headers.to.indexOf('-') > -1) return // TODO: remove + await handleMail(mail.html, mail.subject, mail.headers.to, seqno) + } + }) + mailListener.start() +} + +Init() \ No newline at end of file diff --git a/accountmanager/package-lock.json b/accountmanager/package-lock.json new file mode 100644 index 0000000..70401bd --- /dev/null +++ b/accountmanager/package-lock.json @@ -0,0 +1,142 @@ +{ + "name": "accountmanager", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "addressparser": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz", + "integrity": "sha1-R6++GiqSYhkdtoOOT9HTm0CCF0Y=" + }, + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" + }, + "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=" + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "every": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/every/-/every-0.0.1.tgz", + "integrity": "sha1-lUFd64XgRaVMgndYaSgSXVXpwY4=" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "imap": { + "version": "0.8.19", + "resolved": "https://registry.npmjs.org/imap/-/imap-0.8.19.tgz", + "integrity": "sha1-NniHOTSrCc6mukh0HyhNoq9Z2NU=", + "requires": { + "readable-stream": "1.1.x", + "utf7": ">=1.0.2" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "mail-listener2": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/mail-listener2/-/mail-listener2-0.3.1.tgz", + "integrity": "sha1-GBRJfZYy/Y6vTjywg6qQEpZh+ok=", + "requires": { + "async": "^0.9.0", + "imap": "~0.8.14", + "mailparser": "~0.4.6" + } + }, + "mailparser": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-0.4.9.tgz", + "integrity": "sha1-HQpI1vqqovCawTmO0CPY746xnis=", + "requires": { + "encoding": ">=0.1.4", + "mime": "*", + "mimelib": ">=0.2.17", + "uue": "~1.0.0" + } + }, + "mime": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" + }, + "mimelib": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/mimelib/-/mimelib-0.3.1.tgz", + "integrity": "sha1-eHrdJBXYJ6yzr27EvKHqlZZBiFM=", + "requires": { + "addressparser": "~1.0.1", + "encoding": "~0.1.12" + } + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "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.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "utf7": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utf7/-/utf7-1.0.2.tgz", + "integrity": "sha1-lV9JCq5lO6IguUVqCod2wZk2CZE=", + "requires": { + "semver": "~5.3.0" + } + }, + "uue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/uue/-/uue-1.0.0.tgz", + "integrity": "sha1-ITuUSLmLmLnQPK9gGiI1ib2IZDA=" + }, + "uuid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" + } + } +} diff --git a/accountmanager/package.json b/accountmanager/package.json new file mode 100644 index 0000000..135793b --- /dev/null +++ b/accountmanager/package.json @@ -0,0 +1,16 @@ +{ + "name": "accountmanager", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "every": "0.0.1", + "mail-listener2": "^0.3.1", + "uuid": "^3.3.3" + } +} diff --git a/database.js b/database.js new file mode 100644 index 0000000..19a57d6 --- /dev/null +++ b/database.js @@ -0,0 +1,11 @@ +const config = require('./config') +const mongoose = require('mongoose') +mongoose.connect(config.mongodbURI, { + useNewUrlParser: true, + useUnifiedTopology: true, + useFindAndModify: true, + dbName: config.mongodbName +}) +mongoose.Promise = global.Promise +const Models = require('./models') +module.exports = { mongoose, Models } \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..34e231e --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,14 @@ +version: '3' +services: + redis: + image: redis + container_name: doofnalds_cache + hostname: redis + ports: + - "127.0.0.1:6379:6379" + mongo: + image: "mongo:latest" + container_name: doofnalds_db + hostname: mongodb + ports: + - "127.0.0.1:27017:27017" diff --git a/models/Account.js b/models/Account.js new file mode 100644 index 0000000..6203e16 --- /dev/null +++ b/models/Account.js @@ -0,0 +1,22 @@ +const mongoose = require('mongoose') +const schema = new mongoose.Schema({ + active: { + type: Boolean, + default: false, + }, + free: { + type: Boolean, + default: true, + }, + created: { + type: Date, + default: () => new Date(), + }, + email: String, + deviceId: String, + password: String, + link: String, + token: String, + lastLogin: Date, +}) +module.exports = mongoose.model('Account', schema) \ No newline at end of file diff --git a/models/GoogleCacheResult.js b/models/GoogleCacheResult.js new file mode 100644 index 0000000..5a38fb2 --- /dev/null +++ b/models/GoogleCacheResult.js @@ -0,0 +1,10 @@ +const mongoose = require('mongoose') +const schema = new mongoose.Schema({ + place_id: String, + query: String, + name: String, + latitude: Number, + longitude: Number, + lastRefresh: Date, +}) +module.exports = mongoose.model('GoogleCacheResult', schema) \ No newline at end of file diff --git a/models/Session.js b/models/Session.js new file mode 100644 index 0000000..414025f --- /dev/null +++ b/models/Session.js @@ -0,0 +1,33 @@ +const mongoose = require('mongoose') +const schema = new mongoose.Schema({ + userId: { + type: Number, + unique: true, + }, + active: { + type: Boolean, + default: !!!false, + }, + accounts: { + type: Array({ + type: mongoose.Types.ObjectId, + ref: 'Account' + }), + }, + store: { + type: mongoose.Types.ObjectId, + ref: 'Store' + }, + offers: Object, + + + created: { + type: Date, + default: () => new Date(), + }, + lastInteraction: { + type: Date, + default: () => new Date(), + }, +}) +module.exports = mongoose.model('Session', schema) \ No newline at end of file diff --git a/models/Store.js b/models/Store.js new file mode 100644 index 0000000..152ec8f --- /dev/null +++ b/models/Store.js @@ -0,0 +1,25 @@ +const mongoose = require('mongoose') +const schema = new mongoose.Schema({ + id: Number, + externalId: Number, + + storeId: String, + storeECPId: String, + + latitude: Number, + longitude: Number, + city: String, + postalCode: String, + address: String, + street: String, + phone: String, + seoURL: { + type: String, + unique: true, + }, + name1: String, + name2: String, + + lastRefresh: Date, +}) +module.exports = mongoose.model('Store', schema) \ No newline at end of file diff --git a/models/StoreFinderCorrelation.js b/models/StoreFinderCorrelation.js new file mode 100644 index 0000000..b37d37e --- /dev/null +++ b/models/StoreFinderCorrelation.js @@ -0,0 +1,13 @@ +const mongoose = require('mongoose') +const schema = new mongoose.Schema({ + place_id: String, + query: String, + latitude: Number, + longitude: Number, + store: { + type: mongoose.Types.ObjectId, + ref: 'Store' + }, + lastRefresh: Date, +}) +module.exports = mongoose.model('StoreFinderCorrelation', schema) \ No newline at end of file diff --git a/models/User.js b/models/User.js new file mode 100644 index 0000000..80a3401 --- /dev/null +++ b/models/User.js @@ -0,0 +1,14 @@ +const mongoose = require('mongoose') +const schema = new mongoose.Schema({ + id: { + type: Number, + required: true + }, + ios: { + type: Boolean, + default: false + }, + screenWidth: Number, + screenHeight: Number, +}) +module.exports = mongoose.model('User', schema) \ No newline at end of file diff --git a/models/index.js b/models/index.js new file mode 100644 index 0000000..b7ea45a --- /dev/null +++ b/models/index.js @@ -0,0 +1,8 @@ +module.exports = { + Account: require('./Account'), + Store: require('./Store'), + Session: require('./Session'), + StoreFinderCorrelation: require('./StoreFinderCorrelation'), + GoogleCacheResult: require('./GoogleCacheResult'), + User: require('./User'), +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d13bc76 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,623 @@ +{ + "name": "doofnalds-bot", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "amqp-connection-manager": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/amqp-connection-manager/-/amqp-connection-manager-3.0.0.tgz", + "integrity": "sha512-a5MsUDsG+CqMjwk/WNFSTE0H4pAaWJXw7L24QFa3MeaB+KA05PXoBsppYlIzaIqc1XLWZwjO9J42AFHNrDsVFQ==", + "requires": { + "promise-breaker": "^5.0.0" + } + }, + "amqplib": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.5.5.tgz", + "integrity": "sha512-sWx1hbfHbyKMw6bXOK2k6+lHL8TESWxjAx5hG8fBtT7wcxoXNIsFxZMnFyBjxt3yL14vn7WqBDe5U6BGOadtLg==", + "requires": { + "bitsyntax": "~0.1.0", + "bluebird": "^3.5.2", + "buffer-more-ints": "~1.0.0", + "readable-stream": "1.x >=1.1.9", + "safe-buffer": "~5.1.2", + "url-parse": "~1.4.3" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "bitsyntax": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bitsyntax/-/bitsyntax-0.1.0.tgz", + "integrity": "sha512-ikAdCnrloKmFOugAfxWws89/fPc+nw0OOG1IzIE72uSOg/A3cYptKCjSUhDTuj7fhsJtzkzlv7l3b8PzRHLN0Q==", + "requires": { + "buffer-more-ints": "~1.0.0", + "debug": "~2.6.9", + "safe-buffer": "~5.1.2" + } + }, + "bluebird": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", + "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "bson": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.1.tgz", + "integrity": "sha512-jCGVYLoYMHDkOsbwJZBCqwMHyH4c+wzgI9hG7Z6SZJRXWr+x58pdIbm2i9a/jFGCkRJqRUr8eoI7lDWa0hTkxg==" + }, + "buffer-more-ints": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz", + "integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==" + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "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=" + }, + "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" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ipaddr.js": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "kareem": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz", + "integrity": "sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "requires": { + "mime-db": "1.40.0" + } + }, + "mongodb": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.3.3.tgz", + "integrity": "sha512-MdRnoOjstmnrKJsK8PY0PjP6fyF/SBS4R8coxmhsfEU7tQ46/J6j+aSHF2n4c2/H8B+Hc/Klbfp8vggZfI0mmA==", + "requires": { + "bson": "^1.1.1", + "require_optional": "^1.0.1", + "safe-buffer": "^5.1.2", + "saslprep": "^1.0.0" + } + }, + "mongoose": { + "version": "5.7.7", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.7.7.tgz", + "integrity": "sha512-FU59waB4LKBa9KOnqBUcCcMIVRc09TFo1F8nMxrzSiIWATaJpjxxSSH5FBVUDxQfNdJLfg9uFHxaTxhhwjsZOQ==", + "requires": { + "bson": "~1.1.1", + "kareem": "2.3.1", + "mongodb": "3.3.3", + "mongoose-legacy-pluralize": "1.0.2", + "mpath": "0.6.0", + "mquery": "3.2.2", + "ms": "2.1.2", + "regexp-clone": "1.0.0", + "safe-buffer": "5.1.2", + "sift": "7.0.1", + "sliced": "1.0.1" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "mongoose-legacy-pluralize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", + "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" + }, + "mpath": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.6.0.tgz", + "integrity": "sha512-i75qh79MJ5Xo/sbhxrDrPSEG0H/mr1kcZXJ8dH6URU5jD/knFxCVqVC/gVSW7GIXL/9hHWlT9haLbCXWOll3qw==" + }, + "mquery": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.2.tgz", + "integrity": "sha512-XB52992COp0KP230I3qloVUbkLUxJIu328HBP2t2EsxSFtf4W1HPSOBWOXf1bqxK4Xbb66lfMJ+Bpfd9/yZE1Q==", + "requires": { + "bluebird": "3.5.1", + "debug": "3.1.0", + "regexp-clone": "^1.0.0", + "safe-buffer": "5.1.2", + "sliced": "1.0.1" + }, + "dependencies": { + "bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "promise-breaker": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/promise-breaker/-/promise-breaker-5.0.0.tgz", + "integrity": "sha512-mgsWQuG4kJ1dtO6e/QlNDLFtMkMzzecsC69aI5hlLEjGHFNpHrvGhFi4LiK5jg2SMQj74/diH+wZliL9LpGsyA==" + }, + "proxy-addr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", + "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.0" + } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "regexp-clone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", + "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" + }, + "require_optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "requires": { + "resolve-from": "^2.0.0", + "semver": "^5.1.0" + } + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + }, + "rqt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/rqt/-/rqt-4.0.0.tgz", + "integrity": "sha512-DUm6ZAt6ESeY46aFWhQwBvsOpXABRL5TTkpN/3KJjog7uk2ncYuU4UZ8FQV6qfWybihkMFCdoESNSxYQ5rSZiQ==" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "saslprep": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", + "optional": true, + "requires": { + "sparse-bitfield": "^3.0.3" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "sift": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz", + "integrity": "sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g==" + }, + "sliced": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" + }, + "sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", + "optional": true, + "requires": { + "memory-pager": "^1.0.2" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..e04aba1 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "doofnalds-bot", + "version": "1.0.0", + "description": "", + "main": "config.js", + "dependencies": { + "amqp-connection-manager": "^3.0.0", + "amqplib": "^0.5.5", + "body-parser": "^1.19.0", + "express": "^4.17.1", + "mongoose": "^5.7.7", + "querystring": "^0.2.0", + "rqt": "^4.0.0" + }, + "devDependencies": {}, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} diff --git a/renderservice/app-ios.jpg b/renderservice/app-ios.jpg new file mode 100644 index 0000000..a47fe6d Binary files /dev/null and b/renderservice/app-ios.jpg differ diff --git a/renderservice/app-ios.png b/renderservice/app-ios.png new file mode 100644 index 0000000..a52faff Binary files /dev/null and b/renderservice/app-ios.png differ diff --git a/renderservice/app.png b/renderservice/app.png new file mode 100644 index 0000000..3e2465d Binary files /dev/null and b/renderservice/app.png differ diff --git a/renderservice/index.js b/renderservice/index.js new file mode 100644 index 0000000..8c02448 --- /dev/null +++ b/renderservice/index.js @@ -0,0 +1,119 @@ +const config = require('../config') + +const express = require('../node_modules/express') +const bodyParser = require('../node_modules/body-parser') +const fs = require('fs') +const Jimp = require('jimp') +const sha1 = require('sha1') +const microService = express() +microService.use(bodyParser.json(true)) + +let AndroidAppImg +Jimp.read('./app.png').then((img) => AndroidAppImg = img) +let IOSAppImg +Jimp.read('./app-ios.png').then((img) => IOSAppImg = img) +let OverviewImg +Jimp.read('./overview.png').then((img) => OverviewImg = img) +microService + /** + * Putting the QR Code into a Screenshot + */ + .post('/qrcode', async (req, res) => { + const returnImage = !!req.body.ios + ? IOSAppImg.clone() + : AndroidAppImg.clone(); + + const qrCodeImage = await Jimp.read(Buffer.from(req.body.QrCode, 'base64')) + await qrCodeImage.crop(37, 37, 165, 165) + if (!!req.body.ios) { + /* + 198, 635 + 397, 835 + */ + await qrCodeImage.resize(200, 200, Jimp.RESIZE_NEAREST_NEIGHBOR) + await returnImage.composite(qrCodeImage, 198, 635) + const font = await Jimp.loadFont(Jimp.FONT_SANS_64_BLACK) + await returnImage.print(font, 0, 868, + { + text: req.body.RandomCode, + alignmentX: Jimp.HORIZONTAL_ALIGN_CENTER, + alignmentY: Jimp.VERTICAL_ALIGN_MIDDLE + }, + 591, + 915 - 868 + ); + } else { + await qrCodeImage.resize(421, 421, Jimp.RESIZE_NEAREST_NEIGHBOR) + await returnImage.composite(qrCodeImage, 329, 780) + const font = await Jimp.loadFont(Jimp.FONT_SANS_128_BLACK) + await returnImage.print(font, 0, 1212, + { + text: req.body.RandomCode, + alignmentX: Jimp.HORIZONTAL_ALIGN_CENTER, + alignmentY: Jimp.VERTICAL_ALIGN_MIDDLE + }, + 1080, + 146 + ); + } + res.set('Content-Type', 'image/png') + return res.send(await returnImage.getBufferAsync('image/png')) + }) + /** + * Putting the Offer Pics into a single Image + */ + .post('/overview', async (req, res) => { + let i = 0 + const finalHash = req.body.reduce((complete, url) => sha1(complete + url), '') + const finalHashPath = `./.cache/${ finalHash }.png` + if (fs.existsSync('./.cache') === false) + fs.mkdirSync('./.cache') + console.log(req.body.length, finalHash) + + if (fs.existsSync(finalHashPath) === false) { + const returnImage = OverviewImg.clone(); + await returnImage.resize( + Math.min(req.body.length, 3) * 500, + Math.ceil(req.body.length / 3) * 500 + ) + for (let inUrl of req.body) { + const cachePath = `./.cache/${ sha1(inUrl) }.png` + const url = config.mcdEndpointOfferImage + inUrl + console.log(cachePath, url) + let image + const x = i % 3, y = Math.floor(i / 3) + console.log(`${ i } at x=${ x }, y=${ y }`) + if (fs.existsSync(cachePath) === false) { + let httpImage = await Jimp.read(url) + console.log('downloading') + await httpImage.writeAsync(cachePath) + image = httpImage + } else { + let cacheImage = await Jimp.read(cachePath) + console.log('cache') + image = cacheImage + } + await image.resize(450, 450) + await returnImage.composite(image, 25 + x * 500, 25 + y * 500) + i++ + } + await returnImage.writeAsync(finalHashPath) + res.set('Cache-ID', finalHash) + res.set('Content-Type', 'image/png') + return res.send(await returnImage.getBufferAsync('image/png')) + } else { + const cacheImage = await Jimp.read(finalHashPath) + console.log('loading overview from cache') + res.set('Cache-ID', finalHash) + res.set('Content-Type', 'image/png') + return res.send(await cacheImage.getBufferAsync('image/png')) + } + }) + ; +/* + +329, 780 +750, 1201 +*/ + +microService.listen(config.renderService.port, config.renderService.host) \ No newline at end of file diff --git a/renderservice/overview.png b/renderservice/overview.png new file mode 100644 index 0000000..b84b89d Binary files /dev/null and b/renderservice/overview.png differ diff --git a/renderservice/package-lock.json b/renderservice/package-lock.json new file mode 100644 index 0000000..a535ada --- /dev/null +++ b/renderservice/package-lock.json @@ -0,0 +1,700 @@ +{ + "name": "renderservice", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@jimp/bmp": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.8.5.tgz", + "integrity": "sha512-o/23j1RODQGGjvb2xg+9ZQCHc9uXa5XIoJuXHN8kh8AJBGD7JZYiHMwNHaxJRJvadimCKUeA5udZUJAoaPwrYg==", + "requires": { + "@jimp/utils": "^0.8.5", + "bmp-js": "^0.1.0", + "core-js": "^2.5.7" + } + }, + "@jimp/core": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.8.5.tgz", + "integrity": "sha512-Jto1IdL5HYg7uE15rpQjK6dfZJ6d6gRjUsVCPW50nIfXgWizaTibFEov90W9Bj+irwKrX2ntG3e3pZUyOC0COg==", + "requires": { + "@jimp/utils": "^0.8.5", + "any-base": "^1.1.0", + "buffer": "^5.2.0", + "core-js": "^2.5.7", + "exif-parser": "^0.1.12", + "file-type": "^9.0.0", + "load-bmfont": "^1.3.1", + "mkdirp": "0.5.1", + "phin": "^2.9.1", + "pixelmatch": "^4.0.2", + "tinycolor2": "^1.4.1" + } + }, + "@jimp/custom": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.8.5.tgz", + "integrity": "sha512-hS4qHOcOIL+N93IprsIhFgr8F4XnC2oYd+lRaOKEOg3ptS2vQnceSTtcXsC0//mhq8AV6lNjpbfs1iseEZuTqg==", + "requires": { + "@jimp/core": "^0.8.5", + "core-js": "^2.5.7" + } + }, + "@jimp/gif": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.8.5.tgz", + "integrity": "sha512-Mj8jmv4AS76OY+Hx/Xoyihj02SUZ2ELk+O5x89pODz1+NeGtSWHHjZjnSam9HYAjycvVI/lGJdk/7w0nWIV/yQ==", + "requires": { + "@jimp/utils": "^0.8.5", + "core-js": "^2.5.7", + "omggif": "^1.0.9" + } + }, + "@jimp/jpeg": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.8.5.tgz", + "integrity": "sha512-7kjTY0BiCpwRywk+oPfpLto7cLI+9G0mf4N1bv1Hn+VLQwcXFy2fHyl4qjqLbbY6u4cyZgqN+R8Pg6GRRzv0kw==", + "requires": { + "@jimp/utils": "^0.8.5", + "core-js": "^2.5.7", + "jpeg-js": "^0.3.4" + } + }, + "@jimp/plugin-blit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.8.5.tgz", + "integrity": "sha512-r8Z1CwazaJwZCRbucQgrfprlGyH91tX7GubUsbWr+zy5/dRJAAgaPj/hcoHDwbh3zyiXp5BECKKzKW0x4reL4w==", + "requires": { + "@jimp/utils": "^0.8.5", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-blur": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.8.5.tgz", + "integrity": "sha512-UH5ywpV4YooUh9HXEsrNKDtojLCvIAAV0gywqn8EQeFyzwBJyXAvRNARJp7zr5OPLr9uGXkRLDCO9YyzdlXZng==", + "requires": { + "@jimp/utils": "^0.8.5", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-color": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.8.5.tgz", + "integrity": "sha512-7XHqcTQ8Y1zto1b9P1y8m1dzSjnOpBsD9OZG0beTpeJ5bgPX+hF5ZLmvcM6c5ljkINw5EUF1it07BYbkCxiGQA==", + "requires": { + "@jimp/utils": "^0.8.5", + "core-js": "^2.5.7", + "tinycolor2": "^1.4.1" + } + }, + "@jimp/plugin-contain": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.8.5.tgz", + "integrity": "sha512-ZkiPFx9L0yITiKtYTYLWyBsSIdxo/NARhNPRZXyVF9HmTWSLDUw1c2c1uvETKxDZTAVK+souYT14DwFWWdhsYA==", + "requires": { + "@jimp/utils": "^0.8.5", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-cover": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.8.5.tgz", + "integrity": "sha512-OdT4YAopLOhbhTUQV3R1v5ZZqIaUt3n3vJi/OfTbsak1t9UkPBVdmYPyhoont8zJdtdkF5dW16Ro1FTshytcww==", + "requires": { + "@jimp/utils": "^0.8.5", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-crop": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.8.5.tgz", + "integrity": "sha512-E1Hb+gfu2k74Gkqh96apAyVljsP5MjCH4TY6lECAAEcYKGH/XRhz6lY2dSEjCYE7KtiqjTZzWwYkgAvkwojj9Q==", + "requires": { + "@jimp/utils": "^0.8.5", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-displace": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.8.5.tgz", + "integrity": "sha512-fVgVYTS1HZzAXkg8Lg06PuirSUG5oXYaYYGL+3ZU4tmZn1pyZ+mZyfejpwtymETEYZnmymHoCT4xto19E/IRvA==", + "requires": { + "@jimp/utils": "^0.8.5", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-dither": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.8.5.tgz", + "integrity": "sha512-KSj2y8E3yK7tldjT/8ejqAWw5HFBjtWW6QkcxfW7FdV4c/nsXZXDkMbhqMZ7FkDuSYoAPeWUFeddrH4yipC5iA==", + "requires": { + "@jimp/utils": "^0.8.5", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-flip": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.8.5.tgz", + "integrity": "sha512-2QbGDkurPNAXZUeHLo/UA3tjh+AbAXWZKSdtoa1ArlASovRz8rqtA45YIRIkKrMH82TA3PZk8bgP2jaLKLrzww==", + "requires": { + "@jimp/utils": "^0.8.5", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-gaussian": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.8.5.tgz", + "integrity": "sha512-2zReC5GJcVAXtf3UgzFcHSYN277i02K9Yrhc1xJf3mti00s43uD++B5Ho7/mIo+HrntVvWhxqar7PARdq0lVIg==", + "requires": { + "@jimp/utils": "^0.8.5", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-invert": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.8.5.tgz", + "integrity": "sha512-GyMXPGheHdS14xfDceuZ9hrGm6gE9UG3PfTEjQbJmHMWippLC6yf8kombSudJlUf8q72YYSSXsSFKGgkHa67vA==", + "requires": { + "@jimp/utils": "^0.8.5", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-mask": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.8.5.tgz", + "integrity": "sha512-inD/++XO+MkmwXl9JGYQ8X2deyOZuq9i+dmugH/557p16B9Q6tvUQt5X1Yg5w7hhkLZ00BKOAJI9XoyCC1NFvQ==", + "requires": { + "@jimp/utils": "^0.8.5", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-normalize": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.8.5.tgz", + "integrity": "sha512-8YRWJWBT4NoSAbPhnjQJXGeaeWVrJAlGDv39A54oNH8Ry47fHcE0EN6zogQNpBuM34M6hRnZl4rOv1FIisaWdg==", + "requires": { + "@jimp/utils": "^0.8.5", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-print": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.8.5.tgz", + "integrity": "sha512-BviNpCiA/fEieOqsrWr1FkqyFuiG2izdyyg7zUqyeUTHPwqrTLvXO9cfP/ThG4hZpu5wMQ5QClWSqhZu1fAwxA==", + "requires": { + "@jimp/utils": "^0.8.5", + "core-js": "^2.5.7", + "load-bmfont": "^1.4.0" + } + }, + "@jimp/plugin-resize": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.8.5.tgz", + "integrity": "sha512-gIdmISuNmZQ1QwprnRC5VXVWQfKIiWineVQGebpMAG/aoFOLDXrVl939Irg7Fb/uOlSFTzpAbt1zpJ8YG/Mi2w==", + "requires": { + "@jimp/utils": "^0.8.5", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-rotate": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.8.5.tgz", + "integrity": "sha512-8T9wnL3gb+Z0ogMZmtyI6h3y7TuqW2a5SpFbzFUVF+lTZoAabXjEfX3CAozizCLaT+Duc5H2FJVemAHiyr+Dbw==", + "requires": { + "@jimp/utils": "^0.8.5", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-scale": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.8.5.tgz", + "integrity": "sha512-G+CDH9s7BsxJ4b+mKZ5SsiXwTAynBJ+7/9SwZFnICZJJvLd79Tws6VPXfSaKJZuWnGIX++L8jTGmFORCfLNkdg==", + "requires": { + "@jimp/utils": "^0.8.5", + "core-js": "^2.5.7" + } + }, + "@jimp/plugins": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.8.5.tgz", + "integrity": "sha512-52na0wqfQ3uItIA+C9cJ1EXffhSmABgK7ETClDseUh9oGtynHzxZ97smnFf1ydLjXLrF89Gt+YBxWLyiBGgiZQ==", + "requires": { + "@jimp/plugin-blit": "^0.8.5", + "@jimp/plugin-blur": "^0.8.5", + "@jimp/plugin-color": "^0.8.5", + "@jimp/plugin-contain": "^0.8.5", + "@jimp/plugin-cover": "^0.8.5", + "@jimp/plugin-crop": "^0.8.5", + "@jimp/plugin-displace": "^0.8.5", + "@jimp/plugin-dither": "^0.8.5", + "@jimp/plugin-flip": "^0.8.5", + "@jimp/plugin-gaussian": "^0.8.5", + "@jimp/plugin-invert": "^0.8.5", + "@jimp/plugin-mask": "^0.8.5", + "@jimp/plugin-normalize": "^0.8.5", + "@jimp/plugin-print": "^0.8.5", + "@jimp/plugin-resize": "^0.8.5", + "@jimp/plugin-rotate": "^0.8.5", + "@jimp/plugin-scale": "^0.8.5", + "core-js": "^2.5.7", + "timm": "^1.6.1" + } + }, + "@jimp/png": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.8.5.tgz", + "integrity": "sha512-zT89ucu8I2rsD3FIMIPLgr1OyKn4neD+5umwD3MY8AOB8+6tX5bFtnmTm3FzGJaJuibkK0wFl87eiaxnb+Megw==", + "requires": { + "@jimp/utils": "^0.8.5", + "core-js": "^2.5.7", + "pngjs": "^3.3.3" + } + }, + "@jimp/tiff": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.8.5.tgz", + "integrity": "sha512-Z7uzDcbHuwDg+hy2+UJQ2s5O6sqYXmv6H1fmSf/2dxBrlGMzl8yTc2/BxLrGREeoidDDMcKmXYGAOp4uCsdJjw==", + "requires": { + "core-js": "^2.5.7", + "utif": "^2.0.1" + } + }, + "@jimp/types": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.8.5.tgz", + "integrity": "sha512-XUvpyebZGd1vyFiJyxUT4H9A3mKD7MV2MxjXnay3fNTrcow0UJJspmFw/w+G3TP/1dgrVC4K++gntjR6QWTzvg==", + "requires": { + "@jimp/bmp": "^0.8.5", + "@jimp/gif": "^0.8.5", + "@jimp/jpeg": "^0.8.5", + "@jimp/png": "^0.8.5", + "@jimp/tiff": "^0.8.5", + "core-js": "^2.5.7", + "timm": "^1.6.1" + } + }, + "@jimp/utils": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.8.5.tgz", + "integrity": "sha512-D3+H4BiopDkhUKvKkZTPPJ53voqOkfMuk3r7YZNcLtXGLkchjjukC4056lNo7B0DzjBgowTYsQM3JjKnYNIYeg==", + "requires": { + "core-js": "^2.5.7" + } + }, + "any-base": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", + "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==" + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + }, + "bmp-js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", + "integrity": "sha1-4Fpj95amwf8l9Hcex62twUjAcjM=" + }, + "buffer": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz", + "integrity": "sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "buffer-equal": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", + "integrity": "sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs=" + }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" + }, + "core-js": { + "version": "2.6.10", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.10.tgz", + "integrity": "sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==" + }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "dom-walk": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", + "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=" + }, + "es-abstract": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.0.tgz", + "integrity": "sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg==", + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.0", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-inspect": "^1.6.0", + "object-keys": "^1.1.1", + "string.prototype.trimleft": "^2.1.0", + "string.prototype.trimright": "^2.1.0" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "exif-parser": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz", + "integrity": "sha1-WKnS1ywCwfbwKg70qRZicrd2CSI=" + }, + "file-type": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-9.0.0.tgz", + "integrity": "sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw==" + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "global": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", + "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", + "requires": { + "min-document": "^2.19.0", + "process": "~0.5.1" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + }, + "is-function": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", + "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=" + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "requires": { + "has": "^1.0.1" + } + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "requires": { + "has-symbols": "^1.0.0" + } + }, + "jimp": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.8.5.tgz", + "integrity": "sha512-BW7t/+TCgKpqZw/wHFwqF/A/Tyk43RmzRHyMBdqfOepqunUrajt0RTqowdWyFo4CS2FmD8pFiYfefWjpXFWrCA==", + "requires": { + "@jimp/custom": "^0.8.5", + "@jimp/plugins": "^0.8.5", + "@jimp/types": "^0.8.5", + "core-js": "^2.5.7", + "regenerator-runtime": "^0.13.3" + } + }, + "jpeg-js": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.3.6.tgz", + "integrity": "sha512-MUj2XlMB8kpe+8DJUGH/3UJm4XpI8XEgZQ+CiHDeyrGoKPdW/8FJv6ku+3UiYm5Fz3CWaL+iXmD8Q4Ap6aC1Jw==" + }, + "load-bmfont": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.0.tgz", + "integrity": "sha512-kT63aTAlNhZARowaNYcY29Fn/QYkc52M3l6V1ifRcPewg2lvUZDAj7R6dXjOL9D0sict76op3T5+odumDSF81g==", + "requires": { + "buffer-equal": "0.0.1", + "mime": "^1.3.4", + "parse-bmfont-ascii": "^1.0.3", + "parse-bmfont-binary": "^1.0.5", + "parse-bmfont-xml": "^1.1.4", + "phin": "^2.9.1", + "xhr": "^2.0.1", + "xtend": "^4.0.0" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "requires": { + "dom-walk": "^0.1.0" + } + }, + "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" + } + }, + "object-inspect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", + "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "omggif": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz", + "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==" + }, + "pako": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", + "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==" + }, + "parse-bmfont-ascii": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", + "integrity": "sha1-Eaw8P/WPfCAgqyJ2kHkQjU36AoU=" + }, + "parse-bmfont-binary": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz", + "integrity": "sha1-0Di0dtPp3Z2x4RoLDlOiJ5K2kAY=" + }, + "parse-bmfont-xml": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.4.tgz", + "integrity": "sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ==", + "requires": { + "xml-parse-from-string": "^1.0.0", + "xml2js": "^0.4.5" + } + }, + "parse-headers": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.2.tgz", + "integrity": "sha512-/LypJhzFmyBIDYP9aDVgeyEb5sQfbfY5mnDq4hVhlQ69js87wXfmEI5V3xI6vvXasqebp0oCytYFLxsBVfCzSg==", + "requires": { + "for-each": "^0.3.3", + "string.prototype.trim": "^1.1.2" + } + }, + "phin": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz", + "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==" + }, + "pixelmatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz", + "integrity": "sha1-j0fc7FARtHe2fbA8JDvB8wheiFQ=", + "requires": { + "pngjs": "^3.0.0" + } + }, + "pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==" + }, + "process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=" + }, + "regenerator-runtime": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "sha1": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz", + "integrity": "sha1-rdqnqTFo85PxnrKxUJFhjicA+Eg=", + "requires": { + "charenc": ">= 0.0.1", + "crypt": ">= 0.0.1" + } + }, + "string.prototype.trim": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.0.tgz", + "integrity": "sha512-9EIjYD/WdlvLpn987+ctkLf0FfvBefOCuiEr2henD8X+7jfwPnyvTdmW8OJhj5p+M0/96mBdynLWkxUr+rHlpg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.13.0", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimleft": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", + "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", + "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "timm": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/timm/-/timm-1.6.2.tgz", + "integrity": "sha512-IH3DYDL1wMUwmIlVmMrmesw5lZD6N+ZOAFWEyLrtpoL9Bcrs9u7M/vyOnHzDD2SMs4irLkVjqxZbHrXStS/Nmw==" + }, + "tinycolor2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz", + "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=" + }, + "utif": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/utif/-/utif-2.0.1.tgz", + "integrity": "sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg==", + "requires": { + "pako": "^1.0.5" + } + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "xhr": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.5.0.tgz", + "integrity": "sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ==", + "requires": { + "global": "~4.3.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "xml-parse-from-string": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", + "integrity": "sha1-qQKekp09vN7RafPG4oI42VpdWig=" + }, + "xml2js": { + "version": "0.4.22", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.22.tgz", + "integrity": "sha512-MWTbxAQqclRSTnehWWe5nMKzI3VmJ8ltiJEco8akcC6j3miOhjjfzKum5sId+CWhfxdOs/1xauYr8/ZDBtQiRw==", + "requires": { + "sax": ">=0.6.0", + "util.promisify": "~1.0.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + } + } +} diff --git a/renderservice/package.json b/renderservice/package.json new file mode 100644 index 0000000..d3c828d --- /dev/null +++ b/renderservice/package.json @@ -0,0 +1,15 @@ +{ + "name": "renderservice", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "jimp": "^0.8.5", + "sha1": "^1.1.1" + } +} diff --git a/sessionmanager/index.js b/sessionmanager/index.js new file mode 100644 index 0000000..bf24745 --- /dev/null +++ b/sessionmanager/index.js @@ -0,0 +1,257 @@ +const config = require('../config') + +const express = require('../node_modules/express') +const bodyParser = require('../node_modules/body-parser') +const querystring = require('../node_modules/querystring') + +const microService = express() +microService.use(bodyParser.json(true)) + +// MongoDB +const { mongoose, Models } = require('../database') + +// MCD API +const rqt = require('../node_modules/rqt') +const MCDSession = new rqt.Session({ + host: config.mcdEndpointLegacy, + headers: config.mcdHeadersLegacy, +}) + +const login = async (botId) => { + let dbId = mongoose.Types.ObjectId(botId) + let $user = await Models.Account.findById(dbId) + console.log('logging in', $user._id) + const res = await MCDSession.jqt('/v2/customer/security/authentication?type=traditional', { + method: 'POST', + type: 'json', + data: { + password: $user.password, + type: "email", + loginUsername: $user.email, + } + }) + if (res.status == "ok") { + await Models.Account.findByIdAndUpdate(dbId, { + token: res.details.token, + lastLogin: new Date(), + }) + return true + } else + console.error('logging in', $user._id, 'failed', res) + return false +} +const redeemCode = async (userName, token, storeId, offerId, retryOnError) => { + if (retryOnError == undefined) retryOnError = true + console.log('redeemCode', userName, storeId, offerId, retryOnError) + const res = await MCDSession.jqt('/v3/customer/offer/redemption', { + method: 'POST', + type: 'json', + data: { + "marketId": "DE", + "application": "MOT", + "languageName": "de-DE", + "platform": "android", + "offersIds": [`${ offerId }`], + "storeId": storeId, + "userName": userName, + }, + headers: { + 'Authorization': `Bearer ${ token }` + } + }) + if (!!res.Data && !!res.Data.QrCode) { + console.log('SUCC', res) + return res + } else { + if (retryOnError) { + switch (parseInt(res.statusCode)) { + case 10022: + case 10018: + console.log('not logged in') + break; + case -28006: //McDoofnald Exploded + console.error('McDoofnald Exploded') + break; + } + let retry = await redeemCode(userName, token, storeId, offerId, false) + return retry + } + console.error('error', res) + return false + } +} +const removeMCDType = (obj) => { + for(prop in obj) { + if (prop === '$type') + delete obj[prop] + else if (typeof obj[prop] === 'object') + removeMCDType(obj[prop]) + } +} +const compressOffers = (users) => { + let output = {} + let uniqueOffers = {} + let $users = Object.keys(users) + for (let i=0;i < $users.length;i++) { + let $user = $users[i] + let $offers = users[ $user ] + for (let j=0;j< $offers.length; j++) { + let $offer = $offers[ j ] + if (!uniqueOffers[ $offer.Id ]) { + uniqueOffers[ $offer.Id ] = { + ...$offer, + tokens: [] + } + } + uniqueOffers[ $offer.Id ].tokens.push($user) + } + } + return Object.values(uniqueOffers) +} +const customerOffer = async (userName, token, lat, lng, storeId, retryOnError) => { + if (retryOnError == undefined) retryOnError = true + const res = await MCDSession.jqt('/v3/customer/offer?' + querystring.stringify({ + marketId: 'DE', + application: 'MOT', + languageName:'de-DE', + platform: 'ios', + userName: userName, + latitude: lat, + longitude: lng, + storeId: storeId || '[]' + }), { + method: 'GET', + headers: { + 'Authorization': `Bearer ${ token }` + } + }) + if (!!res.ResultCode && parseInt(res.ResultCode) == 1) { + console.log(res) + return res + } else { + console.error('ERROR', res) + if (retryOnError) { + switch (parseInt(res.statusCode)) { + case 10022: + case 10018: + console.log('not logged in') + break; + case -28006: //McDoofnald Exploded + console.error('McDoofnald Exploded') + break; + } + let retry = await customerOffer(userName, token, lat, lng, storeId, false) + return retry + } + return false + } +} + +microService + /** + * Creating a Session + */ + .post('/sitzung/:user', async (req, res) => { + const userId = parseInt(req.params.user) + let freeAccounts = await Models.Account + .count({ free: true, active: true }) + .limit(config.sessionManager.accountsPerSession); + + if (freeAccounts < config.sessionManager.accountsPerSession) { + return res.status(500).json('Bitte versuchen sie es später erneut, es werden gerade neue Accounts erstellt.') + } + + let accountsForSession = await Models.Account + .find({ free: true, active: true }) + .limit(config.sessionManager.accountsPerSession); + + for (let account of accountsForSession) { + await Models.Account.findByIdAndUpdate(account._id, { + free: false, + lastLogin: new Date(), + }) + await login(account._id) + } + + if (await Models.Session.count({ userId: userId }) > 0) { + await Models.Session.findOneAndDelete({ userId: userId }) + } + let session = await Models.Session.create({ + userId: userId, + accounts: accountsForSession.map(a => mongoose.Types.ObjectId(a._id)), + }) + //console.log(session) + res.json(session) + }) + /** + * Selecting a Restaurant and sending fetched Offers to rabbitmq + */ + .post('/sitzung/:user/restaurant/:restaurant', async (req, res) => { + const userId = parseInt(req.params.user) + const restaurantId = mongoose.Types.ObjectId(req.params.restaurant) + if (await Models.Session.count({ userId: userId }) > 0) { + if (await Models.Store.count({ _id: restaurantId }) === 0) { + return res.json('Fehler: Kein Restaurant mit dieser ID') + } + await Models.Session + .findOne({ userId }) + .update({ store: restaurantId }); + const sessionData = await Models.Session + .findOne({ userId }) + .populate('store') + .populate('accounts'); + // ES6 <3 + const { latitude: storeLat, longitude: storeLon, storeId } = sessionData.store + const output = {} + for (let sessionAccount of sessionData.accounts) { + let offers = await customerOffer(sessionAccount.email, sessionAccount.token, storeLat, storeLon, storeId) + if (!offers || !offers.Data || offers.Data.length === 0) continue; // skip empty responses, in case they somehow are + //offers.Data.map((offer) => console.log(offer.Archived, offer.Expired, offer.Redeemed)) + offers.Data = offers.Data.filter((offer) => !offer.Archived && !offer.Expired && !offer.Redeemed) + offers.Data = offers.Data.map((offer) => { + removeMCDType(offer) + return offer + }) + output[ sessionAccount._id.toString() ] = offers.Data + } + const cOffers = compressOffers(output) + //console.log(JSON.stringify(cOffers, null, "\t")) + await Models.Session + .findOne({ userId }) + .update({ offers: cOffers }) + return res.json(cOffers) + } + res.json(false) + }) + /** + * Redeems the Offer + */ + .get('/sitzung/:user/angebot/:offer', async (req, res) => { + const userId = parseInt(req.params.user) + const offerId = parseInt(req.params.offer) + const sessionData = await Models.Session + .findOne({ userId }) + .populate('store'); + let selectedOffer = sessionData.offers.filter((offer) => offer.Id === offerId) + if (selectedOffer.length === 0) return res.json('Kein Angebot mit der ID gefunden') + selectedOffer = selectedOffer[0] + + let offerResult = false + for (let i in selectedOffer.tokens) { + let offerAccountID = selectedOffer.tokens[ i ] + try { + // console.log('trying code redemtion with account', offerAccountID) + let account = await Models.Account.findById(mongoose.Types.ObjectId(offerAccountID)) + offerResult = await redeemCode(account.email, account.token, sessionData.store.storeId, offerId) + } catch (ex) { + console.error(ex) + } + } + if (!!offerResult) { + removeMCDType(offerResult) + if (offerResult.ResultCode == 1) + return res.json(offerResult.Data) + } + res.json(false) + }) +microService.listen(config.sessionManager.port, config.sessionManager.host) \ No newline at end of file diff --git a/sessionmanager/package-lock.json b/sessionmanager/package-lock.json new file mode 100644 index 0000000..4161266 --- /dev/null +++ b/sessionmanager/package-lock.json @@ -0,0 +1,5 @@ +{ + "name": "sessionmanager", + "version": "1.0.0", + "lockfileVersion": 1 +} diff --git a/sessionmanager/package.json b/sessionmanager/package.json new file mode 100644 index 0000000..8d1f4ed --- /dev/null +++ b/sessionmanager/package.json @@ -0,0 +1,12 @@ +{ + "name": "sessionmanager", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": {} +} diff --git a/storefinder/index.js b/storefinder/index.js new file mode 100644 index 0000000..53e3f84 --- /dev/null +++ b/storefinder/index.js @@ -0,0 +1,213 @@ +const config = require('../config') + +const express = require('../node_modules/express') +const bodyParser = require('../node_modules/body-parser') +// MCD API +const rqt = require('../node_modules/rqt') +const MCDSession = new rqt.Session({ + host: config.mcdEndpointLegacy, + headers: config.mcdHeadersLegacy, +}) +const querystring = require('../node_modules/querystring') + +const microService = express() +microService.use(bodyParser.json(true)) +// MongoDB +const { mongoose, Models } = require('../database') + +const ortSuche = (query) => { + return rqt('https://nominatim.openstreetmap.org/search?format=json&q=' + encodeURIComponent(query + ', Deutschland')) +} +const ortSucheGoogle = async (query) => { + return rqt.jqt('https://maps.googleapis.com/maps/api/place/textsearch/json?' + querystring.stringify({ + key: config.storeFinder.googlePlacesKey, + query: query, + })) +} + +microService + .post('/fillialsuche', async (req, res) => { + let lon, lat, searchfield, place_id + let correlationQuery = {} + + if (!!req.body.query) { + let query = req.body.query.toString() + console.log('Searching with Query') + if (await Models.GoogleCacheResult.count({ query }) === 0) { + let orte = await ortSucheGoogle(query) + orte = orte.results + if (orte.length > 0) { + /*orte = orte.sort((a,b) => { + a = a.importance * (a.type === 'city' ? 2 : 1) + b = b.importance * (b.type === 'city' ? 2 : 1) + return a > b + })*/ + const ort = orte[0] + lon = parseFloat(ort.geometry.location.lng) + lat = parseFloat(ort.geometry.location.lat) + searchfield = ort.name + place_id = ort.place_id + correlationQuery = { + place_id: ort.place_id, + longitude: lon, + latitude: lat, + } + await Models.GoogleCacheResult.create({ + query, + name: searchfield, + place_id: place_id, + longitude: lon, + latitude: lat, + }) + console.log('Google Result', lon, lat, searchfield, place_id) + } else { + console.log('Keine Orte von #1 gefunden') + return res.status(400).json('Keine Orte gefunden') + } + } else { + let cachedResult = await Models.GoogleCacheResult.findOne({ query }) + lon = cachedResult.longitude + lat = cachedResult.latitude + searchfield = cachedResult.name + place_id = cachedResult.place_id + correlationQuery = { + place_id: place_id, + longitude: lon, + latitude: lat, + } + console.log('Cache Result', lon, lat, searchfield, place_id) + + } + } else if (!!req.body.lat) { + console.log('Searching with Location', req.body) + lon = parseFloat(req.body.lon) + lat = parseFloat(req.body.lat) + correlationQuery = { + longitude: parseInt(lon * 10000) / 10000, + latitude: parseInt(lat * 10000) / 10000, + } + searchfield = '' + place_id = false + } else { + console.log('Invalid Search') + return res.status(400).json(false) + } + + let restaurants = [] + if (!!correlationQuery) { + let correlationCount = await Models.StoreFinderCorrelation.count(correlationQuery) + console.log('correlationCount', correlationCount) + if (correlationCount === 0) { + console.log('No Correlations found, asking API') + console.time('MCD Search') + const restaurants = await rqt.jqt('https://www.mcdonalds.de/search', { + method: 'POST', + type: 'application/x-www-form-urlencoded; charset=UTF-8', + data: querystring.stringify({ + longitude: lon, + latitude: lat, + searchfield: searchfield, + radius: 12, // 12km umkreis suche + coupon: true, + }), + timeout: 6000, + }) + .then(($res) => { + if ($res.unfilteredCount === 0) throw 'Keine Restaurants gefunden' + $res.restaurantList = $res.restaurantList.sort((a, b) => a.distance - b.distance) + return $res.restaurantList.map((entry) => { + return entry.restaurant + }) + }) + .then(async ($res) => { + console.log('retaurants', $res) + let restaurants = [] + for (let restaurant of $res) { + let seoCount = await Models.Store.count({ seoURL: restaurant.seoURL }) + if (seoCount == 0) { + let ecpStore = await MCDSession.jqt('/v3/restaurant/location?' + querystring.stringify({ + filter: 'search', + query: JSON.stringify({ + market: 'DE', + pageSize: 1, + local: 'de-DE', + generalStoreStatusCode: 'OPEN,TEMPCLOSE,RENOVATION', + locationCriteria: { + longitude: lon, + latitude: lat, + distance: 22000 + } + }), + }), { method: 'GET', type: 'json', }) + if (ecpStore.length === 0) continue; // Skip Restaurants, we dont have the ECPId for + console.log(ecpStore) + ecpStore = ecpStore[0] + if (ecpStore.identifiers.storeIdentifier.filter((a) => a.identifierType == 'ECPID').length == 0) continue + + await Models.Store.create({ + id: restaurant.id, + externalId: restaurant.externalId, + + storeId: ecpStore.id, + storeECPId: ecpStore.identifiers.storeIdentifier.filter((a) => a.identifierType == 'ECPID')[0].identifierValue, + + latitude: restaurant.latitude, + longitude: restaurant.longitude, + + city: restaurant.city, + postalCode: restaurant.postalCode, + address: restaurant.address, + street: restaurant.street, + phone: restaurant.phone, + seoURL: restaurant.seoURL, + name1: restaurant.name1, + name2: restaurant.name2, + + lastRefresh: new Date(), + }) + } + let dbRestaurant = await Models.Store.findOne({ seoURL: restaurant.seoURL }) + restaurants.push(dbRestaurant) + } + console.timeEnd('MCD Search') + return restaurants + }) + .catch(($err) => { + console.timeEnd('MCD Search') + console.error($err) + return false + }) + if (!!restaurants) { + for (let restaurant of restaurants) { + if (await Models.StoreFinderCorrelation.count({ + ...correlationQuery, + store: restaurant._id, + }) === 0) { + await Models.StoreFinderCorrelation.create({ + ...correlationQuery, + store: restaurant._id, + lastRefresh: new Date(), + }) + } + } + return res.json(restaurants) + } else { + return res.json(false) + } + } else { + console.time('Correlation Search') + console.log('Correlations found, returning them...') + let restaurantsDB = await Models.StoreFinderCorrelation + .find(correlationQuery) + .select('store') + .populate('store'); + restaurants = restaurantsDB.map(corr => corr.store) + console.timeEnd('Correlation Search') + return res.json(restaurants) + } + } + + return res.json(false) + }) + +microService.listen(config.storeFinder.port, config.storeFinder.host) \ No newline at end of file diff --git a/storefinder/package-lock.json b/storefinder/package-lock.json new file mode 100644 index 0000000..3b8577a --- /dev/null +++ b/storefinder/package-lock.json @@ -0,0 +1,5 @@ +{ + "name": "storefinder", + "version": "1.0.0", + "lockfileVersion": 1 +} diff --git a/storefinder/package.json b/storefinder/package.json new file mode 100644 index 0000000..726e9e1 --- /dev/null +++ b/storefinder/package.json @@ -0,0 +1,12 @@ +{ + "name": "storefinder", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": {} +} diff --git a/telegrambot/index.js b/telegrambot/index.js new file mode 100644 index 0000000..f284bb1 --- /dev/null +++ b/telegrambot/index.js @@ -0,0 +1,411 @@ +const config = require('../config') +const rqt = require('../node_modules/rqt') +const MCDSession = new rqt.Session({ + host: config.mcdEndpoint, + headers: config.mcdHeaders, +}) + +const Telegraf = require('telegraf') +const Markup = require('telegraf/markup') +const Extra = require('telegraf/extra') +const Stage = require('telegraf/stage') +const { leave } = Stage +const Scene = require('telegraf/scenes/base') +const rateLimit = require('telegraf-ratelimit') + +// MongoDB +const { mongoose, Models } = require('../database') + +const bot = new Telegraf(config.telegramBot.token) + +/* Redis Stuff */ +const Redis = require('redisng') +const redis = new Redis() +redis.connect(config.redisHost, config.redisPort) +const memorysession = require('telegraf/session') +const RedisSession = require('telegraf-session-redis') +const session = new RedisSession({ + store: { + host: config.redisHost, + port: config.redisPort, + }, + getSessionKey: (ctx) => { + if (!ctx.from) return console.log('fail redis key', ctx); + return ctx.from.id.toString(); + } +}) +bot.use(memorysession()) +//bot.use(session) +bot.use(rateLimit({ + window: 3000, + limit: 10, + onLimitExceeded: (ctx, next) => ctx.reply('Sie haben zuviele Anfragen geschickt, bitte warten Sie') +})) +bot.use(async (ctx, next) => { + const start = new Date() + //console.log(ctx) + await next() + const ms = new Date() - start + console.log('Response time: %sms', ms) +}) +// bot.use(Telegraf.log()) + +const cancelText = '\n\nZum abbrechen /cancel schicken!' +const restartText = '\n\nZum neustarten /start schicken!' +const errorTextSessionManager = 'Tut mir leid, der SessionManager ist gerade nicht erreichbar. Bitte kontaktiere den Entwickler' +const errorTextRenderService = 'Tut mir leid, der RenderService ist gerade nicht erreichbar. Bitte kontaktiere den Entwickler' +const errorTextStoreFinder = 'Tut mir leid, der StoreFinder ist gerade nicht erreichbar. Bitte kontaktiere den Entwickler' + + +// Setup 1 Stage +const setup0Stage = new Scene('setup') +setup0Stage.enter(async (ctx) => { + await ctx.reply('Willkommen! Bevor wir loslegen können, setze bitte einige Einstellungen.', + Markup + .keyboard([ + ['Ich nutze Android', 'Ich nutze ein iPhone'], + ['❌ Überspringen'], + ]) + .resize() + .extra() + ) +}) +setup0Stage.hears('Ich nutze Android', async (ctx) => { + console.log('android') + await ctx.replyWithChatAction('typing') + await ctx.reply('Du hast dich für ein Android entschieden', Markup.removeKeyboard()) + await Models.User.create({ + id: ctx.from.id, + ios: false, + screenWidth: 591, + screenHeight: 1280, + }) + let userExists = await Models.User.count({ id: ctx.from.id }) + if (userExists === 0) { + await Models.User.create({ + id: ctx.from.id, + ios: false, + screenWidth: 1080, + screenHeight: 1920, + }) + } else { + await Models.User.findOneAndUpdate({ + id: ctx.from.id + }, { + ios: false, + screenWidth: 1080, + screenHeight: 1920, + }) + } + await ctx.scene.enter('setup1') +}) +setup0Stage.hears('Ich nutze ein iPhone', async (ctx) => { + await ctx.replyWithChatAction('typing') + await ctx.reply('Du hast dich für ein iPhone entschieden', Markup.removeKeyboard()) + let userExists = await Models.User.count({ id: ctx.from.id }) + if (userExists === 0) { + await Models.User.create({ + id: ctx.from.id, + ios: false, + screenWidth: 591, + screenHeight: 1280, + }) + } else { + await Models.User.findOneAndUpdate({ + id: ctx.from.id + }, { + ios: false, + screenWidth: 591, + screenHeight: 1280, + }) + } + await ctx.scene.enter('setup1') +}) +setup0Stage.hears('❌ Überspringen', async (ctx) => { + await ctx.replyWithChatAction('typing') + await ctx.reply('Du hast dich für ein Android-Smartphone mit 1080x1920px entschieden', Markup.removeKeyboard()) + let userExists = await Models.User.count({ id: ctx.from.id }) + if (userExists === 0) { + await Models.User.create({ + id: ctx.from.id, + ios: false, + screenWidth: 1080, + screenHeight: 1920, + }) + } else { + await Models.User.findOneAndUpdate({ + id: ctx.from.id + }, { + id: ctx.from.id, + ios: false, + screenWidth: 1080, + screenHeight: 1920, + }) + } + return ctx.scene.enter('main') +}) + +// Setup 2 Stage +const setup1Stage = new Scene('setup1') +setup1Stage.enter( + (ctx) => ctx.reply('Um ein perfekten Code-Screenshot zu erzeugen benötige ich deine Bildschirmgröße. Dafür musst du mir einfach nur einen Screenshot schicken.', + Markup + .keyboard([ + ['❌ Überspringen'], + ]) + .forceReply() + .resize() + .oneTime() + .extra() + ) +) +setup1Stage.hears('❌ Überspringen', async (ctx) => { + return ctx.scene.enter('main') +}) +setup1Stage.on('message', async (ctx) => { + await ctx.replyWithChatAction('typing') + if (!!ctx.message && !!ctx.message.photo) { + let resPhoto = ctx.message.photo.reverse()[0] + await Models.User.findOneAndUpdate({ + id: ctx.from.id + }, { + screenWidth: resPhoto.width, + screenHeight: resPhoto.height, + }) + ctx.reply('Deine Bildschirmauflösung wurde gesetzt.') + } else { + return ctx.reply('Bitte schick mir einen Screenshot als Bild oder wähle "Überspringen"!') + } + return ctx.scene.enter('main') +}) + + +// Main Stage +const mainStage = new Scene('main') +mainStage.enter( + (ctx) => ctx.reply('Willkommen! Schicke mir einen Standort über 📎 oder einen Ort(z.B. Berlin)' + cancelText, Extra.markup((markup) => { + return markup.resize() + .keyboard([ + markup.locationRequestButton('Aktuellen Standort senden') + ]) + })) +) +mainStage.on('message', async (ctx) => { + await ctx.replyWithChatAction('typing') + let results = [] + let url = `http://${ config.storeFinder.host }:${ config.storeFinder.port }/fillialsuche` + try { + if (!!ctx.message.location) { + results = await rqt.jqt(url, { + method: 'POST', + type: 'json', + data: { + lon: ctx.message.location.longitude, + lat: ctx.message.location.latitude, + }, + timeout: 15000, + }) + } else if (!!ctx.message.text) { + results = await rqt.jqt(url, { + method: 'POST', + type: 'json', + data: { + query: ctx.message.text, + }, + timeout: 15000, + }) + } else { + return ctx.reply('Schicke mir einen Standort über 📎 oder einen Ort(z.B. Berlin)' + cancelText) + } + } catch (e) { + console.error(e) + return ctx.reply(errorTextStoreFinder + cancelText) + } + + if (typeof(results) == 'object' && (!!results || results.length > 0)) { + await ctx.reply('Bitte wähle eine Filliale aus' + cancelText, + Markup.inlineKeyboard( + results.map(restaurant => { + return [Markup.callbackButton(`${ restaurant.street } ${ restaurant.address }`, restaurant._id)] + }) + ).oneTime().extra() + ) + } else { + return ctx.reply('Ort nicht gefunden') + } +}) +mainStage.action(/([0-9a-z]{12,24})/, async (ctx) => { + await ctx.replyWithChatAction('typing') + let dbId = mongoose.Types.ObjectId(ctx.match[1]) + ctx.deleteMessage() + ctx.session.fillialSelection = dbId + try { + ctx.answerCbQuery('Lade Angebote...') + let url = `http://${ config.sessionManager.host }:${ config.sessionManager.port }/sitzung/${ ctx.from.id }` + let session = await rqt.jqt(url, { method: 'POST', timeout: 15000 }) + ctx.session.id = session._id + ctx.scene.enter('angebotSelect') + } catch (e) { + console.error(e) + return ctx.reply(errorTextSessionManager + cancelText) + } +}) +// Offer Selection +const angebotSelectStage = new Scene('angebotSelect') +angebotSelectStage.enter(async (ctx) => { + let loadMsg = await ctx.reply('Lade Angebote...', Markup.removeKeyboard().extra()) + let offers = [] + await ctx.replyWithChatAction('upload_photo') + try { + let url = `http://${ config.sessionManager.host }:${ config.sessionManager.port }/sitzung/${ ctx.from.id }/restaurant/${ ctx.session.fillialSelection }` + offers = await rqt.jqt(url, { method: 'POST', timeout: 15000 }) + offers = offers.filter((offer) => offer.OfferType !== 9) + } catch (e) { + console.error(e) + return ctx.reply(errorTextSessionManager + restartText) + } + await ctx.deleteMessage(loadMsg.message_id) + let overviewImage, cacheId + try { + let url = `http://${ config.renderService.host }:${ config.renderService.port }/overview` + let response = await rqt.aqt(url, { + data: offers.map(offer => offer.ImageBaseName), + type: 'json', method: 'POST', timeout: 15000, binary: true + }) + cacheId = response.headers['cache-id'] + overviewImage = response.body + } catch (e) { + console.error(e) + return ctx.reply(errorTextRenderService + restartText) + } + let fileId = await redis.get('render-' + cacheId) + let selectMsg + let selectMsgKeyboard = Markup.inlineKeyboard( + offers.map(offer => { + return [Markup.callbackButton(`[${ offer.Id }] ${ offer.Name }`, offer.Id)] + }) + .concat([ + [ Markup.callbackButton('Abbrechen', 'abort') ] + ]) + ).extra() + if (!fileId) { + selectMsg = await ctx.replyWithPhoto({ source: overviewImage }, selectMsgKeyboard) + await redis.set('render-' + cacheId, selectMsg.photo.reverse()[0].file_id) + } else { + console.log('using cached file id') + selectMsg = await ctx.replyWithPhoto(fileId, selectMsgKeyboard) + } + await redis.set('msg-' + ctx.from.id, selectMsg.message_id) + // ctx.scene.state.selectMsg = selectMsg.message_id // doesnt work because of #reason +}) +angebotSelectStage.leave(async (ctx) => { + let msgToDelete = await redis.get('msg-' + ctx.from.id) + if (!!msgToDelete) + try { + await ctx.deleteMessage(msgToDelete) + } catch (e) {} +}) +angebotSelectStage.action('abort', (ctx) => ctx.scene.enter('main')) +angebotSelectStage.action('back', (ctx) => ctx.scene.leave()) +angebotSelectStage.action(/(.*)/, async (ctx) => { + const offerId = parseInt(ctx.match[1]) + try { + ctx.answerCbQuery('Lade Angebot...') + let url = `http://${ config.sessionManager.host }:${ config.sessionManager.port }/sitzung/${ ctx.from.id }/angebot/${ offerId }` + let code = await rqt.jqt(url, { method: 'GET', timeout: 15000, type: 'json' }) + if (!code) throw 'Kein Code vorhanden' + ctx.session.code = code + console.log('entering code stage', !!code) + ctx.scene.enter('angebotCode') + } catch (e) { + console.error(e) + return ctx.reply(errorTextSessionManager + cancelText) + } +}) +// Offer Code +const angebotCodeStage = new Scene('angebotCode') +angebotCodeStage.enter(async (ctx) => { + await ctx.replyWithChatAction('upload_photo') + //let loadMsg = await ctx.reply('Lade Code...' + cancelText) + let codeImage + try { + let code = ctx.session.code + let user = await Models.User.findOne({ id: ctx.from.id }) + code.ios = !!user.ios + code.screenWidth = user.screenWidth + code.screenHeight = user.screenHeight + let url = `http://${ config.renderService.host }:${ config.renderService.port }/qrcode` + codeImage = await rqt.bqt(url, { data: code, type: 'json', method: 'POST', timeout: 6000 }) + } catch (e) { + console.error(e) + return ctx.reply(errorTextRenderService + cancelText) + } + //await ctx.deleteMessage(loadMsg.message_id) + let codeMsg = await ctx.replyWithPhoto({ source: codeImage }, Markup.inlineKeyboard([ + [ + Markup.callbackButton('Zurück', 'back'), + Markup.callbackButton('Nächster Code', 'next') + ], + [ + Markup.callbackButton('Sitzung beenden', 'abort') + ] + ]).extra()) + ctx.session.codeMsg = codeMsg.message_id +}) +angebotCodeStage.leave(async (ctx) => { + if (!!ctx.session.codeMsg) + await ctx.deleteMessage(ctx.session.codeMsg) +}) +angebotCodeStage.action('abort', async (ctx) => { + await ctx.replyWithChatAction('typing') + try { + //TODO: call SessionManager to delete the session + } catch(e) { + } + leaveFunc(ctx) + ctx.scene.enter('main') +}) +angebotCodeStage.action('back', (ctx) => { + ctx.answerCbQuery('') + ctx.scene.enter('angebotSelect') +}) +angebotCodeStage.action('next', async (ctx) => { + ctx.answerCbQuery('') + ctx.scene.enter('angebotCode') +}) +angebotCodeStage.action(/(.*)/, async (ctx) => { + ctx.answerCbQuery('') + console.log(ctx.match[1]) +}) + +const startFunc = async (ctx) => { + let userExists = await Models.User.count({ id: ctx.from.id }) + if (userExists === 0) { + ctx.scene.enter('setup') + } else { + ctx.scene.enter('main') + } +} +// Create scene manager +const stage = new Stage() +const leaveFunc = leave() +stage.command('cancel', (ctx) => { + leaveFunc(ctx) + ctx.reply('Aktion erfolgreich abgebrochen, nutze /start um nochmal neu anzufangen') +}) +stage.command('start', (ctx) => { + leaveFunc(ctx) + startFunc(ctx) +}) + +// Scene registration +stage.register(setup0Stage) +stage.register(setup1Stage) +stage.register(mainStage) +stage.register(angebotSelectStage) +stage.register(angebotCodeStage) + +bot.use(stage.middleware()) +bot.command('start', startFunc) +bot.command('settings', (ctx) => ctx.scene.enter('setup')) +bot.launch() \ No newline at end of file diff --git a/telegrambot/package-lock.json b/telegrambot/package-lock.json new file mode 100644 index 0000000..b777bde --- /dev/null +++ b/telegrambot/package-lock.json @@ -0,0 +1,142 @@ +{ + "name": "tgbot", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/node": { + "version": "12.12.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.5.tgz", + "integrity": "sha512-KEjODidV4XYUlJBF3XdjSH5FWoMCtO0utnhtdLf1AgeuZLOrRbvmU/gaRCVg7ZaQDjVf3l84egiY0mRNe5xE4A==" + }, + "@types/redis": { + "version": "2.8.14", + "resolved": "https://registry.npmjs.org/@types/redis/-/redis-2.8.14.tgz", + "integrity": "sha512-255dzsOLJdXFHBio9/aMHGozNkoiBUgc+g2nlNjbTSp5qcAlmpm4Z6Xs3pKOBLNIKdZbA2BkUxWvYSIwKra0Yw==", + "requires": { + "@types/node": "*" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "double-ended-queue": { + "version": "2.1.0-0", + "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", + "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + }, + "redis": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", + "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", + "requires": { + "double-ended-queue": "^2.1.0-0", + "redis-commands": "^1.2.0", + "redis-parser": "^2.6.0" + } + }, + "redis-commands": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.5.0.tgz", + "integrity": "sha512-6KxamqpZ468MeQC3bkWmCB1fp56XL64D4Kf0zJSwDZbVLLm7KFkoIcHrgRvQ+sk8dnhySs7+yBg94yIkAK7aJg==" + }, + "redis-parser": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", + "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=" + }, + "redis-proto": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/redis-proto/-/redis-proto-2.1.0.tgz", + "integrity": "sha1-hPt/aOpJdAqxQBZwjJr95ioqRIo=" + }, + "redisng": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redisng/-/redisng-2.0.0.tgz", + "integrity": "sha1-Kv3Nxw8UYiVePZizgqLbZwjrmWw=", + "requires": { + "redis-proto": "^2.0.0" + } + }, + "sandwich-stream": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/sandwich-stream/-/sandwich-stream-2.0.2.tgz", + "integrity": "sha512-jLYV0DORrzY3xaz/S9ydJL6Iz7essZeAfnAavsJ+zsJGZ1MOnsS52yRjU3uF3pJa/lla7+wisp//fxOwOH8SKQ==" + }, + "telegraf": { + "version": "3.33.3", + "resolved": "https://registry.npmjs.org/telegraf/-/telegraf-3.33.3.tgz", + "integrity": "sha512-ItSAeE9OjFH+X0rS8DeErccoUZRy2hBl+mDjFWDqyZWyRElxA5L178UpJV7tM6hCVN/leFY+9orfra2JtX4AyQ==", + "requires": { + "@types/node": "^12.0.4", + "debug": "^4.0.1", + "node-fetch": "^2.2.0", + "sandwich-stream": "^2.0.1", + "telegram-typings": "^3.6.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "telegraf-ratelimit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/telegraf-ratelimit/-/telegraf-ratelimit-2.0.0.tgz", + "integrity": "sha512-OUVJRz+pVDpkOfwvFuv4x5YxcPIl6puPN3HRdicN3NDnT+jQeMzKDwVAkvloUtyH1Ruo1wH4nfB5tZbtJAJ4pQ==", + "requires": { + "debug": "^3.1.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" + } + } + } + }, + "telegraf-session-redis": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/telegraf-session-redis/-/telegraf-session-redis-5.1.0.tgz", + "integrity": "sha512-Ohl9GxorTz3kcgFL38KSYl7U4OqEr6TLNXa841pFC7wwB9Z/zhOt9nkExAFOWu5wHE8wBwApCHt5X63XGvhy3Q==", + "requires": { + "@types/redis": "^2.8.6", + "debug": "^4.1.1", + "redis": "^2.6.0-1" + } + }, + "telegram-typings": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/telegram-typings/-/telegram-typings-3.6.1.tgz", + "integrity": "sha512-njVv1EAhIZnmQVLocZEADYUyqA1WIXuVcDYlsp+mXua/XB0pxx+PKtMSPeZ/EE4wPWTw9h/hA9ASTT6yQelkiw==" + } + } +} diff --git a/telegrambot/package.json b/telegrambot/package.json new file mode 100644 index 0000000..cbdaa21 --- /dev/null +++ b/telegrambot/package.json @@ -0,0 +1,17 @@ +{ + "name": "tgbot", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "redisng": "^2.0.0", + "telegraf": "^3.33.3", + "telegraf-ratelimit": "^2.0.0", + "telegraf-session-redis": "^5.1.0" + } +}