Greasy Fork is available in English.

AB Links Solver

Solves AbLink images

// ==UserScript==
// @name         AB Links Solver
// @namespace    ABLinks Solver(Solves Ablinks images)
// @version      3.1
// @description  Solves AbLink images
// @author       engageub
// @match        *://*/*
// @noframes
// @connect      https://unpkg.com
// @require      https://unpkg.com/opencv.js@1.2.1/opencv.js
// @require      https://unpkg.com/jimp@0.5.2/browser/lib/jimp.min.js
// @require      https://unpkg.com/tesseract.js@2.1.5/dist/tesseract.min.js
// @grant        GM_xmlhttpRequest
// @antifeature  referral-link
// ==/UserScript==

// This script solves Ablink images with words and having 3 or 4 different options
// Number identification logic for comparing words and numbers will be implemented in the next versions
// Accuracy can be improved by adding more filters for different types of images and fonts
// This script does not have a global matcher, you will need to add the websites in the matcher section manually, till
// all the solutions are implemented
// Your account will be locked for 24 hours, if 3 incorrect solutions are provided consecutively in 10 minutes. (This is the default but depends on website)
// To avoid this add a rotator to change the website whenever an incorrect solution is provided.

// TODO: Refactor Code
(function() {
    'use strict';

    var questions = [];
    var questionImages = [];
    var questionImage = "";
    var questionImageSource = "";
    var numericWordArray = ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"];

    async function waitForImage(imgElement) {
        return await new Promise(res => {
            if (imgElement.complete) {
                return res();
            }
            imgElement.onload = () => res();
            imgElement.onerror = () => res();
        });
    }

    async function toDataURL(c){
        return await new Promise(function(resolve){
            const dataURI = c.toDataURL('image/png');
            return resolve(dataURI);
        })

    }

    async function removeNoiseUsingImageData(imgdata,width,height,threshold){
        return await new Promise(function(resolve){
            var noiseCount =0;
            var noiseRowStart = 0;
            for (let column = 0; column < width; column++) {
                let count = 0;
                for (let row = 0; row < height; row++) {

                    let position = row * width + column;
                    let pixelAtPosition = imgdata[position];

                    //Remove noise from first row and last row
                    if(row == 0 || row == height-1){
                        imgdata[position] = 0xFFFFFFFF;
                    }

                    if (pixelAtPosition == 0xFF000000){
                        if(noiseCount == 0){
                            noiseRowStart = row;
                        }
                        noiseCount++;
                    }else{
                        //Define the number of consecutive pixels to be considered as noise
                        if(noiseCount > 0 && noiseCount <= threshold){
                            //Start from noiseRow till current row and remove noise
                            while(noiseRowStart < row){
                                let noisePosition = noiseRowStart * width + column;
                                imgdata[noisePosition] = 0xFFFFFFFF;
                                noiseRowStart++;
                            }
                        }
                        noiseCount =0;
                    }
                }
            }
            return resolve(imgdata);
        })

    }

    async function imageUsingOCRAntibotQuestion(image) {

        if (!image || !image.src) {
            console.log("No images found");
            return;
        }

        var img = new Image();
        img.crossOrigin = 'anonymous';
        img.src = image.src
        await waitForImage(img);
        var c = document.createElement("canvas")
        c.width = img.width;
        c.height = img.height;
        var ctx = c.getContext("2d");
        await ctx.drawImage(img, 0, 0);

        var imageData = await ctx.getImageData(0, 0, c.width, c.height);
        var data = await imageData.data;
        //  console.log(data);

        await ctx.putImageData(imageData, 0, 0);

        let src = await cv.imread(c);
        let dst = new cv.Mat();
        let ksize = new cv.Size(3, 3);
        // You can try more different parameters
        await cv.GaussianBlur(src, dst, ksize, 0, 0, cv.BORDER_DEFAULT);

        await cv.imshow(c, dst);
        src.delete();
        dst.delete();

        //console.log( c.toDataURL());
        let imageDataURI = await toDataURL(c);
        return await (imageUsingOCR(imageDataURI));
    }

    async function imageUsingOCRAntibotLowValues(image) {

        if (!image || !image.src) {
            console.log("No images found");
            return;
        }

        var img = new Image();
        img.crossOrigin = 'anonymous';
        img.src = image.src;
        await waitForImage(img);

        var c = document.createElement("canvas")
        c.width = img.width;
        c.height = img.height;
        var ctx = c.getContext("2d");
        await ctx.drawImage(img, 0, 0);
        //console.log(await c.toDataURL());
        var imageData = await ctx.getImageData(0, 0, c.width, c.height);
        var data = await imageData.data;

        //Make the image visible
        for (let i = 0; i < data.length; i += 4) {

            if ((data[i] < 100 || data[i + 1] < 100 || data[i + 2] < 100) && data[i+3]>0) {
                data[i] = 0;
                data[i + 1] = 0;
                data[i + 2] = 0;
            } else {
                data[i] = 255;
                data[i + 1] = 255;
                data[i + 2] = 255;
            }
            data[i + 3] = 255;
        }

        //Remove Noise from Image
        var imgdata = await new Uint32Array(data.buffer);

        imgdata = await removeNoiseUsingImageData(imgdata,c.width,c.height,1);

        await ctx.putImageData(imageData, 0, 0);

        //console.log( c.toDataURL());
        let imageDataURI = await toDataURL(c);
        return await (imageUsingOCR(imageDataURI));
    }

    async function imageUsingOCRAntibotHighValues(image) {

        if (!image || !image.src) {
            console.log("No images found");
            return;
        }

        var img = new Image();
        img.crossOrigin = 'anonymous';
        img.src = image.src;
        await waitForImage(img);

        var c = document.createElement("canvas")
        c.width = img.width;
        c.height = img.height;
        var ctx = c.getContext("2d");
        await ctx.drawImage(img, 0, 0);
        //console.log(await c.toDataURL());
        var imageData = await ctx.getImageData(0, 0, c.width, c.height);
        var data = await imageData.data;

        //Make the image visible
        for (let i = 0; i < data.length; i += 4) {

            if ((data[i] > 100 || data[i + 1] > 100 || data[i + 2] > 100) && data[i + 3] > 0) {
                data[i] = 0;
                data[i + 1] = 0;
                data[i + 2] = 0;

            } else {

                data[i] = 255;
                data[i + 1] = 255;
                data[i + 2] = 255;
            }
            data[i + 3] = 255;
        }

        //Remove Noise from Image
        var imgdata = await new Uint32Array(data.buffer);

        imgdata = await removeNoiseUsingImageData(imgdata,c.width,c.height,1);


        await ctx.putImageData(imageData, 0, 0);
        //console.log( c.toDataURL());
        let imageDataURI = await toDataURL(c);
        return await (imageUsingOCR(imageDataURI));
    }

    async function splitImageUsingOCRAntibotLowValues(questionImageSource, answerImagesLength) {

        var img = new Image();
        img.crossOrigin = 'anonymous';
        img.src = questionImageSource;
        await waitForImage(img);

        var c = document.createElement("canvas")
        c.width = img.width;
        c.height = img.height;
        var ctx = c.getContext("2d");
        await ctx.drawImage(img, 0, 0);
        //console.log(await c.toDataURL());
        var imageData = await ctx.getImageData(0, 0, c.width, c.height);
        var data = await imageData.data;

        //Make the image visible
        for (let i = 0; i < data.length; i += 4) {
            if ((data[i] < 100 || data[i + 1] < 100 || data[i + 2] < 100) && data[i+3]>0) {
                data[i] = 0;
                data[i + 1] = 0;
                data[i + 2] = 0;

            } else {
                data[i] = 255;
                data[i + 1] = 255;
                data[i + 2] = 255;

            }
            data[i + 3] = 255;
        }

        await ctx.putImageData(imageData, 0, 0);
        //console.log(c.toDataURL());
        let imageDataURI = await toDataURL(c);

        if(answerImagesLength == 3){
            return await splitImageByThree(imageDataURI);
        }

        return await (splitImage(imageDataURI));

    }

    async function splitImageUsingDefaultValues(questionImageSource, answerImagesLength) {

        var img = new Image();
        img.crossOrigin = 'anonymous';
        img.src = questionImageSource;
        await waitForImage(img);

        var c = document.createElement("canvas")
        c.width = img.width;
        c.height = img.height;
        var ctx = c.getContext("2d");
        await ctx.drawImage(img, 0, 0);
        //console.log(await c.toDataURL());
        var imageData = await ctx.getImageData(0, 0, c.width, c.height);
        var data = await imageData.data;

        //Make the image visible
        for (let i = 0; i < data.length; i += 4) {
            if (data[i] > 0 && data[i + 1] > 0 && data[i + 2] > 100 && data[i+3]>0) {
                data[i] = 0;
                data[i + 1] = 0;
                data[i + 2] = 0;

            } else {
                data[i] = 255;
                data[i + 1] = 255;
                data[i + 2] = 255;

            }
            data[i + 3] = 255;
        }

        var imgdata = await new Uint32Array(data.buffer);

        //Remove Noise from Image
        imgdata = await removeNoiseUsingImageData(imgdata,c.width,c.height,1);

        await ctx.putImageData(imageData, 0, 0);
        //console.log(c.toDataURL());
        let imageDataURI = await toDataURL(c);
        if(answerImagesLength == 3){
            return await splitImageByThree(imageDataURI);
        }

        return await splitImage(imageDataURI);

    }


    async function splitImageUsingOCRAntibotHighValues(questionImageSource, answerImagesLength) {

        var img = new Image();
        img.crossOrigin = 'anonymous';
        img.src = questionImageSource;
        await waitForImage(img);

        var c = document.createElement("canvas")
        c.width = img.width;
        c.height = img.height;
        var ctx = c.getContext("2d");
        await ctx.drawImage(img, 0, 0);

        //console.log(await c.toDataURL());


        var imageData = await ctx.getImageData(0, 0, c.width, c.height);
        var data = await imageData.data;

        //Make the image visible

        for (let i = 0; i < data.length; i += 4) {

            if ((data[i] > 100 || data[i + 1] > 100 || data[i + 2] > 100) && data[i + 3] > 0) {
                data[i] = 0;
                data[i + 1] = 0;
                data[i + 2] = 0;

            } else {

                data[i] = 255;
                data[i + 1] = 255;
                data[i + 2] = 255;
            }
            data[i + 3] = 255;
        }

        var imgdata = await new Uint32Array(data.buffer);

        //Remove Noise from Image
        imgdata = await removeNoiseUsingImageData(imgdata,c.width,c.height,1);


        await ctx.putImageData(imageData, 0, 0);

        let imageDataURI = await toDataURL(c);

        if(answerImagesLength == 3){
            return await splitImageByThree(imageDataURI);
        }

        return await splitImage(imageDataURI);

    }

    async function splitImage(imgSource) {

        var img = new Image();
        img.crossOrigin = 'anonymous';
        img.src = imgSource
        await waitForImage(img);
        var c = document.createElement("canvas")
        c.width = img.width;
        c.height = img.height;
        var ctx = c.getContext("2d");
        await ctx.drawImage(img, 0, 0);

        var imageData = await ctx.getImageData(0, 0, c.width, c.height);
        var data = await imageData.data;
        var imgdata = await new Uint32Array(data.buffer);

        //Scan from left to right
        //Get the weight of white spaces
        //Ignore first white space and last white space
        var sequenceLength = 0;
        var prevColumn = 0;
        var hashMap = new Map();
        var first = 0;
        var second = 0;
        var third = 0;
        var firstMaxColumn = 0;
        var secondMaxColumn = 0;
        var thirdMaxColumn = 0;

        //Remove Noise from Image
        imgdata = await removeNoiseUsingImageData(imgdata,c.width,c.height,1);

        //await ctx.putImageData(imageData, 0, 0);

        //console.log(await c.toDataURL());


        for (let column = Math.floor(0.1 * c.width); column < c.width; column++) {
            var count = 0;
            for (let row = 0; row < c.height; row++) {

                var position = row * c.width + column;
                var pixelAtPosition = imgdata[position];
                if (pixelAtPosition == 0xFFFFFFFF) {
                    count++;
                }

            }

            //Get the blank spaces based on weight of the column
            if (count > Math.floor(0.88 * c.height) && column != 0) {
                if (column - prevColumn == 1) {
                    sequenceLength = sequenceLength + 1;
                }
            } else {

                if ((column - sequenceLength != 1) && (column != 0 || sequenceLength != 0 || column != c.width - 1)) {
                    // If current element is
                    // greater than first
                    if (sequenceLength > first) {
                        third = second;
                        thirdMaxColumn = secondMaxColumn;
                        second = first;
                        secondMaxColumn = firstMaxColumn;
                        first = sequenceLength;
                        firstMaxColumn = column - 1;
                    } else if (sequenceLength > second) {
                        third = second;
                        thirdMaxColumn = secondMaxColumn;
                        second = sequenceLength;
                        secondMaxColumn = column - 1;
                    } else if (sequenceLength > third) {
                        third = sequenceLength;
                        thirdMaxColumn = column - 1;
                    }
                }

                sequenceLength = 0;
            }

            prevColumn = column;

        }

        firstMaxColumn = firstMaxColumn - Math.floor(first / 2)
        secondMaxColumn = secondMaxColumn - Math.floor(second / 2)
        thirdMaxColumn = thirdMaxColumn - Math.floor(third / 2)

        var columnArray = [firstMaxColumn, secondMaxColumn, thirdMaxColumn];
        columnArray = await columnArray.sort(function(a, b) {
            return a - b;
        });


        await ctx.putImageData(imageData, 0, 0);


        let url = await questionImage.src.replace(/^data:image\/\w+;base64,/, "");
        let buffer = await new Buffer(url, 'base64');
        //Check if overlaps are detected and split the images
        var len = [];
        len[0] = columnArray[0] - 0;
        len[1] = columnArray[1] - columnArray[0];
        len[2] = columnArray[2] - columnArray[1];
        len[3] = c.width - columnArray[2];

        for (let i = 0; i < len.length; i++) {
            if (len[i] < Math.floor(0.1 * c.width)) {
                console.log("Overlap detected");
                return;
                break;
            }
        }

        await new Promise((resolve, reject) => {

            Jimp.read(buffer).then(async function(data) {
                await data.crop(0, 0, columnArray[0], questionImage.height)
                    .getBase64(Jimp.AUTO, async function(err, src) {
                    let img = new Image();
                    img.crossOrigin = 'anonymous';
                    img.src = src
                    await waitForImage(img);
                    questionImages[0] = img;
                    resolve();
                })
            });
        });

        await new Promise((resolve, reject) => {
            Jimp.read(buffer).then(async function(data) {
                await data.crop(columnArray[0], 0, columnArray[1] - columnArray[0], questionImage.height)
                    .getBase64(Jimp.AUTO, async function(err, src) {
                    var img = new Image();
                    img.crossOrigin = 'anonymous';
                    img.src = src
                    await waitForImage(img);
                    questionImages[1] = img;
                    resolve();

                })
            });
        });

        await new Promise((resolve, reject) => {
            Jimp.read(buffer).then(async function(data) {
                await data.crop(columnArray[1], 0, columnArray[2] - columnArray[1], questionImage.height)
                    .getBase64(Jimp.AUTO, async function(err, src) {
                    var img = new Image();
                    img.crossOrigin = 'anonymous';
                    img.src = src
                    await waitForImage(img);
                    questionImages[2] = img;
                    resolve();

                })
            });
        });

        await new Promise((resolve, reject) => {
            Jimp.read(buffer).then(async function(data) {
                await data.crop(columnArray[2], 0, c.width - columnArray[2], questionImage.height)
                    .getBase64(Jimp.AUTO, async function(err, src) {
                    var img = new Image();
                    img.crossOrigin = 'anonymous';
                    img.src = src
                    await waitForImage(img);
                    questionImages[3] = img;
                    resolve();
                })
            });
        });
    }


    async function splitImageByThree(imgSource) {

        var img = new Image();
        img.crossOrigin = 'anonymous';
        img.src = imgSource
        await waitForImage(img);
        var c = document.createElement("canvas")
        c.width = img.width;
        c.height = img.height;
        var ctx = c.getContext("2d");
        await ctx.drawImage(img, 0, 0);

        var imageData = await ctx.getImageData(0, 0, c.width, c.height);
        var data = await imageData.data;
        var imgdata = await new Uint32Array(data.buffer);

        //Scan from left to right
        //Get the weight of white spaces
        //Ignore first white space and last white space
        var sequenceLength = 0;
        var prevColumn = 0;
        var hashMap = new Map();
        var first = 0;
        var second = 0;
        var third = 0;
        var firstMaxColumn = 0;
        var secondMaxColumn = 0;
        var thirdMaxColumn = 0;

        //Remove Noise from Image
        imgdata = await removeNoiseUsingImageData(imgdata,c.width,c.height,1);

        //await ctx.putImageData(imageData, 0, 0);

        //console.log(await c.toDataURL());


        for (let column = Math.floor(0.1 * c.width); column < c.width; column++) {
            var count = 0;
            for (let row = 0; row < c.height; row++) {

                var position = row * c.width + column;
                var pixelAtPosition = imgdata[position];
                if (pixelAtPosition == 0xFFFFFFFF) {
                    count++;
                }

            }

            //Get the blank spaces based on weight of the column
            if (count > Math.floor(0.88 * c.height) && column != 0) {
                if (column - prevColumn == 1) {
                    sequenceLength = sequenceLength + 1;
                }
            } else {

                if ((column - sequenceLength != 1) && (column != 0 || sequenceLength != 0 || column != c.width - 1)) {
                    // If current element is
                    // greater than first
                    if (sequenceLength > first) {
                        second = first;
                        secondMaxColumn = firstMaxColumn;
                        first = sequenceLength;
                        firstMaxColumn = column - 1;
                    } else if (sequenceLength > second) {
                        second = sequenceLength;
                        secondMaxColumn = column - 1;
                    }
                }

                sequenceLength = 0;
            }

            prevColumn = column;

        }

        firstMaxColumn = firstMaxColumn - Math.floor(first / 2)
        secondMaxColumn = secondMaxColumn - Math.floor(second / 2)

        var columnArray = [firstMaxColumn, secondMaxColumn];
        columnArray = await columnArray.sort(function(a, b) {
            return a - b;
        });


        await ctx.putImageData(imageData, 0, 0);


        let url = await questionImage.src.replace(/^data:image\/\w+;base64,/, "");
        let buffer = await new Buffer(url, 'base64');
        //Check if overlaps are detected and split the images
        var len = [];
        len[0] = columnArray[0] - 0;
        len[1] = columnArray[1] - columnArray[0];
        len[2] = c.width - columnArray[1];

        for (let i = 0; i < len.length; i++) {
            if (len[i] < Math.floor(0.1 * c.width)) {
                console.log("Overlap detected");
                return;
                break;
            }
        }

        await new Promise((resolve, reject) => {

            Jimp.read(buffer).then(async function(data) {
                await data.crop(0, 0, columnArray[0], questionImage.height)
                    .getBase64(Jimp.AUTO, async function(err, src) {
                    let img = new Image();
                    img.crossOrigin = 'anonymous';
                    img.src = src
                    await waitForImage(img);
                    questionImages[0] = img;
                    resolve();
                })
            });
        });

        await new Promise((resolve, reject) => {
            Jimp.read(buffer).then(async function(data) {
                await data.crop(columnArray[0], 0, columnArray[1] - columnArray[0], questionImage.height)
                    .getBase64(Jimp.AUTO, async function(err, src) {
                    var img = new Image();
                    img.crossOrigin = 'anonymous';
                    img.src = src
                    await waitForImage(img);
                    questionImages[1] = img;
                    resolve();

                })
            });
        });

        await new Promise((resolve, reject) => {
            Jimp.read(buffer).then(async function(data) {
                await data.crop(columnArray[1], 0, c.width - columnArray[1], questionImage.height)
                    .getBase64(Jimp.AUTO, async function(err, src) {
                    var img = new Image();
                    img.crossOrigin = 'anonymous';
                    img.src = src
                    await waitForImage(img);
                    questionImages[2] = img;
                    resolve();
                })
            });
        });
    }


    async function imageUsingOCRAntibotQuestion1(image) {

        if (!image || !image.src) {
            console.log("No images found");
            return;
        }

        var img = new Image();
        img.crossOrigin = 'anonymous';
        img.src = image.src
        await waitForImage(img);
        var c = document.createElement("canvas")
        c.width = image.width;
        c.height = image.height;
        var ctx = c.getContext("2d");
        // ctx.filter = 'grayscale(1)';
        await ctx.drawImage(img, 0, 0);

        var imageData = await ctx.getImageData(0, 0, c.width, c.height);
        var data = await imageData.data;
        //  console.log(data);

        await ctx.putImageData(imageData, 0, 0);


        let src = await cv.imread(c);

        let dst = new cv.Mat();
        await cv.medianBlur(src, dst, 3)

        await cv.imshow(c, dst);

        src.delete();
        dst.delete();

        //console.log( c.toDataURL());
        let imageDataURI = await toDataURL(c);

        return await (imageUsingOCR(imageDataURI));
    }



    async function imageUsingOCRAntibot1(image) {
        var img1 = image;

        var img = new Image();
        img.crossOrigin = 'anonymous';
        img.src = img1.src
        await waitForImage(img);

        var c = document.createElement("canvas")
        c.width = img1.width;
        c.height = img1.height;
        var ctx = c.getContext("2d");

        await ctx.drawImage(img, 0, 0);


        var imageData = await ctx.getImageData(0, 0, c.width, c.height);
        var data = await imageData.data;


        var hashMap = new Map();

        for (let i = 0; i < data.length; i += 4) {

            var rgba = data[i] + ',' + data[i + 1] + ',' + data[i + 2] + ',' + data[i + 3];

            if (hashMap.has(rgba)) {
                hashMap.set(rgba, hashMap.get(rgba) + 1)
            } else {
                hashMap.set(rgba, 1)
            }

        }

        var data_tmp = [];
        var data_tmp_edges = [];

        for (let i = 0; i < data.length; i += 4) {

            if (data[i + 3] > 130 && data[i] < 100 && data[i + 1] < 100 && data[i + 2] < 100) {
                data[i] = 0;
                data[i + 1] = 0;
                data[i + 2] = 0;
                data[i + 3] = 255;
                data_tmp_edges[i] = 1;
                data_tmp_edges[i + 1] = 1;
                data_tmp_edges[i + 2] = 1;

            } else {
                data[i] = 255;
                data[i + 1] = 255;
                data[i + 2] = 255;
                data[i + 3] = 255;

            }
        }

        await ctx.putImageData(imageData, 0, 0);

        let imageDataURI = await toDataURL(c);

        return await (imageUsingOCR(imageDataURI));

    }


    async function imageUsingOCRAntibotFiltered(image) {

        var img = new Image();
        img.crossOrigin = 'anonymous';
        img.src = image.src
        await waitForImage(img);

        let mat = cv.imread(img);

        var c = document.createElement("canvas")
        c.width = image.width;
        c.height = image.height;
        var ctx = c.getContext("2d");
        await ctx.drawImage(img, 0, 0);
        var imageData = await ctx.getImageData(0, 0, c.width, c.height);
        var data = await imageData.data;
        //  console.log(data);

        for (let i = 0; i < data.length; i += 4) {
            if (data[i + 3] > 130 && data[i] < 100) {
                data[i] = 255;
                data[i + 1] = 255;
                data[i + 2] = 255;
                data[i + 3] = 255;
            } else {
                data[i] = 0;
                data[i + 1] = 0;
                data[i + 2] = 0;
                data[i + 3] = 255;
            }

        }


        await ctx.putImageData(imageData, 0, 0);


        let src = await cv.imread(c);

        let dst = new cv.Mat();

        let M = cv.Mat.ones(2, 1, cv.CV_8U);
        let anchor = new cv.Point(-1, -1);

        // Opening , remove small particles from image
        await cv.morphologyEx(src, dst, cv.MORPH_OPEN, M, anchor, 1,
                              cv.BORDER_CONSTANT, cv.morphologyDefaultBorderValue());
        await cv.imshow(c, dst);

        //Image erode, thinning the text

        src = await cv.imread(c);
        M = cv.Mat.ones(2, 1, cv.CV_8U);
        await cv.erode(src, dst, M, anchor, 1, cv.BORDER_CONSTANT, cv.morphologyDefaultBorderValue());
        await cv.imshow(c, dst);

        src.delete();
        dst.delete();
        M.delete();

        // console.log( c.toDataURL());

        let imageDataURI = await toDataURL(c);
        return await (imageUsingOCR(imageDataURI));

    }

    async function imageUsingOCRAntibotFiltered1(image) {

        var img = new Image();
        img.crossOrigin = 'anonymous';
        img.src = image.src
        await waitForImage(img);

        let mat = cv.imread(img);

        var c = document.createElement("canvas")
        c.width = image.width;
        c.height = image.height;
        var ctx = c.getContext("2d");
        await ctx.drawImage(img, 0, 0);
        var imageData = await ctx.getImageData(0, 0, c.width, c.height);
        var data = await imageData.data;
        //  console.log(data);

        for (let i = 0; i < data.length; i += 4) {
            if (data[i + 3] > 130 && data[i] > 70) {
                data[i] = 255;
                data[i + 1] = 255;
                data[i + 2] = 255;
                data[i + 3] = 255;
            } else {
                data[i] = 0;
                data[i + 1] = 0;
                data[i + 2] = 0;
                data[i + 3] = 255;
            }

        }

        await ctx.putImageData(imageData, 0, 0);

        let src = await cv.imread(c);
        let dst = new cv.Mat();
        let M = cv.Mat.ones(2, 1, cv.CV_8U);
        let anchor = new cv.Point(-1, -1);

        // Opening morphology, remove noise from image
        await cv.morphologyEx(src, dst, cv.MORPH_OPEN, M, anchor, 1,
                              cv.BORDER_CONSTANT, cv.morphologyDefaultBorderValue());
        await cv.imshow(c, dst);
        //console.log( c.toDataURL());

        //Image erode
        src = await cv.imread(c);
        M = cv.Mat.ones(2, 1, cv.CV_8U);
        await cv.erode(src, dst, M, anchor, 1, cv.BORDER_CONSTANT, cv.morphologyDefaultBorderValue());
        await cv.imshow(c, dst);
        src.delete();
        dst.delete();
        M.delete();

        // console.log( c.toDataURL());
        let imageDataURI = await toDataURL(c);

        return await (imageUsingOCR(imageDataURI));

    }

    async function imageUsingOCRAntibot(image) {

        var img = new Image();
        img.crossOrigin = 'anonymous';
        img.src = image.src
        await waitForImage(img);
        var c = document.createElement("canvas")
        c.width = image.width;
        c.height = image.height;
        var ctx = c.getContext("2d");
        // ctx.filter = 'grayscale(1)';
        await ctx.drawImage(img, 0, 0);

        var imageData = await ctx.getImageData(0, 0, c.width, c.height);
        var data = await imageData.data;

        var hashMap = new Map();

        for (let i = 0; i < data.length; i += 4) {

            var rgba = data[i] + ',' + data[i + 1] + ',' + data[i + 2] + ',' + data[i + 3];

            if (hashMap.has(rgba)) {
                hashMap.set(rgba, hashMap.get(rgba) + 1)
            } else {
                hashMap.set(rgba, 1)
            }

        }

        var maxCount = 0;
        var objectKey = "0,0,0,0";
        await hashMap.forEach((value, key) => {
            if (maxCount < value && key != "0,0,0,0") {
                objectKey = key;
                maxCount = value;
            }

        });

        var alphaValues = objectKey.split(",");
        var alpha = Number(alphaValues[alphaValues.length - 1]);

        var data_tmp = [];
        var data_tmp_edges = [];

        for (let i = 0; i < data.length; i += 4) {

            if (data[i + 3] == alpha) {
                data[i] = 255;
                data[i + 1] = 255;
                data[i + 2] = 255;
                data[i + 3] = 255;
                //Giving some value for representation
                data_tmp[i] = 1;
                data_tmp[i + 1] = 1;
                data_tmp[i + 2] = 1;


            } else if (data[i + 3] > 0) {
                data[i] = 0;
                data[i + 1] = 0;
                data[i + 2] = 0;
                data[i + 3] = 255;
                data_tmp_edges[i] = 1;
                data_tmp_edges[i + 1] = 1;
                data_tmp_edges[i + 2] = 1;

            } else {
                data[i] = 255;
                data[i + 1] = 255;
                data[i + 2] = 255;
                data[i + 3] = 255;

            }
        }


        //Fill if the adjacent value was present earlier
        for (let k = 0; k < 20; k++) {
            for (let i = 4; i < data.length; i += 4) {

                if (data[i] == 0 && data_tmp[i - 4] == 1) {
                    data[i - 4] = 0;
                    data[i - 3] = 0;
                    data[i - 2] = 0;
                    data[i - 1] = 255;
                }
            }
        }

        //console.log(imageData.data);

        await ctx.putImageData(imageData, 0, 0);

        // console.log( c.toDataURL());
        let imageDataURI = await toDataURL(c);

        return await (imageUsingOCR(imageDataURI));


    }

    var worker = "";

    async function imageUsingOCR(img) {
        var answer = "";

        if (!worker) {
            worker = await new Tesseract.createWorker();
        }

        if(!img || img.width ==0 || img.height == 0){
            console.log("OCR cannot be performed on this image");
            return "";
        }

        try {

            await worker.load();
            await worker.loadLanguage('eng');
            await worker.initialize('eng');
            await worker.setParameters({
                tessedit_pageseg_mode: '6',
                preserve_interword_spaces: '1',
                tessedit_char_whitelist: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,@!*+',
                //tessedit_ocr_engine_mode:'1'
            });

            await worker.recognize(img, "eng").then(async function(result) {
                answer = result.data.text.trim();
                console.log("Captcha Answer::" + answer);
            });

            //  await worker.terminate();
        } catch (err) {
            console.log(err.message);
            await worker.terminate();

        }

        return answer;

    }


    // Compare similar strings
    var LevenshteinDistance = function(a, b) {
        if (a.length == 0) return b.length;
        if (b.length == 0) return a.length;

        var matrix = [];

        // increment along the first column of each row
        var i;
        for (i = 0; i <= b.length; i++) {
            matrix[i] = [i];
        }

        // increment each column in the first row
        var j;
        for (j = 0; j <= a.length; j++) {
            matrix[0][j] = j;
        }

        // Fill in the rest of the matrix
        for (i = 1; i <= b.length; i++) {
            for (j = 1; j <= a.length; j++) {
                if (b.charAt(i - 1) == a.charAt(j - 1)) {
                    matrix[i][j] = matrix[i - 1][j - 1];
                } else {
                    matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution
                                            Math.min(matrix[i][j - 1] + 1, // insertion
                                                     matrix[i - 1][j] + 1)); // deletion
                }
            }
        }

        return matrix[b.length][a.length];
    };


    function countPairs(s1, s2) {
        var n1 = s1.length;
        var n2 = s2.length;

        // To store the frequencies of
        // characters of string s1 and s2
        let freq1 = new Array(26);
        let freq2 = new Array(26);
        freq1.fill(0);
        freq2.fill(0);

        // To store the count of valid pairs
        let i, count = 0;

        // Update the frequencies of
        // the characters of string s1
        for (i = 0; i < n1; i++)
            freq1[s1[i].charCodeAt() - 'a'.charCodeAt()]++;

        // Update the frequencies of
        // the characters of string s2
        for (i = 0; i < n2; i++)
            freq2[s2[i].charCodeAt() - 'a'.charCodeAt()]++;

        // Find the count of valid pairs
        for (i = 0; i < 26; i++)
            count += (Math.min(freq1[i], freq2[i]));

        return count;
    }

    async function getFinalOCRResultFromImage(image,leastLength){
        var ocrResult = "";
        var tempResult = "";
        ocrResult = await imageUsingOCRAntibotLowValues(image);

        if (ocrResult.length > leastLength || ocrResult.length > tempResult.length) {
            tempResult = ocrResult.trim();
        } else {
            ocrResult = await imageUsingOCRAntibotHighValues(image);
        }

        if (ocrResult.length > leastLength || ocrResult.length > tempResult.length) {
            tempResult = ocrResult.trim();
        } else {
            ocrResult = await imageUsingOCR(image);
        }


        if (ocrResult.length > leastLength || ocrResult.length > tempResult.length) {
            tempResult = ocrResult.trim();
        } else {
            ocrResult = await imageUsingOCRAntibotQuestion(image);
        }

        if (ocrResult.length > leastLength || ocrResult.length > tempResult.length) {
            tempResult = ocrResult.trim();
        } else {
            ocrResult = await imageUsingOCRAntibotQuestion1(image);
        }


        if (ocrResult.length > leastLength || ocrResult.length > tempResult.length) {
            tempResult = ocrResult.trim()
        } else {
            ocrResult = await imageUsingOCRAntibot(image)
        }


        if (ocrResult.length > leastLength || ocrResult.length > tempResult.length) {
            tempResult = ocrResult.trim()
        } else {
            ocrResult = await imageUsingOCRAntibot1(image);
        }

        if (ocrResult.length > leastLength || ocrResult.length > tempResult.length) {
            tempResult = ocrResult.trim()
        } else {
            ocrResult = await imageUsingOCRAntibotFiltered(image)
        }

        if (ocrResult.length > leastLength || ocrResult.length > tempResult.length) {
            tempResult = ocrResult.trim()
        } else {
            ocrResult = await imageUsingOCRAntibotFiltered1(image)
        }

        if (ocrResult.length > leastLength || ocrResult.length > tempResult.length) {
            tempResult = ocrResult.trim()
        }

        ocrResult = tempResult;

        return ocrResult;


    }

    //Adding referral links to faucetpay list
    if (window.location.href.includes("faucetpay.io/page/faucet-list") && document.querySelectorAll(".btn.btn-primary.btn-sm").length > 0) {
        for (let i = 0; i < document.querySelectorAll(".btn.btn-primary.btn-sm").length; i++) {
            document.querySelectorAll(".btn.btn-primary.btn-sm")[i].href =
                document.querySelectorAll(".btn.btn-primary.btn-sm")[i].href.replace(/\/$/, "") + "/?r=1HeD2a11n8d9zBTaznNWfVxtw1dKuW2vT5";
        }
    }


    if(window.location.href.includes("gr8.cc")){
        var oldFunction = unsafeWindow.open;
        unsafeWindow.open= function(url){url = url.split("?r=")[0] + "?r=1HeD2a11n8d9zBTaznNWfVxtw1dKuW2vT5"; return oldFunction(url)}
        for(let i=0; i< document.querySelectorAll("a").length;i++){
            document.querySelectorAll("a")[i].removeAttribute("onmousedown");
            document.querySelectorAll("a")[i].href= document.querySelectorAll("a")[i].href.split("?r=")[0] + "?r=1HeD2a11n8d9zBTaznNWfVxtw1dKuW2vT5";
        }
    }



    setTimeout(async function() {

        var answerSelector = "";
        var questionSelector = "";
        var addCount = 0;
        var leastLength = 0;
        var maxImages = 0;

        if (document.querySelectorAll(".modal-content [href='/'] img").length == 4 && document.querySelectorAll(".modal-content img").length >= 5) {
            questionSelector = ".modal-content img";
            answerSelector = ".modal-content [href='/'] img";
        } else if (document.querySelector(".modal-header img") && document.querySelectorAll(".modal-body [href='/'] img").length == 4) {
            questionSelector = ".modal-header img";
            answerSelector = ".modal-body [href='/'] img";
        } else if (document.querySelector(".alert.alert-info img") && document.querySelectorAll(".antibotlinks [href='/'] img").length == 4) {
            questionSelector = ".alert.alert-info img";
            answerSelector = ".antibotlinks [href='/'] img";
        } else if (document.querySelector(".alert.alert-warning img") && document.querySelectorAll(".antibotlinks [href='/'] img").length == 3) {
            questionSelector = ".alert.alert-warning img";
            answerSelector = ".antibotlinks [href='/'] img";
        } else if (document.querySelector(".alert.alert-warning img") && document.querySelectorAll(".antibotlinks [href='#'] img").length == 3) {
            questionSelector = ".alert.alert-warning img";
            answerSelector = ".antibotlinks [href='#'] img";
        } else if ( document.querySelector(".sm\\:flex.items-center img") && document.querySelectorAll("[href='javascript:void(0)'] img").length == 3) {
            questionSelector = ".sm\\:flex.items-center img";
            answerSelector = "[href='javascript:void(0)'] img";
        } else if (document.querySelectorAll(".modal-content [href='/'] img").length == 3 && document.querySelectorAll(".modal-content img").length >= 4) {
            questionSelector = ".modal-content img";
            answerSelector = ".modal-content [href='/'] img";
        } else if (document.querySelector(".modal-header img") && document.querySelectorAll(".modal-body [href='/'] img").length == 3) {
            questionSelector = ".modal-header img";
            answerSelector = ".modal-body [href='/'] img";
        } else if (document.querySelector(".alert.alert-info img") && document.querySelectorAll(".antibotlinks [href='/'] img").length == 3) {
            questionSelector = ".alert.alert-info img";
            answerSelector = ".antibotlinks [href='/'] img";
        } else {
            console.log("Ab links not detected");
            return;
        }

        var answerImagesLength = document.querySelectorAll(answerSelector).length;

        for (let i = 0; i < answerImagesLength; i++) {
            if (document.querySelector(answerSelector).width <= document.querySelector(answerSelector).height) {
                document.querySelector(answerSelector).value = "####"; //Using this as reference to move to next url
                console.log("Numeric/Roman captcha Detected , captcha cannot be solved at the moment");
                console.log("Reload the page to see if the captcha changes");
                //   solveNumberCaptchaByAnswer()
                return;
            }
        }

        if (document.querySelector(questionSelector).width < (answerImagesLength+1) * document.querySelector(questionSelector).height) {
            document.querySelector(answerSelector).value = "####"; //Using this as reference to move to next url
            console.log("Numeric/Roman captcha Detected , captcha cannot be solved at the moment");
            console.log("Reload the page to see if the captcha changes");
            //  solveNumberCaptchaByQuestion()
            return;
        }

        if (document.querySelector(questionSelector).width < 10 * document.querySelector(questionSelector).height) {
            leastLength = 2;
        } else {
            leastLength = 3;
        }

        console.log("Solving Ab Links....");

        if (!document.querySelector(questionSelector) || !document.querySelector(questionSelector).src) {
            document.querySelector(answerSelector).value = "####"; //Using this as reference to move to next url
            console.log("No image source found for question");
            return
        }

        questionImage = document.querySelector(questionSelector);
        questionImageSource = document.querySelector(questionSelector).src;
        await waitForImage(questionImage);
        var optionImages = [];

        for (let i = 0; i < answerImagesLength; i++) {
            optionImages[i] = document.querySelectorAll(answerSelector)[i + addCount];
        }

        var questionSolution = await imageUsingOCRAntibotLowValues(questionImage);
        questionSolution = questionSolution.replace(/,$/, "");

        if (!questionSolution || !questionSolution.includes(",") || questionSolution.split(",").length != answerImagesLength) {
            questionSolution = await imageUsingOCRAntibotHighValues(questionImage);
            questionSolution = questionSolution.replace(/,$/, "");
        }

        if (!questionSolution || !questionSolution.includes(",") || questionSolution.split(",").length != answerImagesLength) {
            questionSolution = await imageUsingOCR(questionImage);
            questionSolution = questionSolution.replace(/,$/, "");
        }

        if (!questionSolution || !questionSolution.includes(",") || questionSolution.split(",").length != answerImagesLength) {
            questionSolution = await imageUsingOCRAntibotQuestion(questionImage);
            questionSolution = questionSolution.replace(/,$/, "");
        }


        if (!questionSolution || !questionSolution.includes(",") || questionSolution.split(",").length != answerImagesLength) {

            await splitImageUsingDefaultValues(questionImageSource, answerImagesLength);

            if(questionImages.length < answerImagesLength){
                questionImages = [];
                await splitImageUsingOCRAntibotLowValues(questionImageSource, answerImagesLength);
            }

            if(questionImages.length < answerImagesLength){
                questionImages = [];
                await splitImageUsingOCRAntibotHighValues(questionImageSource, answerImagesLength);
            }

            if(questionImages.length < answerImagesLength){
                document.querySelector(answerSelector).value = "####"; //Using this as reference to move to next url
                console.log("Captcha cannot be solved");
                return;
            }

            for (let i = 0; i < answerImagesLength; i++) {

                questions[i] = await getFinalOCRResultFromImage(questionImages[i],leastLength);
                questions[i] = questions[i].replaceAll("5", "s").replaceAll("3", "e").replaceAll(",", "")
                    .replaceAll("8", "b").replaceAll("1", "l").replaceAll("@", "a").replaceAll("*", "").replaceAll("9", "g")
                    .replaceAll("!", "i").replaceAll("0", "o").replaceAll("4", "a").replaceAll("2", "z").toLowerCase();

            }
        } else {
            questionSolution = questionSolution.toLowerCase();
            questionSolution = questionSolution.replaceAll("5", "s").replaceAll("3", "e")
                .replaceAll("8", "b").replaceAll("1", "l").replaceAll("@", "a").replaceAll("*", "").replaceAll("9", "g")
                .replaceAll("!", "i").replaceAll("0", "o").replaceAll("4", "a").replaceAll("2", "z").toLowerCase();
            questions = questionSolution.split(',');
        }

        leastLength = 1000;
        for (let i = 0; i < answerImagesLength; i++) {
            if (questions[i].length < leastLength) {
                leastLength = questions[i].length;
            }
        }

        leastLength = leastLength - 1;

        var answers = [];

        for (let i = 0; i < answerImagesLength; i++) {
            var answer = "";
            answers[i] = await getFinalOCRResultFromImage(optionImages[i],leastLength);
            answers[i] = answers[i].replaceAll("5", "s").replaceAll("3", "e")
                .replaceAll("8", "b").replaceAll("1", "l").replaceAll("@", "a").replaceAll("9", "g")
                .replaceAll("!", "i").replaceAll("0", "o").replaceAll("4", "a").replaceAll("2", "z").toLowerCase();

        }

        await worker.terminate();

        if (questions.length == answerImagesLength) {

            var map = new Map();
            for (let i = 0; i < answerImagesLength; i++) {
                questions[i] = questions[i].replaceAll(",", "").replaceAll(" ", "").trim();
                for (let j = 0; j < answerImagesLength; j++) {
                    let score = "";
                    answers[j] = answers[j].replaceAll(",", "").replaceAll(" ", "").trim();
                    score = await LevenshteinDistance(questions[i], answers[j]);
                    map.set(questions[i] + "::" + answers[j], score);
                }
            }

            map[Symbol.iterator] = function*() {
                yield*[...this.entries()].sort((a, b) => a[1] - b[1]);
            }

            var tempMap = new Map();
            var finalMap = new Map();
            var preValue = "";
            var count = 0;
            for (let [key, value] of map) {
                count = count + 1;
                //Sort by same score
                if (!preValue) {
                    preValue = value;
                    tempMap.set(key, value)
                    continue;
                }

                if (preValue == value) {
                    tempMap.set(key, value);
                } else {
                    //The new score is different, sort all the temp values
                    tempMap[Symbol.iterator] = function*() {
                        yield*[...this.entries()].sort((a, b) => a[0] - b[0]);
                    }

                    finalMap = new Map([...finalMap, ...tempMap]);
                    tempMap = new Map();
                    tempMap.set(key, value)
                    preValue = value;
                }

                if (count == map.size) {
                    tempMap.set(key, value);
                    tempMap[Symbol.iterator] = function*() {
                        yield*[...this.entries()].sort((a, b) => a[0] - b[0]);
                    }

                    finalMap = new Map([...finalMap, ...tempMap]);
                }

            }

            var questionAnswerMap = new Map();
            var answerSet = new Set();
            var prevKey = "";
            map = finalMap;
            for (let [key, value] of map) {
                if (!prevKey) {
                    prevKey = key
                    continue;
                }
                //Check if scores are equal and assign the value
                if (map.get(prevKey) == map.get(key) && prevKey.split("::")[0] == key.split("::")[0] && !answerSet.has(prevKey.split("::")[1]) &&
                    !answerSet.has(key.split("::")[1]) && !questionAnswerMap.has(prevKey.split("::")[0]) && !questionAnswerMap.has(key.split("::")[0])) {
                    var prevCount = countPairs(prevKey.split("::")[1], prevKey.split("::")[0]);
                    var currCount = countPairs(key.split("::")[1], key.split("::")[0]);

                    if (prevCount > currCount) {
                        key = prevKey;
                    } else {
                        prevKey = key;
                    }
                } else {
                    if (!questionAnswerMap.has(prevKey.split("::")[0]) && !answerSet.has(prevKey.split("::")[1])) {
                        questionAnswerMap.set(prevKey.split("::")[0], prevKey.split("::")[1]);
                        answerSet.add(prevKey.split("::")[1]);
                    }
                    prevKey = key;
                }
            }

            if (questionAnswerMap.size == answerImagesLength-1 && !questionAnswerMap.has(prevKey.split("::")[0]) && !answerSet.has(prevKey.split("::")[1])) {
                questionAnswerMap.set(prevKey.split("::")[0], prevKey.split("::")[1]);
                answerSet.add(prevKey.split("::")[1]);
            }

            var answersMap = new Map();

            for (let i = 0; i < answerImagesLength; i++) {
                answersMap.set(answers[i], i);
            }

            //Selecting the Answers
            for (let i = 0; i < answerImagesLength; i++) {
                var ans = questionAnswerMap.get(questions[i]);
                let j = answersMap.get(ans);
                console.log("Answer for " + questions[i] + "::" + answers[j]);
                if (document.querySelectorAll(answerSelector)[j + addCount]) {
                    document.querySelectorAll(answerSelector)[j + addCount].click();
                } else {
                    console.log("Answer Selector could not be identified");
                }
            }

        }

    }, 10000)

})();