scripts/userscripts/tsaplus.user.js

192 lines
6.9 KiB
JavaScript

// ==UserScript==
// @name TSA+
// @match https://tsa.hust.edu.vn/dang-ky-thi*
// @namespace tretrauit-dev
// @icon https://tsa.hust.edu.vn/static/media/logo-square.d162284b.svg
// @grant none
// @version 1.0
// @author tretrauit
// @description 18:56:28 16/5/2024
// @require https://raw.githubusercontent.com/naugtur/insertionQuery/master/insQ.min.js
// @homepageURL https://gitlab.com/tretrauit/scripts
// @supportURL https://gitlab.com/tretrauit/scripts/-/issues
// @downloadURL https://git.tretrauit.me/tretrauit/scripts/raw/branch/main/userscripts/tsaplus.user.js
// ==/UserScript==
const sleep = ms => new Promise(r => setTimeout(r, ms));
function getElementByInnerText(element, text) {
return Array.from(element.querySelectorAll("*")).find((e) => e.innerText === text);
}
// Replace console functions with our own.
// biome-ignore lint/complexity/noStaticOnlyClass: console replacement (in SessionStorage) since TSA disabled it fuck.
class console {
static log(...args) {
const log = sessionStorage.getItem("tsaplus.console.log");
if (log) {
sessionStorage.setItem("tsaplus.console.log", `${log}\n${args.toString()}`);
} else {
sessionStorage.setItem("tsaplus.console.log", args.toString());
}
}
static info(...args) {
const log = sessionStorage.getItem("tsaplus.console.info");
if (log) {
sessionStorage.setItem("tsaplus.console.info", `${log}\n${args.toString()}`);
} else {
sessionStorage.setItem("tsaplus.console.info", args.toString());
}
}
static warn(...args) {
const log = sessionStorage.getItem("tsaplus.console.warn");
if (log) {
sessionStorage.setItem("tsaplus.console.warn", `${log}\n${args.toString()}`);
} else {
sessionStorage.setItem("tsaplus.console.warn", args.toString());
}
}
static error(...args) {
const log = sessionStorage.getItem("tsaplus.console.error");
if (log) {
sessionStorage.setItem("tsaplus.console.error", `${log}\n${args.toString()}`);
} else {
sessionStorage.setItem("tsaplus.console.error", args.toString());
}
}
}
unsafeWindow.console = console;
// Hijack XHR with ours.
let lastExamDetail = null;
const OrigXHR = unsafeWindow.XMLHttpRequest;
function xhrResponseCallback(xhr) {
if (xhr.responseURL.includes("https://api-hust.khaothi.online/my/apiv1/exams/get-exam-plan-detail")) {
console.log(`Set lastExamDetail to ${xhr.responseURL}`);
lastExamDetail = xhr.response;
}
}
class XMLHttpRequest extends OrigXHR {
constructor(...args) {
super(...args);
// alert("XHR created");
this.onload = () => {
xhrResponseCallback(this);
// alert("XHR loaded");
};
}
send(...args) {
super.send(...args);
}
}
unsafeWindow.XMLHttpRequest = XMLHttpRequest;
insertionQ(".ant-modal-root").every(async (_) => {
let retry = 0;
while (retry < 50 && lastExamDetail === null) {
retry += 1;
await sleep(100);
if (retry >= 50) {
return;
}
}
console.log("Found lastExamDetail");
const examDetail = JSON.parse(lastExamDetail);
lastExamDetail = null;
const examId = examDetail.data.exam.id;
const rsp = await fetch(`https://api-hust.khaothi.online/my/apiv1/exam-plan/get-test-sites?exam_id=${examId}`, {
headers: {
Authorization: `Bearer ${localStorage.getItem("authen_access_token")}`
}
})
const data = await rsp.json();
const [element, elementType] = await Promise.any([(async () => {
let element;
do {
await sleep(10);
element = getElementByInnerText(document, "Thông tin đăng ký");
retry += 1;
if (retry >= 100) {
return [null, null];
}
}
while (element === undefined || element.parentElement === undefined);
return [element, "register"];
})(), (async () => {
let element;
do {
await sleep(10);
element = getElementByInnerText(document, "Điểm TSA");
retry += 1;
if (retry >= 100) {
return [null, null];
}
}
while (element === undefined || element.parentElement === undefined);
return [element, "score"];
})()])
const waveInfo = {};
const genericInfo = {
totalContestants: 0,
totalCapacity: 0
};
switch (elementType) {
case "register":
firstCol = element.parentElement.parentElement.children[0];
break;
case "score":
firstCol = element.parentElement.parentElement.parentElement.parentElement;
break;
case null:
return;
}
for (const testSite of data.data.test_sites) {
if (testSite.name.startsWith("Đợt")) {
const wave = testSite.name.split(" ")[1];
if (!waveInfo[wave]) {
waveInfo[wave] = {
totalContestants: 0,
totalCapacity: 0
}
}
waveInfo[wave].totalContestants += testSite.count_test_taker;
if (testSite.max_test_taker === 0) {
waveInfo[wave].totalCapacity += testSite.count_test_taker;
} else {
waveInfo[wave].totalCapacity += testSite.max_test_taker;
}
} else {
genericInfo.totalContestants += testSite.count_test_taker;
if (testSite.max_test_taker === 0) {
genericInfo.totalCapacity += testSite.count_test_taker;
} else {
genericInfo.totalCapacity += testSite.max_test_taker;
}
}
}
console.log(`Wave info: ${JSON.stringify(waveInfo)}`);
console.log(`Generic info: ${JSON.stringify(genericInfo)}`);
if (Object.keys(waveInfo).length > 0) {
for (const [waveName, wave] of Object.entries(waveInfo)) {
console.log("Adding wave info");
const pElement = document.createElement("p");
pElement.innerHTML = `Số thí sinh đăng ký đợt ${waveName}: ${wave.totalContestants}/${wave.totalCapacity}`;
if (elementType === "register") {
firstCol.insertBefore(pElement, firstCol.children[1]);
} else {
firstCol.insertBefore(pElement, firstCol.children[0]);
}
}
} else {
console.log("Adding generic info");
const pElement = document.createElement("p");
pElement.innerHTML = `Số thí sinh đăng ký: ${genericInfo.totalContestants}/${genericInfo.totalCapacity}`;
if (elementType === "register") {
firstCol.insertBefore(pElement, firstCol.children[1]);
} else {
firstCol.insertBefore(pElement, firstCol.children[0]);
}
}
});