// ==UserScript==
// @name [KissAnime] Captcha Solver
// @namespace https://greasyfork.org/en/users/193865-westleym
// @author WestleyM
// @version 2018.07.11.2
// @icon http://kissanime.ru/Content/images/favicon.ico
// @description Saves initial responses to KissAnime captcha and auto-selects images if it knows the answer.
// @grant none
// @include http://kissanime.ru/Special/AreYouHuman2*
// @require http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
// ==/UserScript==
function main() {
//Variable declarations
var currentVersion = "2018.07.11";
var installText = "Thank you for installing [KissAnime] Captcha Solver!";
var updateText = "There was an issue with descriptions that matched multiple images. The images would be saved incorrectly, to where those solutions became useless. I believe I've fixed the issue though. Images that have already been saved that way shouldn't cause any problems with new solutions that match the same description.\nI'll need to look at new lists in the next few days to see if this fix worked.\n\nYou can reach me through GreasyFork @WestleyM or Reddit @WarriorSolution\nPlease report any bugs or issues!";
var $ = window.jQuery;
var formVerify = document.getElementById("formVerify");
var words = [], undefinedWords = [], unknownWords = [], knownWords = [],
imageSrc = [], clickImage = [], imageData = [], imageElements = [], multiImageFlag = [];
var matchFound = 0, count = 0, impExpFlag = 0, askedForHelp = 0, PHObjFlag = 0;
var wordImagePairs = {}, wordsObj = {}, imageObj = {}, clickedImgs = {}, placeholderObjOne = {}, placeholderObjTwo = {};
var dataURL = "";
var impExpButton, inputSubmit, exportButton,
firstDiv, PElements, thirdPElement, alertBoxDiv,
alertBoxText, importExport, inputJSON, lineSeparator,
exportDirections, exportBox; //Variables used for created HTML elements
if (formVerify === null) {
var link = document.getElementsByTagName("a");
link = link[0];
if (localStorage.getItem("KCS-lastDescriptions") != null) {
wordsObj = JSON.parse(localStorage.getItem("KCS-lastDescriptions"));
localStorage.removeItem(wordsObj.firstWord);
localStorage.removeItem(wordsObj.secondWord);
localStorage.removeItem("KCS-lastDescriptions");
console.log("Deleted the last two entries.");
}
console.log("Redirecting page. . . .");
link.click();
}
if (formVerify != null) { //User is on the regular captcha page
//Alerts for initial install or update of the script
if (localStorage.getItem("KCS-version") === null && localStorage.getItem("version") === null) { messagePusher("install"); }
if (localStorage.getItem("KCS-version") != currentVersion && localStorage.getItem("KCS-version") != null) { messagePusher("update"); }
if (localStorage.getItem("KCS-version") === null && localStorage.getItem("version") != null) { messagePusher("update"); }
//Image onclick events
imageElements = $("#formVerify").find("img").toArray();
imageElements.forEach(function(currentImage, imageIndex) { currentImage.onclick = function() { onClickEvents("image", currentImage, imageIndex); } });
//Create custom HTML
customHTML();
//Import/Export onclick function calls
impExpButton.onclick = function() { onClickEvents("impExpButton") };
inputSubmit.onclick = function() { onClickEvents("inputSubmit") };
exportButton.onclick = function() { onClickEvents("exportButton") };
//Avoid conflicts, start main processes
this.$ = this.jQuery = jQuery.noConflict(true);
$(document).ready(function() {
wordGrabber();
unknownWordGrabber();
knownWordGrabber();
imageGrabber();
clickImages();
console.log("Unknown words: " + unknownWords);
console.log("Known words: " + knownWords);
if (unknownWords[0] != undefined) { //Ask for help with the first unknown word
askForHelp(unknownWords[0]);
}
});
}
//Functions
function askForHelp(word) { //Asks you to select an answer when the script doesn't know.
alertBoxText.innerText = "Please select image: " + word;
localStorage.setItem("KCS-helpWord", word);
}
function unknownWordGrabber() { //Finds the words that the script doesn't know the answer to
words.forEach(function(word) {
if(!localStorage.getItem("KCS-" + word)) { //If the solution isn't found in the local storage, it will be added to the "unknownWords" array
unknownWords.push(word);
}
});
}
function knownWordGrabber() { //Finds the words that the script knows the answer to
words.forEach(function(word) {
if(localStorage.getItem("KCS-" + word)) { //If solution is found in the local storage, it will be added to the "knownWords" array
knownWords.push(word);
}
});
}
function wordGrabber() { //Grabs span elements that are children of the "formVerify" form. This will include the two sections saying what to select. Ex: "cat, glasses, 0"
var pElements = $("#formVerify").find("p").toArray();
var finalPElement = pElements[pElements.length-1];
var wordElements = $(finalPElement).find("span").toArray();
var firstDesc = wordElements[0].innerText;
var secondDesc = wordElements[1].innerText;
words = [firstDesc, secondDesc];
//Saves the descriptions to local Storage
var lastDescriptions = { "firstWord":firstDesc, "secondWord":secondDesc };
var DescJSON = JSON.stringify(lastDescriptions);
localStorage.setItem("KCS-lastDescriptions", DescJSON);
}
function imageGrabber() {
imageElements.forEach(function(image, index) {
var objKey = "image" + index.toString();
var imageData = convertToDataUrl(image);
imageData = minimiseDataUrl(minimiseDataUrl(minimiseDataUrl(imageData, 5), 4), 3);
imageObj[objKey] = imageData;
});
}
function convertToDataUrl(img) {
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL("image/png");
return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
}
function minimiseDataUrl(dataUrl,jump) {
var a = "";
for(var i = 0; i < dataUrl.length; i=i+jump) {
a += dataUrl.charAt(i);
}
return a;
}
function clickImages() {
knownWords.forEach(function(word) {
var i = 0;
for (var key in imageObj) {
if (localStorage.getItem("KCS-" + word) == imageObj[key]) {
$("[indexValue='" + i + "']").click();
break;
} else if (i === Object.keys(imageObj).length-1) {
var multiImageDesc = localStorage.getItem("KCS-" + word);
var foundFlag = 0;
try {
JSON.parse(multiImageDesc);
for (var j = 0; j < Object.keys(multiImageDesc).length; j++) {
if (multiImageDesc[j] === imageObj[key]) {
console.log("Description with multiple images found and clicked: " + word);
$("[indexValue='" + i + "']").click();
foundFlag = 1;
}
}
}
catch(err) {}
if (foundFlag === 0) {
console.log("Description with multiple images found. Solution unknown: " + word);
multiImageFlag.push(word);
unknownWords.push(word);
knownWords.splice(knownWords.indexOf(word), 1);
}
}
i++;
}
});
}
function convertSolutions() {
if (localStorage.getItem("KCS-version") === null || localStorage.getItem("KCS-version") === undefined) {
for (var i = 0; i < localStorage.length; i++) {
if (localStorage.key(i) != "KCS-helpWord" && localStorage.key(i) != "KCS-lastDescriptions" && localStorage.key(i) != "KCS-version") {
localStorage.setItem("KCS-" + localStorage.key(i),localStorage.getItem(localStorage.key(i)));
}
}
}
}
function messagePusher(type) {
switch(type) {
case "install":
console.log(installText);
localStorage.removeItem("version");
localStorage.removeItem("lastDescriptions");
localStorage.removeItem("helpWord");
localStorage.setItem("KCS-version", currentVersion);
break;
case "update":
alert("(You will only see this message once per update)\n\n" + updateText);
localStorage.removeItem("version");
localStorage.removeItem("lastDescriptions");
localStorage.removeItem("helpWord");
localStorage.setItem("KCS-version", currentVersion);
break;
}
}
function customHTML() {
//Message box
firstDiv = $("#formVerify").find("div").toArray()[0];
firstDiv.style.cssText = "width:100%;"; //The box holding the information at the top was not wide enough originally
PElements = $(firstDiv).find("p").toArray();
if (PElements.length === 2) {
PElements[0].style.cssText = "opacity:0; height:0px; width:100%; line-height:0px; font-size:0px;";
}
if (PElements.length === 3) {
PElements[0].style.cssText = "display: none;";
PElements[1].style.cssText = "opacity:0; height:0px; width:100%; line-height:0px; font-size:0px;";
}
thirdPElement = PElements[PElements.length-1];
thirdPElement.style.cssText = "opacity:0; height:0px; width:100%; line-height:0px; font-size:0px;"; //Hides where it lists both selection choices. This is to insure users select the images in the correct order.
alertBoxDiv = document.createElement("div"); //Creation of div element which will contain the below text element
alertBoxDiv.style.cssText = "background:#518203; color:white; height:30px; width:100%; line-height:30px; text-align:center;";
alertBoxText = document.createElement("h3"); //Creation of text element which will say the descriptions of images the script doesn't know the answer to
alertBoxText.innerText = "Checking data. . . .";
alertBoxText.style.cssText = "background:#518203; color:white; height:100%; width:100%; text-align:center; font-size: 20px; margin-top:0px;";
alertBoxDiv.insertAdjacentElement("afterbegin", alertBoxText); //Inserting "alertBoxText" into "alertBoxDiv" at the top
thirdPElement.insertAdjacentElement("afterend", alertBoxDiv); //Placing "alertBoxDiv" at the end of "mainBlock"
//Import/Export area
importExport = document.createElement("div");
importExport.style.cssText = "display:block; background: #111111; color:white; width:970px; padding:2px; text-align:center; margin-left:auto; margin-right:auto; border:1px solid #2f2f2f;";
importExport.id = "importExport";
impExpButton = document.createElement("p");
impExpButton.style.cssText = "background:#518203; color:white; height:15px; width:960px; margin-top:5px; margin-bottom:5px; text-align:center; font-size: 15px; padding:5px; cursor:pointer;";
impExpButton.innerText = "[+] Solution List Importing/Exporting";
impExpButton.id = "impExpButton";
inputJSON = document.createElement("input");
inputJSON.type = "text";
inputJSON.name = "JSON input";
inputJSON.id = "inputJSON";
inputJSON.placeholder = "Paste solution here (will not overwrite existing solutions)";
inputJSON.style.cssText = "display:none; width:50%; margin-left:auto; margin-right:auto; margin-bottom:5px;";
inputSubmit = document.createElement("div");
inputSubmit.style.cssText = "display:none; background:#518203; color:white; height:20px; width:50%; margin-left:auto; margin-right:auto; margin-bottom:5px; border:1px solid #5a5a5a; cursor:pointer;";
inputSubmit.innerText = "Submit";
inputSubmit.id = "inputSubmit";
lineSeparator = document.createElement("div");
lineSeparator.style.cssText = "display:none; background:#5f5f5f; height:3px; width:100%; margin-left:auto; margin-right:auto; margin-bottom:5px;";
lineSeparator.id = "lineSeparator";
exportButton = document.createElement("div");
exportButton.style.cssText = "display:none; background:#518203; color:white; height:20px; width:50%; margin-left:auto; margin-right:auto; margin-bottom:5px; border:1px solid #5a5a5a; cursor:pointer;";
exportButton.innerText = "Export list";
exportButton.id = "exportButton";
exportDirections = document.createElement("div");
exportDirections.style.cssText = "display:none; background:#518203; color:white; height:20px; width:50%; margin-left:auto; margin-right:auto; margin-bottom:5px; border:1px solid #5a5a5a;";
exportDirections.innerText = "Copy the below data: (triple click to select all)";
exportDirections.id = "exportDirections";
exportBox = document.createElement("p");
exportBox.style.cssText = "display:none; #111111; color:white; width:75%; margin-left:auto; margin-right:auto; margin-top:0px; margin-bottom:5px; text-align:center; font-size:10px; border:1px solid #2f2f2f; word-wrap: break-word; overflow:auto; max-height:500px;";
exportBox.innerText = "";
exportBox.id = "exportBox";
importExport.insertAdjacentElement("afterbegin", impExpButton);
importExport.insertAdjacentElement("beforeend", inputSubmit);
inputSubmit.insertAdjacentElement("afterend", lineSeparator);
lineSeparator.insertAdjacentElement("afterend", exportButton);
exportButton.insertAdjacentElement("afterend", exportDirections);
exportDirections.insertAdjacentElement("afterend", exportBox);
impExpButton.insertAdjacentElement("afterend", inputJSON);
document.getElementById("containerRoot").insertAdjacentElement("afterend", importExport);
}
function onClickEvents(clickedItem, clickedImage, imageIndexValue) {
switch(clickedItem) {
case "impExpButton":
if (impExpFlag === 0) {
impExpButton.innerText = "[-] Solution List Importing/Exporting";
inputJSON.style.display = "block";
inputSubmit.style.display = "block";
lineSeparator.style.display = "block";
exportButton.style.display = "block";
impExpFlag = 1;
} else {
impExpButton.innerText = "[+] Solution List Importing/Exporting";
inputJSON.style.display = "none";
inputSubmit.style.display = "none";
lineSeparator.style.display = "none";
exportButton.style.display = "none";
exportDirections.style.display = "none";
exportBox.style.display = "none";
impExpFlag = 0;
}
break;
case "exportButton":
//Grab data from local storage and convert to JSON string
for (var i = 0; i < localStorage.length; i++) {
if (localStorage.key(i) != "KCS-helpWord" && localStorage.key(i) != "KCS-lastDescriptions" && localStorage.key(i) != "KCS-version") {
wordImagePairs[localStorage.key(i)] = localStorage.getItem(localStorage.key(i));
}
}
var wordImagePairsJSON = JSON.stringify(wordImagePairs);
exportBox.innerText = wordImagePairsJSON;
exportDirections.style.display = "block";
exportBox.style.display = "block";
break;
case "inputSubmit":
var inputData = inputJSON.value;
try {
var newCaptchaData = JSON.parse(inputData);
Object.keys(newCaptchaData).forEach(function(current) {
current = current.replace("KCS-", ""); //Allows for compatibility between old export lists and new ones.
if (localStorage.getItem("KCS-" + current) === null) {
if (newCaptchaData["KCS-" + current] != null) {
localStorage.setItem("KCS-" + current, newCaptchaData["KCS-" + current]);
} else {
localStorage.setItem("KCS-" + current, newCaptchaData[current]);
}
}
});
inputSubmit.innerText = "Submitted successfully!";
console.log("Solution list has been updated.");
}
catch(err) {
inputSubmit.innerText = "There was an issue. Check the console.";
console.log("Issue with list upload: " + err);
}
break;
case "image":
if ($(clickedImage).attr("class") === "imgCapSelect") {
clickedImgs[localStorage.getItem("KCS-helpWord")] = imageIndexValue;
} else {
words.forEach(function(word) {
if (imageIndexValue === clickedImgs[word]) {
delete clickedImgs[word];
}
});
}
if (Object.keys(clickedImgs).length === words.length) {
for (var key in clickedImgs) {
if (key !== multiImageFlag[0] && key !== multiImageFlag[1]) {
localStorage.setItem("KCS-" + key, imageObj["image" + clickedImgs[key].toString()]);
} else {
var currentSolution = localStorage.getItem(key);
try {
JSON.parse(currentSolution);
currentSolution[Object.keys(currentSolution).length] = imageObj["image" + clickedImgs[key].toString()];
}
catch(err) {
if (PHObjFlag === 0) {
placeholderObjOne[0] = currentSolution;
placeholderObjOne[1] = imageObj["image" + clickedImgs[key].toString()];
currentSolution = placeholderObjOne;
PHObjFlag = 1;
} else if (PHObjFlag === 1) {
placeholderObjTwo[0] = currentSolution;
placeholderObjTwo[1] = imageObj["image" + clickedImgs[key].toString()];
currentSolution = placeholderObjTwo;
PHObjFlag = 2;
}
}
JSON.stringify(currentSolution);
localStorage.setItem("KCS-" + key, currentSolution);
}
}
alertBoxText.innerText = "Selections complete. Loading next page. . . .";
}
if (Object.keys(clickedImgs).length < words.length) {
words.forEach(function(word, index) {
if (clickedImgs[word] === undefined && askedForHelp === 0) {
askForHelp(word);
askedForHelp = 1;
}
});
askedForHelp = 0;
}
}
}
}
main();