First Commit

master
cheetah 5 years ago
commit 960e3d1f3c

193
.gitignore vendored

@ -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/

@ -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<len;i++)
temp+=keylistalpha.charAt(Math.floor(Math.random()*keylistalpha.length));
for (i=0;i<lenspec;i++)
temp+=keylistspec.charAt(Math.floor(Math.random()*keylistspec.length));
for (i=0;i<len;i++)
temp+=keylistint.charAt(Math.floor(Math.random()*keylistint.length));
temp=temp.split('').sort(function(){return 0.5-Math.random()}).join('');
return temp;
}
const makeAccount = async() => {
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()

@ -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=="
}
}
}

@ -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"
}
}

@ -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 }

@ -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"

@ -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)

@ -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)

@ -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)

@ -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)

@ -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)

@ -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)

@ -0,0 +1,8 @@
module.exports = {
Account: require('./Account'),
Store: require('./Store'),
Session: require('./Session'),
StoreFinderCorrelation: require('./StoreFinderCorrelation'),
GoogleCacheResult: require('./GoogleCacheResult'),
User: require('./User'),
}

623
package-lock.json generated

@ -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="
}
}
}

@ -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"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

@ -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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

@ -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=="
}
}
}

@ -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"
}
}

@ -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)

@ -0,0 +1,5 @@
{
"name": "sessionmanager",
"version": "1.0.0",
"lockfileVersion": 1
}

@ -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": {}
}

@ -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)

@ -0,0 +1,5 @@
{
"name": "storefinder",
"version": "1.0.0",
"lockfileVersion": 1
}

@ -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": {}
}

@ -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()

@ -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=="
}
}
}

@ -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"
}
}
Loading…
Cancel
Save