notif-visa-ext/scripts/content.js

660 lines
24 KiB
JavaScript
Raw Normal View History

2024-04-22 07:36:09 +00:00
// Copyright (c) 2024, snegov
// All rights reserved.
2024-04-21 04:03:26 +00:00
const pathnameRegex = /^\/\w{2}-\w{2}\/n?iv/;
2024-04-21 06:50:49 +00:00
const MAX_SIGNIN_ATTEMPTS = 1;
const PAGE_WAIT_TIME = 3792;
const MINUTE = 60;
const RANDOM_JITTER = 0.1;
2024-04-24 22:12:57 +00:00
const SOFT_BAN_TIMEOUT = 27;
2024-04-22 07:36:09 +00:00
const NOTIF_CHANNEL = "snegov_test"
2024-04-21 04:03:26 +00:00
let config = {
activate: null,
username: null,
password: null,
frequency: null,
2024-04-22 00:09:33 +00:00
apptId: null,
currentAppt: {
consulate: null,
date: null,
},
2024-04-22 00:09:33 +00:00
signinAttempts: null,
consulates: null,
2024-04-22 09:56:06 +00:00
deltaAppt: null,
deltaNow: null,
2024-04-21 04:03:26 +00:00
};
2024-04-21 06:50:49 +00:00
let isRunning = false;
2024-04-23 06:30:55 +00:00
let msg = "";
let isFoundAppointment = false;
2024-04-21 04:03:26 +00:00
2024-04-22 07:36:09 +00:00
async function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function sendNotification(message, channel = NOTIF_CHANNEL) {
await fetch('https://ntfy.sh/' + channel, {
method: 'POST',
2024-04-26 05:44:54 +00:00
body: "[US visa bot] " + message,
2024-04-22 07:36:09 +00:00
})
.then(() => console.log('Notification sent'))
.catch(e => console.error(e));
}
function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
function getJitter(frequency) {
return frequency * MINUTE * RANDOM_JITTER;
}
function getFutureDate(minutes, maxRandomSeconds = 0) {
// return date some amount of minutes in future plus random amount of seconds
let futureDate = new Date();
futureDate.setSeconds(futureDate.getSeconds() + minutes * MINUTE + getRandomInt(maxRandomSeconds));
return futureDate.toISOString();
}
2024-04-21 04:03:26 +00:00
2024-04-25 02:46:05 +00:00
function hiddenPassword(config) {
return {
...config,
password: config.password.replace(/./g, "*"),
};
}
function diffObjects(obj1, obj2) {
let diff = {};
for (let key in obj1) {
if (typeof obj1[key] === 'object' && obj1[key] !== null && obj2[key]) {
let nestedDiff = diffObjects(obj1[key], obj2[key]);
if (Object.keys(nestedDiff).length > 0) {
diff[key] = nestedDiff;
}
} else if (obj2[key] && obj1[key] !== obj2[key]) {
diff[key] = [obj1[key], obj2[key]];
}
}
for (let key in obj2) {
if (obj1[key] === undefined && obj2[key] !== undefined) {
diff[key] = [undefined, obj2[key]];
}
}
return diff;
}
2024-04-26 05:44:54 +00:00
function getPathnameParts(pathname) {
let pathParts = pathname.split('/');
let locale = pathParts[1] || 'en-us';
let visaType = pathParts[2] || 'niv';
2024-04-27 05:35:51 +00:00
return [ locale, visaType ];
2024-04-26 05:44:54 +00:00
}
2024-04-21 04:03:26 +00:00
function isSignInPage() {
2024-04-22 00:09:33 +00:00
return Boolean(window.location.pathname.match(/^\/\w{2}-\w{2}\/n?iv\/users\/sign_in/));
2024-04-21 04:03:26 +00:00
}
function isLoggedOutPage() {
2024-04-22 00:09:33 +00:00
return Boolean(window.location.pathname.match(/^\/\w{2}-\w{2}\/n?iv$/));
2024-04-21 04:03:26 +00:00
}
function isDashboardPage() {
2024-04-22 00:09:33 +00:00
return Boolean(window.location.pathname.match(/^\/\w{2}-\w{2}\/n?iv\/groups\/\d+/));
2024-04-21 04:03:26 +00:00
}
function isAppointmentPage() {
2024-04-22 00:09:33 +00:00
return Boolean(window.location.pathname.match(/^\/\w{2}-\w{2}\/n?iv\/schedule\/\d+\/appointment$/));
2024-04-21 04:03:26 +00:00
}
function isConfirmationPage() {
2024-04-22 00:09:33 +00:00
return Boolean(window.location.pathname.match(/^\/\w{2}-\w{2}\/n?iv\/schedule\/\d+\/appointment\/instructions$/));
2024-04-21 04:03:26 +00:00
}
function isNotEnglishPage() {
2024-04-22 00:09:33 +00:00
return Boolean(isSignInPage()
2024-04-21 04:03:26 +00:00
|| isLoggedOutPage()
|| isDashboardPage()
|| isAppointmentPage()
|| isConfirmationPage()
) && !window.location.pathname.match(/^\/en-/);
}
2024-04-26 05:44:54 +00:00
async function handleHttpError(e) {
if (e.response && e.response.status === 401) {
msg = "Unauthorized";
console.log(msg);
await sendNotification(msg);
await logOut();
} else {
console.error(e);
}
}
2024-04-21 04:03:26 +00:00
async function switchToEnglishPage() {
window.location.href(window.location.pathname.replace(/^\/\w{2}-{2}/, '/en-us'));
2024-04-21 06:50:49 +00:00
await delay(PAGE_WAIT_TIME);
2024-04-21 04:03:26 +00:00
// Should be on English page
}
2024-04-26 05:44:54 +00:00
async function logOut() {
let [locale, visaType] = getPathnameParts(window.location.pathname);
let signOutPath = `/${locale}/${visaType}/users/sign_out`;
window.location.href = window.location.origin + signOutPath;
await delay(PAGE_WAIT_TIME);
return isLoggedOutPage();
}
2024-04-21 04:03:26 +00:00
async function goToSignInPage() {
2024-04-26 05:44:54 +00:00
let [locale, visaType] = getPathnameParts(window.location.pathname);
let signInPath = `/${locale}/${visaType}/users/sign_in`;
window.location.href = window.location.origin + signInPath;
2024-04-21 06:50:49 +00:00
await delay(PAGE_WAIT_TIME);
2024-04-22 00:09:33 +00:00
return isSignInPage();
2024-04-21 04:03:26 +00:00
}
2024-04-26 05:44:54 +00:00
async function goToDashboardPage() {
let [locale, visaType] = getPathnameParts(window.location.pathname);
let dashboardPath = `/${locale}/${visaType}/account`;
window.location.href = window.location.origin + dashboardPath;
await delay(PAGE_WAIT_TIME);
return isDashboardPage();
}
2024-04-21 04:03:26 +00:00
async function enterCredentials() {
document.getElementById("user_email").value = config.username;
document.getElementById("user_password").value = config.password;
2024-04-21 06:50:49 +00:00
let policyConfirmed = document.querySelector('[for="policy_confirmed"]');
if (!policyConfirmed.getElementsByTagName('input')[0].checked) {
policyConfirmed.click();
}
2024-04-21 04:03:26 +00:00
document.querySelector("#sign_in_form input[type=submit]").click();
2024-04-21 06:50:49 +00:00
await delay(PAGE_WAIT_TIME);
2024-04-22 00:09:33 +00:00
return isDashboardPage();
}
async function getAppointmentId() {
let appointments = document.querySelectorAll("p.consular-appt [href]")
if (appointments.length > 1) {
console.log("Multiple appointments found, taking the first one");
2024-04-21 06:50:49 +00:00
}
2024-04-22 00:09:33 +00:00
let apptId = appointments[0].href.replace(/\D/g, "");
return apptId;
2024-04-21 04:03:26 +00:00
}
2024-04-20 05:14:53 +00:00
async function getConsulates() {
let consulatesSelect = document.querySelector("#appointments_consulate_appointment_facility_id")
let consulatesDict = {};
for (let option of consulatesSelect.options) {
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)),
2024-04-24 22:12:19 +00:00
"autobook": false,
};
}
return consulatesDict;
}
async function getAvailableDates(consulateId) {
let addressUri = window.location.pathname + `/address/${consulateId}`
fetch(addressUri, { headers: { "x-requested-with": "XMLHttpRequest" } })
.catch(error => console.error('Error:', error));
let datesUri = window.location.pathname + `/days/${consulateId}.json?appointments[expedite]=false`
let dates = fetch(datesUri, { headers: {
"x-requested-with": "XMLHttpRequest",
"accept": "application/json, text/javascript, */*; q=0.01",
// "cache-control": "no-cache",
}})
.then(d => d.json())
2024-04-27 06:45:56 +00:00
.catch(async e => {
await handleHttpError(e);
throw e;
})
.then(data => {
let dateList = data.map(item => item.date);
dateList.sort();
return dateList;
})
2024-04-27 06:45:56 +00:00
.catch(e => null);
return dates;
}
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);
let availableDates = dates.filter(d => new Date(d) >= minDate && new Date(d) < maxDate);
return availableDates;
}
async function getAvailableTimes(consulateId, date) {
let uri = window.location.pathname + `/times/${consulateId}.json?date=${date}&appointments[expedite]=false`
let times = await fetch(uri, { headers: { "x-requested-with": "XMLHttpRequest" } })
.then(d => d.json())
2024-04-27 06:45:56 +00:00
.catch(async e => {
await handleHttpError(e);
throw e;
})
.then(data => data.available_times)
2024-04-27 06:45:56 +00:00
.catch(e => null);
return times;
2024-04-20 05:14:53 +00:00
}
async function runner() {
2024-04-21 06:50:49 +00:00
if (isRunning) {
return;
}
isRunning = true;
// console.log('runner start');
2024-04-20 05:14:53 +00:00
let prev_config = Object.assign({}, config);
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);
2024-04-22 00:09:33 +00:00
config.signinAttempts = result['__signinAttempts'] || 0;
config.apptId = result['__apptId'] || null;
config.currentAppt = result['__currentAppt'] || { consulate: null, date: null };
config.consulates = result['__consulates'] || null;
2024-04-22 09:56:06 +00:00
config.deltaAppt = result['__deltaAppt'] || 1;
config.deltaNow = result['__deltaNow'] || 1;
2024-04-20 05:14:53 +00:00
if (prev_config.activate === null) {
2024-04-25 02:46:05 +00:00
console.log('Reading config: ' + JSON.stringify(hiddenPassword(config)));
2024-04-21 06:50:49 +00:00
isRunning = false;
2024-04-20 05:14:53 +00:00
return;
}
for (let key in config) {
if (config.hasOwnProperty(key)
2024-04-23 05:03:26 +00:00
&& !_.isEqual(config[key], prev_config[key])) {
2024-04-25 02:46:05 +00:00
msg = `Config change: ${key}`
if (key === 'password') {
msg += ', ******** => ********';
} else if (key === 'consulates') {
msg += `, ${JSON.stringify(diffObjects(config[key], prev_config[key]))}`;
} else {
msg += `, ${prev_config[key]} => ${config[key]}`;
}
console.log(msg);
2024-04-21 04:03:26 +00:00
// reduce wait times for consulates if frequency is increased
2024-04-20 05:14:53 +00:00
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;
wasChanged = true;
console.log(`Reducing wait time for ${consulate} from ${config.consulates[consulate].nextCheckAt} to ${newNextCheckAt}`);
}
}
2024-04-25 02:46:05 +00:00
// TODO maybe causes additional requests
if (wasChanged) {
await chrome.storage.local.set({ "__consulates": config.consulates });
2024-04-20 05:14:53 +00:00
}
}
2024-04-21 04:03:26 +00:00
if (key === 'activate') {
if (config[key]) {
2024-04-21 06:50:49 +00:00
console.log('Activating extension');
2024-04-21 04:03:26 +00:00
} else {
2024-04-21 06:50:49 +00:00
console.log('Deactivating extension');
2024-04-21 04:03:26 +00:00
await chrome.storage.local.set({ "__status": "inactive" });
}
}
// clear signin attempts when credentials are changed
if (key === 'username' && config[key]
|| key === 'password' && config[key]) {
2024-04-22 00:09:33 +00:00
config.signinAttempts = 0;
await chrome.storage.local.set({ "__signinAttempts": config.signinAttempts });
2024-04-21 04:03:26 +00:00
}
2024-04-20 05:14:53 +00:00
}
}
if (!config.activate) {
2024-04-21 06:50:49 +00:00
isRunning = false;
2024-04-20 05:14:53 +00:00
return;
}
if (config.username === "" || config.password === "") {
2024-04-21 06:50:49 +00:00
console.log('Username or password is empty');
2024-04-21 04:03:26 +00:00
await chrome.storage.local.set({ "__status": "missing credentials" });
2024-04-21 06:50:49 +00:00
isRunning = false;
2024-04-20 05:14:53 +00:00
return;
}
if (config.frequency <= 0) {
2024-04-21 06:50:49 +00:00
console.log('Frequency is 0 or negative');
2024-04-21 04:03:26 +00:00
await chrome.storage.local.set({ "__status": "invalid frequency" });
2024-04-21 06:50:49 +00:00
isRunning = false;
2024-04-20 05:14:53 +00:00
return;
}
2024-04-26 06:14:38 +00:00
// Check if current time is between 11pm and 9am UTC (4pm - 2am PST)
let now = new Date();
let currentHourUTC = now.getUTCHours();
if (currentHourUTC >= 23 || currentHourUTC < 9) {
// Continue running the code
} else {
2024-04-26 09:53:55 +00:00
await chrome.storage.local.set({ "__status": "not operational hours" });
2024-04-26 06:14:38 +00:00
isRunning = false;
return;
}
if (isFoundAppointment) {
// don't do anything if appointment is found and manual booking is required
if (isAppointmentPage()) {
isRunning = false;
return;
// we're not on an appointment page, so seems like we're done, and we can check dates again
} else {
isFoundAppointment = false;
}
2024-04-26 06:18:36 +00:00
}
2024-04-22 00:09:33 +00:00
if (isNotEnglishPage()) {
2024-04-21 04:03:26 +00:00
await switchToEnglishPage();
}
2024-04-20 05:14:53 +00:00
2024-04-22 00:09:33 +00:00
if (isLoggedOutPage()) {
if (!await goToSignInPage()) {
2024-04-23 06:30:55 +00:00
msg = 'Failed to go to sign in page';
console.log(msg);
await chrome.storage.local.set({ "__status": msg });
2024-04-22 00:09:33 +00:00
isRunning = false;
return;
};
}
else if (isSignInPage()) {
2024-04-22 00:09:33 +00:00
// Prevent brute forcing
if (config.signinAttempts >= MAX_SIGNIN_ATTEMPTS) {
await chrome.storage.local.set({ "__status": "too many sign in attempts" });
isRunning = false;
return;
}
// Sign in
2024-04-23 06:30:55 +00:00
msg = 'Signing in attempt: ' + config.signinAttempts;
console.log(msg)
await chrome.storage.local.set({ "__status": msg });
2024-04-22 00:09:33 +00:00
let signedIn = await enterCredentials();
config.signinAttempts += 1;
await chrome.storage.local.set({ "__signinAttempts": config.signinAttempts });
if (!signedIn) {
2024-04-23 06:30:55 +00:00
msg = 'Failed to sign in';
console.log(msg);
await chrome.storage.local.set({ "__status": msg });
2024-04-22 00:09:33 +00:00
isRunning = false;
return;
};
}
else if (isDashboardPage()) {
2024-04-22 00:09:33 +00:00
// 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 });
// 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 });
2024-04-22 00:09:33 +00:00
} else {
2024-04-23 06:30:55 +00:00
msg = 'No appointments found';
console.log(msg);
await chrome.storage.local.set({ "__status": msg });
2024-04-22 00:09:33 +00:00
isRunning = false;
return;
}
}
// get current appointment date
2024-04-23 08:42:15 +00:00
let apptInfoCard = document.querySelector("p.consular-appt [href*='" + config.apptId + "']").parentNode.parentNode.parentNode
if (!apptInfoCard.querySelector("h4").innerText.match(/Attend Appointment/)) {
2024-04-23 06:30:55 +00:00
msg = 'Appointment not available';
console.log(msg);
await chrome.storage.local.set({ "__status": msg });
2024-04-22 00:09:33 +00:00
isRunning = false;
return;
}
2024-04-23 08:42:15 +00:00
let apptInfo = apptInfoCard.querySelector("p.consular-appt").innerText;
let apptConsulate = (apptInfo.match(/at (\w+)/) || [])[1];
let apptDate = new Date(apptInfo.match(/\d{1,2} \w+, \d{4}/)[0]);
2024-04-22 00:09:33 +00:00
apptDate = apptDate.toISOString().slice(0, 10);
2024-04-23 08:42:15 +00:00
if (apptDate && apptConsulate
&& (apptDate != config.currentAppt.date
|| apptConsulate != config.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 });
2024-04-22 00:09:33 +00:00
}
// go to appointment page
let apptLink = apptInfoCard.querySelector("p.consular-appt [href]")
.getAttribute("href").replace("/addresses/consulate", "/appointment");
2024-04-22 00:09:33 +00:00
window.location.href = apptLink;
await delay(PAGE_WAIT_TIME);
2024-04-21 04:03:26 +00:00
}
else if (isAppointmentPage()) {
2024-04-23 08:09:10 +00:00
// if no apptDate, fetch it from dashboard page
if (!config.currentAppt.date) {
2024-04-23 08:09:10 +00:00
console.log('No appointment date is set, going back to dashboard');
2024-04-26 05:44:54 +00:00
await goToDashboardPage();
2024-04-23 08:09:10 +00:00
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();
await delay(PAGE_WAIT_TIME);
isRunning = false;
return;
}
if (!document.getElementById("consulate_date_time")) {
2024-04-23 06:30:55 +00:00
msg = 'No available appointments';
console.log(msg);
await chrome.storage.local.set({ "__status": 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" });
isRunning = false;
return;
}
// TODO choose ASC facility
// 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);
if (selectedConsulates.length == 0) {
2024-04-23 06:30:55 +00:00
msg = 'No selected consulates found';
console.log(msg);
await chrome.storage.local.set({ "__status": msg });
isRunning = false;
return;
}
2024-04-26 05:49:46 +00:00
let processedConsulates = 0;
for (let consulate of selectedConsulates) {
2024-04-26 05:49:46 +00:00
// only one consulate per run
if (processedConsulates > 0) {
break;
}
// skip if not time to check
if (config.consulates[consulate].nextCheckAt > new Date().toISOString()) {
continue;
}
2024-04-26 05:49:46 +00:00
processedConsulates += 1;
2024-04-26 06:33:26 +00:00
msg = `Checking dates for ${consulate}`;
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));
if (!availDates) {
2024-04-23 06:30:55 +00:00
msg = `Failed to fetch available dates in ${consulate}`;
console.log(msg);
await chrome.storage.local.set({ "__status": msg });
continue;
}
// if empty list, either we're banned or non operational hours or dead consulate
// wait for some time before checking again
2024-04-26 06:14:38 +00:00
let now = new Date();
let currentHourUTC = now.getUTCHours();
let currentMinuteUTC = now.getUTCMinutes();
if (availDates.length == 0) {
2024-04-23 06:30:55 +00:00
msg = `No available dates in ${consulate}, probably banned`;
console.log(msg);
await chrome.storage.local.set({ "__status": msg });
2024-04-26 06:14:38 +00:00
// 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));
2024-04-26 06:14:38 +00:00
}
continue;
}
2024-04-25 02:46:05 +00:00
msg = `Available dates for ${consulate}: ${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];
}
// filter dates with our requests
let filteredDates = await filterDates(availDates, config.currentAppt.date, config.deltaAppt, config.deltaNow);
if (!filteredDates.length) {
2024-04-26 06:33:26 +00:00
console.log(`No better dates in ${consulate}, currently available ${availDates[0]}`);
await chrome.storage.local.set({ "__status": `No better dates in ${consulate}`});
continue;
}
console.log(`Dates worth rescheduling in ${consulate}: ${filteredDates}`);
let chosenDate = filteredDates[0];
await chrome.storage.local.set({ "__status": `Found in ${consulate} 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 = "<option></option>"
let availTimes = await getAvailableTimes(config.consulates[consulate].id, chosenDate);
if (!availTimes) {
2024-04-23 06:30:55 +00:00
msg = `Failed to fetch available timeslots in ${consulate} at ${chosenDate}`;
console.log(msg);
await chrome.storage.local.set({ "__status": msg});
continue;
}
if (availTimes.length == 0) {
2024-04-23 06:30:55 +00:00
msg = `No timeslots in ${consulate} at ${chosenDate}`;
console.log(msg);
await chrome.storage.local.set({ "__status": msg});
continue;
}
console.log(`Available timeslots in ${consulate} at ${chosenDate}: ${availTimes}`);
let chosenTime = availTimes[0];
// fill timeslot in reschedule form
await delay(PAGE_WAIT_TIME);
document.getElementById("appointments_consulate_appointment_time").innerHTML = `<option value='${chosenTime}'>${chosenTime}</option>`;
document.getElementById("appointments_consulate_appointment_time").value = chosenTime;
// TODO process ASC facilities here
await delay(PAGE_WAIT_TIME);
document.getElementById("appointments_submit").removeAttribute("disabled");
document.getElementById("appointments_submit").click();
2024-04-22 07:36:09 +00:00
2024-04-23 06:20:43 +00:00
msg = `Found better appointment in ${consulate} at ${chosenDate} ${chosenTime}`;
console.log(msg);
await sendNotification(msg);
2024-04-23 06:20:43 +00:00
2024-04-25 00:01:31 +00:00
if (!config.consulates[consulate].autobook) {
isFoundAppointment = true;
2024-04-25 00:01:31 +00:00
} else {
2024-04-23 06:20:43 +00:00
await delay(PAGE_WAIT_TIME);
2024-04-25 00:01:31 +00:00
msg = `Auto booking in ${consulate} at ${chosenDate} ${chosenTime}`;
console.log(msg);
await sendNotification(msg);
2024-04-23 06:20:43 +00:00
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}`);
}
}
await chrome.storage.local.set({ "__consulates": config.consulates });
isRunning = false;
return;
}
2024-04-20 05:14:53 +00:00
2024-04-22 07:36:09 +00:00
else if (isConfirmationPage) {
2024-04-26 05:44:54 +00:00
// go back to dashboard after successful reschedule
2024-04-22 07:36:09 +00:00
await delay(PAGE_WAIT_TIME);
config.currentAppt = { consulate: null, date: null};
await chrome.storage.local.set({"__currentAppt": config.currentAppt});
2024-04-23 08:09:10 +00:00
console.log('Rescheduled successfully');
// switch off autobook for all consulates after successful reschedule
for (let consulate in config.consulates) {
config.consulates[consulate].autobook = false;
}
2024-04-26 05:44:54 +00:00
await goToDashboardPage();
2024-04-22 07:36:09 +00:00
}
2024-04-21 06:50:49 +00:00
// console.log('runner done');
isRunning = false;
2024-04-20 05:14:53 +00:00
}
setInterval(runner, 1000);