From 807dece8b3d8f897254df926eed15562941ed37a Mon Sep 17 00:00:00 2001 From: tretrauit Date: Fri, 17 May 2024 00:36:55 +0700 Subject: [PATCH] feat: tsaplus.user.js --- userscripts/tsaplus.user.js | 188 ++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 userscripts/tsaplus.user.js diff --git a/userscripts/tsaplus.user.js b/userscripts/tsaplus.user.js new file mode 100644 index 0000000..18010b3 --- /dev/null +++ b/userscripts/tsaplus.user.js @@ -0,0 +1,188 @@ +// ==UserScript== +// @name TSA+ +// @namespace Violentmonkey Scripts +// @match https://tsa.hust.edu.vn/dang-ky-thi* +// @namespace tretrauit-dev +// @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 +// ==/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]); + } + } +});