You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
284 lines
14 KiB
JavaScript
284 lines
14 KiB
JavaScript
5 years ago
|
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()
|