您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Chess.com Stockfish Bot with Enhanced Auto-Match using Stockfish Online API
当前为
// ==UserScript== // @name Chess.com Stockfish Bot // @namespace BottleOrg Scripts // @version 1.6.2.19 // @description Chess.com Stockfish Bot with Enhanced Auto-Match using Stockfish Online API // @author [REDACTED] // @license Chess.com Bot/Cheat by [REDACTED] // @match https://www.chess.com/play/* // @match https://www.chess.com/game/* // @match https://www.chess.com/puzzles/* // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== // @grant GM_getValue // @grant GM_setValue // @grant GM_xmlhttpRequest // @grant GM_getResourceText // @grant GM_registerMenuCommand // @require https://greasyfork.org/scripts/445697/code/index.js // @require https://code.jquery.com/jquery-3.6.0.min.js // @run-at document-start // ==/UserScript== const currentVersion = '1.6.2.19'; const scriptURL = 'https://greasyfork.org/en/scripts/526240-chess-com-stockfish-bot'; const stockfishAPI_URI = "https://stockfish.online/api/s/v2.php"; // Function to check if the Stockfish API domain is accessible function checkStockfishDomainAccess(callback) { console.log("Checking if Stockfish API domain is accessible..."); const testFen = "rnbqkbnr/pppppppp/5n2/8/8/5N2/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; const testDepth = 1; const testUrl = `${stockfishAPI_URI}?fen=${encodeURIComponent(testFen)}&depth=${testDepth}`; GM_xmlhttpRequest({ method: "GET", url: testUrl, onload: function(response) { if (response.status === 200) { console.log("Stockfish API domain is accessible."); callback(true); } else { console.error("Stockfish API request failed with status:", response.status); callback(false); } }, onerror: function(error) { console.error("Stockfish API request blocked or failed:", error); // Check if the error indicates the request was blocked (e.g., by an ad-blocker) if (error && error.statusText && error.statusText.includes("net::ERR_BLOCKED_BY_CLIENT")) { alert("Allow domain request to stockfish.online for the script to be able to work!"); } else { alert("Failed to connect to Stockfish API. Please check your internet connection or allow requests to stockfish.online."); } callback(false); }, timeout: 5000 // Set a timeout of 5 seconds for the test request }); } function checkForUpdate() { console.log("Checking for script updates..."); GM_xmlhttpRequest({ method: "GET", url: scriptURL, onload: function(response) { if (response.status === 200) { const html = response.responseText; const versionMatch = html.match(/@version\s+([\d.]+)/); if (versionMatch && versionMatch[1]) { const latestVersion = versionMatch[1]; console.log("Latest version found:", latestVersion); if (compareVersions(latestVersion, currentVersion) > 0) { const message = `New Version: ${latestVersion} has been uploaded. Would you like me to take you there or continue with old version ${currentVersion}? (Not recommended for stability)`; if (confirm(message)) { window.location.href = scriptURL; } else { console.log("User chose to continue with old version."); } } else { console.log("No newer version available."); } } else { console.error("Could not find version in Greasy Fork page."); } } else { console.error("Failed to fetch script page:", response.status); } }, onerror: function(error) { console.error("Error checking for update:", error); } }); } function compareVersions(v1, v2) { const parts1 = v1.split('.').map(Number); const parts2 = v2.split('.').map(Number); for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) { const p1 = parts1[i] || 0; const p2 = parts2[i] || 0; if (p1 > p2) return 1; if (p1 < p2) return -1; } return 0; } function main() { var myVars = document.myVars = { autoMove: false, autoRun: false, autoMatch: false, delay: 0.1, hasAutoMatched: false, gameEnded: false, stockfishAccessible: false }; var myFunctions = document.myFunctions = {}; var currentStockfishVersion = "Stockfish API"; var uiElementsLoaded = false; // Check Stockfish API access before proceeding checkStockfishDomainAccess(function(isAccessible) { myVars.stockfishAccessible = isAccessible; if (!isAccessible) { console.error("Stockfish API is not accessible. Script functionality will be limited."); return; // Stop script execution if Stockfish API is not accessible } proceedWithMainLogic(); }); function proceedWithMainLogic() { var stop_b = 0, stop_w = 0, s_br = 0, s_br2 = 0, s_wr = 0, s_wr2 = 0; myFunctions.rescan = function() { console.log("Rescanning board..."); var boardElement = document.querySelector('chess-board, wc-chess-board'); if (!boardElement) { console.warn("No board element found. Using default FEN."); return "rnbqkbnr/pppppppp/5n2/8/8/5N2/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; } var pieces = $(boardElement).find(".piece").map(function() { return this.className; }).get(); if (!pieces.length) { console.warn("No pieces found. Using default FEN."); return "rnbqkbnr/pppppppp/5n2/8/8/5N2/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; } var boardArray = Array(64).fill(''); pieces.forEach(piece => { var classes = piece.split(' '); var squareClass = classes.find(c => c.startsWith('square-')); var pieceClass = classes.find(c => /^[wb][prnbqk]$/.test(c)); if (squareClass && pieceClass) { var squareNum = squareClass.replace('square-', ''); var file = parseInt(squareNum[0]) - 1; var rank = parseInt(squareNum[1]) - 1; var square = (7 - rank) * 8 + file; if (square >= 0 && square < 64) { var pieceChar = {'wp': 'P', 'bp': 'p', 'wr': 'R', 'br': 'r', 'wn': 'N', 'bn': 'n', 'wb': 'B', 'bb': 'b', 'wq': 'Q', 'bq': 'q', 'wk': 'K', 'bk': 'k'}[pieceClass]; boardArray[square] = pieceChar; } } }); var fen = ''; for (var i = 0; i < 64; i++) { if (i % 8 === 0 && i > 0) fen += '/'; var piece = boardArray[i]; if (!piece) { var emptyCount = 1; while (i + 1 < 64 && !boardArray[i + 1] && (i + 1) % 8 !== 0) { emptyCount++; i++; } fen += emptyCount; } else { fen += piece; } } var turn = $('.coordinates').children().first().text() === "1" ? 'b' : 'w'; var castling = (stop_w ? '' : 'KQ') + (stop_b ? '' : 'kq') || '-'; fen += ` ${turn} ${castling} - 0 1`; console.log("Generated FEN:", fen); return fen; }; myFunctions.color = function(dat) { console.log("myFunctions.color CALLED with:", dat); const bestmoveUCI = dat.split(' ')[1]; console.log("Extracted bestmove UCI:", bestmoveUCI); if (myVars.autoMove) myFunctions.movePiece(bestmoveUCI); else myFunctions.highlightMove(bestmoveUCI); isThinking = false; myFunctions.spinner(); }; myFunctions.highlightMove = function(bestmoveUCI) { var res1 = bestmoveUCI.substring(0, 2), res2 = bestmoveUCI.substring(2, 4); $(board).prepend(`<div class="highlight square-${res2}" style="background-color: rgb(235, 97, 80); opacity: 0.71;"></div>`) .children(':first').delay(1800).queue(function() { $(this).remove(); }); $(board).prepend(`<div class="highlight square-${res1}" style="background-color: rgb(235, 97, 80); opacity: 0.71;"></div>`) .children(':first').delay(1800).queue(function() { $(this).remove(); }); console.log("Highlighted:", bestmoveUCI); }; myFunctions.movePiece = function(bestmoveUCI) { console.log("movePiece CALLED with:", bestmoveUCI); if (!board || !board.game) { console.error("Board or board.game not initialized!"); return; } const fromSquare = bestmoveUCI.substring(0, 2); const toSquare = bestmoveUCI.substring(2, 4); const legalMoves = board.game.getLegalMoves(); console.log("Legal moves:", legalMoves); let foundMove = legalMoves.find(move => move.from === fromSquare && move.to === toSquare); if (foundMove) { console.log("Executing move:", foundMove); board.game.move({ ...foundMove, promotion: 'q', animate: true, userGenerated: true }); console.log("Move executed:", bestmoveUCI); } else { console.warn("No legal move found for:", bestmoveUCI); } }; myFunctions.reloadChessEngine = function() { console.log("Reload not needed for API."); }; myFunctions.loadChessEngine = function() { console.log("Using Stockfish API."); if (uiElementsLoaded) $('#engineVersionText')[0].innerHTML = "Engine: <strong>Stockfish API</strong>"; }; myFunctions.fetchBestMoveFromAPI = function(fen, depth) { if (!myVars.stockfishAccessible) { console.error("Stockfish API is not accessible. Cannot fetch best move."); isThinking = false; myFunctions.spinner(); return; } const apiURL = `${stockfishAPI_URI}?fen=${encodeURIComponent(fen)}&depth=${depth}`; console.log(`Fetching from: ${apiURL}`); GM_xmlhttpRequest({ method: "GET", url: apiURL, onload: function(response) { if (response.status === 200) { try { const jsonResponse = JSON.parse(response.responseText); if (jsonResponse.success) { console.log("API Response:", jsonResponse); myFunctions.color(jsonResponse.bestmove); } else { console.error("API failed:", jsonResponse); isThinking = false; myFunctions.spinner(); } } catch (e) { console.error("API parse error:", e); isThinking = false; myFunctions.spinner(); } } else { console.error("API error:", response.status); isThinking = false; myFunctions.spinner(); } }, onerror: function(error) { console.error("API request error:", error); isThinking = false; myFunctions.spinner(); } }); }; myFunctions.startNewGame = function() { console.log("Starting new game..."); const modalNewGameButton = $('.game-over-modal-content .game-over-buttons-component .cc-button-component:not([aria-label="Rematch"])'); if (modalNewGameButton.length) { modalNewGameButton[0].click(); console.log("Clicked New <x> min button from game-over modal."); myVars.hasAutoMatched = true; return; } const newGameButton = $('.game-over-buttons-component .cc-button-component:not([aria-label="Rematch"])'); if (newGameButton.length) { newGameButton[0].click(); console.log("Clicked New <x> min button from game-over."); myVars.hasAutoMatched = true; return; } const newGameTab = $('[data-tab="newGame"]'); if (newGameTab.length) { newGameTab[0].click(); console.log("Clicked New Game tab."); setTimeout(() => { const playButton = $('.cc-button-component.cc-button-primary.cc-button-xx-large.cc-button-full'); if (playButton.length) { playButton[0].click(); console.log("Clicked Play button."); myVars.hasAutoMatched = true; // Wait for the modal to appear and then attempt to click the guest button setTimeout(() => { attemptGuestButtonClick(); }, 2000); // Increased delay to ensure modal loads } else { console.error("Play button not found!"); } }, 500); } else { console.error("New Game tab not found!"); } }; myFunctions.declineRematch = function() { const declineButton = $('.cc-button-component.cc-button-secondary[aria-label="Decline"], .cc-button-component.cc-button-secondary:contains("Decline")'); if (declineButton.length) { declineButton[0].click(); console.log("Declined rematch."); return true; } else { console.log("No rematch decline button found."); return false; } }; var lastValue = 11, MAX_DEPTH = 15, MIN_DEPTH = 1; myFunctions.runChessEngine = function(depth) { if (!myVars.stockfishAccessible) { console.error("Stockfish API is not accessible. Cannot run chess engine."); return; } depth = Math.max(MIN_DEPTH, Math.min(MAX_DEPTH, depth)); var fen = myFunctions.rescan(); console.log(`Analyzing FEN: ${fen}, Depth: ${depth}`); isThinking = true; myFunctions.spinner(); myFunctions.fetchBestMoveFromAPI(fen, depth); lastValue = depth; updateDepthDisplay(); }; function updateDepthDisplay() { if (uiElementsLoaded && $('#depthText')[0]) $('#depthText')[0].innerHTML = `Depth: <strong>${lastValue}</strong>`; } myFunctions.incrementDepth = function(delta) { lastValue = Math.max(MIN_DEPTH, Math.min(MAX_DEPTH, lastValue + delta)); updateDepthDisplay(); }; myFunctions.autoRun = function() { if (board && board.game && board.game.getTurn() === board.game.getPlayingAs()) { myFunctions.runChessEngine(lastValue); } }; document.onkeydown = function(e) { switch (e.keyCode) { case 81: myFunctions.runChessEngine(1); break; // Q case 87: myFunctions.runChessEngine(2); break; // W case 69: myFunctions.runChessEngine(3); break; // E case 82: myFunctions.runChessEngine(4); break; // R case 84: myFunctions.runChessEngine(5); break; // T case 89: myFunctions.runChessEngine(6); break; // Y case 85: myFunctions.runChessEngine(7); break; // U case 73: myFunctions.runChessEngine(8); break; // I case 79: myFunctions.runChessEngine(9); break; // O case 80: myFunctions.runChessEngine(10); break; // P case 65: myFunctions.runChessEngine(11); break; // A case 83: myFunctions.runChessEngine(12); break; // S case 68: myFunctions.runChessEngine(13); break; // D case 70: myFunctions.runChessEngine(14); break; // F case 71: myFunctions.runChessEngine(15); break; // G case 187: myFunctions.incrementDepth(1); break; // + case 189: myFunctions.incrementDepth(-1); break;// - } }; myFunctions.spinner = function() { if (uiElementsLoaded && $('#overlay')[0]) { $('#overlay')[0].style.display = isThinking ? 'block' : 'none'; } }; let dynamicStyles = null; function addAnimation(body) { if (!dynamicStyles) { dynamicStyles = document.createElement('style'); document.head.appendChild(dynamicStyles); } dynamicStyles.sheet.insertRule(body, dynamicStyles.length); } var loaded = false; myFunctions.loadEx = function() { try { console.log("Attempting to load UI..."); board = document.querySelector('chess-board, wc-chess-board'); var div = document.createElement('div'); div.innerHTML = ` <div style="margin: 8px; padding: 10px; background: white; border: 1px solid #000; border-radius: 5px;"> <p id="depthText">Depth: <strong>${lastValue}</strong></p> <button id="depthMinus">-</button> <button id="depthPlus">+</button> <p style="font-size: 12px;">Keys: Q-G (1-15), +/-</p> <p id="engineVersionText">Engine: <strong>Stockfish API</strong></p> <label><input type="checkbox" id="autoRun"> Auto Run</label><br> <label><input type="checkbox" id="autoMove"> Auto Move</label><br> <label><input type="checkbox" id="autoMatch"> Auto-Match</label><br> <label>Min Delay (s): <input type="number" id="timeDelayMin" min="0.1" value="0.1" step="0.1" style="width: 60px;"></label><br> <label>Max Delay (s): <input type="number" id="timeDelayMax" min="0.1" value="1" step="0.1" style="width: 60px;"></label><br> <p style="color: blue; font-size: 12px; margin-top: 5px;">Script is made for guest account since Stockfish makes unhuman moves!</p> </div>`; div.style.cssText = 'position: fixed; top: 10px; right: 10px; z-index: 10000;'; document.body.appendChild(div); setTimeout(() => { $('#depthPlus').off('click').on('click', () => myFunctions.incrementDepth(1)); $('#depthMinus').off('click').on('click', () => myFunctions.incrementDepth(-1)); $('#autoMatch').on('change', () => { myVars.autoMatch = $('#autoMatch')[0].checked; if (myVars.autoMatch && !myVars.hasAutoMatched) { myFunctions.startNewGame(); } }); console.log("Event listeners bound."); }, 100); var spinCont = document.createElement('div'); spinCont.id = 'overlay'; spinCont.style.cssText = 'display: none; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);'; div.appendChild(spinCont); var spinr = document.createElement('div'); spinr.style.cssText = "height: 64px; width: 64px; animation: rotate 0.8s infinite linear; border: 5px solid firebrick; border-right-color: transparent; border-radius: 50%;"; spinCont.appendChild(spinr); addAnimation(`@keyframes rotate { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }`); loaded = true; uiElementsLoaded = true; console.log("UI loaded successfully."); myFunctions.loadChessEngine(); checkForUpdate(); } catch (error) { console.error("loadEx error:", error); } }; function other(delay) { var endTime = Date.now() + delay; var timer = setInterval(() => { if (Date.now() >= endTime) { myFunctions.autoRun(); canGo = true; clearInterval(timer); } }, 10); } // Function to attempt clicking the guest button with retries function attemptGuestButtonClick(attempts = 10, delay = 1000) { if (attempts <= 0) { console.error("Failed to click 'Play as a Guest' button after all retries."); return; } // Check if the modal is present const modal = $('.authentication-modal-content'); if (!modal.length) { console.log("Modal not found, attempts left:", attempts); setTimeout(() => attemptGuestButtonClick(attempts - 1, delay), delay); return; } const guestButton = $('#guest-button.authentication-intro-guest'); if (guestButton.length && !myVars.hasAutoMatched) { console.log("Found 'Play as a Guest' button:", guestButton[0]); guestButton[0].click(); // Use jQuery's click method console.log("Clicked 'Play as a Guest' button."); setTimeout(() => { const playButton = $('.cc-button-component.cc-button-primary.cc-button-xx-large.cc-button-full'); if (playButton.length) { playButton[0].click(); console.log("Clicked Play button after guest prompt."); myVars.hasAutoMatched = true; } else { console.error("Play button not found after guest prompt!"); } }, 1500); // Increased delay to 1500ms to ensure page transition } else { console.log("Guest button not found or already clicked, attempts left:", attempts); setTimeout(() => attemptGuestButtonClick(attempts - 1, delay), delay); } } const waitForChessBoard = setInterval(() => { if (!loaded) { myFunctions.loadEx(); } else { clearInterval(waitForChessBoard); if (!board) board = document.querySelector('chess-board, wc-chess-board'); myVars.autoRun = $('#autoRun')[0].checked; myVars.autoMove = $('#autoMove')[0].checked; myVars.autoMatch = $('#autoMatch')[0].checked; let minDel = parseFloat($('#timeDelayMin')[0].value) || 0.1; let maxDel = parseFloat($('#timeDelayMax')[0].value) || 1; myVars.delay = Math.random() * (maxDel - minDel) + minDel; myFunctions.spinner(); myTurn = board && board.game && board.game.getTurn() === board.game.getPlayingAs(); updateDepthDisplay(); const gameOver = document.querySelector('.game-over-message-component') || document.querySelector('.game-result'); if (gameOver && !myVars.gameEnded) { console.log("Game ended detected (chat or result)."); myVars.gameEnded = true; if (myVars.autoMatch) { myFunctions.declineRematch(); setTimeout(() => { myVars.hasAutoMatched = false; myFunctions.startNewGame(); }, 1000); } } else if (!gameOver && myVars.gameEnded) { myVars.gameEnded = false; } const gameOverModal = $('.game-over-modal-content'); if (myVars.autoMatch && gameOverModal.length && !myVars.hasAutoMatched) { console.log("Game over modal detected."); const newGameButton = gameOverModal.find('.game-over-buttons-component .cc-button-component:not([aria-label="Rematch"])'); if (newGameButton.length) { newGameButton[0].click(); console.log("Clicked New <x> min button from game-over modal in interval."); myVars.hasAutoMatched = true; } else { console.error("New <x> min button not found in game-over modal!"); } } if (myVars.autoRun && canGo && !isThinking && myTurn) { canGo = false; other(myVars.delay * 1000); } } }, 500); setTimeout(() => { if (!loaded) myFunctions.loadEx(); }, 2000); } } var isThinking = false, canGo = true, myTurn = false, board; window.addEventListener("load", () => main());