2023-08-18 15:47:36 +00:00
//
// Copyright © 2023 adithyagenie
//
// SPDX-License-Identifier: AGPL-3.0-or-later
//
2023-08-18 15:34:25 +00:00
// ==UserScript==
2023-09-02 15:29:41 +00:00
// @name Skillrack Captcha Solver
2023-08-18 15:34:25 +00:00
// @namespace https://github.com/adithyagenie/skillrack-captcha-solver
2023-10-22 05:29:48 +00:00
// @version 0.5
2023-08-18 15:34:25 +00:00
// @description Solves math captcha in SkillRack using Tesseract.js
// @author adithyagenie
2023-10-02 15:33:52 +00:00
// @license AGPL-3.0-or-later
2023-09-02 13:44:44 +00:00
// @include https://www.skillrack.com/faces/candidate/codeprogram.xhtml
// @include https://www.skillrack.com/faces/candidate/tutorprogram.xhtml
// @include https://www.skillrack.com/faces/candidate/codeprogramgroup.xhtml
2023-10-22 05:29:48 +00:00
// @require https://cdn.jsdelivr.net/npm/tesseract.js@5.0.2/dist/tesseract.min.js
2023-08-18 15:34:25 +00:00
// ==/UserScript==
2023-10-22 05:29:48 +00:00
const USERNAME = "" ;
2023-08-18 15:34:25 +00:00
( function ( ) {
2023-09-02 13:44:44 +00:00
"use strict" ;
2023-09-02 13:59:40 +00:00
// Clear all sessionstorage data if going back out of solve.
2023-09-02 13:44:44 +00:00
if (
window . location . href ==
"https://www.skillrack.com/faces/candidate/codeprogramgroup.xhtml"
) {
2023-09-02 13:59:40 +00:00
if ( sessionStorage . getItem ( "Solvebtnid" ) )
sessionStorage . removeItem ( "Solvebtnid" ) ;
if ( sessionStorage . getItem ( "captchaFail" ) )
sessionStorage . removeItem ( "captchaFail" ) ;
2023-09-02 13:44:44 +00:00
}
2023-09-02 13:59:40 +00:00
2023-09-02 13:44:44 +00:00
function onClick ( event ) {
2023-09-02 13:59:40 +00:00
// Get solve button click
2023-09-02 13:44:44 +00:00
if (
event . target . tagName === "SPAN" &&
event . target . parentNode . tagName === "BUTTON"
) {
if ( event . target . textContent === "Solve" ) {
2023-09-02 13:59:40 +00:00
// Store button id of problem solve button.
sessionStorage . setItem (
"Solvebtnid" ,
event . target . parentNode . id
) ;
2023-09-02 13:44:44 +00:00
}
}
}
document . addEventListener ( "click" , onClick , false ) ;
2023-08-18 15:34:25 +00:00
// Wait for window to load
window . addEventListener ( "load" , function ( ) {
2023-09-02 13:59:40 +00:00
// Detect if last captcha attempt was a fail to re-nav back
if ( sessionStorage . getItem ( "captchaFail" ) ) {
2023-09-02 13:44:44 +00:00
console . log (
2023-09-02 13:59:40 +00:00
"Detected captcha fail. Attempting to open last open page."
2023-09-02 13:44:44 +00:00
) ;
2023-09-02 13:59:40 +00:00
// Reset captcha state
sessionStorage . removeItem ( "captchaFail" ) ;
// Get old button id
const old = sessionStorage . getItem ( "Solvebtnid" ) ;
2023-09-02 13:44:44 +00:00
if ( old ) {
const oldbutt = document . getElementById ( old ) ;
if ( oldbutt ) oldbutt . click ( ) ;
}
return ;
}
2023-08-18 15:34:25 +00:00
console . log ( "Checking for captchas" ) ;
2023-09-02 13:59:40 +00:00
// Get the captcha
// Different image ids for tutorial and track websites
2023-09-02 13:44:44 +00:00
let image ;
if (
window . location . href ==
"https://www.skillrack.com/faces/candidate/codeprogram.xhtml"
)
2023-10-22 05:29:48 +00:00
image = document . getElementById ( "j_id_6x" ) ;
2023-09-02 13:44:44 +00:00
else if (
window . location . href ==
"https://www.skillrack.com/faces/candidate/tutorprogram.xhtml"
)
2023-10-22 05:29:48 +00:00
image = document . getElementById ( "j_id_5o" ) ;
2023-09-02 13:44:44 +00:00
2023-08-18 15:34:25 +00:00
const textbox = document . getElementById ( "capval" ) ;
const button = document . getElementById ( "proceedbtn" ) ;
if ( image == null ) {
2023-09-02 13:44:44 +00:00
console . log ( "Captcha not found." ) ;
2023-08-18 15:34:25 +00:00
return ;
}
2023-09-02 13:44:44 +00:00
2023-09-02 13:59:40 +00:00
// Check if past captcha submission was a failure
2023-09-02 13:44:44 +00:00
const errors = document . getElementsByClassName ( "ui-growl-item" ) ;
if ( errors . length > 0 ) {
if ( errors [ 0 ] . textContent . includes ( "Incorrect Captcha" ) ) {
if (
window . location . href ==
"https://www.skillrack.com/faces/candidate/tutorprogram.xhtml"
) {
alert ( "Unable to solve captcha :(" ) ;
return ;
}
2023-09-02 13:59:40 +00:00
sessionStorage . setItem ( "captchaFail" , "true" ) ;
2023-09-02 13:44:44 +00:00
console . log ( "Detected failed attempt at solving captcha" ) ;
2023-10-22 05:29:48 +00:00
const back = document . getElementById ( "j_id_5s" ) ;
2023-09-02 13:44:44 +00:00
back . click ( ) ;
return ;
}
}
2023-09-02 13:59:40 +00:00
// Get time for logging
2023-08-20 15:41:53 +00:00
const time = new Date ( ) . getTime ( ) ;
2023-09-02 13:59:40 +00:00
// Clear captcha state
if ( sessionStorage . getItem ( "captchaFail" ) ) {
sessionStorage . removeItem ( "captchaFail" ) ;
2023-09-02 13:44:44 +00:00
}
2023-08-18 15:34:25 +00:00
// Invert colours for better ocr
2023-08-20 15:41:53 +00:00
function invertColors ( image ) {
2023-08-18 15:34:25 +00:00
const canvas = document . createElement ( "canvas" ) ;
const ctx = canvas . getContext ( "2d" ) ;
2023-08-20 15:41:53 +00:00
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 ( ) ;
2023-08-18 15:34:25 +00:00
}
2023-10-22 05:29:48 +00:00
// 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 ) {
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 ;
}
2023-08-18 15:34:25 +00:00
2023-09-02 13:59:40 +00:00
// Parse OCR result and solve the problem
2023-08-18 15:34:25 +00:00
function getNums ( text ) {
2023-08-20 15:41:53 +00:00
const a = text . replace ( " " , "" ) . replace ( "=" , "" ) . split ( "+" ) ;
return parseInt ( a [ 0 ] ) + parseInt ( a [ 1 ] ) ;
2023-08-18 15:34:25 +00:00
}
2023-08-20 15:41:53 +00:00
const invertedimg = invertColors ( image ) ;
console . log ( ` Converting image: ${ new Date ( ) . getTime ( ) - time } ms. ` ) ;
2023-09-02 13:59:40 +00:00
// Image Processing with Tesseract.js
2023-08-20 15:41:53 +00:00
Tesseract . recognize ( invertedimg , "eng" , {
2023-10-22 05:29:48 +00:00
whitelist : "1234567890+=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@ " ,
2023-08-20 15:41:53 +00:00
psm : 7 ,
} )
2023-08-18 15:34:25 +00:00
. then ( ( { data : { text } } ) => {
2023-08-20 15:41:53 +00:00
console . log ( ` OCR: ${ new Date ( ) . getTime ( ) - time } ms. ` ) ;
2023-09-02 13:59:40 +00:00
// Solve the Math Problem
2023-08-20 15:41:53 +00:00
try {
2023-10-22 05:29:48 +00:00
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 \n STRING RECOGNISED: ${ text } ` )
return ;
}
2023-08-18 15:34:25 +00:00
console . log (
2023-08-20 15:41:53 +00:00
"Found math captcha. Auto-filling answer: " ,
result
2023-08-18 15:34:25 +00:00
) ;
textbox . value = result ;
2023-09-02 13:59:40 +00:00
// Click the submit button
2023-08-18 15:34:25 +00:00
button . click ( ) ;
2023-08-20 15:41:53 +00:00
console . log ( ` Took ${ new Date ( ) . getTime ( ) - time } ms. ` ) ;
} catch ( e ) {
console . error ( e ) ;
2023-08-18 15:34:25 +00:00
}
return ;
} )
. catch ( ( error ) => {
console . error ( "Error processing captcha:" , error ) ;
} ) ;
} ) ;
} ) ( ) ;