diff --git a/src/nsh.js b/src/nsh.js
index ba3633d..9b27e3d 100644
--- a/src/nsh.js
+++ b/src/nsh.js
@@ -14,6 +14,7 @@ const { fetchOTPFromNusuk } = require("./lib/imap");
const { nusukNationalities: nationalities } = require("./data/nationalities");
const childProcess = require("child_process");
const sharp = require("sharp");
+const abortController = new AbortController();
let page;
let data;
@@ -369,6 +370,8 @@ async function pageContentHandler(currentConfig) {
case "verify-register-email":
emailCodeCounter = 0;
clearTimeout(timerHandler);
+ // stop captcha attempts
+ abortController.abort();
await page.waitForSelector(
"#otp-inputs > input.form-control.signup-otp.me-1",
@@ -413,6 +416,11 @@ async function pageContentHandler(currentConfig) {
"body > main > div.signup > div > div.container-lg.container-fluid.position-relative.h-100 > div > div > div.row > div > form > button";
await util.clickWhenReady(createAccountSelector, page);
+ // save the email only at this stage
+ kea.updatePassenger(data.system.accountId, passenger.passportNumber, {
+ email: passenger.email,
+ phone: passenger.phone,
+ });
clicked[currentConfig.name] = {};
clicked[currentConfig.name][passenger.passportNumber] = true;
}
@@ -973,15 +981,9 @@ async function signup_step1(selectedTraveler) {
const passenger = data.travellers[selectedTraveler];
emailAddress = suggestEmail(selectedTraveler);
telephoneNumber = suggestPhoneNumber(selectedTraveler);
- console.log(
- "📢[nsh.js:489]: emailAddress and Telephone: ",
- emailAddress,
- telephoneNumber
- );
- await kea.updatePassenger(data.system.accountId, passenger.passportNumber, {
- email: emailAddress,
- phone: telephoneNumber,
- });
+ // store temporarily in the passenger object
+ passenger.email = emailAddress;
+ passenger.phone = telephoneNumber;
const nationality = getNationalityUUID(nationalities, data.system.country.name);
await util.commit(
@@ -1008,7 +1010,8 @@ async function signup_step1(selectedTraveler) {
const captchaCode = await util.SolveIamNotARobot(
"#g-recaptcha-response",
URLS.SIGN_UP,
- "6LcNy-0jAAAAAJDOXjYW4z7yV07DWyivFD1mmjek"
+ "6LcNy-0jAAAAAJDOXjYW4z7yV07DWyivFD1mmjek",
+ abortController.signal
);
if (captchaCode) {
@@ -1055,7 +1058,8 @@ async function loginPassenger(selectedTraveler) {
const loginCaptchaValue = await util.SolveIamNotARobot(
"#g-recaptcha-response",
URLS.LOGIN,
- "6LcNy-0jAAAAAJDOXjYW4z7yV07DWyivFD1mmjek"
+ "6LcNy-0jAAAAAJDOXjYW4z7yV07DWyivFD1mmjek",
+ abortController.signal
);
if (!loginCaptchaValue) {
util.infoMessage(page, `Manual captcha required`);
@@ -1238,6 +1242,12 @@ async function uploadFakePassport() {
await util.commitFile("#passportPhoto", blankPassportPath);
}
+function formatTime(seconds) {
+ const minutes = Math.floor(seconds / 60).toString().padStart(2, "0");
+ const secs = (seconds % 60).toString().padStart(2, "0");
+ return `00:${minutes}:${secs}`;
+}
+
async function pasteOTPCode(err, code) {
if (err === "no-code") {
setTimeout(async () => {
@@ -1256,8 +1266,8 @@ async function pasteOTPCode(err, code) {
await page.$eval(
"#hajonsoft-commander-alert",
(el, i) =>
- (el.innerText = `Checking email 00:00:${i*30}/00:02:30 فحص البريد `),
- emailCodeCounter
+ (el.innerText = `Checking email ${i}/00:02:30 فحص البريد`),
+ formatTime(emailCodeCounter * 3)
);
} catch { }
getOTPCode();
diff --git a/src/util.js b/src/util.js
index d16e522..39c3c7f 100644
--- a/src/util.js
+++ b/src/util.js
@@ -40,7 +40,6 @@ const imgurClient = new ImgurClient({
let page;
let browser;
-
function getChromePath() {
switch (os.platform()) {
case "darwin":
@@ -227,7 +226,7 @@ async function initPage(config, onContentLoaded, data) {
}
const launchOptions = {
- headless: process.argv.includes("--debug") ? false : isCloudRun || isHeadless,
+ headless: process.argv.includes("--debug") ? false : isCloudRun || isHeadless,
ignoreHTTPSErrors: true,
defaultViewport,
args,
@@ -246,7 +245,7 @@ async function initPage(config, onContentLoaded, data) {
await pauseMessage(page, 5);
try {
await dialog.accept();
- } catch {}
+ } catch { }
});
if (process.argv.length > 2) {
@@ -279,7 +278,7 @@ async function initPage(config, onContentLoaded, data) {
} else {
fs.readdir(vaccineFolder, (err, files) => {
for (const file of files) {
- fs.unlink(path.join(vaccineFolder, file), (err) => {});
+ fs.unlink(path.join(vaccineFolder, file), (err) => { });
}
});
}
@@ -302,7 +301,7 @@ async function createControlsFile(
url,
container,
xPath,
- fieldFunction = async () => {}
+ fieldFunction = async () => { }
) {
const logFolder = getPath("log");
if (!fs.existsSync(logFolder)) {
@@ -392,8 +391,7 @@ function findConfig(url, config) {
if (urlConfig) {
infoMessage(
page,
- `✈️ Workflow: ${urlConfig.name} ${
- urlConfig.url || urlConfig.regex
+ `✈️ Workflow: ${urlConfig.name} ${urlConfig.url || urlConfig.regex
} ${timeElapsed()} seconds`,
2
);
@@ -569,7 +567,7 @@ function getMofaImportString(passenger) {
const importJSON = JSON.parse(importContent);
return " - MOFA: " + importJSON?.status;
}
- } catch {}
+ } catch { }
return "";
}
@@ -578,19 +576,16 @@ function getOptionNode(passenger, cursor) {
${cursor + 1}-
- ${
- passenger.nationality?.isArabic
- ? passenger?.nameArabic?.given + " " + passenger.nameArabic.last
- : passenger.name.full
- } - ${passenger.passportNumber} - ${passenger?.nationality?.name} - ${
- passenger?.gender || "gender"
- } - ${passenger?.dob?.age || "age"} years old${getMofaImportString(
- passenger
- )}${
- passenger.email?.includes(".companion") || passenger.isCompanion
+ ${passenger.nationality?.isArabic
+ ? passenger?.nameArabic?.given + " " + passenger.nameArabic.last
+ : passenger.name.full
+ } - ${passenger.passportNumber} - ${passenger?.nationality?.name} - ${passenger?.gender || "gender"
+ } - ${passenger?.dob?.age || "age"} years old${getMofaImportString(
+ passenger
+ )}${passenger.email?.includes(".companion") || passenger.isCompanion
? "(companion)"
: ""
- }
+ }
`;
@@ -619,8 +614,7 @@ async function controller(page, structure, travellers) {
// .filter((t) => !t.email.includes(".companion"))
.map(
(traveller, cursor) =>
- ``
@@ -629,9 +623,8 @@ async function controller(page, structure, travellers) {
try {
await page.waitForSelector(structure.controller.selector);
- const controllerHandleMethod = `handleEagle${
- structure.controller.name || "Send"
- }Click`;
+ const controllerHandleMethod = `handleEagle${structure.controller.name || "Send"
+ }Click`;
const htmlFileName = path.join(__dirname, "assets", "controller.html");
let html = fs.readFileSync(htmlFileName, "utf8");
@@ -654,9 +647,9 @@ async function controller(page, structure, travellers) {
.replace(/{pax}/, pax.length)
.replace(/{current}/, (parseInt(lastTraveler) + 1).toString())
.replace(/{mokhaa}/, controller.mokhaa ? "block" : "none")}`.replace(
- /{sendall}/,
- "Continuous مستمر"
- );
+ /{sendall}/,
+ "Continuous مستمر"
+ );
},
[
structure,
@@ -688,19 +681,19 @@ async function controller(page, structure, travellers) {
await page.exposeFunction("getVisaCount", getVisaCount);
await page.exposeFunction(
"handleWTUClick",
- structure.controller.wtuAction || (() => {})
+ structure.controller.wtuAction || (() => { })
);
await page.exposeFunction(
"handleGMAClick",
- structure.controller.gmaAction || (() => {})
+ structure.controller.gmaAction || (() => { })
);
await page.exposeFunction(
"handleBAUClick",
- structure.controller.bauAction || (() => {})
+ structure.controller.bauAction || (() => { })
);
await page.exposeFunction(
"handleTWFClick",
- structure.controller.twfAction || (() => {})
+ structure.controller.twfAction || (() => { })
);
await page.exposeFunction(
"handleLoadImportedOnlyClick",
@@ -708,7 +701,7 @@ async function controller(page, structure, travellers) {
);
await page.exposeFunction(
"handleNSKClick",
- structure.controller.nskAction || (() => {})
+ structure.controller.nskAction || (() => { })
);
await page.exposeFunction("closeBrowser", closeBrowser);
}
@@ -723,7 +716,7 @@ function registerLoop() {
}
function unregisterLoop() {
if (fs.existsSync(getPath("loop.txt"))) {
- fs.unlink(getPath("loop.txt"), (err) => {});
+ fs.unlink(getPath("loop.txt"), (err) => { });
}
}
function getVisaCount() {
@@ -750,9 +743,8 @@ async function commander(page, structure, travellers) {
try {
await page.waitForSelector(structure.controller.selector);
- const controllerHandleMethod = `handleEagle${
- structure.controller.name || "Budgie"
- }Click`;
+ const controllerHandleMethod = `handleEagle${structure.controller.name || "Budgie"
+ }Click`;
const isLoop = fs.existsSync(getPath("loop.txt"));
const htmlFileName = path.join(__dirname, "assets", "commander.html");
@@ -776,8 +768,8 @@ async function commander(page, structure, travellers) {
.replace(
/{title}/g,
structureParam.controller.title +
- " " +
- structureParam.controller.arabicTitle
+ " " +
+ structureParam.controller.arabicTitle
);
container.outerHTML = controller.keepOriginalElement
? `${container.outerHTML}${htmlContent}
`
@@ -842,7 +834,7 @@ async function handleLoadImportedOnlyClick() {
passportNumber: jsonData.passportNumber,
});
}
- } catch {}
+ } catch { }
}
const data = {
@@ -1029,8 +1021,7 @@ async function downloadAndResizeImage(
let imagePath = path.join(folder, `${passenger.passportNumber}.jpg`);
const resizedPath = path.join(
folder,
- `${passenger.passportNumber}_${width ?? ""}x${height ?? ""}.${
- convertToPNG ? "png" : "jpg"
+ `${passenger.passportNumber}_${width ?? ""}x${height ?? ""}.${convertToPNG ? "png" : "jpg"
}`
);
@@ -1476,7 +1467,7 @@ async function commitCaptchaTokenWithSelector(
}
}
-async function SolveIamNotARobot(responseSelector, url, siteKey) {
+async function SolveIamNotARobot(responseSelector, url, siteKey, signal) {
const data = await axios.get(
`http://2captcha.com/in.php?key=${global.captchaKey}&method=userrecaptcha&googlekey=${siteKey}&pageurl=${url}`
);
@@ -1487,6 +1478,10 @@ async function SolveIamNotARobot(responseSelector, url, siteKey) {
try {
for (let i = 0; i < 10; i++) {
+ if (signal?.aborted) {
+ console.log("Captcha solving aborted.");
+ throw new Error("Captcha solving was cancelled.");
+ }
const res = await axios.get(
`http://2captcha.com/res.php?key=${global.captchaKey}&action=get&id=${id}`
);
@@ -1506,7 +1501,7 @@ async function SolveIamNotARobot(responseSelector, url, siteKey) {
// wait 5 seconds
await new Promise((resolve) => setTimeout(resolve, 5000));
}
- } catch {}
+ } catch { }
}
const premiumSupportAlert = async (page, selector, data) => {
await page.waitForSelector(selector);
@@ -1821,8 +1816,8 @@ function generateMRZ(passenger) {
// letters that are a part of the number fields and their check digits.
const compositeCheckDigit = checkDigit(
codeLine2.substring(0, 10) +
- codeLine2.substring(13, 20) +
- codeLine2.substring(21, 43)
+ codeLine2.substring(13, 20) +
+ codeLine2.substring(21, 43)
);
codeLine2 += compositeCheckDigit.replace(/[-]/g, "<");
}