Improve performance, usability and code readability.

Signed-off-by: adithyagenie <adithyagenie@gmail.com>

Fix infinite looping
This commit is contained in:
adithyagenie 2024-03-08 01:28:04 +05:30
parent c530eadda9
commit 07c4e59d5e
Signed by: adithyagenie
GPG key ID: C66E41599E458D96
2 changed files with 113 additions and 170 deletions

View file

@ -12,17 +12,19 @@ Use GreaseMonkey/TamperMonkey to run the script.
### Optional username parsing ### Optional username parsing
**You don't need to do this unless you see a warning asking you to do so.** **You don't need to do this unless you have a username containing '+' and numbers together.**
The script handles most of the username checking by itself, but if it fails to do so and gives an alert instead, replace the `USERNAME` variable on the top of the script from the string the alert gives. The script handles most of the username checking by itself except when the username might contain special symbols which might mimic the captcha. You can fix this by replacing the `USERNAME` variable on the top of the script to match your username from the captcha image.
For example, if the alert says For example, if the captcha is:
``` ```
STRING RECOGNISED: bch12xyz232@abcd 123+456= abcd123+21@xyz
420+69=
``` ```
change the first line as change the first line (by visiting the greasemonkey/tampermonkey dashboard) as
```js ```js
const USERNAME = "bch12xyz232@abcd"; const USERNAME = "abcd123+21@xyz";
``` ```
and save the script. and save the script.

View file

@ -7,7 +7,7 @@
// ==UserScript== // ==UserScript==
// @name Skillrack Captcha Solver // @name Skillrack Captcha Solver
// @namespace https://github.com/adithyagenie/skillrack-captcha-solver // @namespace https://github.com/adithyagenie/skillrack-captcha-solver
// @version 0.6 // @version 0.7
// @description Solves math captcha in SkillRack using Tesseract.js // @description Solves math captcha in SkillRack using Tesseract.js
// @author adithyagenie // @author adithyagenie
// @license AGPL-3.0-or-later // @license AGPL-3.0-or-later
@ -16,108 +16,17 @@
// ==/UserScript== // ==/UserScript==
const USERNAME = ""; const USERNAME = "";
const TUTOR = /https:\/\/(www.)?skillrack\.com\/faces\/candidate\/tutorprogram\.xhtml/gi;
const ERROR = "ui-growl-item";
const TUTOR_IMG = "j_id_5o";
const NON_TUTOR_IMG = "j_id_6x";
const BACK_BTN = "j_id_5s";
const CAPTCHA_ID = "capval";
const PROCEED_BTN_ID = "proceedbtn";
(function () { (function () {
"use strict"; "use strict";
// Clear all sessionstorage data if going back out of solve.
if (
window.location.href.match(
/https:\/\/(www.)?skillrack\.com\/faces\/candidate\/codeprogramgroup\.xhtml/gi
)
) {
if (sessionStorage.getItem("Solvebtnid"))
sessionStorage.removeItem("Solvebtnid");
if (sessionStorage.getItem("captchaFail"))
sessionStorage.removeItem("captchaFail");
}
function onClick(event) {
// Get solve button click
if (
event.target.tagName === "SPAN" &&
event.target.parentNode.tagName === "BUTTON"
) {
if (event.target.textContent === "Solve") {
// Store button id of problem solve button.
sessionStorage.setItem(
"Solvebtnid",
event.target.parentNode.id
);
}
}
}
document.addEventListener("click", onClick, false);
// Wait for window to load
window.addEventListener("load", function () {
// Detect if last captcha attempt was a fail to re-nav back
if (sessionStorage.getItem("captchaFail")) {
console.log(
"Detected captcha fail. Attempting to open last open page."
);
// Reset captcha state
sessionStorage.removeItem("captchaFail");
// Get old button id
const old = sessionStorage.getItem("Solvebtnid");
if (old) {
const oldbutt = document.getElementById(old);
if (oldbutt) oldbutt.click();
}
return;
}
console.log("Checking for captchas");
// Get the captcha
// Different image ids for tutorial and track websites
let image;
if (
window.location.href.match(
/https:\/\/(www.)?skillrack\.com\/faces\/candidate\/codeprogram\.xhtml/gi
)
)
image = document.getElementById("j_id_6x");
else if (
window.location.href.match(
/https:\/\/(www.)?skillrack\.com\/faces\/candidate\/tutorprogram\.xhtml/gi
)
)
image = document.getElementById("j_id_5o");
const textbox = document.getElementById("capval");
const button = document.getElementById("proceedbtn");
if (image == null) {
console.log("Captcha not found.");
return;
}
// Check if past captcha submission was a failure
const errors = document.getElementsByClassName("ui-growl-item");
if (errors.length > 0) {
if (errors[0].textContent.includes("Incorrect Captcha")) {
if (
window.location.href.match(
/https:\/\/(www.)?skillrack\.com\/faces\/candidate\/tutorprogram\.xhtml/gi
)
) {
alert("Unable to solve captcha :(");
return;
}
sessionStorage.setItem("captchaFail", "true");
console.log("Detected failed attempt at solving captcha");
const back = document.getElementById("j_id_5s");
back.click();
return;
}
}
// Get time for logging
const time = new Date().getTime();
// Clear captcha state
if (sessionStorage.getItem("captchaFail")) {
sessionStorage.removeItem("captchaFail");
}
// Invert colours for better ocr // Invert colours for better ocr
function invertColors(image) { function invertColors(image) {
const canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
@ -132,70 +41,102 @@ const USERNAME = "";
} }
// Remove username from captcha // Remove username from captcha
/** function solveCaptcha(text) {
* const cleanedText = text.replace(new RegExp(USERNAME, "gi"), "").trim();
* @param {string} text const match = cleanedText.match(/(\d+)\s*\+\s*(\d+)/);
* @returns if (match) {
*/ return parseInt(match[1], 10) + parseInt(match[2], 10);
function removeText(text) {
text = text.replace(USERNAME, "");
let i = text.length - 1;
i = text.lastIndexOf("+");
if (i == -1) {
alert("Oops! I wasn't able to parse the captcha correctly :( Please enter the captcha or try re-visitng again!");
console.error("Error parsing username.");
return;
} }
i--; else {
for (i; i >= 0; i--) { handleIncorrrectCaptcha();
if (!("1234567890".includes(text[i]))) { return -1;
return text.slice(i + 1);
} }
} }
console.error("Error parsing username.");
return;
}
function handleCaptcha() {
// Parse OCR result and solve the problem // Get the captcha
function getNums(text) { const captchaImageId = window.location.href.includes("tutorprogram") ? TUTOR_IMG : NON_TUTOR_IMG;
const a = text.replace(" ", "").replace("=", "").split("+"); const image = document.getElementById(captchaImageId);
return parseInt(a[0]) + parseInt(a[1]); const textbox = document.getElementById(CAPTCHA_ID);
const button = document.getElementById(PROCEED_BTN_ID);
if (!image || !textbox || !button) {
console.log("Captcha or input elements not found.");
return;
} }
const invertedimg = invertColors(image); const invertedimg = invertColors(image);
console.log(`Converting image: ${new Date().getTime() - time} ms.`);
// Image Processing with Tesseract.js // Image Processing with Tesseract.js
Tesseract.recognize(invertedimg, "eng", { Tesseract.recognize(invertedimg, "eng", {
whitelist: "1234567890+=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@ ", whitelist: "1234567890+=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@ ",
psm: 6, psm: 6,
}) })
.then(({ data: { text } }) => { .then(({ data: { text } }) => {
console.log(`OCR: ${new Date().getTime() - time} ms. Result: ${text}`); console.log(`OCR: Result: ${text}`);
// Solve the Math Problem // Solve the Math Problem
try { const result = solveCaptcha(text);
const mathprob = removeText(text); if (result == -1) return;
const result = getNums(mathprob); else if (result === null) {
if (isNaN(result)) { alert(`Unable to solve math captcha...`)
alert(`Unable to solve math captcha... Check the readme file on https://github.com/adithyagenie/skillrack-captcha-solver for instructions on optional username parsing.\n\nSTRING RECOGNISED: ${text}`)
return; return;
} }
console.log(
"Found math captcha. Auto-filling answer: ",
result
);
textbox.value = result; textbox.value = result;
// Click the submit button // Click the submit button
button.click(); button.click();
console.log(`Took ${new Date().getTime() - time} ms.`);
} catch (e) {
console.error(e);
}
return; return;
}) })
.catch((error) => { .catch((error) => {
console.error("Error processing captcha:", error); alert("Error processing captcha:", error);
}); });
}
function handleIncorrrectCaptcha() {
// If in tutorial pages, can't go back.
if (
window.location.href.match(TUTOR)
) {
alert("Unable to solve captcha :(");
let captext = prompt("Captcha:");
if (captext == null) return;
const result = solveCaptcha(captext);
const textbox = document.getElementById(CAPTCHA_ID);
const button = document.getElementById(PROCEED_BTN_ID);
textbox.value = result;
button.click();
return;
}
sessionStorage.setItem("captchaFail", "true");
document.getElementById(BACK_BTN)?.click();
return;
}
document.addEventListener("click", (event) => {
if (
event.target.tagName === "SPAN" && event.target.parentNode.tagName === "BUTTON" && event.target.textContent === "Solve"
) {
// Store button id of problem solve button.
sessionStorage.setItem("Solvebtnid", event.target.parentNode.id);
}
}, false);
// Wait for window to load
window.addEventListener("load", function () {
// Detect if last captcha attempt was a fail to re-nav back
if (sessionStorage.getItem("captchaFail")) {
// Reset captcha state
sessionStorage.removeItem("captchaFail");
// Get old button id
const old = sessionStorage.getItem("Solvebtnid");
if (old) {
const oldbutt = document.getElementById(old);
oldbutt?.click();
}
return;
}
const errors = document.getElementsByClassName(ERROR);
if (errors.length > 0 && errors[0].textContent.includes("Incorrect Captcha")) {
handleIncorrrectCaptcha();
}
handleCaptcha();
}); });
})(); })();