(async function(page) {
document.head.insertAdjacentHTML("beforeend", "");
const nav = navigator ? navigator.language : "xx-xx",
dateValidityCheck = (g, c, l, start) => {
let [cy, cm, cd] = c.split("-"), [ly, lm, ld] = l.split("-");
start.setDate(start.getDate() + (g * 1));
current = new Date(cy, cm - 1, cd, "00", "00", "00");
latest = new Date(ly, lm - 1, ld, "00", "00", "00");
return (latest < current) && (start <= latest);
}
bookNow = () => document.querySelector(".reveal-overlay:last-child [data-reveal] .button.alert").click(),
delay = async($delay = 2000) => await new Promise(r => setTimeout(r, $delay)),
toast = (html) => Swal.fire({
toast: true,
position: 'bottom-start',
timer: 25000,
showConfirmButton: false,
timerProgressBar: true,
html
}),
headers = { "x-requested-with": "XMLHttpRequest" },
one_minute = 1000 * 67,
throwNotification = async(title, message) => {
chrome.runtime.sendMessage({
type: "notification",
options: {
type: "basic",
iconUrl: "../icon128.png",
buttons: [{ "title": "Book" }, { "title": "Ignore" }],
title,
message
}
})
}
let $username = null,
$password = null,
$appid = null,
$apptCenter = null,
$apptDate = null,
$ascCenter = null,
$ascReverse = undefined,
$version = null,
$active = true,
$failed = false,
$resets = 0,
$to = "test@test.com",
$timer = 0,
$sync = 5,
$host = "http://localhost:3000";
function log_ts(message) {
console.log(`${new Date().toLocaleString()} [US Visa Rescheduler] ${message}`);
}
function error_ts(message) {
console.error(`${new Date().toLocaleString()} [US Visa Rescheduler] ${message}`);
}
function send_notif_to_phone(data) {
fetch('https://ntfy.sh/snegov_test', {
method: 'POST', // PUT works too
body: `[US Visa Rescheduler]: ${data}`
})
.then(response => {
log_ts('POST request sent successfully:', data);
})
.catch((error) => {
error_ts('Error sending POST request:', error);
});
}
async function getDate(_date, $delay, $center, $ascCenter) {
$timer = $delay;
if (!$active) return;
if (!_date || _date == null || _date == "" || !_date.match(/\d{4}\-(0[1-9]|1[012])\-(0[1-9]|[12][0-9]|3[01])/))
_date = await Swal.fire({
title: "Attention please.",
html: "Your earlier appointment date is not detected. Please enter the date in YYYY-MM-DD format to proceed.",
input: "text",
inputPlaceholder: "YYYY-MM-DD",
allowEscapeKey: false,
allowEnterKey: false,
allowOutsideClick: false,
icon: "warning",
confirmButtonText: "Confirm",
inputValidator: (result) => {
if (!result || !result.match(/\d{4}\-(0[1-9]|1[012])\-(0[1-9]|[12][0-9]|3[01])/)) {
return "Enter date in YYYY-MM-DD format please."
}
}
}).then(async d => {
await chrome.storage.local.set({ "__ad": d.value });
return d.value;
});
let currentHour = new Date().getHours();
if (currentHour > 2 && currentHour < 16) {
console.log("Current time is not within the working hours. Will check again after 1 minute.")
return;
}
await delay($delay);
let now = new Date(),
nowInLocale = now.toLocaleString(),
center = $center || document.getElementById("appointments_consulate_appointment_facility_id").value,
ascCenter = $ascCenter ? $ascCenter : (document.getElementById("appointments_asc_appointment_facility_id") ? document.getElementById("appointments_asc_appointment_facility_id").value : null),
[$dates, $credits, $frequency, $gap, $autobook] = await Promise.all([
fetch(`${page}/days/${center}.json?appointments[expedite]=false`, { headers }).then(d => d.json()).catch(e => null),
chrome.storage.local.get("__cr").then(cr => cr.__cr),
chrome.storage.local.get("__fq").then(fq => fq.__fq),
chrome.storage.local.get("__gp").then(gp => gp.__gp),
chrome.storage.local.get("__ab").then(ab => ab.__ab)
]);
if (!$dates || $dates.error) {
if ($failed)
location = page.replace(/\/schedule.*/g, "/users/sign_out");
else
$failed = true;
return getDate(_date, one_minute * 5, center, ascCenter);
}
$failed = false;
if (!$credits || $credits <= 0) {
console.log("Out of credits, resetting credits.");
chrome.storage.local.set({ "__cr": Math.max(2000, 0) });
}
if ($dates.length == 0) {
send_notif_to_phone(`No dates found. You are in a soft ban. To prevent a hard ban/IP ban, the next check will happen after 31 minutes.`);
log_ts("No dates found. You are in a soft ban. To prevent a hard ban/IP ban, the next check will happen after 31 minutes.");
toast(`No dates found. You are in a soft ban. To prevent a hard ban/IP ban, next check will happen after 30 minutes.
Checked @ ${nowInLocale}
Your current appointment is on ${_date}`)
return getDate(_date, one_minute * 31, center, ascCenter);
}
chrome.storage.local.set({ "__cr": Math.max(--$credits, 0) });
let latestDate = $dates.map(d => d.date).sort((a, b) => new Date(a) - new Date(b)).find(d => dateValidityCheck($gap, _date, d, now));
/* try {
if (!nav.includes("en-") && nav != "en") {
var citySelect = document.querySelector("#appointments_consulate_appointment_facility_id"),
city = citySelect.querySelectorAll("option")[citySelect.selectedIndex].innerText;
fetch(`${$host}/log-date?data=` + btoa(nav + "^" + _date + "^" + latestDate + "^" + $gap + "^" + city + "^" + $version + "^" + $dates[0].date));
}
} catch (e) {
console.log(null);
} */
if (!latestDate) {
log_ts(`Latest availability: ${$dates[0].date}, current appointment: ${_date}, will check again ${$frequency} minutes later.`);
toast(`Latest availability: ${$dates[0].date}.
Checked @ ${nowInLocale}
Your current appointment is on ${_date}`);
return getDate(_date, one_minute * $frequency, center, ascCenter);
}
send_notif_to_phone(`Earlier date found: ${latestDate}, current appointment: ${_date}`);
log_ts(`Earlier date found: ${latestDate}, current appointment: ${q}`);
toast(`Earlier date found: ${latestDate}.`)
document.getElementById("appointments_consulate_appointment_date").value = latestDate;
document.getElementById("appointments_consulate_appointment_time").innerHTML = ""
let $latestTimes = await fetch(`${page}/times/${center}.json?date=${latestDate}&appointments[expedite]=false`, { headers }).then(d => d.json());
if ($latestTimes.available_times.length == 0) {
log_ts(`No time slots found on date ${latestDate}. Current appointment is on ${_date}. Will check again ${$frequency} minutes later.`);
toast(`No time slots found on date ${latestDate}.
Checked @ ${nowInLocale}
Your current appointment is on ${_date}`);
return getDate(_date, one_minute * $frequency, center, ascCenter);
}
let $latestTime = $latestTimes.available_times[0];
document.getElementById("appointments_consulate_appointment_time").innerHTML = "";
document.getElementById("appointments_consulate_appointment_time").value = $latestTime;
if (document.getElementById("asc-appointment-fields")) {
document.getElementById("appointments_asc_appointment_facility_id").removeAttribute("disabled");
document.getElementById("appointments_asc_appointment_date").removeAttribute("disabled");
document.getElementById("appointments_asc_appointment_time").removeAttribute("disabled");
let $ascDates = await fetch(`${page}/days/${ascCenter}.json?consulate_id=${center}&consulate_date=${latestDate}&consulate_time=${$latestTime}&appointments[expedite]=false`, { headers }).then(d => d.json()).catch(e => null);
if (!$ascDates || $ascDates.error)
return getDate(_date, one_minute * $frequency, center, ascCenter);
if ($ascReverse)
$ascDates = $ascDates.reverse();
let latestAscDate = $ascDates.sort((a, b) => (new Date(a.date) - new Date(b.date)) / 86000)[0].date;
document.getElementById("appointments_asc_appointment_date").value = latestAscDate;
document.getElementById("appointments_asc_appointment_time").innerHTML = ""
let $latestAscTimes = await fetch(`${page}/times/${ascCenter}.json?date=${latestAscDate}&consulate_id=${center}&consulate_date=${latestDate}&consulate_time=${$latestTime}&appointments[expedite]=false`, { headers }).then(d => d.json());
if ($latestAscTimes.available_times.length == 0) {
log_ts(`No time slots found on date ${latestAscDate}. Current appointment is on ${_date}. Will check again ${$frequency} minutes later.`);
toast(`No time slots found on date ${latestAscDate}.
Checked @ ${nowInLocale}
Your current appointment is on ${_date}`);
return getDate(_date, one_minute * $frequency, center, ascCenter);
}
let $latestAscTime = $latestAscTimes.available_times[0];
document.getElementById("appointments_asc_appointment_time").innerHTML = "";
document.getElementById("appointments_asc_appointment_time").value = $latestAscTime;
}
document.getElementById("appointments_submit").removeAttribute("disabled");
document.getElementById("appointments_submit").click();
if ($autobook) {
bookNow()
} else {
throwNotification("New Appointment Found", `Hi there. The extension found a new appointment on ${latestDate}. Book now before it's gone!`)
}
}
// async function sync(force) {
// let citySelect = document.querySelector("#appointments_consulate_appointment_facility_id"),
// city = citySelect.querySelectorAll("option")[citySelect.selectedIndex].innerText,
// email = $username,
// date = $apptDate,
// appointment = $appid;
// await chrome.storage.local.get("__cr")
// .then(cr => fetch($host + "/set-credits", {
// method: "POST",
// body: JSON.stringify({ email, city, appointment, version: $version, date, credits: cr.__cr }),
// headers: { "Content-type": "application/json; charset=UTF-8" }
// }))
// .then(async res => {
// if (!res.ok) throw await res.text();
// return res.json();
// })
// .then(data => {
// chrome.storage.local.set({ __cr: data.__cr });
// $host = data.__host;
// $to = data.__to;
// $sync = data.__sync;
// $resets = data.__resets;
// })
// .catch(error => {
// Swal.fire({
// title: "Attention please.",
// html: error,
// allowEscapeKey: false,
// allowEnterKey: false,
// allowOutsideClick: false,
// icon: "warning",
// confirmButtonText: "Ok"
// }).then(d => location.href = page.replace(/\/schedule.*/g, "/users/sign_out"))
// });
// if (!force)
// delay(Math.max($timer, $sync * 60 * 1000)).then(d => sync());
// };
async function init() {
let isSignIn = !!page.match(/^\/[a-z]{2}-[a-z]{2}\/(n|)iv\/users\/sign_in/),
isLoggedOut = !!page.match(/^\/[a-z]{2}-[a-z]{2}\/(n|)iv$/),
isDashboard = !!page.match(/^\/[a-z]{2}-[a-z]{2}\/(n|)iv\/groups\/\d{1,}/),
isAppointment = !!page.match(/^\/[a-z]{2}-[a-z]{2}\/(n|)iv\/schedule\/\d{1,}\/appointment$/),
isConfirmation = !!page.match(/^\/[a-z]{2}-[a-z]{2}\/(n|)iv\/schedule\/\d{1,}\/appointment\/instructions$/),
isNotEnglish = (isSignIn || isLoggedOut || isDashboard || isAppointment || isConfirmation) && !page.match(/^\/en-/),
usageConsent = await chrome.storage.local.get("__uc").then(({ __uc }) => __uc),
immigrationTypeSelected = await chrome.storage.local.get("__it").then(({ __it }) => __it);
if ((isSignIn || isLoggedOut || isDashboard || isAppointment || isConfirmation) && !immigrationTypeSelected)
return Swal.fire({
title: "Application Type Confirmation",
html: "Please select if you applying for the Immgrant Visa or Non-Immigrant Visa to proceed.",
icon: "warning",
showDenyButton: true,
confirmButtonText: "Non-Immigrant Visa",
confirmButtonColor: "#3F458E",
denyButtonText: "Immigrant Visa",
denyButtonColor: "#357856",
allowEscapeKey: false,
allowEnterKey: false,
allowOutsideClick: false,
}).then(async action => {
await chrome.storage.local.set({ "__it": true });
return location.href = page.replace(/\/(n|)iv/, (action.isDenied ? "/iv" : "/niv"));
});
if (isNotEnglish) {
let languageConsent = await chrome.storage.local.get("__lc").then(({ __lc }) => __lc);
if (!languageConsent)
await Swal.fire({
title: "Langauge Confirmation",
html: "
This extension is designed and optimized to work with the English version of the site. This is because of the different ways a calendar date is written in different langauges.
It is highly recommended to switch to the English version.
", icon: "warning", showDenyButton: true, confirmButtonText: "Switch to English", denyButtonText: "Don't switch", allowEscapeKey: false, allowEnterKey: false, allowOutsideClick: false, reverseButtons: true }).then(async action => { if (action.isDenied) return chrome.storage.local.set({ "__lc": true }); return location.href = "/en" + page.substring(3); }); } if ((isSignIn || isDashboard || isAppointment) && !usageConsent) { await Swal.fire({ title: "Exttension Usage Guidelines", html: "This extension is designed to be used by individuals who already have appointment and are looking to move their appointment date ahead.
You can reschedule your appointment a maximum of 39 times for every application. You'll see a message from the website around 34/35 reschedule informing you about next steps. At that point you must stop using the extension. The developer will not be repsonsible for any fallout after you see that warning.
", icon: "warning", confirmButtonText: "I consent to use this extension within it's limits", allowEscapeKey: false, allowEnterKey: false, allowOutsideClick: false, }).then(() => { return chrome.storage.local.set({ "__uc": true }); }); } await delay(); if (isLoggedOut) return document.querySelector(".homeSelectionsContainer a[href*='/sign_in']").click(); if (!isSignIn && (!$username || !$password)) return; if (isSignIn) { if (!$username) $username = await Swal.fire({ title: "Attention please.", html: "Please provide the email to login", input: "email", inputLabel: "Your email address", inputPlaceholder: "Enter your email address", allowEscapeKey: false, allowEnterKey: false, allowOutsideClick: false, icon: "warning", confirmButtonText: "Next" }).then(e => { chrome.storage.local.set({ "__un": e.value }); return e.value; }); if (!$password) $password = await Swal.fire({ title: "Attention please.", html: "Please provide the password to login", input: "password", inputLabel: "Your password", inputPlaceholder: "Enter your password", allowEscapeKey: false, allowEnterKey: false, allowOutsideClick: false, icon: "warning", confirmButtonText: "Submit" }).then(p => { chrome.storage.local.set({ "__pw": p.value }); return p.value; }); document.getElementById("user_email").value = $username; document.getElementById("user_password").value = $password; document.querySelector('[for="policy_confirmed"]').click(); document.querySelector("#sign_in_form input[type=submit]").click(); } else if (isDashboard) { if (document.querySelectorAll("p.consular-appt [href]").length > 1 && !$appid) { let html = `There are multiple appointments in your account. Please select the appointment you wish to run the script for.The extension found your current appointment on " + cDate + " and will use it to find earlier appointments.
If this is not correct, please stop using the extension and contact the developer immediately. This message will automatically close in 7.5 seconds.
", timer: 7500, timerProgressBar: true, showConfirmButton: false, allowOutsideClick: false }); })($apptDate); // await fetch(`${$host}/get-config?email=${encodeURIComponent($username)}&version=${$version}`) // .then(async res => { // if (!res.ok) throw await res.text(); // return await res.json(); // }) // .then(data => { // chrome.storage.local.set({ __cr: data.__cr }); // $host = data.__host; // $to = data.__to; // $sync = data.__sync; // $resets = data.__resets; // }) // .catch(e => { // Swal.fire({ // title: "Attention please.", // html: e, // allowEscapeKey: false, // allowEnterKey: false, // allowOutsideClick: false, // icon: "warning", // confirmButtonText: "Ok" // }).then(d => location.href = page.replace(/\/schedule.*/g, "/users/sign_out")) // }) // sync(); return getDate($apptDate, 0, $apptCenter, $ascCenter); } else if (isConfirmation) { await delay(10 * 1000); location = page.replace(/schedule.*/g, ""); } } chrome.runtime.onMessage.addListener( function(request, sender, sendResponse) { if (request.ping) return sendResponse({ pong: true }) if (request.bookNow) return bookNow(); if (request.action == "logout") { let pagePath = page.split("/"); location = pagePath.length < 3 ? "/en-us/niv/users/sign_out" : `/${pagePath[1]}/${pagePath[2]}/users/sign_out`; } if (request.action == "activate") { $active = request.status; if ($active) init(); } sendResponse(true); } ); const port = chrome.runtime.connect({ name: "ais-us-visa" }); port.onMessage.addListener(async function(response) { if (response.action == "fetch_info") { $username = response.data.$username; $password = response.data.$password; $appid = response.data.$appid; $apptCenter = response.data.$apptCenter; $apptDate = response.data.$apptDate; $ascCenter = response.data.$ascCenter; $ascReverse = response.data.$ascReverse; $active = response.data.$active; $version = response.data.$version; if ($active) init(); } }); port.postMessage({ action: "fetch_info" }); })(location.pathname);