Advertisement
Guest User

Auto Guess Button UserScript for skribbl.io

a guest
May 17th, 2024
254
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name     WordSleuth
  3. // @namespace  https://greasyfork.org/en/users/1084087-fermion
  4. // @version   0.6.2
  5. // @description A script that helps you guess words in skribblio
  6. // @author    fermion
  7. // @match    http*://www.skribbl.io/*
  8. // @match    http*://skribbl.io/*
  9. // @icon     https://www.google.com/s2/favicons?sz=64&domain=skribbl.io
  10. // @grant    GM_setValue
  11. // @grant    GM_getValue
  12. // @license MIT
  13. // @downloadURL https://update.greasyfork.org/scripts/467016/WordSleuth.user.js
  14. // @updateURL https://update.greasyfork.org/scripts/467016/WordSleuth.meta.js
  15. // ==/UserScript==
  16. (function() {
  17.   'use strict';
  18.  
  19.   class WordSleuth {
  20.     constructor() {
  21.       this.correctAnswers = GM_getValue('correctAnswers', []);
  22.       this.possibleWords = [];
  23.       this.tempWords = [];
  24.       this.alreadyGuessed = [];
  25.       this.closeWord = '';
  26.       this.players = {};
  27.       this.createParentElement();
  28.       this.createGuessElement();
  29.       this.createExportButton();
  30.       this.fetchAndStoreLatestWordlist();
  31.       this.observeHintsAndInput();
  32.       this.observePlayers();
  33.       this.createAutoGuessToggle();
  34.       this.autoGuessing = false;
  35.       this.autoGuessInterval = null;
  36.       this.createDelayInput();
  37.       this.autoGuessDelay = 1500;
  38.  
  39.       this.adminList = [1416559798, 2091817853];
  40.     }
  41.  
  42.     createAutoGuessToggle() {
  43.       this.autoGuessButton = document.createElement('button');
  44.       this.autoGuessButton.innerHTML = 'Auto Guess: OFF';
  45.       this.autoGuessButton.style = 'position: absolute; bottom: calc(100% + 10px); right: 100px; z-index: 9999; padding: 5px 10px; font-size: 12px; background-color: #333; color: #fff; border: none; border-radius: 5px;';
  46.       this.parentElement.appendChild(this.autoGuessButton);
  47.       this.autoGuessButton.addEventListener('click', () => this.toggleAutoGuess());
  48.     }
  49.  
  50.  
  51.     toggleAutoGuess() {
  52.       this.autoGuessing = !this.autoGuessing;
  53.       this.autoGuessButton.innerHTML = `Auto Guess: ${this.autoGuessing ? 'ON' : 'OFF'}`;
  54.  
  55.       if (this.autoGuessing) {
  56.         this.startAutoGuessing();
  57.       } else {
  58.         this.stopAutoGuessing();
  59.       }
  60.     }
  61.  
  62.     startAutoGuessing() {
  63.       if (this.autoGuessInterval) return;
  64.  
  65.       this.autoGuessInterval = setInterval(() => {
  66.         if (this.possibleWords.length > 0) {
  67.           const inputElem = document.querySelector('#game-chat input[data-translate="placeholder"]');
  68.           const formElem = document.querySelector('#game-chat form');
  69.           inputElem.value = this.possibleWords.shift();
  70.           formElem.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));
  71.           if (this.possibleWords.length === 0) {
  72.             this.stopAutoGuessing();
  73.           }
  74.         }
  75.       }, this.autoGuessDelay);
  76.     }
  77.  
  78.     stopAutoGuessing() {
  79.       clearInterval(this.autoGuessInterval);
  80.       this.autoGuessInterval = null;
  81.     }
  82.  
  83.     createDelayInput() {
  84.       this.delayInput = document.createElement('input');
  85.       this.delayInput.type = 'number';
  86.       this.delayInput.min = '500';
  87.       this.delayInput.value = '2000';
  88.       this.delayInput.style = 'position: absolute; bottom: calc(100% + 10px); right: 220px; z-index: 9999; padding: 10px; width: 75px; font-size: 12px; background-color: #fff; color: #333; border: 1px solid #ccc; border-radius: 5px; height: 25px;';
  89.       this.parentElement.appendChild(this.delayInput);
  90.  
  91.       this.delayInput.addEventListener('keydown', (event) => {
  92.         if (event.key === 'Enter') {
  93.           this.updateAutoGuessDelay();
  94.         }
  95.       });
  96.     }
  97.  
  98.     updateAutoGuessDelay() {
  99.       const newDelay = parseInt(this.delayInput.value);
  100.       if (isNaN(newDelay) || newDelay < 500) {
  101.         alert('Please enter a valid delay (minimum 500 ms).');
  102.         return;
  103.       }
  104.       this.autoGuessDelay = newDelay;
  105.       if (this.autoGuessing) {
  106.         this.stopAutoGuessing();
  107.         this.startAutoGuessing();
  108.       }
  109.     }
  110.  
  111.     createParentElement() {
  112.       this.parentElement = document.createElement('div');
  113.       this.parentElement.style = 'position: fixed; bottom: 0; right: 0; width: 100%; height: auto;';
  114.       document.body.appendChild(this.parentElement);
  115.     }
  116.  
  117.     createGuessElement() {
  118.       this.guessElem = document.createElement('div');
  119.       this.guessElem.style = 'padding: 10px; background-color: white; max-height: 200px; overflow-x: auto; white-space: nowrap; width: 100%;';
  120.       this.parentElement.appendChild(this.guessElem);
  121.     }
  122.  
  123.     createExportButton() {
  124.       this.exportButton = document.createElement('button');
  125.       this.exportButton.innerHTML = 'Export Answers';
  126.       this.exportButton.style = 'position: absolute; bottom: calc(100% + 10px); right: 0; z-index: 9999; padding: 5px 10px; font-size: 12px; background-color: #333; color: #fff; border: none; border-radius: 5px;';
  127.       this.parentElement.appendChild(this.exportButton);
  128.       this.exportButton.addEventListener('click', () => this.exportNewWords());
  129.     }
  130.  
  131.     exportNewWords() {
  132.       this.fetchWords('https://raw.githubusercontent.com/kuel27/wordlist/main/wordlist.txt').then(latestWords => {
  133.         const newWords = this.correctAnswers.filter(word => !latestWords.includes(word));
  134.  
  135.         let blob = new Blob([newWords.join('\n')], {
  136.           type: 'text/plain;charset=utf-8'
  137.         });
  138.         let a = document.createElement('a');
  139.         a.href = URL.createObjectURL(blob);
  140.         a.download = 'newWords.txt';
  141.         a.style.display = 'none';
  142.         document.body.appendChild(a);
  143.         a.click();
  144.         document.body.removeChild(a);
  145.       });
  146.     }
  147.  
  148.     fetchAndStoreLatestWordlist() {
  149.       this.fetchWords('https://raw.githubusercontent.com/kuel27/wordlist/main/wordlist.txt').then(words => {
  150.         words.forEach(word => {
  151.           if (!this.correctAnswers.includes(word)) {
  152.             this.correctAnswers.push(word);
  153.           }
  154.         });
  155.       });
  156.     }
  157.  
  158.     fetchWords(url) {
  159.       return fetch(url)
  160.         .then(response => {
  161.           if (!response.ok) {
  162.             throw new Error(`HTTP error! status: ${response.status}`);
  163.           }
  164.  
  165.           return response.text();
  166.         })
  167.         .then(data => data.split('\n').map(word => word.trim()))
  168.         .catch(error => {
  169.           console.error(`There was an error with the fetch operation: ${error.message}`);
  170.           return [];
  171.         });
  172.     }
  173.  
  174.     observePlayers() {
  175.       const playersContainer = document.querySelector(".players-list");
  176.       if (playersContainer) {
  177.         const config = {
  178.           childList: true,
  179.           subtree: true
  180.         };
  181.         const observer = new MutationObserver((mutationsList) => this.playersObserverCallback(mutationsList));
  182.         observer.observe(playersContainer, config);
  183.       }
  184.     }
  185.  
  186.     playersObserverCallback(mutationsList) {
  187.       for (let mutation of mutationsList) {
  188.         if (mutation.type === 'childList') {
  189.           this.updatePlayersList();
  190.         }
  191.       }
  192.     }
  193.  
  194.     generateID(inputString) {
  195.       let hash = 0;
  196.  
  197.       if (inputString.length === 0) {
  198.         return hash.toString();
  199.       }
  200.  
  201.       for (let i = 0; i < inputString.length; i++) {
  202.         const char = inputString.charCodeAt(i);
  203.         hash = ((hash << 5) - hash) + char;
  204.         hash |= 0;
  205.       }
  206.  
  207.       return Math.abs(hash).toString();
  208.     }
  209.  
  210.     updatePlayersList() {
  211.       const playerElems = document.querySelectorAll(".player");
  212.       playerElems.forEach(playerElem => {
  213.         const colorElem = playerElem.querySelector(".color");
  214.         const eyesElem = playerElem.querySelector(".eyes");
  215.         const mouthElem = playerElem.querySelector(".mouth");
  216.         const playerNameElem = playerElem.querySelector(".player-name");
  217.  
  218.         if (!mouthElem || !eyesElem || !mouthElem || !playerNameElem) {
  219.           return;
  220.         }
  221.  
  222.         let playerName = playerNameElem.textContent;
  223.         const isMe = playerNameElem.classList.contains("me");
  224.  
  225.         if (isMe) {
  226.           playerName = playerName.replace(" (You)", "");
  227.         }
  228.  
  229.         const colorStyle = window.getComputedStyle(colorElem).backgroundPosition;
  230.         const eyesStyle = window.getComputedStyle(eyesElem).backgroundPosition;
  231.         const mouthStyle = window.getComputedStyle(mouthElem).backgroundPosition;
  232.  
  233.         const playerId = this.generateID(`${colorStyle}_${eyesStyle}_${mouthStyle}`);
  234.  
  235.         if (this.adminList.includes(parseInt(playerId))) {
  236.           playerElem.style.background = "linear-gradient(to right, red, yellow)";
  237.           playerNameElem.style.fontWeight = "bold";
  238.         }
  239.  
  240.         this.players[playerId] = {
  241.           element: playerElem,
  242.           name: playerName.trim()
  243.         };
  244.       });
  245.     }
  246.  
  247.     observeHintsAndInput() {
  248.       this.observeHints();
  249.       this.observeInput();
  250.       this.observeChat();
  251.     }
  252.  
  253.     observeHints() {
  254.       const targetNodes = [
  255.         document.querySelector('.hints .container'),
  256.         document.querySelector('.words'),
  257.         document.querySelector('#game-word'),
  258.       ];
  259.       const config = {
  260.         childList: true,
  261.         subtree: true
  262.       };
  263.  
  264.       const observer = new MutationObserver((mutationsList) => this.hintObserverCallback(mutationsList));
  265.       targetNodes.forEach(targetNode => {
  266.         if (targetNode) {
  267.           observer.observe(targetNode, config);
  268.         }
  269.       });
  270.     }
  271.  
  272.     hintObserverCallback(mutationsList) {
  273.       const inputElem = document.querySelector('#game-chat input[data-translate="placeholder"]');
  274.       if (inputElem.value) return;
  275.  
  276.       for (let mutation of mutationsList) {
  277.         if (mutation.type === 'childList') {
  278.           this.checkIfAllHintsRevealed();
  279.           this.checkWordsElement();
  280.           this.generateGuesses();
  281.         }
  282.       }
  283.     }
  284.  
  285.     checkIfAllHintsRevealed() {
  286.       const hintElems = Array.from(document.querySelectorAll('.hints .hint'));
  287.  
  288.       if (hintElems.every(elem => elem.classList.contains('uncover'))) {
  289.         const correctAnswer = hintElems.map(elem => elem.textContent).join('').trim().toLowerCase();
  290.  
  291.         if (!correctAnswer || /[^a-zA-Z.\s-]/g.test(correctAnswer)) {
  292.           return;
  293.         }
  294.  
  295.         if (!this.correctAnswers.includes(correctAnswer)) {
  296.           this.correctAnswers.push(correctAnswer);
  297.           GM_setValue('correctAnswers', this.correctAnswers);
  298.         }
  299.       }
  300.     }
  301.  
  302.     observeChat() {
  303.       const chatContainer = document.querySelector('.chat-content');
  304.       const observer = new MutationObserver((mutationsList) => this.chatObserverCallback(mutationsList));
  305.       observer.observe(chatContainer, {
  306.         childList: true
  307.       });
  308.     }
  309.  
  310.     chatObserverCallback(mutationsList) {
  311.       for (let mutation of mutationsList) {
  312.         if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
  313.           let messageNode = mutation.addedNodes[0];
  314.           let message = messageNode.textContent;
  315.           let computedStyle = window.getComputedStyle(mutation.addedNodes[0]);
  316.  
  317.           if (computedStyle.color === 'rgb(226, 203, 0)' && message.includes('is close!')) {
  318.             this.closeWord = message.split(' ')[0];
  319.           }
  320.  
  321.           if (computedStyle.color === 'rgb(57, 117, 206)') {
  322.             this.tempWords = this.correctAnswers.slice();
  323.             this.alreadyGuessed = [];
  324.             this.closeWord = '';
  325.           }
  326.  
  327.           if (message.includes(': ')) {
  328.             let username = message.split(': ')[0];
  329.             let guess = message.split(': ')[1];
  330.             if (!this.alreadyGuessed.includes(guess)) {
  331.               this.alreadyGuessed.push(guess);
  332.             }
  333.  
  334.             for (let playerId in this.players) {
  335.               if (this.players.hasOwnProperty(playerId) &&
  336.                 this.players[playerId].name === username &&
  337.                 this.adminList.includes(Number(playerId))) {
  338.                 messageNode.style.background = 'linear-gradient(to right, #fc2d2d 40%, #750000 60%)';
  339.                 messageNode.style.webkitBackgroundClip = 'text';
  340.                 messageNode.style.webkitTextFillColor = 'transparent';
  341.                 messageNode.style.fontWeight = '700';
  342.                 messageNode.style.textShadow = '2px 2px 4px rgba(0, 0, 0, 0.3)';
  343.                 break;
  344.               }
  345.             }
  346.           }
  347.  
  348.           this.generateGuesses();
  349.         }
  350.       }
  351.     }
  352.  
  353.     checkWordsElement() {
  354.       const wordElems = Array.from(document.querySelectorAll('.words.show .word'));
  355.  
  356.       wordElems.forEach(elem => {
  357.         const word = elem.textContent.trim().toLowerCase();
  358.  
  359.         if (!word || /[^a-zA-Z.\s-]/g.test(word)) {
  360.           return;
  361.         }
  362.  
  363.         if (word.trim() !== "" && !this.correctAnswers.includes(word)) {
  364.           this.correctAnswers.push(word);
  365.           GM_setValue('correctAnswers', this.correctAnswers);
  366.         }
  367.       });
  368.     }
  369.  
  370.     observeInput() {
  371.       const inputElem = document.querySelector('#game-chat input[data-translate="placeholder"]');
  372.       inputElem.addEventListener('input', this.generateGuesses.bind(this));
  373.       inputElem.addEventListener('keydown', this.handleKeyDown.bind(this));
  374.  
  375.       const formElem = document.querySelector('#game-chat form');
  376.       formElem.addEventListener('submit', this.generateGuesses.bind(this));
  377.     }
  378.  
  379.     handleKeyDown(event) {
  380.       if (event.key === 'Tab' && this.possibleWords.length > 0) {
  381.         event.preventDefault();
  382.         const inputElem = document.querySelector('#game-chat input[data-translate="placeholder"]');
  383.         inputElem.value = this.possibleWords[0];
  384.         inputElem.focus();
  385.         this.generateGuesses();
  386.       }
  387.     }
  388.  
  389.     levenshteinDistance(a, b) {
  390.       const matrix = [];
  391.  
  392.       for (let i = 0; i <= b.length; i++) {
  393.         matrix[i] = [i];
  394.       }
  395.  
  396.       for (let j = 0; j <= a.length; j++) {
  397.         matrix[0][j] = j;
  398.       }
  399.  
  400.       for (let i = 1; i <= b.length; i++) {
  401.         for (let j = 1; j <= a.length; j++) {
  402.           if (b.charAt(i - 1) == a.charAt(j - 1)) {
  403.             matrix[i][j] = matrix[i - 1][j - 1];
  404.           } else {
  405.             matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1,
  406.               Math.min(matrix[i][j - 1] + 1,
  407.                 matrix[i - 1][j] + 1));
  408.           }
  409.         }
  410.       }
  411.  
  412.       return matrix[b.length][a.length];
  413.     }
  414.  
  415.     generateGuesses() {
  416.       const hintElems = Array.from(document.querySelectorAll('.hints .hint'));
  417.       const inputElem = document.querySelector('#game-chat input[data-translate="placeholder"]');
  418.       const hintParts = hintElems.map(elem => elem.textContent === '_' ? '.' : elem.textContent).join('').split(' ');
  419.       const inputText = inputElem.value ? String(inputElem.value) : '';
  420.  
  421.       this.tempWords = this.tempWords.filter(word => {
  422.         if (this.alreadyGuessed.includes(word)) {
  423.           return false;
  424.         }
  425.  
  426.         if (this.closeWord.length > 0 && this.levenshteinDistance(word, this.closeWord) > 1) {
  427.           return false;
  428.         }
  429.  
  430.         let wordParts = word.split(' ');
  431.  
  432.         if (wordParts.length !== hintParts.length) {
  433.           return false;
  434.         }
  435.  
  436.         for (let i = 0, len = wordParts.length; i < len; i++) {
  437.           if (wordParts[i].length !== hintParts[i].length) {
  438.             return false;
  439.           }
  440.         }
  441.  
  442.         if (hintParts.join(' ').trim().length <= 0 && inputText.trim().length <= 0) {
  443.           return true;
  444.         }
  445.  
  446.         let hintRegex = new RegExp(`^${hintParts.join(' ')}$`, 'i');
  447.         if (!hintRegex.test(word)) {
  448.           return false;
  449.         }
  450.  
  451.         return true;
  452.       });
  453.  
  454.       this.possibleWords = this.tempWords.filter(word => {
  455.         let inputTextRegex = new RegExp(`^${inputText}`, 'i');
  456.         if (!inputTextRegex.test(word)) {
  457.           return false;
  458.         }
  459.  
  460.         return true;
  461.       });
  462.  
  463.       this.closeWord = '';
  464.       this.guessElem.innerHTML = '';
  465.       this.renderGuesses(this.possibleWords, inputElem);
  466.     }
  467.  
  468.     renderGuesses(possibleWords, inputElem) {
  469.       possibleWords.slice(0, 100).forEach((word, index) => {
  470.         const wordElem = document.createElement('div');
  471.         wordElem.textContent = word;
  472.         wordElem.style = 'font-weight: bold; display: inline-block; padding: 5px; margin-right: 2px; color: white; text-shadow: 2px 2px 2px black;';
  473.         const maxValue = possibleWords.length > 100 ? 100 : possibleWords.length;
  474.         let hueValue = possibleWords.length > 1 ? Math.floor(360 * index / (maxValue - 1)) : 0;
  475.         wordElem.style.backgroundColor = `hsl(${hueValue}, 100%, 50%)`;
  476.  
  477.         this.addHoverEffect(wordElem, hueValue);
  478.         this.addClickFunctionality(wordElem, word, inputElem, hueValue);
  479.         this.guessElem.appendChild(wordElem);
  480.       });
  481.     }
  482.  
  483.     addHoverEffect(wordElem, hueValue) {
  484.       wordElem.addEventListener('mouseenter', function() {
  485.         if (!this.classList.contains('pressed')) {
  486.           this.style.backgroundColor = 'lightgray';
  487.         }
  488.         this.classList.add('hovered');
  489.       });
  490.  
  491.       wordElem.addEventListener('mouseleave', function() {
  492.         if (!this.classList.contains('pressed')) {
  493.           this.style.backgroundColor = `hsl(${hueValue}, 100%, 50%)`;
  494.         }
  495.         this.classList.remove('hovered');
  496.       });
  497.     }
  498.  
  499.  
  500.     addClickFunctionality(wordElem, word, inputElem, colorValue) {
  501.       wordElem.addEventListener('mousedown', function() {
  502.         wordElem.classList.add('pressed');
  503.         wordElem.style.backgroundColor = 'gray';
  504.       });
  505.  
  506.       wordElem.addEventListener('mouseup', function() {
  507.         wordElem.classList.remove('pressed');
  508.         if (!wordElem.classList.contains('hovered')) {
  509.           wordElem.style.backgroundColor = `rgb(${colorValue}, ${255 - colorValue}, 0)`;
  510.         } else {
  511.           wordElem.style.backgroundColor = 'lightgray';
  512.         }
  513.       });
  514.  
  515.       wordElem.addEventListener('click', function() {
  516.         const formElem = document.querySelector('#game-chat form');
  517.         inputElem.value = word;
  518.         formElem.dispatchEvent(new Event('submit', {
  519.           bubbles: true,
  520.           cancelable: true
  521.         }));
  522.       });
  523.     }
  524.   }
  525.  
  526.   new WordSleuth();
  527. })();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement