From 960e3d1f3c036837f21abf38c8378226c76ee026 Mon Sep 17 00:00:00 2001 From: cheetah Date: Tue, 5 Nov 2019 18:30:29 +0100 Subject: [PATCH] First Commit --- .gitignore | 193 +++++++++ accountmanager/index.js | 284 +++++++++++++ accountmanager/package-lock.json | 142 +++++++ accountmanager/package.json | 16 + database.js | 11 + docker-compose.yaml | 14 + models/Account.js | 22 + models/GoogleCacheResult.js | 10 + models/Session.js | 33 ++ models/Store.js | 25 ++ models/StoreFinderCorrelation.js | 13 + models/User.js | 14 + models/index.js | 8 + package-lock.json | 623 +++++++++++++++++++++++++++ package.json | 21 + renderservice/app-ios.jpg | Bin 0 -> 42835 bytes renderservice/app-ios.png | Bin 0 -> 73026 bytes renderservice/app.png | Bin 0 -> 45947 bytes renderservice/index.js | 119 ++++++ renderservice/overview.png | Bin 0 -> 4531 bytes renderservice/package-lock.json | 700 +++++++++++++++++++++++++++++++ renderservice/package.json | 15 + sessionmanager/index.js | 257 ++++++++++++ sessionmanager/package-lock.json | 5 + sessionmanager/package.json | 12 + storefinder/index.js | 213 ++++++++++ storefinder/package-lock.json | 5 + storefinder/package.json | 12 + telegrambot/index.js | 411 ++++++++++++++++++ telegrambot/package-lock.json | 142 +++++++ telegrambot/package.json | 17 + 31 files changed, 3337 insertions(+) create mode 100644 .gitignore create mode 100644 accountmanager/index.js create mode 100644 accountmanager/package-lock.json create mode 100644 accountmanager/package.json create mode 100644 database.js create mode 100644 docker-compose.yaml create mode 100644 models/Account.js create mode 100644 models/GoogleCacheResult.js create mode 100644 models/Session.js create mode 100644 models/Store.js create mode 100644 models/StoreFinderCorrelation.js create mode 100644 models/User.js create mode 100644 models/index.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 renderservice/app-ios.jpg create mode 100644 renderservice/app-ios.png create mode 100644 renderservice/app.png create mode 100644 renderservice/index.js create mode 100644 renderservice/overview.png create mode 100644 renderservice/package-lock.json create mode 100644 renderservice/package.json create mode 100644 sessionmanager/index.js create mode 100644 sessionmanager/package-lock.json create mode 100644 sessionmanager/package.json create mode 100644 storefinder/index.js create mode 100644 storefinder/package-lock.json create mode 100644 storefinder/package.json create mode 100644 telegrambot/index.js create mode 100644 telegrambot/package-lock.json create mode 100644 telegrambot/package.json 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 0000000000000000000000000000000000000000..a47fe6d7df9fb2d60f75c1158ce8c259a0933fe1 GIT binary patch literal 42835 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 VS6-dF(gQh3t?|bQB+74J{|^YP96100 literal 0 HcmV?d00001 diff --git a/renderservice/app-ios.png b/renderservice/app-ios.png new file mode 100644 index 0000000000000000000000000000000000000000..a52faff8757fd088ee244163508f0580642262ad GIT binary patch literal 73026 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 z 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 literal 0 HcmV?d00001 diff --git a/renderservice/app.png b/renderservice/app.png new file mode 100644 index 0000000000000000000000000000000000000000..3e2465d8a8aa5a116a963afde3cc99833d847a18 GIT binary patch literal 45947 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 E1KpXtF#rGn literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b84b89d608ffd44ed02353d3b22b37db90108e28 GIT binary patch literal 4531 zcmeAS@N?(olHy`uVBq!ia0y~yV7+nk|nMYCC>S|xv6<249-QVi6yBi3gww484B*6z5(Hl zeBulYg1VkAjv*Dd-W+6P1ZruR@NfM)4v_7mU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0q zLtr!nMnhmU1V%$(ScgEv?Pg{nkPjXI$8T2-0F4Wb0#FE?W7@>Rz}OkzTn^%Sy85}S Ib4q9e04KaH{Qv*} literal 0 HcmV?d00001 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" + } +}