Improve performance, usability and code readability.
Signed-off-by: adithyagenie <adithyagenie@gmail.com> Fix infinite looping
This commit is contained in:
parent
c530eadda9
commit
07c4e59d5e
2 changed files with 113 additions and 170 deletions
14
README.md
14
README.md
|
@ -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.
|
269
userscript.js
269
userscript.js
|
@ -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();
|
||||
});
|
||||
})();
|
||||
|
|
Loading…
Reference in a new issue