Compare commits

...

29 Commits

Author SHA1 Message Date
3478fd5a6e Bump version 2024-08-01 15:53:22 -07:00
8697c717b8 Send notification on appointment update 2024-08-01 14:06:01 -07:00
c31ab1eb64 Use last timeslot in the selected day 2024-08-01 13:29:41 -07:00
cba2d21c7e Bump version 2024-05-02 14:27:37 -07:00
e50e3c43f6 Rework soft ban functional 2024-05-02 14:26:55 -07:00
5ddffe06e7 Change default notification channel 2024-05-02 13:16:45 -07:00
7331c266f4 Do not send notification on HTTP401 2024-05-02 13:16:12 -07:00
18c8b2112c Revert "Remove operational hours"
This reverts commit 425ff34ea0.
2024-05-02 13:15:17 -07:00
2aaaa96dcf Fix HTTP401 error 2024-04-30 16:42:52 -07:00
425ff34ea0 Remove operational hours 2024-04-30 16:41:36 -07:00
3c72a20878 Update currentDate in popup to "-" if date not found 2024-04-29 22:34:59 -07:00
4f83907506 Don't ask for address when getting dates 2024-04-29 18:43:17 -07:00
5e96252bb4 Bump version 2024-04-27 22:53:38 -07:00
17bc09d67e Probably fixed 401 error 2024-04-27 22:48:50 -07:00
6af70c07bc Reduce runner poll period to 0.5s 2024-04-27 22:48:24 -07:00
ce539b61a1 Fix entering credentials 2024-04-27 22:48:07 -07:00
ca84e18468 Send notification on MAX_LOGIN_ATTEMPTS 2024-04-27 22:47:57 -07:00
f66c6ecbf9 Close modal warning on signin page 2024-04-27 22:47:35 -07:00
f580244a5d Increase MAX_SIGNIN_ATTEMPTS to 3 2024-04-27 22:47:10 -07:00
90a58961d4 Fix live current & best dates in popup 2024-04-27 22:46:50 -07:00
3f2ba8bd2a Separate configuration and context for consulates 2024-04-27 22:10:32 -07:00
d4c8835e9b Make request identical to real requests 2024-04-27 14:05:08 -07:00
776b0afdab Bump version 2024-04-26 23:46:14 -07:00
c428e4ab11 Probably fix HTTP 401 2024-04-26 23:45:56 -07:00
d19b9ced8f Update frequency config
- Set values from 0.5 to 10 with 0.5 step
- Add random increment equal to 10% of frequency parameter
2024-04-26 23:16:59 -07:00
4a506de6fe Fix error in getPathnameParts() 2024-04-26 22:35:51 -07:00
2070cdb30a Add status for non operational hours 2024-04-26 02:53:55 -07:00
fcb9f9dbf3 Bump version 2024-04-26 02:36:22 -07:00
094e2d2b81 UI rearrangement 2024-04-26 02:36:05 -07:00
4 changed files with 364 additions and 258 deletions

View File

@ -1,7 +1,7 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "not-a-rescheduler", "name": "not-a-rescheduler",
"version": "0.0.1", "version": "0.0.10",
"permissions": [ "storage", "tabs", "activeTab", "notifications", "declarativeContent" ], "permissions": [ "storage", "tabs", "activeTab", "notifications", "declarativeContent" ],
"content_scripts": [ "content_scripts": [
{ {

View File

@ -55,7 +55,7 @@
<label for="frequency">Check frequency<br>(every <span id="frequency_info">N</span> minutes)</label> <label for="frequency">Check frequency<br>(every <span id="frequency_info">N</span> minutes)</label>
</div> </div>
<div class="col"> <div class="col">
<input type="range" id="frequency" name="frequency" min="1" max="15" step="1"> <input type="range" id="frequency" name="frequency" min="0.5" max="10" step="0.5">
</div> </div>
</div> </div>
<div class="row"> <div class="row">
@ -75,12 +75,7 @@
</div> </div>
</div> </div>
<!-- consulates --> <!-- current appointment -->
<div class="row">
<div class="col">Consulates:</div>
</div>
<div id="consulatesConfig">
</div>
<div class="row"> <div class="row">
<div class="col"> <div class="col">
Current appointment: <span id="currApptConsulate">somewhere</span>, <span id="currApptDate">some time</span> Current appointment: <span id="currApptConsulate">somewhere</span>, <span id="currApptDate">some time</span>
@ -91,6 +86,12 @@
Status: <span class="smooth-text" id="status">Inactive</span> Status: <span class="smooth-text" id="status">Inactive</span>
</div> </div>
</div> </div>
<!-- consulates -->
<div id="consulatesConfig">
</div>
<!-- buttons -->
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<button type="button" class="btn btn-info btn-sm" id="showConfigButton">Config</button> <button type="button" class="btn btn-info btn-sm" id="showConfigButton">Config</button>

View File

@ -14,34 +14,33 @@ function smoothTextChange(element, newText) {
document.getElementById("version").innerText = `v${$version}`; document.getElementById("version").innerText = `v${$version}`;
await chrome.storage.local.get().then(items => { await chrome.storage.local.get().then(items => {
document.getElementById("activate").checked = items["__activate"] || false; document.getElementById("activate").checked = items["cfg_activate"] || false;
document.getElementById("username").value = items["__username"] || ""; document.getElementById("username").value = items["cfg_username"] || "";
document.getElementById("password").value = items["__password"] || ""; document.getElementById("password").value = items["cfg_password"] || "";
document.getElementById("frequency").value = items["__frequency"] || 1; document.getElementById("frequency").value = items["cfg_frequency"] || 1;
document.getElementById("frequency_info").innerText = items["__frequency"] || 1; document.getElementById("frequency_info").innerText = items["cfg_frequency"] || 1;
document.getElementById("status").innerText = items["__status"] || "unknown"; document.getElementById("status").innerText = items["ctx_statusMsg"] || "unknown";
let currentAppt = items["__currentAppt"] || {"consulate": "somewhere", "date": "some time"}; let currentAppt = items["ctx_currentAppt"] || {"consulate": "somewhere", "date": "some time"};
document.getElementById("currApptConsulate").innerText = currentAppt["consulate"]; document.getElementById("currApptConsulate").innerText = currentAppt["consulate"];
document.getElementById("currApptDate").innerText = currentAppt["date"]; document.getElementById("currApptDate").innerText = currentAppt["date"];
document.getElementById("deltaAppt").value = items["__deltaAppt"] || 1; document.getElementById("deltaAppt").value = items["cfg_deltaAppt"] || 1;
document.getElementById("deltaNow").value = items["__deltaNow"] || 1; document.getElementById("deltaNow").value = items["cfg_deltaNow"] || 1;
}); });
// update frequency value // update frequency value
chrome.storage.onChanged.addListener((changes, area) => { chrome.storage.onChanged.addListener((changes, area) => {
if (changes.__frequency) if (changes.cfg_frequency)
document.getElementById("frequency_info").innerText = changes.__frequency.newValue; document.getElementById("frequency_info").innerText = changes.cfg_frequency.newValue;
}); });
// update status // update status
chrome.storage.onChanged.addListener((changes, area) => { chrome.storage.onChanged.addListener((changes, area) => {
if (changes.__status) if (changes.ctx_statusMsg)
// document.getElementById("status").innerText = changes.__status.newValue; smoothTextChange(document.getElementById("status"), changes.ctx_statusMsg.newValue);
smoothTextChange(document.getElementById("status"), changes.__status.newValue);
}); });
// activate checkbox // activate checkbox
document.getElementById("activate").addEventListener("change", async e => { 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 // credentials
@ -53,8 +52,8 @@ function smoothTextChange(element, newText) {
async function save_credentials() { async function save_credentials() {
await chrome.storage.local.set({ await chrome.storage.local.set({
"__username": usernameField.value, "cfg_username": usernameField.value,
"__password": passwordField.value "cfg_password": passwordField.value
}); });
saveStatusElement.classList.add("show"); saveStatusElement.classList.add("show");
setTimeout(() => { setTimeout(() => {
@ -74,7 +73,7 @@ function smoothTextChange(element, newText) {
}); });
saveCredsButton.addEventListener("click", async () => { saveCredsButton.addEventListener("click", async () => {
await save_credentials(); await save_credentials();
await chrome.storage.local.set({ "__signinAttempts": 0 }); await chrome.storage.local.set({ "ctx_signinAttempts": 0 });
}); });
showPasswordButton.addEventListener("mousedown", function() { showPasswordButton.addEventListener("mousedown", function() {
@ -86,18 +85,19 @@ function smoothTextChange(element, newText) {
// range sliders // range sliders
document.getElementById("frequency").addEventListener("input", function() { 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() { 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() { document.getElementById("deltaNow").addEventListener("change", function() {
chrome.storage.local.set({ __deltaNow: this.value }); chrome.storage.local.set({ cfg_deltaNow: this.value });
}); });
// consulates // consulates
await chrome.storage.local.get(['__consulates']).then((result) => { await chrome.storage.local.get(['cfg_consulates', 'ctx_consulates']).then((result) => {
let consulates = result['__consulates']; let consCfg = result['cfg_consulates'];
let consCtx = result['ctx_consulates'];
let html = ` let html = `
<table class="table table-striped"> <table class="table table-striped">
@ -107,15 +107,15 @@ function smoothTextChange(element, newText) {
<th scope="col">City</th> <th scope="col">City</th>
<th scope="col" style="white-space: nowrap;">Current Date</th> <th scope="col" style="white-space: nowrap;">Current Date</th>
<th scope="col" style="white-space: nowrap;">Best Date</th> <th scope="col" style="white-space: nowrap;">Best Date</th>
<th scope="col">Autobook</th> <th scope="col">Book</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
` `
for (let c in consulates) { for (let c in consCtx) {
let cSelected = consulates[c].isSelected === true ? "checked" : ""; let cSelected = consCfg[c]?.isSelected === true ? "checked" : "";
let cAutobook = consulates[c].autobook === true ? "checked" : ""; let cAutoBook = consCfg[c]?.autoBook === true ? "checked" : "";
let cId = consulates[c].id; let cId = consCtx[c].id;
html += ` html += `
<tr> <tr>
<td> <td>
@ -124,12 +124,12 @@ function smoothTextChange(element, newText) {
</div> </div>
</td> </td>
<td style="white-space: nowrap;">${c}</td> <td style="white-space: nowrap;">${c}</td>
<td style="white-space: nowrap;"><span class="smooth-text" id="currentDate-${cId}">${consulates[c].currentDate || "-"}</span></td> <td style="white-space: nowrap;"><span class="smooth-text" id="currentDate-${cId}">${consCtx[c].currentDate || "-"}</span></td>
<td style="white-space: nowrap;"><span class="smooth-text" id="bestDate-${cId}">${consulates[c].bestDate || "-"}</span></td> <td style="white-space: nowrap;"><span class="smooth-text" id="bestDate-${cId}">${consCtx[c].bestDate || "-"}</span></td>
<td> <td>
<div class="form-check form-switch" style="text-align: left;"> <div class="form-check form-switch" style="text-align: left;">
<input class="form-check-input" type="checkbox" role="switch" id="autobook-${cId}" ${cAutobook}> <input class="form-check-input" type="checkbox" role="switch" id="autoBook-${cId}" ${cAutoBook}>
<!--<label class="form-check-label" for="autobook-${cId}">Autobook</label>--> <!--<label class="form-check-label" for="autoBook-${cId}">Autobook</label>-->
</div> </div>
</td> </td>
</tr> </tr>
@ -138,30 +138,31 @@ function smoothTextChange(element, newText) {
html += `</tbody></table>`; html += `</tbody></table>`;
document.getElementById('consulatesConfig').innerHTML = html; document.getElementById('consulatesConfig').innerHTML = html;
for (let c in consulates) { for (let c in consCtx) {
let cId = consulates[c].id; let cId = consCtx[c].id;
document.getElementById(`isSelected-${cId}`).addEventListener("change", async e => { document.getElementById(`isSelected-${cId}`).addEventListener("change", async e => {
consulates[c].isSelected = e.target.checked; consCfg[c].isSelected = e.target.checked;
await chrome.storage.local.set({ "__consulates": consulates }); await chrome.storage.local.set({ "cfg_consulates": consCfg });
}); });
document.getElementById(`autobook-${cId}`).addEventListener("change", async e => { document.getElementById(`autoBook-${cId}`).addEventListener("change", async e => {
consulates[c].autobook = e.target.checked; consCfg[c].autoBook = e.target.checked;
await chrome.storage.local.set({ "__consulates": consulates }); await chrome.storage.local.set({ "cfg_consulates": consCfg });
}); });
// update current & best dates // update current & best dates
chrome.storage.onChanged.addListener((changes, area) => { chrome.storage.onChanged.addListener((changes, area) => {
if (changes.__consulates && changes.__consulates.newValue[c].currentDate) { if (changes.ctx_consulates) {
let newCurrentDate = changes.ctx_consulates.newValue[c].currentDate || "-";
let newBestDate = changes.ctx_consulates.newValue[c].bestDate || "-";
let el = document.getElementById(`currentDate-${cId}`); let el = document.getElementById(`currentDate-${cId}`);
if (el.innerText != changes.__consulates.newValue[c].currentDate) if (el && el.innerText != newCurrentDate)
smoothTextChange(document.getElementById(`currentDate-${cId}`), changes.__consulates.newValue[c].currentDate); smoothTextChange(el, newCurrentDate);
el = document.getElementById(`bestDate-${cId}`);
if (el && el.innerText != newBestDate)
smoothTextChange(el, newBestDate);
} }
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 +173,12 @@ function smoothTextChange(element, newText) {
chrome.storage.local.clear(); chrome.storage.local.clear();
// keep user parameters // keep user parameters
chrome.storage.local.set({ chrome.storage.local.set({
"__activate": items["__activate"] || false, "cfg_activate": items["cfg_activate"] || false,
"__username": items["__username"] || "", "cfg_username": items["cfg_username"] || "",
"__password": items["__password"] || "", "cfg_password": items["cfg_password"] || "",
"__frequency": items["__frequency"] || 1, "cfg_frequency": items["cfg_frequency"] || 1,
"__deltaAppt": items["__deltaAppt"] || 1, "cfg_deltaAppt": items["cfg_deltaAppt"] || 1,
"__deltaNow": items["__deltaNow"] || 1, "cfg_deltaNow": items["cfg_deltaNow"] || 1,
"__autobook": items["__autobook"] || false,
}); });
}); });
document.getElementById("status").innerText = "unknown"; document.getElementById("status").innerText = "unknown";

View File

@ -2,27 +2,35 @@
// All rights reserved. // All rights reserved.
const pathnameRegex = /^\/\w{2}-\w{2}\/n?iv/; const pathnameRegex = /^\/\w{2}-\w{2}\/n?iv/;
const MAX_SIGNIN_ATTEMPTS = 1; const MAX_SIGNIN_ATTEMPTS = 3;
const PAGE_WAIT_TIME = 3792; const PAGE_WAIT_TIME = 3792;
const MINUTE = 60; const MINUTE = 60;
const RND_VAR_COEFF = 0.1;
const SOFT_BAN_TIMEOUT = 27; const SOFT_BAN_TIMEOUT = 27;
const NOTIF_CHANNEL = "snegov_test" const NOTIF_CHANNEL = "snegov";
const CHECK_OPER_HOURS = true;
const OPER_HOURS_START = 23;
const OPER_HOURS_END = 9;
let config = { let cfg = {
activate: null, activate: undefined,
username: null, username: undefined,
password: null, password: undefined,
frequency: null, frequency: undefined,
apptId: null, consulates: undefined,
currentAppt: { deltaAppt: undefined,
consulate: null, deltaNow: undefined,
date: null,
},
signinAttempts: null,
consulates: null,
deltaAppt: null,
deltaNow: null,
}; };
let ctx = {
apptId: undefined,
currentAppt: {
consulate: undefined,
date: undefined,
},
signinAttempts: undefined,
consulates: undefined,
statusMsg: undefined,
}
let isRunning = false; let isRunning = false;
let msg = ""; let msg = "";
let isFoundAppointment = false; let isFoundAppointment = false;
@ -32,11 +40,12 @@ async function delay(ms) {
} }
async function sendNotification(message, channel = NOTIF_CHANNEL) { async function sendNotification(message, channel = NOTIF_CHANNEL) {
const msg = "[US visa bot] " + message
await fetch('https://ntfy.sh/' + channel, { await fetch('https://ntfy.sh/' + channel, {
method: 'POST', method: 'POST',
body: "[US visa bot] " + message, body: msg,
}) })
.then(() => console.log('Notification sent')) .then(() => console.log('Notification sent: ' + msg))
.catch(e => console.error(e)); .catch(e => console.error(e));
} }
@ -44,18 +53,26 @@ function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max)); return Math.floor(Math.random() * Math.floor(max));
} }
function getFutureDate(minutes, maxRandomSeconds = 0) { function getFutureDate(minutes, frequency) {
// return date some amount of minutes in future plus random amount of seconds // return date some amount of minutes in future plus random amount of seconds
let futureDate = new Date(); let futureDate = new Date();
futureDate.setMinutes(futureDate.getMinutes() + minutes); const randomVariation = frequency * MINUTE * RND_VAR_COEFF;
futureDate.setSeconds(futureDate.getSeconds() + getRandomInt(maxRandomSeconds)); const randomSign = Math.random() < 0.5 ? -1 : 1;
const offset = randomSign * getRandomInt(randomVariation);
futureDate.setSeconds(futureDate.getSeconds() + minutes * MINUTE + offset);
return futureDate.toISOString(); return futureDate.toISOString();
} }
function hiddenPassword(config) { function isNoBanTime(frequency = 5) {
let now = new Date();
return (now.getHours() === OPER_HOURS_START && now.getMinutes() < frequency);
}
function hiddenPassword(cfg) {
return { return {
...config, ...cfg,
password: config.password.replace(/./g, "*"), password: cfg.password.replace(/./g, "*"),
}; };
} }
@ -82,11 +99,39 @@ function diffObjects(obj1, obj2) {
return diff; 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),
};
}
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) { function getPathnameParts(pathname) {
let pathParts = pathname.split('/'); let pathParts = pathname.split('/');
let locale = pathParts[1] || 'en-us'; let locale = pathParts[1] || 'en-us';
let visaType = pathParts[2] || 'niv'; let visaType = pathParts[2] || 'niv';
return { locale, visaType }; return [ locale, visaType ];
} }
function isSignInPage() { function isSignInPage() {
@ -118,19 +163,6 @@ function isNotEnglishPage() {
) && !window.location.pathname.match(/^\/en-/); ) && !window.location.pathname.match(/^\/en-/);
} }
async function handleHttpError(e) {
if (e.response && e.response.status === 401) {
msg = "Unauthorized";
console.log(msg);
await sendNotification(msg);
await logOut();
return null;
} else {
console.error(e);
return null;
}
}
async function switchToEnglishPage() { async function switchToEnglishPage() {
window.location.href(window.location.pathname.replace(/^\/\w{2}-{2}/, '/en-us')); window.location.href(window.location.pathname.replace(/^\/\w{2}-{2}/, '/en-us'));
await delay(PAGE_WAIT_TIME); await delay(PAGE_WAIT_TIME);
@ -161,9 +193,21 @@ async function goToDashboardPage() {
return isDashboardPage(); return isDashboardPage();
} }
async function enterCredentials() { async function enterCredentials(username, password) {
document.getElementById("user_email").value = config.username; // close warning modal
document.getElementById("user_password").value = config.password; let modalElement = Array.from(document.querySelectorAll('div'))
.find(d => d.textContent.includes('You need to sign in or sign up before continuing.'));
if (modalElement && window.getComputedStyle(modalElement).display !== 'none') {
let buttons = Array.from(modalElement.querySelectorAll('button'));
let okButton = buttons.find(b => b.textContent === 'OK');
if (okButton) {
okButton.click();
await delay(PAGE_WAIT_TIME);
}
}
document.getElementById("user_email").value = username;
document.getElementById("user_password").value = password;
let policyConfirmed = document.querySelector('[for="policy_confirmed"]'); let policyConfirmed = document.querySelector('[for="policy_confirmed"]');
if (!policyConfirmed.getElementsByTagName('input')[0].checked) { if (!policyConfirmed.getElementsByTagName('input')[0].checked) {
policyConfirmed.click(); policyConfirmed.click();
@ -179,7 +223,6 @@ async function getAppointmentId() {
if (appointments.length > 1) { if (appointments.length > 1) {
console.log("Multiple appointments found, taking the first one"); console.log("Multiple appointments found, taking the first one");
} }
let apptId = appointments[0].href.replace(/\D/g, ""); let apptId = appointments[0].href.replace(/\D/g, "");
return apptId; return apptId;
} }
@ -192,32 +235,41 @@ async function getConsulates() {
if (!option.value) continue; // skip empty option if (!option.value) continue; // skip empty option
consulatesDict[option.text] = { consulatesDict[option.text] = {
"id": parseInt(option.value), "id": parseInt(option.value),
"isSelected": option.selected,
"bestDate": null,
"currentDate": null,
"nextCheckAt": getFutureDate(0, config.frequency * MINUTE),
"autobook": false,
}; };
} }
return consulatesDict; return consulatesDict;
} }
async function getAvailableDates(consulateId) { async function getAvailableDates(consulateId) {
let uri = window.location.pathname + `/days/${consulateId}.json?appointments[expedite]=false` const datesUri = window.location.pathname + `/days/${consulateId}.json?appointments[expedite]=false`
let dates = fetch(uri, { headers: { "x-requested-with": "XMLHttpRequest" }}) try {
.then(d => d.json()) const response = await fetch(datesUri, { headers: {
.then(data => { "x-requested-with": "XMLHttpRequest",
let dateList = data.map(item => item.date); "accept": "application/json, text/javascript, */*; q=0.01",
dateList.sort(); "cache-control": "no-cache",
return dateList; }});
})
.catch(async e => handleHttpError(e)); if (!response.ok) {
return dates; if (response.status === 401) {
console.log('Logged out due to 401 error');
await logOut();
}
const body = await response.text();
throw new Error(`HTTP error ${response.status}: ${body}`);
}
const data = await response.json();
const dateList = data.map(item => item.date);
dateList.sort();
return dateList;
} catch (e) {
console.error('Error:', e);
return null;
}
} }
async function filterDates(dates, currentAppt, deltaFromAppt, deltaFromNow) { async function filterDates(dates, currentAppt, deltaFromAppt, deltaFromNow) {
let maxDate = new Date(currentAppt); let maxDate = new Date(currentAppt);
// let maxDate = new Date("2029-09-09");
maxDate.setDate(maxDate.getDate() - deltaFromAppt); maxDate.setDate(maxDate.getDate() - deltaFromAppt);
let minDate = new Date(); let minDate = new Date();
minDate.setDate(minDate.getDate() + deltaFromNow); minDate.setDate(minDate.getDate() + deltaFromNow);
@ -227,10 +279,10 @@ async function filterDates(dates, currentAppt, deltaFromAppt, deltaFromNow) {
async function getAvailableTimes(consulateId, date) { async function getAvailableTimes(consulateId, date) {
let uri = window.location.pathname + `/times/${consulateId}.json?date=${date}&appointments[expedite]=false` let uri = window.location.pathname + `/times/${consulateId}.json?date=${date}&appointments[expedite]=false`
let times = await fetch(uri, { headers: { "x-requested-with": "XMLHttpRequest" } }) let times = fetch(uri, { headers: { "x-requested-with": "XMLHttpRequest" } })
.then(d => d.json()) .then(d => d.json())
.then(data => data.available_times) .then(data => data.available_times)
.catch(e => handleHttpError(e)); .catch(e => null);
return times; return times;
} }
@ -239,103 +291,115 @@ async function runner() {
return; return;
} }
isRunning = true; 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)); let result = await new Promise(resolve => chrome.storage.local.get(null, resolve));
config.activate = result['__activate'] || false; cfg.activate = result['cfg_activate'] || false;
config.username = result['__username'] || ""; cfg.username = result['cfg_username'] || "";
config.password = result['__password'] || ""; cfg.password = result['cfg_password'] || "";
config.frequency = parseInt(result['__frequency'] || 1); cfg.frequency = parseFloat(result['cfg_frequency'] || 1);
config.signinAttempts = result['__signinAttempts'] || 0; cfg.consulates = result['cfg_consulates'] || {};
config.apptId = result['__apptId'] || null; cfg.deltaAppt = result['cfg_deltaAppt'] || 1;
config.currentAppt = result['__currentAppt'] || { consulate: null, date: null }; cfg.deltaNow = result['cfg_deltaNow'] || 1;
config.consulates = result['__consulates'] || null;
config.deltaAppt = result['__deltaAppt'] || 1;
config.deltaNow = result['__deltaNow'] || 1;
if (prev_config.activate === null) { ctx.apptId = result['ctx_apptId'] || null;
console.log('Reading config: ' + JSON.stringify(hiddenPassword(config))); 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; isRunning = false;
return; return;
} }
for (let key in config) { for (let key in cfg) {
if (config.hasOwnProperty(key) if (cfg.hasOwnProperty(key)
&& !_.isEqual(config[key], prev_config[key])) { && !_.isEqual(cfg[key], prev_cfg[key])) {
msg = `Config change: ${key}` msg = `Config change: ${key}`
if (key === 'password') { if (key === 'password') {
msg += ', ******** => ********'; msg += ', ******** => ********';
} else if (key === 'consulates') { } else if (key === 'consulates') {
msg += `, ${JSON.stringify(diffObjects(config[key], prev_config[key]))}`; msg += `, ${JSON.stringify(diffObjects(cfg[key], prev_cfg[key]))}`;
} else { } else {
msg += `, ${prev_config[key]} => ${config[key]}`; msg += `, ${prev_cfg[key]} => ${cfg[key]}`;
} }
console.log(msg); console.log(msg);
// reduce wait times for consulates if frequency is increased // reduce wait times for consulates if frequency is increased
if (key === 'frequency') { if (key === 'frequency') {
let wasChanged = false; let wasChanged = false;
for (let consulate in config.consulates) { for (let c in ctx.consulates) {
let newNextCheckAt = getFutureDate(config.frequency, 10); let newNextCheckAt = getFutureDate(cfg.frequency, cfg.frequency);
if (config.consulates[consulate].nextCheckAt > newNextCheckAt) { if (ctx.consulates[c].nextCheckAt > newNextCheckAt) {
config.consulates[consulate].nextCheckAt = newNextCheckAt; console.log(`Reducing wait time for ${c} from ${ctx.consulates[c].nextCheckAt} to ${newNextCheckAt}`);
ctx.consulates[c].nextCheckAt = newNextCheckAt;
wasChanged = true; wasChanged = true;
console.log(`Reducing wait time for ${consulate} from ${config.consulates[consulate].nextCheckAt} to ${newNextCheckAt}`);
} }
} }
// TODO maybe causes additional requests // TODO maybe causes additional requests
if (wasChanged) { if (wasChanged) {
await chrome.storage.local.set({ "__consulates": config.consulates }); await chrome.storage.local.set({ "ctx_consulates": ctx.consulates });
} }
} }
if (key === 'activate') { if (key === 'activate') {
if (config[key]) { if (cfg[key]) {
console.log('Activating extension'); console.log('Activating extension');
} else { } else {
console.log('Deactivating extension'); 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 // clear signin attempts when credentials are changed
if (key === 'username' && config[key] if (key === 'username' && cfg[key]
|| key === 'password' && config[key]) { || key === 'password' && cfg[key]) {
config.signinAttempts = 0; ctx.signinAttempts = 0;
await chrome.storage.local.set({ "__signinAttempts": config.signinAttempts }); await chrome.storage.local.set({ "ctx_signinAttempts": ctx.signinAttempts });
} }
} }
} }
if (!config.activate) { if (!cfg.activate) {
isRunning = false; isRunning = false;
return; return;
} }
if (config.username === "" || config.password === "") { if (cfg.username === "" || cfg.password === "") {
console.log('Username or password is empty'); 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; isRunning = false;
return; return;
} }
if (config.frequency <= 0) { if (cfg.frequency <= 0) {
console.log('Frequency is 0 or negative'); 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; isRunning = false;
return; return;
} }
// Check if current time is between 11pm and 9am UTC (4pm - 2am PST) if (CHECK_OPER_HOURS) {
let now = new Date(); // Check if current time is between 11pm and 9am UTC (4pm - 2am PST)
let currentHourUTC = now.getUTCHours(); let now = new Date();
if (currentHourUTC >= 23 || currentHourUTC < 9) { let currentHourUTC = now.getUTCHours();
// Continue running the code if (currentHourUTC >= OPER_HOURS_START || currentHourUTC < OPER_HOURS_END) {
} else { // Continue running the code
isRunning = false; } else {
return; await chrome.storage.local.set({ "ctx_statusMsg": "not operational hours" });
isRunning = false;
return;
}
} }
if (isFoundAppointment) { if (isFoundAppointment) {
@ -357,7 +421,7 @@ async function runner() {
if (!await goToSignInPage()) { if (!await goToSignInPage()) {
msg = 'Failed to go to sign in page'; msg = 'Failed to go to sign in page';
console.log(msg); console.log(msg);
await chrome.storage.local.set({ "__status": msg }); await chrome.storage.local.set({ "ctx_statusMsg": msg });
isRunning = false; isRunning = false;
return; return;
}; };
@ -366,23 +430,28 @@ async function runner() {
else if (isSignInPage()) { else if (isSignInPage()) {
// Prevent brute forcing // Prevent brute forcing
if (config.signinAttempts >= MAX_SIGNIN_ATTEMPTS) { if (ctx.signinAttempts >= MAX_SIGNIN_ATTEMPTS) {
await chrome.storage.local.set({ "__status": "too many sign in attempts" }); if (prev_ctx.signinAttempts < MAX_SIGNIN_ATTEMPTS) {
msg = 'Too many sign in attempts';
console.log(msg);
await sendNotification(msg);
}
await chrome.storage.local.set({ "ctx_statusMsg": "too many sign in attempts" });
isRunning = false; isRunning = false;
return; return;
} }
// Sign in // Sign in
msg = 'Signing in attempt: ' + config.signinAttempts; msg = 'Signing in attempt: ' + ctx.signinAttempts;
console.log(msg) console.log(msg)
await chrome.storage.local.set({ "__status": msg }); await chrome.storage.local.set({ "ctx_statusMsg": msg });
let signedIn = await enterCredentials(); let signedIn = await enterCredentials(cfg.username, cfg.password);
config.signinAttempts += 1; ctx.signinAttempts += 1;
await chrome.storage.local.set({ "__signinAttempts": config.signinAttempts }); await chrome.storage.local.set({ "ctx_signinAttempts": ctx.signinAttempts });
if (!signedIn) { if (!signedIn) {
msg = 'Failed to sign in'; msg = 'Failed to sign in';
console.log(msg); console.log(msg);
await chrome.storage.local.set({ "__status": msg }); await chrome.storage.local.set({ "ctx_statusMsg": msg });
isRunning = false; isRunning = false;
return; return;
}; };
@ -391,31 +460,31 @@ async function runner() {
else if (isDashboardPage()) { else if (isDashboardPage()) {
// reset signin attempts when successfully logged in // reset signin attempts when successfully logged in
config.signinAttempts = 0; ctx.signinAttempts = 0;
await chrome.storage.local.set({ "__status": "fetching appointment info" }); await chrome.storage.local.set({ "ctx_statusMsg": "fetching appointment info" });
await chrome.storage.local.set({ "__signinAttempts": config.signinAttempts }); await chrome.storage.local.set({ "ctx_signinAttempts": ctx.signinAttempts });
// get appointmentId // get appointmentId
if (!config.apptId) { if (!ctx.apptId) {
config.apptId = await getAppointmentId(); ctx.apptId = await getAppointmentId();
if (config.apptId) { if (ctx.apptId) {
console.log(`Appointment ID: ${config.apptId}`); console.log(`Appointment ID: ${ctx.apptId}`);
await chrome.storage.local.set({ "__apptId": config.apptId }); await chrome.storage.local.set({ "ctx_apptId": ctx.apptId });
} else { } else {
msg = 'No appointments found'; msg = 'No appointments found';
console.log(msg); console.log(msg);
await chrome.storage.local.set({ "__status": msg }); await chrome.storage.local.set({ "ctx_statusMsg": msg });
isRunning = false; isRunning = false;
return; return;
} }
} }
// get current appointment date // 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/)) { if (!apptInfoCard.querySelector("h4").innerText.match(/Attend Appointment/)) {
msg = 'Appointment not available'; msg = 'Appointment not available';
console.log(msg); console.log(msg);
await chrome.storage.local.set({ "__status": msg }); await chrome.storage.local.set({ "ctx_statusMsg": msg });
isRunning = false; isRunning = false;
return; return;
} }
@ -423,14 +492,23 @@ async function runner() {
let apptConsulate = (apptInfo.match(/at (\w+)/) || [])[1]; let apptConsulate = (apptInfo.match(/at (\w+)/) || [])[1];
let apptDate = new Date(apptInfo.match(/\d{1,2} \w+, \d{4}/)[0]); let apptDate = new Date(apptInfo.match(/\d{1,2} \w+, \d{4}/)[0]);
apptDate = apptDate.toISOString().slice(0, 10); apptDate = apptDate.toISOString().slice(0, 10);
if (apptDate && apptConsulate if (!apptDate || !apptConsulate) {
&& (apptDate != config.currentAppt.date msg = 'Failed to fetch appointment date or consulate';
|| apptConsulate != config.currentAppt.consulate)) { console.log(msg);
console.log(`New appointment date: ${apptDate} at ${apptConsulate}, await chrome.storage.local.set({ "ctx_statusMsg": msg });
old: ${config.currentAppt.consulate} at ${config.currentAppt.date}`); isRunning = false;
config.currentAppt.date = apptDate; return;
config.currentAppt.consulate = apptConsulate; }
await chrome.storage.local.set({ "__currentAppt": config.currentAppt });
if (apptDate != ctx.currentAppt.date || apptConsulate != ctx.currentAppt.consulate) {
msg = `New appointment date: ${apptDate} at ${apptConsulate}`
if (ctx.currentAppt.date != null) {
sendNotification(msg);
}
msg += `, was: ${ctx.currentAppt.consulate} at ${ctx.currentAppt.date}`
console.log(msg);
ctx.currentAppt = { consulate: apptConsulate, date: apptDate };
await chrome.storage.local.set({ "ctx_currentAppt": ctx.currentAppt });
} }
// go to appointment page // go to appointment page
@ -442,14 +520,13 @@ async function runner() {
else if (isAppointmentPage()) { else if (isAppointmentPage()) {
// if no apptDate, fetch it from dashboard page // 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'); console.log('No appointment date is set, going back to dashboard');
await goToDashboardPage(); await goToDashboardPage();
isRunning = false; isRunning = false;
return; return;
} }
// await chrome.storage.local.set({ "__status": "fetching consulates" });
let applicantForm = document.querySelector('form[action*="' + window.location.pathname + '"]'); let applicantForm = document.querySelector('form[action*="' + window.location.pathname + '"]');
if (applicantForm && applicantForm.method.toLowerCase() == "get") { if (applicantForm && applicantForm.method.toLowerCase() == "get") {
applicantForm.submit(); applicantForm.submit();
@ -458,21 +535,40 @@ async function runner() {
return; return;
} }
// TODO maybe it's a rare case
if (!document.getElementById("consulate_date_time")) { if (!document.getElementById("consulate_date_time")) {
msg = 'No available appointments'; msg = 'No available appointments';
console.log(msg); console.log(msg);
await chrome.storage.local.set({ "__status": msg }); await chrome.storage.local.set({ "ctx_statusMsg": msg });
isRunning = false; isRunning = false;
return; return;
}; };
if (!config.consulates) { if (!ctx.consulates || Object.keys(ctx.consulates).length == 0) {
config.consulates = await getConsulates(); await chrome.storage.local.set({ "ctx_statusMsg": "Fetching consulates" });
await chrome.storage.local.set({ "__consulates": config.consulates }); ctx.consulates = await getConsulates();
} if (!ctx.consulates || Object.keys(ctx.consulates).length == 0) {
if (!config.consulates) { msg = "No consulates found";
console.log('No consulates found'); console.log(msg);
await chrome.storage.local.set({ "__status": "no consulates found" }); 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; isRunning = false;
return; return;
} }
@ -481,104 +577,112 @@ async function runner() {
// document.querySelector("#appointments_asc_appointment_facility_id [selected]").innerText // document.querySelector("#appointments_asc_appointment_facility_id [selected]").innerText
// for each selected consulate check available dates // 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) { if (selectedConsulates.length == 0) {
msg = 'No selected consulates found'; msg = 'No selected consulates found';
console.log(msg); console.log(msg);
await chrome.storage.local.set({ "__status": msg }); await chrome.storage.local.set({ "ctx_statusMsg": msg });
isRunning = false; isRunning = false;
return; return;
} }
let processedConsulates = 0; let processedConsulates = 0;
for (let consulate of selectedConsulates) { for (let c of selectedConsulates) {
// only one consulate per run // only one consulate per run
if (processedConsulates > 0) { if (processedConsulates > 0) {
break; break;
} }
// Reset all soft bans in the first 5 minutes of the first operational hour
let now = new Date();
if (isNoBanTime(cfg.frequency)) {
let nextCheckAt = new Date(ctx.consulates[c].nextCheckAt);
let differenceInMinutes = (nextCheckAt.getTime() - now.getTime()) / (1000 * 60);
if (differenceInMinutes > cfg.frequency) {
console.log(`Resetting soft ban for ${c}`);
ctx.consulates[c].nextCheckAt = getFutureDate(0, cfg.frequency);
}
}
// skip if not time to check // skip if not time to check
if (config.consulates[consulate].nextCheckAt > new Date().toISOString()) { if (ctx.consulates[c].nextCheckAt > new Date().toISOString()) {
continue; continue;
} }
processedConsulates += 1; processedConsulates += 1;
msg = `Checking dates for ${consulate}`; msg = `Checking dates for ${c}`;
console.log(msg); console.log(msg);
await chrome.storage.local.set({ "__status": msg }); await chrome.storage.local.set({ "ctx_statusMsg": msg });
let availDates = await getAvailableDates(config.consulates[consulate].id); let availDates = await getAvailableDates(ctx.consulates[c].id);
config.consulates[consulate].nextCheckAt = getFutureDate(config.frequency, 10); ctx.consulates[c].nextCheckAt = getFutureDate(cfg.frequency, cfg.frequency);
if (!availDates) { if (!availDates) {
msg = `Failed to fetch available dates in ${consulate}`; msg = `Failed to fetch available dates in ${c}`;
console.log(msg); console.log(msg);
await chrome.storage.local.set({ "__status": msg }); await chrome.storage.local.set({ "ctx_statusMsg": msg });
continue; continue;
} }
// if empty list, either we're banned or non operational hours or dead consulate // if empty list, either we're banned or non operational hours or dead consulate
// wait for some time before checking again // wait for some time before checking again
let now = new Date();
let currentHourUTC = now.getUTCHours();
let currentMinuteUTC = now.getUTCMinutes();
if (availDates.length == 0) { if (availDates.length == 0) {
msg = `No available dates in ${consulate}, probably banned`; msg = `No available dates in ${c}`;
console.log(msg); 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 (!isNoBanTime(cfg.frequency)) {
if (!(currentHourUTC === 23 && currentMinuteUTC < 5)) { console.log(`Setting soft ban for ${c}`);
config.consulates[consulate].nextCheckAt = getFutureDate(SOFT_BAN_TIMEOUT, 10); ctx.consulates[c].nextCheckAt = getFutureDate(SOFT_BAN_TIMEOUT, cfg.frequency);
} }
ctx.consulates[c].currentDate = null;
continue; continue;
} }
msg = `Available dates for ${consulate}: ${availDates.slice(0, 5)}`; msg = `Available dates for ${c}: ${availDates.slice(0, 5)}`;
if (availDates.length > 5) { if (availDates.length > 5) {
msg += ` and ${availDates.length - 10} more`; msg += ` and ${availDates.length - 10} more`;
} }
console.log(msg); console.log(msg);
config.consulates[consulate].currentDate = availDates[0]; ctx.consulates[c].currentDate = availDates[0];
if (!config.consulates[consulate].bestDate if (!ctx.consulates[c].bestDate || availDates[0] < ctx.consulates[c].bestDate) {
|| availDates[0] < config.consulates[consulate].bestDate) { console.log(`New record for ${c}: ${availDates[0]}`);
console.log(`New record for ${consulate}: ${availDates[0]}`); ctx.consulates[c].bestDate = availDates[0];
config.consulates[consulate].bestDate = availDates[0];
} }
// filter dates with our requests // 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) { if (!filteredDates.length) {
console.log(`No better dates in ${consulate}, currently available ${availDates[0]}`); console.log(`No better dates in ${c}, currently available ${availDates[0]}`);
await chrome.storage.local.set({ "__status": `No better dates in ${consulate}`}); await chrome.storage.local.set({ "ctx_statusMsg": `no better dates in ${c}`});
continue; continue;
} }
console.log(`Dates worth rescheduling in ${consulate}: ${filteredDates}`); console.log(`Dates worth rescheduling in ${c}: ${filteredDates}`);
let chosenDate = filteredDates[0]; 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 // fill date in reschedule form
await delay(PAGE_WAIT_TIME); await delay(PAGE_WAIT_TIME);
document.getElementById("appointments_consulate_appointment_date").value = chosenDate; document.getElementById("appointments_consulate_appointment_date").value = chosenDate;
document.getElementById("appointments_consulate_appointment_time").innerHTML = "<option></option>" document.getElementById("appointments_consulate_appointment_time").innerHTML = "<option></option>"
let availTimes = await getAvailableTimes(config.consulates[consulate].id, chosenDate); let availTimes = await getAvailableTimes(ctx.consulates[c].id, chosenDate);
if (!availTimes) { 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); console.log(msg);
await chrome.storage.local.set({ "__status": msg}); await chrome.storage.local.set({ "ctx_statusMsg": msg});
continue; continue;
} }
if (availTimes.length == 0) { if (availTimes.length == 0) {
msg = `No timeslots in ${consulate} at ${chosenDate}`; msg = `No timeslots in ${c} at ${chosenDate}`;
console.log(msg); console.log(msg);
await chrome.storage.local.set({ "__status": msg}); await chrome.storage.local.set({ "ctx_statusMsg": msg});
continue; continue;
} }
console.log(`Available timeslots in ${consulate} at ${chosenDate}: ${availTimes}`); console.log(`Available timeslots in ${c} at ${chosenDate}: ${availTimes}`);
let chosenTime = availTimes[0]; let chosenTime = availTimes[availTimes.length - 1];
// fill timeslot in reschedule form // fill timeslot in reschedule form
await delay(PAGE_WAIT_TIME); await delay(PAGE_WAIT_TIME);
@ -591,28 +695,29 @@ async function runner() {
document.getElementById("appointments_submit").removeAttribute("disabled"); document.getElementById("appointments_submit").removeAttribute("disabled");
document.getElementById("appointments_submit").click(); 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); console.log(msg);
await sendNotification(msg); await sendNotification(msg);
if (!config.consulates[consulate].autobook) { if (!cfg.consulates[c].autoBook) {
isFoundAppointment = true; isFoundAppointment = true;
} else { } else {
await delay(PAGE_WAIT_TIME); await delay(PAGE_WAIT_TIME);
msg = `Auto booking in ${consulate} at ${chosenDate} ${chosenTime}`; msg = `Auto booking in ${c} at ${chosenDate} ${chosenTime}`;
console.log(msg); console.log(msg);
await sendNotification(msg); await sendNotification(msg);
document.querySelector(".reveal-overlay:last-child [data-reveal] .button.alert").click(); document.querySelector(".reveal-overlay:last-child [data-reveal] .button.alert").click();
} }
} // end consulates loop } // end consulates loop
for (let consulate in config.consulates) { for (let c in ctx.consulates) {
if (!prev_config?.consulates?.[consulate]) continue; if (!prev_ctx?.consulates?.[c]) continue;
if (config.consulates[consulate].nextCheckAt != prev_config.consulates[consulate].nextCheckAt) { if (ctx.consulates[c].nextCheckAt != prev_ctx.consulates[c].nextCheckAt) {
console.log(`Next check for ${consulate} at ${config.consulates[consulate].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; isRunning = false;
return; return;
} }
@ -620,13 +725,13 @@ async function runner() {
else if (isConfirmationPage) { else if (isConfirmationPage) {
// go back to dashboard after successful reschedule // go back to dashboard after successful reschedule
await delay(PAGE_WAIT_TIME); await delay(PAGE_WAIT_TIME);
config.currentAppt = { consulate: null, date: null}; ctx.currentAppt = { consulate: null, date: null};
await chrome.storage.local.set({"__currentAppt": config.currentAppt}); await chrome.storage.local.set({"ctx_currentAppt": ctx.currentAppt});
console.log('Rescheduled successfully'); console.log('Rescheduled successfully');
// switch off autobook for all consulates after successful reschedule // switch off autoBook for all consulates after successful reschedule
for (let consulate in config.consulates) { for (let c in cfg.consulates) {
config.consulates[consulate].autobook = false; cfg.consulates[c].autoBook = false;
} }
await goToDashboardPage(); await goToDashboardPage();
@ -636,4 +741,4 @@ async function runner() {
isRunning = false; isRunning = false;
} }
setInterval(runner, 1000); setInterval(runner, 500);