♟-GabiBot-: Chess Bot And ModMenu!♟ (No Key) - SYNTAX ROBUST V3 (Expanded)

GabiBot is a ModMenu (Key Removed) with Evaluation Bar and PV Display.

Before you install, Greasy Fork would like you to know that this script contains antifeatures, which are things there for the script author's benefit, rather than yours.

This script is only fully functional after you sign up for something, like joining a group, subscribing to a channel, or liking a page.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name          ♟-GabiBot-: Chess Bot And ModMenu!♟ (No Key) - SYNTAX ROBUST V3 (Expanded)
// @namespace     http://tampermonkey.net/
// @version       1.1
// @description   GabiBot is a ModMenu (Key Removed) with Evaluation Bar and PV Display.
// @author        thehackerclient (Modified & Expanded)
// @match         https://www.chess.com/play/computer
// @license       MIT
// @icon          https://www.google.com/s2/favicons?sz=64&domain=chess.com
// @grant         none
// @antifeature   membership
// ==/UserScript==

(async function() {

    // INCREASED DELAY to 5000ms (5 seconds)
    setTimeout(async function(){
        alert("Stockfish Loaded!")
        console.log("GabiBot: Starting GUI creation sequence.");

        async function startStockfish(key){
            var menuWrap = document.createElement("div")
            menuWrap.id="menuWrap"
            var menuWrapStyle = document.createElement("style")

            // *** UPDATED: Added PV Display item ***
            menuWrap.innerHTML = [
                '<div id="topText">',
                '    <a id="modTitle">-GabiBot-</a>',
                '    <a>Ctrl+B To Hide</a>',
                '</div>',
                '<div id="itemsList">',
                '    <div name="enableHack" class="listItem">',
                '        <input class="checkboxMod" type="checkbox">',
                '        <a class="itemDescription">Enable Hack: </a>',
                '        <a class="itemState">Off</a>',
                '    </div>',
                '    <div name="autoMove" class="listItem">',
                '        <input class="checkboxMod" type="checkbox">',
                '        <a class="itemDescription">Auto Move:</a>',
                '        <a class="itemState">Off</a>',
                '    </div>',
                '    <div name="botPower" class="listItem">',
                '        <input min="1" max="15" value="12" class="rangeSlider" type="range">',
                '        <a class="itemDescription">Bot Power:</a>',
                '        <a class="itemState">12</a>',
                '    </div>',
                '    <div name="autoMoveSpeed" class="listItem">',
                '        <input min="1" max="10" value="4" class="rangeSlider" type="range">',
                '        <a class="itemDescription">Auto Move Speed:</a>',
                '        <a class="itemState">3</a>',
                '    </div>',
                '    <div name="updateSpeed" class="listItem">',
                '        <input min="1" max="10" value="8" class="rangeSlider" type="range">',
                '        <a class="itemDescription">Update Speed:</a>',
                '        <a class="itemState">8</a>',
                '    </div>',
                '    <div name="currentEvaluation" class="listItem">',
                '        <a class="itemDescription">Current Evaluation:</a>',
                '        <a class="itemState">-</a>',
                '    </div>',
                '    <div name="bestMove" class="listItem">',
                '        <a class="itemDescription">Best Move:</a>',
                '        <a class="itemState">-</a>',
                '    </div>',
                '    <div name="pvDisplay" class="listItem">',
                '        <a class="itemDescription">PV (Prediction):</a>',
                '        <a class="itemState pv-text-state" title="Principal Variation">-</a>', // Use a custom class for styling PV
                '    </div>',
                '    <div name="information" class="listItem">',
                '        <a class="itemDescription">Information: </a>',
                '        <a class="itemState">GabiHarMotion (1242 Elo)</a>',
                '    </div>',
                '</div>'
            ].join('');

            // *** UPDATED: Added CSS for Evaluation Bar and PV Display ***
            menuWrapStyle.innerHTML = [
                '#menuWrap {',
                '    font-family: monospace;',
                '    border-radius: 1vh;',
                '    z-index: 1000000;',
                '    grid: none;',
                '    display: grid;',
                '    grid-template-columns: 90%;',
                '    grid-template-rows: 15% 85%;',
                '    justify-content: center;',
                '    width: 350px;',
                '    height: 400px;',
                '    position: absolute;',
                '    border: 1px solid rgb(100 100 100);',
                '    background: rgb(16, 16, 16);',
                '    opacity: 0.98;',
                '    user-select: none;',
                '    max-width: 60vw;',
                '    top: 100px;',
                '    left: 100px;',
                '}',
                // New CSS for Principal Variation text to allow long strings
                '.pv-text-state {',
                '    color: white;',
                '    margin-left: 3%;',
                '    max-width: 50%;',
                '    overflow-x: auto;',
                '    white-space: nowrap;',
                '}',
                // New CSS for the Evaluation Bar next to the board
                '#evaluationBarWrap {',
                '    position: absolute;',
                '    height: 100%;',
                '    width: 20px;',
                '    background-color: #333;',
                '    z-index: 999999;',
                '    right: -25px;',
                '    top: 0;',
                '    border-radius: 5px;',
                '    overflow: hidden;',
                '}',
                '#evaluationBar {',
                '    width: 100%;',
                '    position: absolute;',
                '    bottom: 50%;',
                '    transition: height 0.5s ease, bottom 0.5s ease;',
                '}',
                '#evaluationBar.white-advantage {',
                '    background-color: #f7d247;', /* Gold for White */
                '}',
                '#evaluationBar.black-advantage {',
                '    background-color: #3d8bff;', /* Blue for Black */
                '}',
                '#topText {',
                '    width: 40%;',
                '    justify-self: center;',
                '    text-align: center;',
                '}',
                '#modTitle {',
                '    color: white;',
                '    justify-self: center;',
                '    margin-bottom: 5%;',
                '    font-size: 17px;',
                '}',
                '#itemsList{',
                '    overflow-x: hidden;',
                '}',
                '::-webkit-scrollbar {',
                '    width: 8px;',
                '}',
                '::-webkit-scrollbar-thumb {',
                '    background: #888;',
                '    height: 10px;',
                '}',
                '.listItem {',
                '    display: flex;',
                '    align-items: center;',
                '    margin-bottom: 6%;',
                '}',
                '.checkboxMod {',
                '    outline: #acacac 1px solid;',
                '    vertical-align: middle;',
                '    appearance: none;',
                '    border-radius: 30%;',
                '    height: 20px;',
                '    width: 20px;',
                '    top: 30%;',
                '    background: #303030;',
                '    margin-right: 5%;',
                '}',
                '.checkboxMod:checked {',
                '    background-color: #808080;',
                '}',
                '.rangeSlider {',
                '    -webkit-appearance: none;',
                '    width: 45%;',
                '    height: 15px;',
                '    border-radius: 5px;',
                '    background: #b0b0b0;',
                '    outline: none;',
                '    margin-right: 6%;',
                '}',
                '.rangeSlider::-webkit-slider-thumb {',
                '    appearance: none;',
                '    width: 17px;',
                '    height: 17px;',
                '    border-radius: 50%;',
                '    background: #505050;',
                '    cursor: pointer;',
                '}',
                '.itemDescription{',
                '    color: white;',
                '}',
                '.itemState{',
                '    color: white;',
                '    margin-left: 3%;',
                '}'
            ].join('');

            document.body.appendChild(menuWrap)
            document.body.appendChild(menuWrapStyle)

            // *** UPDATED: Added new window variable for PV ***
            window.hackEnabled = 0
            window.botPower = 12
            window.updateSpeed = 8
            window.autoMove = 0
            window.autoMoveSpeed = 4
            window.currentEvaluation = 0
            window.bestMove = ""
            window.principalVariation = "" // New variable

            var itemWrap = document.getElementById("menuWrap")

            function getElementByName(name,selector){return selector.querySelector(`[name="${name}"]`)}
            function getInputElement(element){return element.children[0]}
            function getStateElement(element){return element.children[element.children.length-1]}

            function modFunction(name,type,variable){
                var modElement = getElementByName(name,itemWrap)
                var modState = getStateElement(modElement)
                var modInput = getInputElement(modElement)

                if(type=="text"){
                    // PV is often a long string, so we use the 'title' attribute for hover text
                    if(name === "pvDisplay") modState.title = eval(variable);
                    modState.innerHTML=eval(variable)
                }
                modInput.onmouseup=e=>{
                    if(e.button==0){
                        if(type=="checkbox"){
                            modState.innerHTML=["Off","On"][Number(!modInput.checked)]
                            // Robust eval syntax
                            eval(variable + "=" + !modInput.checked)
                        }
                        if(type=="range"){
                            modState.innerHTML=modInput.value
                            // Robust eval syntax
                            eval(variable + "=" + modInput.value)
                        }
                    }
                }
            }

            modFunction("enableHack","checkbox","window.hackEnabled")
            modFunction("autoMove","checkbox","window.autoMove")

            modFunction("botPower","range","window.botPower")
            modFunction("autoMoveSpeed","range","window.autoMoveSpeed")
            modFunction("updateSpeed","range","window.updateSpeed")

            function updateTexts(){
                modFunction("currentEvaluation","text","window.currentEvaluation");
                modFunction("bestMove","text","window.bestMove");
                modFunction("pvDisplay","text","window.principalVariation"); // New line for PV
                // Robust eval syntax for text
                modFunction("information","text","context.user.username + ' (' + context.user.rating + ' Elo)'");
            }

            // *** NEW FUNCTION: Update Evaluation Bar ***
            function updateEvaluationBar(evaluation, playingAs) {
                const bar = document.getElementById("evaluationBar");
                if (!bar) return;

                let score = 0;
                // Convert mate scores (M-1, M+3) to high numerical values
                if (typeof evaluation === 'string' && evaluation.includes('M')) {
                    let mateScore = parseInt(evaluation.replace('M', ''));
                    score = Math.sign(mateScore) * 1000;
                } else {
                    score = parseFloat(evaluation);
                }

                const maxScore = 8;
                let normalizedScore = Math.max(-maxScore, Math.min(maxScore, score));

                // Scale for visual effect (0-100% height)
                let height = Math.abs(normalizedScore) * (50 / maxScore);

                // If the board is flipped (playing as Black) invert the visual logic
                if (playingAs === 2) {
                    normalizedScore *= -1;
                }

                bar.style.height = `${height}%`;
                // Position: from 50% upwards for White advantage, downwards for Black advantage
                bar.style.bottom = normalizedScore > 0 ? '50%' : `${50 - height}%`;

                // Set class for color
                bar.classList.remove('white-advantage', 'black-advantage');
                if (normalizedScore > 0.5) {
                    bar.classList.add('white-advantage');
                } else if (normalizedScore < -0.5) {
                    bar.classList.add('black-advantage');
                }
            }


            var board = document.querySelector('.board');
            var drawingBoard = document.createElement("canvas");
            var drawingBoardCTX = drawingBoard.getContext("2d");

            // *** NEW HTML FOR EVAL BAR: Check for board and add bar structure ***
            if (!board) {
                console.error("GabiBot Error: Could not find the chess board element (selector: .board). The script may be outdated.");
                return;
            }

            // Add Evaluation Bar Structure
            var evalBarWrap = document.createElement("div");
            evalBarWrap.id = "evaluationBarWrap";
            var evalBar = document.createElement("div");
            evalBar.id = "evaluationBar";
            evalBarWrap.appendChild(evalBar);
            board.appendChild(evalBarWrap);
            // End Eval Bar HTML

            // Set canvas size to match the board
            drawingBoard.width=board.clientWidth;
            drawingBoard.height=board.clientHeight;
            board.appendChild(drawingBoard);

            function clear(){
                drawingBoardCTX.clearRect(0,0,board.clientWidth,board.clientHeight)
            }

            async function executeAction(bestmove){

                // Flip the drawing board if playing as Black
                if(board.game.getPlayingAs()==2){
                    drawingBoard.style.rotate="180deg"
                }else{
                    drawingBoard.style.rotate="0deg"
                }
                clear()

                console.log(bestmove)
                bestmove = bestmove.split(" ")[1] // Format: 'bestmove e2e4' -> 'e2e4'

                var tileSize = (drawingBoard.clientWidth/8)
                var letters = ["a","b","c","d","e","f","g","h"];

                // Calculate coordinates for drawing the move arrow
                var x1 = letters.indexOf(bestmove[0])+1;
                var y1 = 9-Number(bestmove[1]);
                var x2 = letters.indexOf(bestmove[2])+1;
                var y2 = 9-Number(bestmove[3]);

                // Draw the arrow
                drawingBoardCTX.beginPath();
                drawingBoardCTX.moveTo(x1*tileSize-(tileSize/2),y1*tileSize-(tileSize/2));
                drawingBoardCTX.lineTo(x2*tileSize-(tileSize/2),y2*tileSize-(tileSize/2));
                drawingBoardCTX.lineWidth=tileSize/5;
                drawingBoardCTX.strokeStyle="#00ff0050"; // Green with transparency
                drawingBoardCTX.stroke();


                if(window.autoMove){
                    setTimeout(function(){
                        // Find the move object and execute it
                        var legalMoves = game.getLegalMoves()
                        for(var i=0;i<legalMoves.length;i++){
                            if(legalMoves[i].from==bestmove.split("")[0]+bestmove.split("")[1]){
                                if(legalMoves[i].to==bestmove.split("")[2]+bestmove.split("")[3]){
                                    var move = legalMoves[i]
                                    // 'game' object is expected to be available in the global scope of chess.com
                                    game.move({
                                        ...move,
                                        promotion: 'false',
                                        animate: false,
                                        userGenerated: true
                                    });
                                }
                            }
                        }
                    },5000-window.autoMoveSpeed*500) // Speed control: faster if autoMoveSpeed is higher
                }
            }

            var updateBotRunning = false;

            async function updateBot() {
                // Throttle the update rate based on user setting (updateSpeed)
                var updateBotInterval = setTimeout(async function(){

                    updateTexts();
                    var board = document.querySelector('.board');

                    if(!window.hackEnabled) {
                        clear();
                        updateEvaluationBar(0, 1); // Reset bar
                        updateBotRunning = false;
                        clearInterval(updateBotInterval)
                        return;
                    }

                    // Check for board availability again during updates
                    if (!board || !board.game) {
                        console.error("GabiBot Warning: Board or game object is unavailable during update. Retrying...");
                        updateBot(); // Recursively retry after a small delay
                        return;
                    }

                    // *** NEW FEATURE: Game State Awareness ***
                    if (board.game.isGameOver()) {
                        var result = board.game.getGameOverReason();
                        window.currentEvaluation = `GAME OVER: ${result.toUpperCase()}`;
                        window.bestMove = "No moves possible.";
                        window.principalVariation = "Game has ended.";
                        clear(); // Clear the arrow
                        updateEvaluationBar(0, board.game.getPlayingAs()); // Reset bar
                        updateTexts(); // Update the menu
                        // Stop analysis loop until a new game starts
                        updateBotRunning = false;
                        return;
                    }
                    // *** END Game State Awareness ***

                    updateBotRunning = true;


                    // Get FEN from the chess.com game object
                    var FEN = board.game.getFEN();
                    var depth = window.botPower;

                    // Call the Stockfish API
                    // Note: The 'pv' (Principal Variation) should be returned by this API
                    let response = await fetch(`https://stockfish.online/api/s/v2.php?fen=${encodeURIComponent(FEN)}&depth=${depth}`);
                    let data = await response.json();

                    window.bestMove = data.bestmove || "Calculating...";
                    window.currentEvaluation = data.evaluation || "-";
                    // *** NEW FEATURE: Principal Variation (PV) Display ***
                    window.principalVariation = data.pv || "PV not available";

                    var bestmove = data.bestmove;

                    // *** UPDATED: Call Evaluation Bar update ***
                    var playingAs = board.game.getPlayingAs(); // 1 for White, 2 for Black
                    updateEvaluationBar(window.currentEvaluation, playingAs);


                    if (bestmove && bestmove.includes("bestmove")) {
                         executeAction(bestmove);
                    }


                    // Use requestAnimationFrame for smooth GUI updates (if any) and recursive call
                    requestAnimationFrame(()=>{
                        if (updateBotRunning) updateBot();
                    });
                },1100-(window.updateSpeed*100))
            }

            // Re-run the bot logic whenever a change event happens (like a user move)
            document.addEventListener("change", updateBot);

            // Start the bot immediately without a key check
            updateBot();


            var draggingElement = document.getElementById("modTitle")
            dragElement(itemWrap,draggingElement);

            // Function to allow the menu window to be dragged
            function dragElement(elmnt,elmnt2) {
                var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
                elmnt2.onmousedown = dragMouseDown;
                function dragMouseDown(e) {
                    e = e || window.event;
                    e.preventDefault();
                    pos3 = e.clientX;
                    pos4 = e.clientY;
                    document.onmouseup = closeDragElement;
                    document.onmousemove = elementDrag;
                }
                function elementDrag(e) {
                    e = e || window.event;
                    e.preventDefault();
                    pos1 = pos3 - e.clientX;
                    pos2 = pos4 - e.clientY;
                    pos3 = e.clientX;
                    pos4 = e.clientY;
                    elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
                    elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
                }
                function closeDragElement() {
                    document.onmouseup = null;
                    document.onmousemove = null;
                }
            }

            // Keyboard shortcut for menu visibility
            var menuHidden = 0
            document.addEventListener("keyup",(e)=>{
                if(e.key=="b"&&e.ctrlKey){
                    if(!menuHidden){menuWrap.style.display="none"}
                    else{menuWrap.style.display="grid"}
                    menuHidden^=1
                }
            })
        }

        // Start the bot with a dummy key (key is ignored in this version)
        await startStockfish("key_removed")

    },5000) // Initial 5 second delay to wait for chess.com to load
})();