// ==UserScript== // @name Chess Plus+ // @namespace https://github.com/longkidkoolstar // @version 1.1 // @description Add Essential/Quality of life tweaks to Chess.com // @author longkidkoolstar // @license CC BY-NC-ND 4.0 // @icon https://cdn4.iconfinder.com/data/icons/chess-game-funny-colour/32/chess_game_funy_colour_ok_13-1024.png // @require https://greasyfork.org/scripts/471295-tweaking/code/Tweaking.js // @require https://greasyfork.org/scripts/471409-arrive/code/Arrive.js // @match https://www.chess.com/* // @grant GM_getValue // @grant GM_setValue // @downloadURL none // ==/UserScript== (function () { 'use strict'; // Check if Auto Queue is on var autoQueue = GM_getValue('autoQueue', false); var lichessAnalysis = GM_getValue('lichessAnalysis', true); // Function to toggle Lichess Analysis on/off function toggleLichessAnalysis() { lichessAnalysis = !lichessAnalysis; GM_setValue('lichessAnalysis', lichessAnalysis); console.log('Lichess Analysis is now ' + (lichessAnalysis ? 'on' : 'off')); } // Function to handle the Lichess Analysis button click function handleLichessAnalysisClick() { if (lichessAnalysis) { sendToLichess(); } else { alert("Tweak not Enabled in Menu. Enable it to Use!"); } } // Function to toggle Auto Queue on/off function toggleAutoQueue() { autoQueue = !autoQueue; GM_setValue('autoQueue', autoQueue); console.log('Auto Queue is now ' + (autoQueue ? 'on' : 'off')); if (autoQueue) { clickButton(); startObserver(); } else { stopObserver(); } } // Function to click the "New" button function clickButton() { var buttons = document.querySelectorAll('button'); for (var i = 0; i < buttons.length; i++) { if (buttons[i].innerText.includes('New')) { buttons[i].click(); break; } } } // Observer instance var observer = null; // Function to start observing the button function startObserver() { observer = new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { if (mutation.addedNodes.length > 0) { clickButton(); } }); }); observer.observe(document.body, { childList: true, subtree: true }); } // Function to stop observing the button function stopObserver() { if (observer) { observer.disconnect(); observer = null; } } // If Auto Queue is on, click the button whenever it becomes available if (autoQueue) { clickButton(); startObserver(); } // Main loop checkGameStatus(); function checkGameStatus() { document.arrive('.game-review-buttons-review', function () { // Find chess.com analysisButton var analysisButton = document.querySelector('.ui_v5-button-component.ui_v5-button-primary.ui_v5-button-full.game-review-buttons-button'); if (analysisButton.className == 'ui_v5-button-component ui_v5-button-primary ui_v5-button-full game-review-buttons-button') { Arrive.unbindAllArrive(); injectButton(analysisButton); checkGameStatus(); } }); } // Injects a button similar to chess.com's native "Analysis" button function injectButton(analysisButton) { // Duplicate the original button let newButton = analysisButton.cloneNode(true); // Style it and link it to the Lichess import function. newButton.childNodes[2].innerText = 'Lichess Analysis'; newButton.style.margin = '8px 0px 0px 0px'; newButton.style.padding = '0px 0px 0px 0px'; newButton.childNodes[0].classList.remove('icon-font-chess'); newButton.childNodes[0].classList.add('button-class'); newButton.classList.add('shine-hope-anim'); newButton.childNodes[0].style['height'] = '3.805rem'; newButton.addEventListener('click', handleLichessAnalysisClick); // Update the click event handler); // Append back into the DOM let parentNode = analysisButton.parentNode; parentNode.append(newButton); } // Make request to Lichess through the API (fetch) function sendToLichess() { // 1. Get PGN // Get and click download button on chess.com let downloadButton = document.getElementsByClassName('icon-font-chess share live-game-buttons-button')[0]; downloadButton.click(); // Wait for share tab to pop up document.arrive('.share-menu-tab-pgn-textarea', function () { Arrive.unbindAllArrive(); // Get PGN from text Area var PGN = document.getElementsByClassName('share-menu-tab-pgn-textarea')[0].value; // Exit out of download view (x button) document.querySelector('div.icon-font-chess.x.ui_outside-close-icon').click(); // 2. Send a POST request to Lichess to import the current game let importUrl = 'https://lichess.org/api/import'; let req = { pgn: PGN }; post(importUrl, req) .then((response) => { // Open the page on a new tab let url = response['url'] ? response['url'] : ''; if (url) { let lichessPage = window.open(url); } else alert('Could not import game'); }) .catch((e) => { alert('Error getting response from lichess.org'); throw new Error('Response error'); }); }); } // async POST function async function post(url = '', data = {}) { var formBody = []; for (var property in data) { var encodedKey = encodeURIComponent(property); var encodedValue = encodeURIComponent(data[property]); formBody.push(encodedKey + '=' + encodedValue); } const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: formBody.join('&'), }); return response.json(); } // Add a Tweaks dropdown menu var tweaksMenu = document.createElement('div'); tweaksMenu.classList.add('chess-com-tweaks-menu'); tweaksMenu.innerHTML = `
`; var expanded = false; var menuButton = tweaksMenu.querySelector('#tweaksButton'); menuButton.addEventListener('click', function (event) { event.preventDefault(); expanded = !expanded; tweaksMenu.classList.toggle('expanded', expanded); }); tweaksMenu.querySelector('#lichessAnalysisToggle').addEventListener('change', function (event) { toggleLichessAnalysis(); }); tweaksMenu.querySelector('#autoQueueToggle').addEventListener('change', function (event) { toggleAutoQueue(); }); // Add a CSS class to the document body for the chess.com theme document.body.classList.add('chess-com-theme'); document.body.appendChild(tweaksMenu); })();