NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name x // @namespace xx // @version 1 // @license MIT // @description vv // @author bb // @match *://*.hcaptcha.com/*hcaptcha-challenge* // @match *://*.hcaptcha.com/*checkbox* // @grant GM_xmlhttpRequest // @connect https://cdnjs.cloudflare.com // @connect https://cdn.jsdelivr.net // @connect https://unpkg.com // @connect *://*.hcaptcha.com/* // @require https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js // @require https://unpkg.com/jimp@0.5.2/browser/lib/jimp.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/tesseract.js/2.0.0-alpha.2/tesseract.min.js // @require https://cdn.jsdelivr.net/npm/@tensorflow/tfjs/dist/tf.min.js // @require https://cdn.jsdelivr.net/npm/@tensorflow-models/coco-ssd // ==/UserScript== (function() { 'use strict'; var selectedImageCount = 0; var tensorFlowModel = undefined; var worker = undefined; //Node Selectors const CHECK_BOX = "#checkbox"; const SUBMIT_BUTTON = ".button-submit"; const TASK_IMAGE_BORDER = ".task-image .border"; const IMAGE = ".task-image .image"; const TASK_IMAGE = ".task-image"; const PROMPT_TEXT = ".prompt-text"; const NO_SELECTION = ".no-selection"; const CHALLENGE_INPUT_FIELD = ".challenge-input .input-field"; const CHALLENGE_INPUT = ".challenge-input"; const IMAGE_FOR_OCR = ".challenge-image .zoom-image"; //Attributes const ARIA_CHECKED = "aria-checked"; const ARIA_HIDDEN = "aria-hidden"; //Values that can be changed for other languages const AIRPLANE = "airplane"; const BICYCLE = "bicycle"; const BOAT = "boat"; const CAR = "car"; const MOTORBUS = "motorbus"; const MOTORCYCLE = "motorcycle"; const TRAIN = "train"; const TRUCK = "truck"; const TRANSPORT_TYPES = [AIRPLANE, BICYCLE, BOAT, CAR, MOTORBUS, MOTORCYCLE, TRAIN, TRUCK]; const SENTENCE_TEXT_A = "Please click each image containing a "; const SENTENCE_TEXT_AN = "Please click each image containing an "; const LANGUAGE_FOR_OCR = "eng"; // Option to override the default image matching //const ENABLE_TENSORFLOW = true; String.prototype.includesOneOf = function(arrayOfStrings) { //If this is not an Array, compare it as a String if (!Array.isArray(arrayOfStrings)) { return this.toLowerCase().includes(arrayOfStrings.toLowerCase()); } for (var i = 0; i < arrayOfStrings.length; i++) { if ((arrayOfStrings[i].substr(0, 1) == "=" && this.toLowerCase() == arrayOfStrings[i].substr(1).toLowerCase()) || (this.toLowerCase().includes(arrayOfStrings[i].toLowerCase()))) { return true; } } return false; } function matchImagesUsingTensorFlow(imageUrl, word, i) { try { var img = new Image(); img.crossOrigin = "Anonymous"; img.src = imageUrl; img.onload = () => { initializeTensorFlowModel().then(model => model.detect(img)) .then(function(predictions) { var predictionslen = predictions.length; for (var j = 0; j < predictionslen; j++) { if (qSelectorAll(IMAGE)[i] && (qSelectorAll(IMAGE)[i].style.background).includes(imageUrl) && qSelectorAll(TASK_IMAGE_BORDER)[i].style.opacity == 0 && predictions[j].class.includesOneOf(word)) { qSelectorAll(TASK_IMAGE)[i].click(); break; } } selectedImageCount = selectedImageCount + 1; }); } } catch (err) { console.log(err.message); } } //Different Models can be set later based on usecase //Ref Models: https://github.com/tensorflow/tfjs-models async function initializeTensorFlowModel() { if (!tensorFlowModel) { tensorFlowModel = await cocoSsd.load(); } return tensorFlowModel; } //Initialize TesseractWorker function initializeTesseractWorker() { if(!worker){ worker = new Tesseract.TesseractWorker(); } } function qSelectorAll(selector) { return document.querySelectorAll(selector); } function qSelector(selector) { return document.querySelector(selector); } async function getSynonyms(word) { //TODO: Format this to JSON string var tempWord = word; if (!tempWord.includesOneOf(TRANSPORT_TYPES)) { console.log("New word or different cyrillic"); var img = await convertTextToImage(word); tempWord = await convertImageToText(img); img.removeAttribute("src"); word = tempWord; } if (word == MOTORBUS) { word = ['bus', 'motorbus']; } else if (word == CAR) { word = ['=car', 'coupe', 'jeep', 'limo', 'sport utility vehicle', 'station wagon', 'hatchback', 'bumper car', 'modelT', 'electric battery', 'cruiser']; } else if (word == AIRPLANE) { word = ['airplane', 'plane', 'aircraft', 'aeroplane', 'hangar', 'Airdock', 'JumboJet', 'jetliner', 'stealth fighter', 'field artillery'] } else if (word == TRAIN) { word = ['train', 'rail', 'cable car', 'locomotive', 'subway station'] } else if (word == BOAT) { word = ['=boat', '=barge', 'houseboat', 'bobsled', 'pontoon', 'small boat', 'SnowBlower', 'Sea-coast', 'PaddleSteamer', 'Freighter', 'Sternwheeler', 'kayak', 'canoe', 'deck', 'DockingFacility', 'surfboard', 'ship', '=cruise', 'watercraft', 'sail', 'canvas', '=raft'] } else if (word == BICYCLE) { word = ['bicycle', 'tricycle', 'mountain bike', 'AcceleratorPedal', 'macaw', 'knot'] } else if (word == MOTORCYCLE) { word = ['motorcycle', 'windshield', 'dashboard'] } else if (word == TRUCK) { word = ['truck', 'cargocontainer', 'bazooka'] } else { console.log("Word does not match. New type identified::" + word); } return word } function isHidden(el) { return (el.offsetParent === null) } if (window.location.href.includes("checkbox")) { var checkboxInterval = setInterval(function() { if (!qSelector(CHECK_BOX)) { clearInterval(checkboxInterval); } else if (qSelector(CHECK_BOX).getAttribute(ARIA_CHECKED) == "true") { clearInterval(checkboxInterval); } else if (!isHidden(qSelector(CHECK_BOX)) && qSelector(CHECK_BOX).getAttribute(ARIA_CHECKED) == "false") { qSelector(CHECK_BOX).click(); } else { return; } }, 5000); } else { try { initializeTesseractWorker(); selectImages(); } catch (err) { console.log(err); console.log("Tesseract could not be initialized"); } } function selectImagesAfterDelay(delay) { setTimeout(function() { selectImages(); }, delay * 1000); } function triggerEvent(el, type) { var e = document.createEvent('HTMLEvents'); e.initEvent(type, false, true); el.dispatchEvent(e); } // Small hack to select the nodes function unsure(targetNodeText) { var targetNode = Array.from(qSelectorAll('div')) .find(el => el.textContent === targetNodeText); //Works for now //TODO: Select clothing //TODO: Draw boxes around images if (targetNode) { triggerEvent(targetNode, 'mousedown'); triggerEvent(targetNode, 'mouseup'); if (qSelector(SUBMIT_BUTTON)) { qSelector(SUBMIT_BUTTON).click(); } } return selectImagesAfterDelay(1); } function getUrlFromString(urlString) { var urlMatch = urlString.match(/(?<=\(\").+?(?=\"\))/g); if (!urlMatch) { return 0; } var imageUrl = urlMatch[0]; return imageUrl; } function getImageList() { var imageList = []; if (qSelectorAll(IMAGE).length > 0) { for (var i = 0; i < 9; i++) { var urlString = qSelectorAll(IMAGE)[i].style.background; var imageUrl = getUrlFromString(urlString); if (imageUrl == 0) { //console.log("Image url is empty"); return imageList; } imageList[i] = imageUrl; } } return imageList; } function waitUntilImageSelection() { var imageIntervalCount = 0; var imageInterval = setInterval(function() { imageIntervalCount = imageIntervalCount + 1; if (selectedImageCount == 9) { clearInterval(imageInterval); if (qSelector(SUBMIT_BUTTON)) { qSelector(SUBMIT_BUTTON).click(); } return selectImagesAfterDelay(5); } else if (imageIntervalCount > 8) { clearInterval(imageInterval); return selectImages(); } else { } }, 3000); } function waitForImagesToAppear() { var checkImagesSelectedCount = 0; var waitForImagesInterval = setInterval(function() { checkImagesSelectedCount = checkImagesSelectedCount + 1; if (qSelectorAll(IMAGE) && qSelectorAll(IMAGE).length == 9) { clearInterval(waitForImagesInterval); return selectImages(); } else if (checkImagesSelectedCount > 30) { clearInterval(waitForImagesInterval); } else if (qSelector(CHALLENGE_INPUT_FIELD) && qSelector(NO_SELECTION).getAttribute(ARIA_HIDDEN) != true) { clearInterval(waitForImagesInterval); return imageUsingOCR(); } else if(qSelector('canvas')){ var $el = $(qSelector('canvas')); var offset = $el.offset() console.log(offset) var event = jQuery.Event('mousedown', { which: 1, pageX: offset.left+200, pageY: offset.top+300, }) $el.trigger(event); var event = jQuery.Event('mousedown', { which: 1, pageX: offset.left+300, pageY: offset.top+400, }) $el.trigger(event); }else{ var targetNodeList = ["3 or more items of furniture", "Equipped space or room", "Photo is clean, no watermarks, logos or text overlays", "An interior photo of room", "Unsure", "Photo is sharp"]; for (var j = 0; j < targetNodeList.length; j++) { var targetNode = Array.from(qSelectorAll('div')) .find(el => el.textContent === targetNodeList[j]); if (targetNode) { //console.log("Target Node Found"); clearInterval(waitForImagesInterval); return unsure(targetNodeList[j]); } } } }, 5000); } function makeid(length) { var result = ''; var characters = 'abcdefghijklmnopqrstuvwxyz'; var charactersLength = characters.length; for ( var i = 0; i < length; i++ ) { result += characters.charAt(Math.floor(Math.random() * charactersLength)); } return result; } function preProcessImage(imageUrl) { //Darken and Brighten Jimp.read(imageUrl).then(function(data) { data.color([ { apply: 'darken', params: [20] } ]).color([ { apply: 'brighten', params: [20] } ]) .greyscale() .getBase64(Jimp.AUTO, function(err, src) { var img = document.createElement("img"); img.setAttribute("src", src); worker.recognize(img, LANGUAGE_FOR_OCR).then(function(data) { //Remove Image After recognizing img.removeAttribute("src"); //If null change to other methods //if (data && data.text && data.text.length > 0) { inputChallenge(postProcessImage(data), imageUrl); return selectImages(); //} else { //preProcessImageMethod2(imageUrl); //} }); }); }); } function postProcessImage(data) { var filterValues = ['\n', '{', '}', '[', ']']; for (var i = 0; i < filterValues.length; i++) { data.text = data.text.replaceAll(filterValues[i], ""); } return data; } // Using Tesseract to recognize images function imageUsingOCR() { try { //console.log("Image using OCR"); var urlString = qSelector(IMAGE_FOR_OCR).style.background; var imageUrl = getUrlFromString(urlString); if (imageUrl == 0) { return selectImagesAfterDelay(1); } preProcessImage(imageUrl); } catch (err) { console.log(err.message); return selectImagesAfterDelay(1); } } async function convertTextToImage(text) { //Convert Text to image var canvas = document.createElement("canvas"); canvas.width = 620; canvas.height = 80; var ctx = canvas.getContext('2d'); ctx.font = "30px Arial"; ctx.fillText(text, 10, 50); var img = document.createElement("img"); img.src = canvas.toDataURL(); return img; } async function convertImageToText(img) { //Convert Image to Text var text = ""; await worker.recognize(img, LANGUAGE_FOR_OCR).then(function(data) { text = data.text; // console.log("Recognized Text::" + text); }); return text.trim(); } function inputChallenge(data, imageUrl) { try { if ((qSelector(IMAGE_FOR_OCR).style.background).includes(imageUrl)) { console.log(data.text); var targetNode = qSelector(CHALLENGE_INPUT_FIELD); targetNode.value = data.text.replaceAll("\n", ""); var challengeInput = qSelector(CHALLENGE_INPUT); triggerEvent(challengeInput, 'input'); // Set a timeout if you want to see the text qSelector(SUBMIT_BUTTON).click(); } } catch (err) { console.log(err.message); } } async function selectImages() { if (qSelectorAll(IMAGE) && qSelectorAll(IMAGE).length == 9 && qSelector(NO_SELECTION).getAttribute(ARIA_HIDDEN) != true) { selectedImageCount = 0; try { await initializeTensorFlowModel(); var word = qSelector(PROMPT_TEXT).innerText; if (word && (word.includes(SENTENCE_TEXT_A) || word.includes(SENTENCE_TEXT_AN))) { word = word.replace(SENTENCE_TEXT_A, ''); word = word.replace(SENTENCE_TEXT_AN, ''); } else { //TODO: Select images from example and identify the word for other languages } } catch (err) { console.log(err.message); return selectImagesAfterDelay(5); } var imageList = []; try { imageList = getImageList(); if (imageList.length != 9) { //console.log("Waiting"); return selectImagesAfterDelay(5); } } catch (err) { console.log(err.message); return selectImagesAfterDelay(5); } //Get Synonyms for the word word = await getSynonyms(word); //console.log("words are::" + word); for (var i = 0; i < 9; i++) { //if (ENABLE_TENSORFLOW) { matchImagesUsingTensorFlow(imageList[i], word, i); //} else { // matchImages(imageList[i], word, i); //} } waitUntilImageSelection(); } else { waitForImagesToAppear(); } } })();