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
**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
const USERNAME = "bch12xyz232@abcd";
const USERNAME = "abcd123+21@xyz";
```
and save the script.

View file

@ -7,7 +7,7 @@
// ==UserScript==
// @name 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
// @author adithyagenie
// @license AGPL-3.0-or-later
@ -16,186 +16,127 @@
// ==/UserScript==
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 () {
"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");
}
// 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();
}
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);
// Remove username from captcha
function solveCaptcha(text) {
const cleanedText = text.replace(new RegExp(USERNAME, "gi"), "").trim();
const match = cleanedText.match(/(\d+)\s*\+\s*(\d+)/);
if (match) {
return parseInt(match[1], 10) + parseInt(match[2], 10);
}
else {
handleIncorrrectCaptcha();
return -1;
}
}
// 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;
}
function handleCaptcha() {
// Get the captcha
const captchaImageId = window.location.href.includes("tutorprogram") ? TUTOR_IMG : NON_TUTOR_IMG;
const image = document.getElementById(captchaImageId);
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;
}
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
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.`);
const invertedimg = invertColors(image);
// Image Processing with Tesseract.js
Tesseract.recognize(invertedimg, "eng", {
whitelist: "1234567890+=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@ ",
psm: 6,
})
.then(({ data: { text } }) => {
console.log(`OCR: ${new Date().getTime() - time} ms. Result: ${text}`);
console.log(`OCR: Result: ${text}`);
// Solve the Math Problem
try {
const mathprob = removeText(text);
const result = getNums(mathprob);
if (isNaN(result)) {
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;
}
console.log(
"Found math captcha. Auto-filling answer: ",
result
);
textbox.value = result;
// Click the submit button
button.click();
console.log(`Took ${new Date().getTime() - time} ms.`);
} catch (e) {
console.error(e);
}
const result = solveCaptcha(text);
if (result == -1) return;
else if (result === null) {
alert(`Unable to solve math captcha...`)
return;
}
textbox.value = result;
// Click the submit button
button.click();
return;
})
.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();
});
})();