Files
puppeteerjs/src/workers/CommandorPage.js
T
2022-09-17 15:39:53 +02:00

389 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const {_android: android, devices} = require("playwright");
const {SolveCaptcha, TWO_CAPTCHA_CONNECTION_FAILED} = require("./SolveCaptcha");
const ReserveResultPojo = require("../models/ReserveResultPojo");
const PublishType = require("../models/PublishType");
const beep = require('beepbeep')
// const RDV_URL = "http://192.168.1.16:8000/test_appointment.html"
const RDV_URL = "https://rendezvousparis.hermes.com/client/register";
BLANK_URL = "about:blank"
const ERROR_CAPTCHA_UNSOLVABLE = "ERROR_CAPTCHA_UNSOLVABLE";
const COUNTRY_ID = "#phone_country"
const PHONE_NUMBER = "#phone_number"
const EMAIL_ID = "#email"
const PREFER_STORE = "#prefer"
const LAST_NAME = "#surname"
const FIRST_NAME = "#name"
const CGU_ID = "#cgu"
const PROCESSING_ID = "#processing"
const PASSPORT_ID = "#passport_id"
const TIME_OUT = 60 * 1000 * 4//4 mins
const CONFIRMED_MESSAGE = "Your request for a Leather Goods appointment has been registered"
const CONFIRMED_MESSAGE_FR = "Votre demande de rendez-vous Maroquinerie a bien été enregistrée et nous vous en remercions."
const MESSAGE_URL_VALIDATION_FR = "Nous avons envoyé un lien par e-mail."
const MESSAGE_URL_VALIDATION_EN = "Please click on the link we sent by email"
const DOUBLE_REQUEST_ERROR_MESSAGE = "A request with the same data has already been validated today."
const DOUBLE_REQUEST_ERROR_MESSAGE_FR = "Une demande avec les données saisies a déjà été validée aujourdhui."
const TOO_MANY_REQUEST_ERROR_MESSAGE = "Due to a large number of requests"
const TOO_MANY_REQUEST_ERROR_MESSAGE_FR = "Suite à un trop grand nombre de demandes"
const CAPTCHA_ERROR_MESSAGE = "Error verifying captcha, please try again"
const CAPTCHA_ERROR_MESSAGE_FR = "La vérification du captcha a échoué"
REGEX_RDV_URL = "https:\/\/rendezvousparis\.hermes\.com\/client\/register\/[A-Z0-9]+"
function delay(delayInms) {
return new Promise(resolve => {
setTimeout(() => {
resolve(2);
}, delayInms);
});
}
function getRandom() {
return Math.floor(Math.random() * 3);
}
function getRandomWaitTime() {
return getRandom() * 1000
}
class CommandorPage {
constructor(contact, device, mongoManager) {
this.contact = contact;
this.device = device;
this.mongoManager = mongoManager;
this.isFillingFields = false;
this.isTerminated = false;
}
async loadPage() {
// Connect to the device.
console.log("loadPage() called");
await this.device.shell('am force-stop com.android.chrome');
const context = await this.device.launchBrowser();
// await context.clearCookies()
// Use BrowserContext as usual.
this.page = await context.newPage();
this.page.on("load", (loadedPage) => {
this.onPageLoad(loadedPage)
})
this.page.on("response", (response) => {
this.onResponse(response)
})
try {
await this.page.goto(RDV_URL, {timeout: 90 * 1000});
} catch (e) {
console.log(e)
this.isTerminated = true
}
let cancel
const intervalTask = setInterval(() => {
if (this.isTerminated) {
console.log("request terminated, will close device")
context.close()
// this.page.close()
this.device.close()
clearInterval(intervalTask)
cancel()
return context
}
}, 10 * 1000)//interval of 10 seconds
await new Promise(function (fulfill, reject) {
cancel = function () {
fulfill(Promise.resolve())
}
setTimeout(fulfill, TIME_OUT, 5)
}).then(console.log)
}
async chooseCountry(page) {
if (!page.isClosed()) {
try {
await page.locator(COUNTRY_ID).focus();
await delay(getRandomWaitTime())
await page.click(COUNTRY_ID);
await delay(getRandomWaitTime())
await page.selectOption(COUNTRY_ID, 'FR');
await delay(getRandomWaitTime())
} catch (e) {
console.log(e);
this.isTerminated = true;
}
// await page.click(COUNTRY_ID);
}
}
async fillEmail(page) {
try {
if (!page.isClosed()) {
await page.locator(EMAIL_ID).focus();
await delay(getRandomWaitTime())
await page.locator(EMAIL_ID).fill(this.contact.mail);
}
} catch (e) {
console.log(e)
}
}
async inputPhoneNumber(page) {
try {
if (!page.isClosed()) {
await page.locator(PHONE_NUMBER).focus()
await page.locator(PHONE_NUMBER).fill("0" + this.contact.phoneNumber)
}
} catch (e) {
console.log(e);
this.isTerminated = true;
}
}
async inputName(page) {
try {
if (!page.isClosed()) {
await page.locator(LAST_NAME).focus()
await delay(getRandomWaitTime())
await page.locator(LAST_NAME).fill(this.contact.lastName)
await page.locator(FIRST_NAME).focus()
await delay(getRandomWaitTime())
await page.locator(FIRST_NAME).fill(this.contact.firstName)
}
} catch (e) {
console.log(e);
this.isTerminated = true
}
}
async inputPassportId(page) {
try {
if (!page.isClosed()) {
await page.locator(PASSPORT_ID).focus()
await delay(getRandomWaitTime())
await page.locator(PASSPORT_ID).fill(this.contact.passportNumber.toString())
}
} catch (e) {
console.log(e);
this.isTerminated = true;
}
}
async checkCGU(page) {
try {
if (!page.isClosed()) {
await page.locator(CGU_ID).focus()
await page.locator(CGU_ID).click()
await delay(getRandomWaitTime())
await page.locator(PROCESSING_ID).focus()
await page.locator(PROCESSING_ID).click()
}
} catch (e) {
console.log(e);
this.isTerminated = true;
}
}
async chooseStore(page) {
// try {
// if (!page.isClosed()) {
// await page.locator(PREFER_STORE).focus()
// await delay(1000)
// await page.click(PREFER_STORE);
// await page.selectOption(PREFER_STORE, "faubourg");
// }
// } catch (e) {
// console.log(e);
// this.isTerminated = true;
// }
// await page.click(PREFER_STORE);
}
async fillFields(page) {
console.log("fillFields called")
if (!this.isFillingFields) {
this.isFillingFields = true;
await this.chooseStore(page);
await this.inputName(page);
await this.chooseCountry(page);
await this.inputPhoneNumber(page)
await this.fillEmail(page)
await this.inputPhoneNumber(page)
await this.inputPassportId(page)
await this.checkCGU(page)
await this.resolveCaptcha(page)
await delay(10 * 1000)
}
}
async clickValid(page) {
await delay(getRandomWaitTime())
try {
if (!this.page.isClosed()) {
this.page.evaluate(() => {
let element = document.getElementsByClassName("btn")[0];
if (typeof element !== 'undefined')
document.getElementsByClassName("btn")[0].focus();
})
await delay(getRandomWaitTime())
if (!this.page.isClosed()) {
try {
this.page.evaluate(() => {
document.getElementsByClassName("btn")[0].click();
})
} catch (e) {
console.log(e)
}
}
}
} catch (e) {
console.log(e)
}
}
async resolveCaptcha(page) {
if (RDV_URL.includes("192")) {
await this.push_message_to_queue(PublishType.SUCCESS)
return
}
this.captchaSolver = new SolveCaptcha(page);
await this.captchaSolver.start((solution) => {
console.log("solution is: " + solution);
if (solution !== ERROR_CAPTCHA_UNSOLVABLE && solution !== TWO_CAPTCHA_CONNECTION_FAILED) {
try {
if (!page.isClosed()) {
page.evaluate((solution) => {
let element = document.getElementById("g-recaptcha-response");
if (element != null)
document.getElementById("g-recaptcha-response").innerHTML = solution;
}, solution)
this.clickValid();
}
} catch (e) {
console.log(e)
this.isTerminated = true;
}
} else {
this.isTerminated = true;
}
})
}
async onPageLoad(currentPage) {
try {
let content = await currentPage.content();
let captcha_url = "geo.captcha-delivery.com/captcha";
if (content.toString().includes(captcha_url)) {
await this.checkAudioBtn();
beep(20)
console.log("发现datadome");
} else {
if (currentPage.url() === RDV_URL) {
await this.fillFields(this.page);
if (this.isFillingFields)
await this.getErrors()
} else {
if (content.includes(MESSAGE_URL_VALIDATION_FR) || content.includes(MESSAGE_URL_VALIDATION_EN)) {
console.log("successful");
await this.push_message_to_queue(PublishType.SUCCESS);
} else {
// try to get errors
await this.getErrors()
}
}
}
} catch (e) {
console.log(e)
}
}
async checkAudioBtn() {
// let iframe = await this.page.locator("iframe").contentFrame;
// let audioBtn = iframe.locator("#captcha__audio__button");
// if (audioBtn) {
// console.log("audioBtn found")
// } else {
// console.log("audioBtn not found")
// }
}
async onResponse(response) {
// let rex = new RegExp(REGEX_RDV_URL)
// console.log("onResponse with url:" + response.url())
// if (rex.test(response.url())) {
// console.log("rdv url found:" + response.url())
// await this.push_message_to_db(PublishType.SUCCESS, response.url())
// }
}
async push_message_to_queue(publishType) {
let url = this.page.url();
let splitedUrl = url.split("/");
let id = splitedUrl[splitedUrl.length - 1];
if (url === "https://rendezvousparis.hermes.com/client/welcome") {
return
}
// save to mongoDb
let reserve = ReserveResultPojo.create_from_contact(this.contact, id, this.page.url(), 1, publishType);
reserve.source_from = this.device.model();
await this.mongoManager.saveReserveToDb(reserve.to_mongo_dict())
this.isTerminated = true
}
async push_message_to_db(publishType, url) {
let splitedUrl = url.split("/");
let id = splitedUrl[splitedUrl.length - 1];
if (url === "https://rendezvousparis.hermes.com/client/welcome") {
return
}
// save to mongoDb
let reserve = ReserveResultPojo.create_from_contact(this.contact, id, this.page.url(), 1, publishType);
reserve.source_from = this.device.model();
await this.mongoManager.saveReserveToDb(reserve.to_mongo_dict())
await delay(2 * 1000)
this.isTerminated = true
}
async getErrors() {
if (this.page.url() === BLANK_URL) {
this.isTerminated = true;
} else {
try {
let errorItem = this.page.locator("div.alert");
if (errorItem) {
let errorContent = await errorItem.innerHTML();
console.log("error:" + errorContent);
this.handleError(errorContent);
}
} catch (e) {
console.log(e);
}
}
}
handleError(errorContent) {
if (errorContent.includes(DOUBLE_REQUEST_ERROR_MESSAGE) || errorContent.includes(DOUBLE_REQUEST_ERROR_MESSAGE_FR)) {
this.isTerminated = true;
} else if (errorContent.includes(TOO_MANY_REQUEST_ERROR_MESSAGE) || errorContent.includes(TOO_MANY_REQUEST_ERROR_MESSAGE_FR)) {
// todo, add contact to black list
this.isTerminated = true;
} else if (errorContent.includes(CAPTCHA_ERROR_MESSAGE) || errorContent.includes(CAPTCHA_ERROR_MESSAGE_FR)) {
this.isTerminated = true;
}
}
}
module.exports = CommandorPage