From 07c4e59d5e7929cd8de89e96e33b439ae16232c3 Mon Sep 17 00:00:00 2001 From: adithyagenie Date: Fri, 8 Mar 2024 01:28:04 +0530 Subject: [PATCH] Improve performance, usability and code readability. Signed-off-by: adithyagenie Fix infinite looping --- README.md | 14 +-- userscript.js | 269 ++++++++++++++++++++------------------------------ 2 files changed, 113 insertions(+), 170 deletions(-) diff --git a/README.md b/README.md index 702eb2e..94412a7 100644 --- a/README.md +++ b/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. \ No newline at end of file diff --git a/userscript.js b/userscript.js index 8138009..ef7f555 100644 --- a/userscript.js +++ b/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(); }); })();