Last active
June 11, 2025 08:17
-
-
Save sellithy/0faff5c8bb5789e00a1e9134c2daced4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @author sellithy | |
| // @name Chess.com → WintrChess | |
| // @namespace http://tampermonkey.net/ | |
| // @version 1.1 | |
| // @description Insert a WintrChess favicon button on Chess.com; auto-paste PGN on WintrChess, and starts an analysis | |
| // @match https://www.chess.com/game/* | |
| // @match https://www.chess.com/game/live/* | |
| // @match https://www.chess.com/game/daily/* | |
| // @match https://www.chess.com/analysis/game/live/* | |
| // @match https://wintrchess.com/analysis* | |
| // @require https://cdn.jsdelivr.net/npm/wait-for-the-element@4 | |
| // @downloadURL https://gist.github.com/sellithy/0faff5c8bb5789e00a1e9134c2daced4/raw/108d3d804be20871e437c55ec79df5110e00bef6/analyzeOnWintrChess.user.js | |
| // @grant GM_openInTab | |
| // @run-at document-idle | |
| // ==/UserScript== | |
| const {waitForTheElement} = this["wait-for-the-element"]; | |
| (function() { | |
| 'use strict'; | |
| const WINTR_CHESS_BUTTON_ID = 'tm-wintrchess-btn'; | |
| async function insertWintrChessButton() { | |
| const shareBtn = await waitForTheElement('button[aria-label^="Share"]', {timeout: 1000}); | |
| if (!shareBtn) { | |
| console.error('Timeout waiting for Share button'); | |
| return; | |
| } | |
| if (document.getElementById(WINTR_CHESS_BUTTON_ID)) return; | |
| const wintrBtnImage = document.createElement('img'); | |
| wintrBtnImage.src = 'https://www.google.com/s2/favicons?domain=wintrchess.com&sz=256'; | |
| wintrBtnImage.style.height = shareBtn.clientHeight + 'px'; | |
| wintrBtnImage.style.width = 'auto'; | |
| wintrBtnImage.style.border = 'none' | |
| const wintrBtn = document.createElement('button'); | |
| wintrBtn.id = WINTR_CHESS_BUTTON_ID; | |
| wintrBtn.classList.add('live-game-buttons-button'); | |
| wintrBtn.style.height = wintrBtnImage.style.height; | |
| wintrBtn.style.padding = '0'; | |
| wintrBtn.style['background-color'] = 'transparent'; | |
| wintrBtn.style.border = 'none'; | |
| wintrBtn.appendChild(wintrBtnImage); | |
| wintrBtn.onclick = async () => { | |
| shareBtn.click(); | |
| const dialog = await waitForTheElement('div[role="dialog"]'); | |
| if (!dialog) { | |
| console.error('Share modal did not appear.'); | |
| return; | |
| } | |
| const pgnTab = Array.from(dialog.querySelectorAll('button')) | |
| .find(b => b.textContent.trim() === 'PGN'); | |
| if (!pgnTab) { | |
| console.error('PGN tab not found.'); | |
| return; | |
| } | |
| pgnTab.click(); | |
| const textarea = await waitForTheElement('textarea'); | |
| if (!textarea) { | |
| console.error('PGN textarea did not load.'); | |
| return; | |
| } | |
| const pgn = textarea.value.trim(); | |
| if (!pgn) { | |
| console.error('Failed to retrieve PGN.'); | |
| return; | |
| } | |
| const closeBtn = dialog.querySelector('button[aria-label="Close"]'); | |
| if (closeBtn) closeBtn.click(); | |
| const url = 'https://wintrchess.com/analysis?pgn=' + encodeURIComponent(pgn); | |
| GM_openInTab(url, false); | |
| }; | |
| shareBtn.parentNode.insertBefore(wintrBtn, shareBtn.nextSibling); | |
| } | |
| async function clickAnalyze() { | |
| const pgnParam = new URLSearchParams(location.search).get('pgn'); | |
| if (!pgnParam) return; | |
| const decodedPgn = decodeURIComponent(pgnParam); | |
| const textarea = await waitForTheElement('textarea', {timeout: 1000}); | |
| if (!textarea) { | |
| console.error('Failed to find PGN textarea on WintrChess.'); | |
| return; | |
| } | |
| const select = await waitForTheElement('select:has(option[value="PGN"])', {timeout: 1000}) | |
| if (!select) { | |
| console.error('Failed to find select on WintrChess.'); | |
| return; | |
| } | |
| if (select.value != "PGN") { | |
| select.value = "PGN"; | |
| select.dispatchEvent(new Event("change", { bubbles: true })); | |
| await new Promise(resolve => setTimeout(resolve, 1000)); | |
| } | |
| Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value") | |
| .set.call(textarea, decodedPgn); | |
| textarea.dispatchEvent(new Event("input", { bubbles: true })); | |
| const btn = Array.from(document.querySelectorAll('button')) | |
| .find(b => b.textContent.trim().toLowerCase() === 'analyse'); | |
| if (!btn) { | |
| console.error('Analyse button not found.'); | |
| return; | |
| } | |
| await new Promise(resolve => setTimeout(resolve, 1000)); | |
| btn.click(); | |
| } | |
| (function main() { | |
| if (location.hostname === 'wintrchess.com') { | |
| clickAnalyze(); | |
| } else { | |
| insertWintrChessButton(); | |
| } | |
| })(); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment