From 3f2ba8bd2a1cfe59e6b3a6645b32c95483c64433 Mon Sep 17 00:00:00 2001 From: Maks Snegov Date: Sat, 27 Apr 2024 19:20:50 -0700 Subject: [PATCH] Separate configuration and context for consulates --- popup/popup.js | 116 +++++++-------- scripts/content.js | 343 ++++++++++++++++++++++++++------------------- 2 files changed, 260 insertions(+), 199 deletions(-) diff --git a/popup/popup.js b/popup/popup.js index 49aac1c..66733d8 100644 --- a/popup/popup.js +++ b/popup/popup.js @@ -14,34 +14,33 @@ function smoothTextChange(element, newText) { document.getElementById("version").innerText = `v${$version}`; await chrome.storage.local.get().then(items => { - document.getElementById("activate").checked = items["__activate"] || false; - document.getElementById("username").value = items["__username"] || ""; - document.getElementById("password").value = items["__password"] || ""; - document.getElementById("frequency").value = items["__frequency"] || 1; - document.getElementById("frequency_info").innerText = items["__frequency"] || 1; - document.getElementById("status").innerText = items["__status"] || "unknown"; - let currentAppt = items["__currentAppt"] || {"consulate": "somewhere", "date": "some time"}; + document.getElementById("activate").checked = items["cfg_activate"] || false; + document.getElementById("username").value = items["cfg_username"] || ""; + document.getElementById("password").value = items["cfg_password"] || ""; + document.getElementById("frequency").value = items["cfg_frequency"] || 1; + document.getElementById("frequency_info").innerText = items["cfg_frequency"] || 1; + document.getElementById("status").innerText = items["ctx_statusMsg"] || "unknown"; + let currentAppt = items["ctx_currentAppt"] || {"consulate": "somewhere", "date": "some time"}; document.getElementById("currApptConsulate").innerText = currentAppt["consulate"]; document.getElementById("currApptDate").innerText = currentAppt["date"]; - document.getElementById("deltaAppt").value = items["__deltaAppt"] || 1; - document.getElementById("deltaNow").value = items["__deltaNow"] || 1; + document.getElementById("deltaAppt").value = items["cfg_deltaAppt"] || 1; + document.getElementById("deltaNow").value = items["cfg_deltaNow"] || 1; }); // update frequency value chrome.storage.onChanged.addListener((changes, area) => { - if (changes.__frequency) - document.getElementById("frequency_info").innerText = changes.__frequency.newValue; + if (changes.cfg_frequency) + document.getElementById("frequency_info").innerText = changes.cfg_frequency.newValue; }); // update status chrome.storage.onChanged.addListener((changes, area) => { - if (changes.__status) - // document.getElementById("status").innerText = changes.__status.newValue; - smoothTextChange(document.getElementById("status"), changes.__status.newValue); + if (changes.ctx_statusMsg) + smoothTextChange(document.getElementById("status"), changes.ctx_statusMsg.newValue); }); // activate checkbox document.getElementById("activate").addEventListener("change", async e => { - await chrome.storage.local.set({ "__activate": e.target.checked }); + await chrome.storage.local.set({ "cfg_activate": e.target.checked }); }); // credentials @@ -53,8 +52,8 @@ function smoothTextChange(element, newText) { async function save_credentials() { await chrome.storage.local.set({ - "__username": usernameField.value, - "__password": passwordField.value + "cfg_username": usernameField.value, + "cfg_password": passwordField.value }); saveStatusElement.classList.add("show"); setTimeout(() => { @@ -74,7 +73,7 @@ function smoothTextChange(element, newText) { }); saveCredsButton.addEventListener("click", async () => { await save_credentials(); - await chrome.storage.local.set({ "__signinAttempts": 0 }); + await chrome.storage.local.set({ "ctx_signinAttempts": 0 }); }); showPasswordButton.addEventListener("mousedown", function() { @@ -86,18 +85,19 @@ function smoothTextChange(element, newText) { // range sliders document.getElementById("frequency").addEventListener("input", function() { - chrome.storage.local.set({ __frequency: this.value }); + chrome.storage.local.set({ cfg_frequency: this.value }); }); document.getElementById("deltaAppt").addEventListener("input", function() { - chrome.storage.local.set({ __deltaAppt: this.value }); + chrome.storage.local.set({ cfg_deltaAppt: this.value }); }); document.getElementById("deltaNow").addEventListener("change", function() { - chrome.storage.local.set({ __deltaNow: this.value }); + chrome.storage.local.set({ cfg_deltaNow: this.value }); }); // consulates - await chrome.storage.local.get(['__consulates']).then((result) => { - let consulates = result['__consulates']; + await chrome.storage.local.get(['cfg_consulates', 'ctx_consulates']).then((result) => { + let consCfg = result['cfg_consulates']; + let consCtx = result['ctx_consulates']; let html = ` @@ -112,10 +112,10 @@ function smoothTextChange(element, newText) { ` - for (let c in consulates) { - let cSelected = consulates[c].isSelected === true ? "checked" : ""; - let cAutobook = consulates[c].autobook === true ? "checked" : ""; - let cId = consulates[c].id; + for (let c in consCtx) { + let cSelected = consCfg[c]?.isSelected === true ? "checked" : ""; + let cAutoBook = consCfg[c]?.autoBook === true ? "checked" : ""; + let cId = consCtx[c].id; html += ` - - + + @@ -138,30 +138,35 @@ function smoothTextChange(element, newText) { html += `
@@ -124,12 +124,12 @@ function smoothTextChange(element, newText) { ${c}${consulates[c].currentDate || "-"}${consulates[c].bestDate || "-"}${consCtx[c].currentDate || "-"}${consCtx[c].bestDate || "-"}
- - + +
`; document.getElementById('consulatesConfig').innerHTML = html; - for (let c in consulates) { - let cId = consulates[c].id; + for (let c in consCtx) { + let cId = consCtx[c].id; document.getElementById(`isSelected-${cId}`).addEventListener("change", async e => { - consulates[c].isSelected = e.target.checked; - await chrome.storage.local.set({ "__consulates": consulates }); + consCfg[c].isSelected = e.target.checked; + await chrome.storage.local.set({ "cfg_consulates": consCfg }); }); - document.getElementById(`autobook-${cId}`).addEventListener("change", async e => { - consulates[c].autobook = e.target.checked; - await chrome.storage.local.set({ "__consulates": consulates }); + document.getElementById(`autoBook-${cId}`).addEventListener("change", async e => { + consCfg[c].autoBook = e.target.checked; + await chrome.storage.local.set({ "cfg_consulates": consCfg }); }); // update current & best dates chrome.storage.onChanged.addListener((changes, area) => { - if (changes.__consulates && changes.__consulates.newValue[c].currentDate) { - let el = document.getElementById(`currentDate-${cId}`); - if (el.innerText != changes.__consulates.newValue[c].currentDate) - smoothTextChange(document.getElementById(`currentDate-${cId}`), changes.__consulates.newValue[c].currentDate); + if (area === 'local' && changes.ctx_consulates) { + const newConsulates = changes.ctx_consulates.newValue; + + for (let c in newConsulates) { + if (newConsulates[c].currentDate) { + let el = document.getElementById(`currentDate-${c}`); + if (el && el.innerText != newConsulates[c].currentDate) + smoothTextChange(el, newConsulates[c].currentDate); + } + if (newConsulates[c].bestDate) { + let el = document.getElementById(`bestDate-${c}`); + if (el && el.innerText != newConsulates[c].bestDate) + smoothTextChange(el, newConsulates[c].bestDate); + } + } } - if (changes.__consulates && changes.__consulates.newValue[c].bestDate) { - let el = document.getElementById(`bestDate-${cId}`); - if (el.innerText != changes.__consulates.newValue[c].bestDate) - smoothTextChange(document.getElementById(`bestDate-${cId}`), changes.__consulates.newValue[c].bestDate); - } - }); - + }); } }); @@ -172,13 +177,12 @@ function smoothTextChange(element, newText) { chrome.storage.local.clear(); // keep user parameters chrome.storage.local.set({ - "__activate": items["__activate"] || false, - "__username": items["__username"] || "", - "__password": items["__password"] || "", - "__frequency": items["__frequency"] || 1, - "__deltaAppt": items["__deltaAppt"] || 1, - "__deltaNow": items["__deltaNow"] || 1, - "__autobook": items["__autobook"] || false, + "cfg_activate": items["cfg_activate"] || false, + "cfg_username": items["cfg_username"] || "", + "cfg_password": items["cfg_password"] || "", + "cfg_frequency": items["cfg_frequency"] || 1, + "cfg_deltaAppt": items["cfg_deltaAppt"] || 1, + "cfg_deltaNow": items["cfg_deltaNow"] || 1, }); }); document.getElementById("status").innerText = "unknown"; diff --git a/scripts/content.js b/scripts/content.js index 312b6f4..fc933db 100644 --- a/scripts/content.js +++ b/scripts/content.js @@ -9,21 +9,25 @@ const RANDOM_JITTER = 0.1; const SOFT_BAN_TIMEOUT = 27; const NOTIF_CHANNEL = "snegov_test" -let config = { - activate: null, - username: null, - password: null, - frequency: null, - apptId: null, - currentAppt: { - consulate: null, - date: null, - }, - signinAttempts: null, - consulates: null, - deltaAppt: null, - deltaNow: null, +let cfg = { + activate: undefined, + username: undefined, + password: undefined, + frequency: undefined, + consulates: undefined, + deltaAppt: undefined, + deltaNow: undefined, }; +let ctx = { + apptId: undefined, + currentAppt: { + consulate: undefined, + date: undefined, + }, + signinAttempts: undefined, + consulates: undefined, + statusMsg: undefined, +} let isRunning = false; let msg = ""; let isFoundAppointment = false; @@ -57,10 +61,10 @@ function getFutureDate(minutes, maxRandomSeconds = 0) { return futureDate.toISOString(); } -function hiddenPassword(config) { +function hiddenPassword(cfg) { return { - ...config, - password: config.password.replace(/./g, "*"), + ...cfg, + password: cfg.password.replace(/./g, "*"), }; } @@ -87,6 +91,34 @@ function diffObjects(obj1, obj2) { return diff; } +function hasAllKeys(obj, keys) { + return keys.every(k => obj.hasOwnProperty(k)); +} + +function ensureRequiredConsulateProperties(consCtx, consCfg, frequency = 1) { + let reqCfgKeys = ["isSelected", "autoBook"]; + let reqStateKeys = ["id", "bestDate", "currentDate", "nextCheckAt"]; + + for (let c in consCtx) { + if (!hasAllKeys(consCtx[c], reqStateKeys)) { + consCtx[c] = { + "id": consCtx[c]?.id || null, + "bestDate": consCtx[c]?.bestDate || null, + "currentDate": consCtx[c]?.currentDate || null, + "nextCheckAt": consCtx[c]?.nextCheckAt || getFutureDate(0, frequency * MINUTE), + }; + } + if (!consCfg[c] || !hasAllKeys(consCfg[c], reqCfgKeys)) { + consCfg[c] = { + "isSelected": consCfg[c]?.isSelected || false, + "autoBook": consCfg[c]?.autoBook || false, + }; + } + } + + return [consCtx, consCfg]; +} + function getPathnameParts(pathname) { let pathParts = pathname.split('/'); let locale = pathParts[1] || 'en-us'; @@ -164,9 +196,9 @@ async function goToDashboardPage() { return isDashboardPage(); } -async function enterCredentials() { - document.getElementById("user_email").value = config.username; - document.getElementById("user_password").value = config.password; +async function enterCredentials(username, password) { + document.getElementById("user_email").value = username; + document.getElementById("user_password").value = password; let policyConfirmed = document.querySelector('[for="policy_confirmed"]'); if (!policyConfirmed.getElementsByTagName('input')[0].checked) { policyConfirmed.click(); @@ -182,7 +214,6 @@ async function getAppointmentId() { if (appointments.length > 1) { console.log("Multiple appointments found, taking the first one"); } - let apptId = appointments[0].href.replace(/\D/g, ""); return apptId; } @@ -195,11 +226,6 @@ async function getConsulates() { if (!option.value) continue; // skip empty option consulatesDict[option.text] = { "id": parseInt(option.value), - "isSelected": option.selected, - "bestDate": null, - "currentDate": null, - "nextCheckAt": getFutureDate(0, getJitter(config.frequency)), - "autobook": false, }; } return consulatesDict; @@ -232,7 +258,6 @@ async function getAvailableDates(consulateId) { async function filterDates(dates, currentAppt, deltaFromAppt, deltaFromNow) { let maxDate = new Date(currentAppt); - // let maxDate = new Date("2029-09-09"); maxDate.setDate(maxDate.getDate() - deltaFromAppt); let minDate = new Date(); minDate.setDate(minDate.getDate() + deltaFromNow); @@ -258,91 +283,100 @@ async function runner() { return; } isRunning = true; - // console.log('runner start'); - let prev_config = Object.assign({}, config); + let prev_cfg = Object.assign({}, cfg); + let prev_ctx = Object.assign({}, ctx); let result = await new Promise(resolve => chrome.storage.local.get(null, resolve)); - config.activate = result['__activate'] || false; - config.username = result['__username'] || ""; - config.password = result['__password'] || ""; - config.frequency = parseFloat(result['__frequency'] || 1); - config.signinAttempts = result['__signinAttempts'] || 0; - config.apptId = result['__apptId'] || null; - config.currentAppt = result['__currentAppt'] || { consulate: null, date: null }; - config.consulates = result['__consulates'] || null; - config.deltaAppt = result['__deltaAppt'] || 1; - config.deltaNow = result['__deltaNow'] || 1; + cfg.activate = result['cfg_activate'] || false; + cfg.username = result['cfg_username'] || ""; + cfg.password = result['cfg_password'] || ""; + cfg.frequency = parseFloat(result['cfg_frequency'] || 1); + cfg.consulates = result['cfg_consulates'] || {}; + cfg.deltaAppt = result['cfg_deltaAppt'] || 1; + cfg.deltaNow = result['cfg_deltaNow'] || 1; - if (prev_config.activate === null) { - console.log('Reading config: ' + JSON.stringify(hiddenPassword(config))); + ctx.apptId = result['ctx_apptId'] || null; + ctx.signinAttempts = result['ctx_signinAttempts'] || 0; + ctx.currentAppt = result['ctx_currentAppt'] || { consulate: null, date: null }; + ctx.consulates = result['ctx_consulates'] || {}; + ctx.statusMsg = result['ctx_statusMsg'] || ""; + + [ctx.consulates, cfg.consulates] = ensureRequiredConsulateProperties( + ctx.consulates, cfg.consulates, cfg.frequency + ); + await chrome.storage.local.set({ "cfg_consulates": cfg.consulates }); + await chrome.storage.local.set({ "ctx_consulates": ctx.consulates }); + + if (prev_cfg.activate === undefined) { + console.log('Reading config: ' + JSON.stringify(hiddenPassword(cfg))); isRunning = false; return; } - for (let key in config) { - if (config.hasOwnProperty(key) - && !_.isEqual(config[key], prev_config[key])) { + for (let key in cfg) { + if (cfg.hasOwnProperty(key) + && !_.isEqual(cfg[key], prev_cfg[key])) { msg = `Config change: ${key}` if (key === 'password') { msg += ', ******** => ********'; } else if (key === 'consulates') { - msg += `, ${JSON.stringify(diffObjects(config[key], prev_config[key]))}`; + msg += `, ${JSON.stringify(diffObjects(cfg[key], prev_cfg[key]))}`; } else { - msg += `, ${prev_config[key]} => ${config[key]}`; + msg += `, ${prev_cfg[key]} => ${cfg[key]}`; } console.log(msg); // reduce wait times for consulates if frequency is increased if (key === 'frequency') { let wasChanged = false; - for (let consulate in config.consulates) { - let newNextCheckAt = getFutureDate(config.frequency, getJitter(config.frequency)); - if (config.consulates[consulate].nextCheckAt > newNextCheckAt) { - config.consulates[consulate].nextCheckAt = newNextCheckAt; + for (let c in ctx.consulates) { + let newNextCheckAt = getFutureDate(cfg.frequency, getJitter(cfg.frequency)); + if (ctx.consulates[c].nextCheckAt > newNextCheckAt) { + console.log(`Reducing wait time for ${c} from ${ctx.consulates[c].nextCheckAt} to ${newNextCheckAt}`); + ctx.consulates[c].nextCheckAt = newNextCheckAt; wasChanged = true; - console.log(`Reducing wait time for ${consulate} from ${config.consulates[consulate].nextCheckAt} to ${newNextCheckAt}`); } } // TODO maybe causes additional requests if (wasChanged) { - await chrome.storage.local.set({ "__consulates": config.consulates }); + await chrome.storage.local.set({ "ctx_consulates": ctx.consulates }); } } if (key === 'activate') { - if (config[key]) { + if (cfg[key]) { console.log('Activating extension'); } else { console.log('Deactivating extension'); - await chrome.storage.local.set({ "__status": "inactive" }); + await chrome.storage.local.set({ "ctx_statusMsg": "inactive" }); } } // clear signin attempts when credentials are changed - if (key === 'username' && config[key] - || key === 'password' && config[key]) { - config.signinAttempts = 0; - await chrome.storage.local.set({ "__signinAttempts": config.signinAttempts }); + if (key === 'username' && cfg[key] + || key === 'password' && cfg[key]) { + ctx.signinAttempts = 0; + await chrome.storage.local.set({ "ctx_signinAttempts": ctx.signinAttempts }); } } } - if (!config.activate) { + if (!cfg.activate) { isRunning = false; return; } - if (config.username === "" || config.password === "") { + if (cfg.username === "" || cfg.password === "") { console.log('Username or password is empty'); - await chrome.storage.local.set({ "__status": "missing credentials" }); + await chrome.storage.local.set({ "ctx_statusMsg": "missing credentials" }); isRunning = false; return; } - if (config.frequency <= 0) { + if (cfg.frequency <= 0) { console.log('Frequency is 0 or negative'); - await chrome.storage.local.set({ "__status": "invalid frequency" }); + await chrome.storage.local.set({ "ctx_statusMsg": "invalid frequency" }); isRunning = false; return; } @@ -353,7 +387,7 @@ async function runner() { if (currentHourUTC >= 23 || currentHourUTC < 9) { // Continue running the code } else { - await chrome.storage.local.set({ "__status": "not operational hours" }); + await chrome.storage.local.set({ "ctx_statusMsg": "not operational hours" }); isRunning = false; return; } @@ -377,7 +411,7 @@ async function runner() { if (!await goToSignInPage()) { msg = 'Failed to go to sign in page'; console.log(msg); - await chrome.storage.local.set({ "__status": msg }); + await chrome.storage.local.set({ "ctx_statusMsg": msg }); isRunning = false; return; }; @@ -386,23 +420,23 @@ async function runner() { else if (isSignInPage()) { // Prevent brute forcing - if (config.signinAttempts >= MAX_SIGNIN_ATTEMPTS) { - await chrome.storage.local.set({ "__status": "too many sign in attempts" }); + if (ctx.signinAttempts >= MAX_SIGNIN_ATTEMPTS) { + await chrome.storage.local.set({ "ctx_statusMsg": "too many sign in attempts" }); isRunning = false; return; } // Sign in - msg = 'Signing in attempt: ' + config.signinAttempts; + msg = 'Signing in attempt: ' + ctx.signinAttempts; console.log(msg) - await chrome.storage.local.set({ "__status": msg }); + await chrome.storage.local.set({ "ctx_statusMsg": msg }); let signedIn = await enterCredentials(); - config.signinAttempts += 1; - await chrome.storage.local.set({ "__signinAttempts": config.signinAttempts }); + ctx.signinAttempts += 1; + await chrome.storage.local.set({ "ctx_signinAttempts": ctx.signinAttempts }); if (!signedIn) { msg = 'Failed to sign in'; console.log(msg); - await chrome.storage.local.set({ "__status": msg }); + await chrome.storage.local.set({ "ctx_statusMsg": msg }); isRunning = false; return; }; @@ -411,31 +445,31 @@ async function runner() { else if (isDashboardPage()) { // reset signin attempts when successfully logged in - config.signinAttempts = 0; - await chrome.storage.local.set({ "__status": "fetching appointment info" }); - await chrome.storage.local.set({ "__signinAttempts": config.signinAttempts }); + ctx.signinAttempts = 0; + await chrome.storage.local.set({ "ctx_statusMsg": "fetching appointment info" }); + await chrome.storage.local.set({ "ctx_signinAttempts": ctx.signinAttempts }); // get appointmentId - if (!config.apptId) { - config.apptId = await getAppointmentId(); - if (config.apptId) { - console.log(`Appointment ID: ${config.apptId}`); - await chrome.storage.local.set({ "__apptId": config.apptId }); + if (!ctx.apptId) { + ctx.apptId = await getAppointmentId(); + if (ctx.apptId) { + console.log(`Appointment ID: ${ctx.apptId}`); + await chrome.storage.local.set({ "ctx_apptId": ctx.apptId }); } else { msg = 'No appointments found'; console.log(msg); - await chrome.storage.local.set({ "__status": msg }); + await chrome.storage.local.set({ "ctx_statusMsg": msg }); isRunning = false; return; } } // get current appointment date - let apptInfoCard = document.querySelector("p.consular-appt [href*='" + config.apptId + "']").parentNode.parentNode.parentNode + let apptInfoCard = document.querySelector("p.consular-appt [href*='" + ctx.apptId + "']").parentNode.parentNode.parentNode if (!apptInfoCard.querySelector("h4").innerText.match(/Attend Appointment/)) { msg = 'Appointment not available'; console.log(msg); - await chrome.storage.local.set({ "__status": msg }); + await chrome.storage.local.set({ "ctx_statusMsg": msg }); isRunning = false; return; } @@ -443,14 +477,19 @@ async function runner() { let apptConsulate = (apptInfo.match(/at (\w+)/) || [])[1]; let apptDate = new Date(apptInfo.match(/\d{1,2} \w+, \d{4}/)[0]); apptDate = apptDate.toISOString().slice(0, 10); - if (apptDate && apptConsulate - && (apptDate != config.currentAppt.date - || apptConsulate != config.currentAppt.consulate)) { + if (!apptDate || !apptConsulate) { + msg = 'Failed to fetch appointment date or consulate'; + console.log(msg); + await chrome.storage.local.set({ "ctx_statusMsg": msg }); + isRunning = false; + return; + } + + if (apptDate != ctx.currentAppt.date || apptConsulate != ctx.currentAppt.consulate) { console.log(`New appointment date: ${apptDate} at ${apptConsulate}, - old: ${config.currentAppt.consulate} at ${config.currentAppt.date}`); - config.currentAppt.date = apptDate; - config.currentAppt.consulate = apptConsulate; - await chrome.storage.local.set({ "__currentAppt": config.currentAppt }); + old: ${ctx.currentAppt.consulate} at ${ctx.currentAppt.date}`); + ctx.currentAppt = { consulate: apptConsulate, date: apptDate }; + await chrome.storage.local.set({ "ctx_currentAppt": ctx.currentAppt }); } // go to appointment page @@ -462,14 +501,13 @@ async function runner() { else if (isAppointmentPage()) { // if no apptDate, fetch it from dashboard page - if (!config.currentAppt.date) { + if (!ctx.currentAppt.date) { console.log('No appointment date is set, going back to dashboard'); await goToDashboardPage(); isRunning = false; return; } - // await chrome.storage.local.set({ "__status": "fetching consulates" }); let applicantForm = document.querySelector('form[action*="' + window.location.pathname + '"]'); if (applicantForm && applicantForm.method.toLowerCase() == "get") { applicantForm.submit(); @@ -478,21 +516,40 @@ async function runner() { return; } + // TODO maybe it's a rare case if (!document.getElementById("consulate_date_time")) { msg = 'No available appointments'; console.log(msg); - await chrome.storage.local.set({ "__status": msg }); + await chrome.storage.local.set({ "ctx_statusMsg": msg }); isRunning = false; return; }; - if (!config.consulates) { - config.consulates = await getConsulates(); - await chrome.storage.local.set({ "__consulates": config.consulates }); - } - if (!config.consulates) { - console.log('No consulates found'); - await chrome.storage.local.set({ "__status": "no consulates found" }); + if (!ctx.consulates || Object.keys(ctx.consulates).length == 0) { + await chrome.storage.local.set({ "ctx_statusMsg": "Fetching consulates" }); + ctx.consulates = await getConsulates(); + if (!ctx.consulates || Object.keys(ctx.consulates).length == 0) { + msg = "No consulates found"; + console.log(msg); + await chrome.storage.local.set({ "ctx_statusMsg": msg }); + sendNotification(msg); + } + + [ctx.consulates, cfg.consulates] = ensureRequiredConsulateProperties( + ctx.consulates, cfg.consulates, cfg.frequency + ); + + // if no consulates are selected, select one which we have appointment with + let selectedConsulates = Object.keys(ctx.consulates).filter(c => cfg.consulates[c].isSelected); + if (selectedConsulates.length == 0) { + for (let c in cfg.consulates) { + if (c === ctx.currentAppt.consulate) { + cfg.consulates[c].isSelected = true; + } + } + } + await chrome.storage.local.set({ "cfg_consulates": cfg.consulates }); + await chrome.storage.local.set({ "ctx_consulates": ctx.consulates }); isRunning = false; return; } @@ -501,38 +558,38 @@ async function runner() { // document.querySelector("#appointments_asc_appointment_facility_id [selected]").innerText // for each selected consulate check available dates - let selectedConsulates = Object.keys(config.consulates).filter(c => config.consulates[c].isSelected); + let selectedConsulates = Object.keys(ctx.consulates).filter(c => cfg.consulates[c].isSelected); if (selectedConsulates.length == 0) { msg = 'No selected consulates found'; console.log(msg); - await chrome.storage.local.set({ "__status": msg }); + await chrome.storage.local.set({ "ctx_statusMsg": msg }); isRunning = false; return; } let processedConsulates = 0; - for (let consulate of selectedConsulates) { + for (let c of selectedConsulates) { // only one consulate per run if (processedConsulates > 0) { break; } // skip if not time to check - if (config.consulates[consulate].nextCheckAt > new Date().toISOString()) { + if (ctx.consulates[c].nextCheckAt > new Date().toISOString()) { continue; } processedConsulates += 1; - msg = `Checking dates for ${consulate}`; + msg = `Checking dates for ${c}`; console.log(msg); - await chrome.storage.local.set({ "__status": msg }); - let availDates = await getAvailableDates(config.consulates[consulate].id); - config.consulates[consulate].nextCheckAt = getFutureDate(config.frequency, getJitter(config.frequency)); + await chrome.storage.local.set({ "ctx_statusMsg": msg }); + let availDates = await getAvailableDates(ctx.consulates[c].id); + ctx.consulates[c].nextCheckAt = getFutureDate(cfg.frequency, getJitter(cfg.frequency)); if (!availDates) { - msg = `Failed to fetch available dates in ${consulate}`; + msg = `Failed to fetch available dates in ${c}`; console.log(msg); - await chrome.storage.local.set({ "__status": msg }); + await chrome.storage.local.set({ "ctx_statusMsg": msg }); continue; } @@ -543,61 +600,60 @@ async function runner() { let currentMinuteUTC = now.getUTCMinutes(); if (availDates.length == 0) { - msg = `No available dates in ${consulate}, probably banned`; + msg = `No available dates in ${c}, probably banned`; console.log(msg); - await chrome.storage.local.set({ "__status": msg }); + await chrome.storage.local.set({ "ctx_statusMsg": msg }); // Only set SOFT_BAN_TIMEOUT if it's not the first 5 minutes of 23pm UTC if (!(currentHourUTC === 23 && currentMinuteUTC < 5)) { - config.consulates[consulate].nextCheckAt = getFutureDate(SOFT_BAN_TIMEOUT, getJitter(config.frequency)); + ctx.consulates[c].nextCheckAt = getFutureDate(SOFT_BAN_TIMEOUT, getJitter(cfg.frequency)); } continue; } - msg = `Available dates for ${consulate}: ${availDates.slice(0, 5)}`; + msg = `Available dates for ${c}: ${availDates.slice(0, 5)}`; if (availDates.length > 5) { msg += ` and ${availDates.length - 10} more`; } console.log(msg); - config.consulates[consulate].currentDate = availDates[0]; - if (!config.consulates[consulate].bestDate - || availDates[0] < config.consulates[consulate].bestDate) { - console.log(`New record for ${consulate}: ${availDates[0]}`); - config.consulates[consulate].bestDate = availDates[0]; + ctx.consulates[c].currentDate = availDates[0]; + if (!ctx.consulates[c].bestDate || availDates[0] < ctx.consulates[c].bestDate) { + console.log(`New record for ${c}: ${availDates[0]}`); + ctx.consulates[c].bestDate = availDates[0]; } // filter dates with our requests - let filteredDates = await filterDates(availDates, config.currentAppt.date, config.deltaAppt, config.deltaNow); + let filteredDates = await filterDates(availDates, ctx.currentAppt.date, cfg.deltaAppt, cfg.deltaNow); if (!filteredDates.length) { - console.log(`No better dates in ${consulate}, currently available ${availDates[0]}`); - await chrome.storage.local.set({ "__status": `No better dates in ${consulate}`}); + console.log(`No better dates in ${c}, currently available ${availDates[0]}`); + await chrome.storage.local.set({ "ctx_statusMsg": `no better dates in ${c}`}); continue; } - console.log(`Dates worth rescheduling in ${consulate}: ${filteredDates}`); + console.log(`Dates worth rescheduling in ${c}: ${filteredDates}`); let chosenDate = filteredDates[0]; - await chrome.storage.local.set({ "__status": `Found in ${consulate} better date ${chosenDate}`}); + await chrome.storage.local.set({ "ctx_statusMsg": `Found in ${c} better date ${chosenDate}`}); // fill date in reschedule form await delay(PAGE_WAIT_TIME); document.getElementById("appointments_consulate_appointment_date").value = chosenDate; document.getElementById("appointments_consulate_appointment_time").innerHTML = "" - let availTimes = await getAvailableTimes(config.consulates[consulate].id, chosenDate); + let availTimes = await getAvailableTimes(ctx.consulates[c].id, chosenDate); if (!availTimes) { - msg = `Failed to fetch available timeslots in ${consulate} at ${chosenDate}`; + msg = `Failed to fetch available timeslots in ${c} at ${chosenDate}`; console.log(msg); - await chrome.storage.local.set({ "__status": msg}); + await chrome.storage.local.set({ "ctx_statusMsg": msg}); continue; } if (availTimes.length == 0) { - msg = `No timeslots in ${consulate} at ${chosenDate}`; + msg = `No timeslots in ${c} at ${chosenDate}`; console.log(msg); - await chrome.storage.local.set({ "__status": msg}); + await chrome.storage.local.set({ "ctx_statusMsg": msg}); continue; } - console.log(`Available timeslots in ${consulate} at ${chosenDate}: ${availTimes}`); + console.log(`Available timeslots in ${c} at ${chosenDate}: ${availTimes}`); let chosenTime = availTimes[0]; // fill timeslot in reschedule form @@ -611,28 +667,29 @@ async function runner() { document.getElementById("appointments_submit").removeAttribute("disabled"); document.getElementById("appointments_submit").click(); - msg = `Found better appointment in ${consulate} at ${chosenDate} ${chosenTime}`; + msg = `Found better appointment in ${c} at ${chosenDate} ${chosenTime}`; console.log(msg); await sendNotification(msg); - if (!config.consulates[consulate].autobook) { + if (!cfg.consulates[c].autoBook) { isFoundAppointment = true; } else { await delay(PAGE_WAIT_TIME); - msg = `Auto booking in ${consulate} at ${chosenDate} ${chosenTime}`; + msg = `Auto booking in ${c} at ${chosenDate} ${chosenTime}`; console.log(msg); await sendNotification(msg); document.querySelector(".reveal-overlay:last-child [data-reveal] .button.alert").click(); } } // end consulates loop - for (let consulate in config.consulates) { - if (!prev_config?.consulates?.[consulate]) continue; - if (config.consulates[consulate].nextCheckAt != prev_config.consulates[consulate].nextCheckAt) { - console.log(`Next check for ${consulate} at ${config.consulates[consulate].nextCheckAt}`); + for (let c in ctx.consulates) { + if (!prev_ctx?.consulates?.[c]) continue; + if (ctx.consulates[c].nextCheckAt != prev_ctx.consulates[c].nextCheckAt) { + console.log(`Next check for ${c} at ${ctx.consulates[c].nextCheckAt}`); } } - await chrome.storage.local.set({ "__consulates": config.consulates }); + await chrome.storage.local.set({ "cfg_consulates": cfg.consulates }); + await chrome.storage.local.set({ "ctx_consulates": ctx.consulates }); isRunning = false; return; } @@ -640,13 +697,13 @@ async function runner() { else if (isConfirmationPage) { // go back to dashboard after successful reschedule await delay(PAGE_WAIT_TIME); - config.currentAppt = { consulate: null, date: null}; - await chrome.storage.local.set({"__currentAppt": config.currentAppt}); + ctx.currentAppt = { consulate: null, date: null}; + await chrome.storage.local.set({"ctx_currentAppt": ctx.currentAppt}); console.log('Rescheduled successfully'); - // switch off autobook for all consulates after successful reschedule - for (let consulate in config.consulates) { - config.consulates[consulate].autobook = false; + // switch off autoBook for all consulates after successful reschedule + for (let c in cfg.consulates) { + cfg.consulates[c].autoBook = false; } await goToDashboardPage();