//SHOULD PUT ALL CODE IN ORDER IT SHOULD EXECUTE IN (ie imgclicked goes after setting the event handlers)
//REALLY NEED TO WORK ON PGRM FLOW ITS PRETTY UNREADABLE
//MIGHT WANT TO MAKE AN OBJECT CALLED solve WHICH HAS img AND words FIELDS TO CLEAN UP THE CODE
//HAVE TO THINK ABOUT WHAT THOSE AWFUL ADS COULD DO TO THIS
//MIGHT WANT TO TRY TO REMOVE THE ADS AGIAN
//SHOULD PROBABLY USE THE SAME FUNCTION FOR DISPLAYING RECENT AUTOSOLVES AND DISPLAYING ALL THE SOLVES, JUST PASS THE FUNC A DATABASE AND IT'LL SPIT OUT A DIV
//SOMETIMES IMAGES THAT ARE 1 PIXEL SMALLER OR LARGER THEN USUAL ARE DISPLAYED, IDK WHY AND THERE ONLY SEEMS TO BE 2 SIZES FOR ALL THE IMAGES THAT DO HAVE DOUBLES BUT TO FIX IT YOU'D HAVE TO CHECK COLOR RATIOS ON ALL THE IMAGES AND STUFF AND IT WOULDN'T REALLY BE WORTH IT SO FRIG IT
//EXAMPLES OF DOUBLES OF IMAGES THAT YOU HAVE ARE girl, blue hair (hatsune miku with the leaks) AND dog, brown fur AND turtle, stand
//IT MIGHT HAVE SOMETHING TO DO WITH ME CHANGING MY ZOOM IN CHROME WHEN I FIRST NOTICED THIS BUT ALMOST NONE OF THE IMAGES ARE DIFFERENT SIZES THEN BEFORE AND THEY DONT SEEM TO CHANGE SIZE WHEN I ZOOM IN/OUT SO IDK
// ==UserScript==
// @name Kissanime Anti-Captcha
// @namespace http://tampermonkey.net/
// @version Sept. 2017
// @description Automatically solves captchas on Kissanime.ru
// @author Ehren Julien-Neitzert
// @match http://kissanime.ru/Special/AreYouHuman*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// ==/UserScript==
(function() {
function imgToStr(img) {
canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
context = canvas.getContext('2d'); //a context is something that will let me actually draw to the canvas, 2d is for images and stuff
context.drawImage(img, 0, 0, img.width, img.height); //draw the image on the canvas, FOR SOME REASON IF I DONT SPECIFY EXACTLY HOW TALL AND WIDE THEN IT'LL DRAW AN IMAGE THATS TOO BIG OR TOO SMALL
return canvas.toDataURL("image/png"); //converts img to string which is the png data for the image, to run toDataUrl the img src needs to be not cross origin for whatever reason
}
function strToImg(imgStr) {
var img = document.createElement('img');
img.src = imgStr;
return img;
}
function wordSpansToStrs(words) {
trueWords = [];
for (var i = 0; i < 2; i++) { //get words from spans
text = words[i].innerText; //get the inner text of the word spans
texts = text.split(', '); //split the 2 words apart
trueWords.push(texts);
}
return trueWords;
}
function setImgOnclicks(images) {
for (var i = 0; i < images.length; i++) { //set event listener on every image
images[i].addEventListener('click', imgClicked);
}
}
function imgClicked() { //when a captcha image is clicked
imgStr = imgToStr(this); //convert the image that called this function to a string
console.log("Words already on this image: ", GM_getValue(imgStr));
if (!autoSolves[currentWords]) { //if a person clicked on the captcha image
console.log("HUMAN CLICK");
solves.push([imgStr, trueWords[currentWords]]); //add the word/img combo to the solves
GM_setValue("currentKissanimeSolves", solves); //DO THIS EVERY TIME AN IMAGE IS CLICKED BECAUSE SOMETIMES ONE OF THE 2 WILL BE AUTO SOLVED SO THERE WILL ONLY BE 1 SOLVE, save the solves so that when the next page loads we can check if the solves are good
}
console.log("Found Solves: ", solves);
currentWords++; //go to the next pair of words
this.hidden = true; //hide this image so it can't be unclicked or reclicked
words[1].hidden = false; //unhide the 2nd pair of words
if (autoSolves[currentWords]) { //if the next pair of words are solvable, USING CURRENTWORDS INSTEAD OF JUST OR ELSE WHEN I AUTOCLICK THE 2ND SOLVE IT'LL AUTOCLICK THE 2ND SOLVE REPEADITLY FOREVER, SINCE CURRENTWORDS KEEPS INCREASING EVERY TIME I CLICK AN IMAGE THEN AFTER THE 2ND ONE IT'LL CHECK IF THERES A 3RD ELEMENT IN THE autosolve ARRAY WHICH WILL COME BACK undefined
autoSolves[currentWords].click(); //click the next image
}
}
function saveSolves() { //fired when captcha page is visited agian
solved = GM_getValue("currentKissanimeSolves");
allImgs = GM_getValue("kissanimeAllSolvedImgs");
if (solved) { //if solved isn't empty
console.log("Now Saving: ", solved);
for (s = 0; s < solved.length; s++) { //for every solve
imgStr = solved[s][0];
newWords = solved[s][1];
if (allImgs.indexOf(imgStr) != -1) { //if this img has solves on it
allWords = GM_getValue(imgStr);
for (w = 0; w < newWords.length; w++) { //go through the 2 new words
newWord = newWords[w];
if (allWords.indexOf(newWord) == -1) { //HAVE TO CHECK IF THE WORD'S ALREADY IN THERE CAUSE IF ONE SOLVE IS "emoticon, beer" AND THE NEXT SOLVE IS "emoticon, drink" THEN IT'LL ADD THE WORD emoticon TO THE IMAGE'S WORDS TWICE!
allWords.push(newWord); //if its a new word, add it
}
}
GM_setValue(imgStr, allWords);
} else { //if the captcha does not have any words assosiated with it
allImgs.push(imgStr);
GM_setValue("kissanimeAllSolvedImgs", allImgs); //first add the imgStr to the list of all solved images
GM_setValue(imgStr, newWords); //then create the list of words specifically for the image
}
}
}
}
function dumpSolves() { //sets solves to undefined
solved = GM_getValue("currentKissanimeSolves");
console.log("Dumping: ", solved);
GM_setValue("currentKissanimeSolves", false); //reset the current solves, CANT SET THIS TO undefined CAUSE IF I TRY IT SETS IT TO THE STRING "undefined"
}
function printAllImgs() {
allImgs = GM_getValue("kissanimeAllSolvedImgs");
console.log(allImgs);
}
function makeDisplayAllSolvesButton() {
document.body.appendChild(document.createElement('p')); //just for some indentation
a = document.createElement('a');
a.innerText = "Click here to display the entire database of solves";
a.onclick = displayAllSolves;
a.style = "color:blue; text-decoration:underline;";
a.id = "databaseOpener";
document.body.appendChild(a);
}
function displayAllSolves() {
document.body.removeChild(document.getElementById("databaseOpener"));
allImgs = GM_getValue("kissanimeAllSolvedImgs");
numOfImgs = allImgs.length;
for (s = 0; s < numOfImgs; s++) {
imgStr = allImgs[s];
var img = strToImg(imgStr);
document.body.appendChild(img);
imgWords = GM_getValue(imgStr);
var p = document.createElement('p');
innerText = imgWords.join(', '); //jus' like thon but not
p.innerText = innerText;
document.body.appendChild(p);
}
}
function displayRecentAutoSolves() {
latestAutoSolvesBoxDescGen(); //describes the box of auto solves
console.log("displaying recent auto solves");
latestAutos = GM_getValue("kissanimeRecentAutoSolves");
console.log(latestAutos);
displayBox = document.createElement('div'); //the big box that'll hold everything
displayBox.style = "border-style: solid; border-color: #000000;"; //give it a border
numOfAutos = latestAutos.length;
for (a = 0; a < numOfAutos; a++) { //for all the most recent auto solves
imgBox = document.createElement('div'); //make a div to put the img and its words in
imgBox.style = "display:inline-block; margin-top:20px; margin-left:20px; margin-right:20px; margin-bottom:20px"; //so that the next div will go right beside it and theres enough space to look nice
imgStr = latestAutos[a][0];
img = strToImg(imgStr); //convert the string to an img
console.log(img);
imgBox.appendChild(img); //put the img in the div
solveWords = latestAutos[a][1];
for (w = 0; w < solveWords.length; w++) { //for each of the solve words on the image
p = document.createElement('p'); //make their own element
p.style = 'text-align: center; margin-top:2px; margin-bottom: 2px;'; //center the paragraph in the div and make sure theres not too much space between words
p.innerText = solveWords[w]; //put the word in the element
p.addEventListener('click', removeParagraphSolve); //make it so clicking on the word removes it from solves
imgBox.appendChild(p); //put the word in the same box as its img
}
displayBox.appendChild(imgBox); //put the box in the big box
}
document.body.appendChild(displayBox); //add the big box to the DOM
}
function removeParagraphSolve() { //function called by a paragraph containing a word that describes an image that's in the same div as the paragraph
wordBeingRemoved = this.innerText;
confirmation = confirm("are you sure you'd like to remove " + wordBeingRemoved + " from the image's list of words?");
if (confirmation) {
img = this.parentNode.getElementsByTagName('img')[0]; //get the only image in the save div as the text
imgStr = imgToStr(img);
allImgWords = GM_getValue(imgStr);
index = allImgWords.indexOf(wordBeingRemoved);
if (index != -1) {
allImgWords.pop(); //take the word out of the list of all assosiated words
GM_setValue(imgStr, allImgWords);
} else {
alert(wordBeingRemoved + " could not be removed because it's not associated with the image. Maybe you already removed it? (to see all the words associated with the image, see the full database below)");
}
console.log("all words left on image: ", GM_getValue(imgStr));
}
}
function latestAutoSolvesBoxDescGen() {
p = document.createElement('p');
p.innerText = "Below in the black box are the latest automatic solves. If any of the phrases describing the image are incorrect then click on the phrase to remove it from the above image's list of associated phrases.";
document.body.appendChild(p);
}
function deleteFromDatabase(imgStr) { //removes an image and all its solves from the database
GM_deleteValue(imgStr); //first remove all solve words on the imgStr
allImgs = GM_getValue("kissanimeAllSolvedImgs"); //then remove the imgStr from the list of all solves
index = allImgs.indexOf(imgStr);
allImgs.pop(index);
GM_setValue("kissanimeAllSolvedImgs", allImgs);
}
function removeDuplicateWords() {
allImgs = GM_getValue("kissanimeAllSolvedImgs");
len = allImgs.length;
for (var i = 0; i < len; i++) {
imgStr = allImgs[i];
allImgWords = GM_getValue(imgStr);
newAllImgWords = [];
numOfWords = allImgWords.length;
for (w = 0; w < numOfWords; w++) {
currentWord = allImgWords[w];
if (allImgWords.indexOf(currentWord) == w) {
newAllImgWords.push(currentWord);
}
}
GM_setValue(imgStr, newAllImgWords);
}
}
function getImgSolves(images, wordStrs) {
allImgs = GM_getValue("kissanimeAllSolvedImgs");
autoSolves = [null, null];
for (wordPair = 0; wordPair < 2; wordPair++) { //for each pair of words
for (i = 0; i < images.length; i++) { //for every captcha image
image = images[i];
str = imgToStr(image); //convert the image to a string
if (allImgs.indexOf(str) != -1) { //if there are solves available for the image
imgWords = GM_getValue(str); //get the solve words
captchaWords = wordStrs[wordPair]; //the 2 words that must be matched to an image
pass = true;
for (w = 0; w < captchaWords.length; w++) { //for each captcha word (2)
if (imgWords.indexOf(captchaWords[w]) == -1) { //if this image can't be described by a captcha word, it fails
pass = false;
}
}
if (pass) { //if the image and captcha words match
autoSolves[wordPair] = image;
break; //move on to the next pair of captcha words
}
}
}
}
autosWithWords = []; //this is the array im gonna save to use to display the autosolves on the error page
for (s = 0; s < 2; s++) { //autoSolves will always be 2 long
if (autoSolves[s]) { //NEED TO MAKE SURE IM NOT TRYING TO CONVERT null TO A STR USING imgToStr
autosWithWords.push([imgToStr(autoSolves[s]), wordStrs[s]]);
}
}
GM_setValue("kissanimeRecentAutoSolves", autosWithWords); //set this here so I can display the autosolves on the error page
console.log("Autosolves: ", autoSolves);
return autoSolves;
}
url = window.location.href;
if (url == "http://kissanime.ru/Special/AreYouHuman2") { //if the recent solves were trash
dumpSolves(); //just dump the solves
printAllImgs();
displayRecentAutoSolves();
makeDisplayAllSolvesButton();
}
else { //if the captcha page is being loaded
captchaZone = document.getElementsByClassName('barContent')[0]; //class containing the part of the page with the captchas
words = captchaZone.getElementsByTagName('span'); //contains the spans that contain the words, the only spans in the captchaZone are the descriptive words you have to fufill
trueWords = wordSpansToStrs(words); //gets the strings from the spans, should contain 2 arrays of 2 strings
words[1].hidden = true; //REMEMBER THAT WHILE HIDDEN THE INNER TEXT OF A WORD IS COMPLETELY HECKED, hides the 2nd pair of words so that the user clicks on an image matching the first pair
images = captchaZone.getElementsByTagName('img'); //gets all images in the captchaZone
currentWords = 0; //keeps track of which pair of words we're on
saveSolves(); //save the previous solves (if they were good/existed)
dumpSolves(); //then dump the solves
autoSolves = getImgSolves(images, trueWords);
solves = []; //create new global array to hold the new solves until I can confirm they're both good solves
setImgOnclicks(images); //set the onclicks
if (autoSolves[0]) { //if the first captcha can be autoSolved
autoSolves[0].click();
}
}
})();