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,186 +16,127 @@
// ==/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. // Invert colours for better ocr
if ( function invertColors(image) {
window.location.href.match( const canvas = document.createElement("canvas");
/https:\/\/(www.)?skillrack\.com\/faces\/candidate\/codeprogramgroup\.xhtml/gi const ctx = canvas.getContext("2d");
) canvas.width = image.width;
) { canvas.height = image.height;
if (sessionStorage.getItem("Solvebtnid")) ctx.drawImage(image, 0, 0);
sessionStorage.removeItem("Solvebtnid"); ctx.globalCompositeOperation = "difference";
if (sessionStorage.getItem("captchaFail")) ctx.fillStyle = "white";
sessionStorage.removeItem("captchaFail"); ctx.fillRect(0, 0, canvas.width, canvas.height);
} return canvas.toDataURL();
}
function onClick(event) { // Remove username from captcha
// Get solve button click function solveCaptcha(text) {
if ( const cleanedText = text.replace(new RegExp(USERNAME, "gi"), "").trim();
event.target.tagName === "SPAN" && const match = cleanedText.match(/(\d+)\s*\+\s*(\d+)/);
event.target.parentNode.tagName === "BUTTON" if (match) {
) { return parseInt(match[1], 10) + parseInt(match[2], 10);
if (event.target.textContent === "Solve") { }
// Store button id of problem solve button. else {
sessionStorage.setItem( handleIncorrrectCaptcha();
"Solvebtnid", return -1;
event.target.parentNode.id }
); }
}
}
}
document.addEventListener("click", onClick, false);
// Wait for window to load function handleCaptcha() {
window.addEventListener("load", function () { // Get the captcha
// Detect if last captcha attempt was a fail to re-nav back const captchaImageId = window.location.href.includes("tutorprogram") ? TUTOR_IMG : NON_TUTOR_IMG;
if (sessionStorage.getItem("captchaFail")) { const image = document.getElementById(captchaImageId);
console.log( const textbox = document.getElementById(CAPTCHA_ID);
"Detected captcha fail. Attempting to open last open page." const button = document.getElementById(PROCEED_BTN_ID);
); if (!image || !textbox || !button) {
// Reset captcha state console.log("Captcha or input elements not found.");
sessionStorage.removeItem("captchaFail"); return;
// 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"); const invertedimg = invertColors(image);
// 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
function invertColors(image) {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width = image.width;
canvas.height = image.height;
ctx.drawImage(image, 0, 0);
ctx.globalCompositeOperation = "difference";
ctx.fillStyle = "white";
ctx.fillRect(0, 0, canvas.width, canvas.height);
return canvas.toDataURL();
}
// Remove username from captcha
/**
*
* @param {string} text
* @returns
*/
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--;
for (i; i >= 0; i--) {
if (!("1234567890".includes(text[i]))) {
return text.slice(i + 1);
}
}
console.error("Error parsing username.");
return;
}
// Parse OCR result and solve the problem
function getNums(text) {
const a = text.replace(" ", "").replace("=", "").split("+");
return parseInt(a[0]) + parseInt(a[1]);
}
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; }
} textbox.value = result;
console.log( // Click the submit button
"Found math captcha. Auto-filling answer: ", button.click();
result
);
textbox.value = result;
// Click the submit button
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();
}); });
})(); })();