Skribbl 自动猜词器

一个帮助你在skribblio中猜词的脚本。

  1. // ==UserScript==
  2. // @name Skribbl Autoguesser
  3. // @name:zh-CN Skribbl 自动猜词器
  4. // @name:zh-TW Skribbl 自動猜詞器
  5. // @name:hi स्क्रिब्ल ऑटोगेसर
  6. // @name:es Skribbl Adivinador Automático
  7. // @namespace http://tampermonkey.net/
  8. // @supportURL https://github.com/zkisaboss/reorderedwordlist
  9. // @version 1.01
  10. // @description The best script currently out there for skribbl.
  11. // @description:zh-CN 一个帮助你在skribblio中猜词的脚本。
  12. // @description:zh-TW 一個幫助你在skribblio中猜詞的腳本。
  13. // @description:hi एक स्क्रिप्ट जो आपको स्क्रिब्लियो में शब्दों का अनुमान लगाने में मदद करता है।
  14. // @description:es Un script que te ayuda a adivinar palabras en skribblio.
  15. // @author Zach Kosove
  16. // @match http*://skribbl.io/*
  17. // @icon https://www.google.com/s2/favicons?sz=64&domain=skribbl.io
  18. // @grant GM_setValue
  19. // @grant GM_getValue
  20. // @license MIT
  21. // @compatible chrome
  22. // @compatible firefox
  23. // @compatible opera
  24. // @compatible safari
  25. // @compatible edge
  26. // ==/UserScript==
  27.  
  28. (function() {
  29. 'use strict';
  30.  
  31. // Variables
  32. let autoGuessing = false;
  33.  
  34.  
  35. // UI Elements
  36. const parentElement = document.createElement('div');
  37. Object.assign(parentElement.style, { position: 'fixed', bottom: '0', right: '0', width: '100%', height: 'auto' });
  38. document.body.appendChild(parentElement);
  39.  
  40. const guessElem = document.createElement('div');
  41. Object.assign(guessElem.style, { padding: '10px', backgroundColor: 'white', maxHeight: '200px', overflowX: 'auto', whiteSpace: 'nowrap', width: '100%' });
  42. parentElement.appendChild(guessElem);
  43.  
  44. const settingsElem = document.createElement('div');
  45. Object.assign(settingsElem.style, { position: 'absolute', bottom: 'calc(100%)', right: '0', padding: '10px 5px', display: 'flex', alignItems: 'center', gap: '10px' });
  46. parentElement.appendChild(settingsElem);
  47.  
  48. const autoGuessButton = document.createElement('button');
  49. autoGuessButton.innerHTML = `Auto Guess: ${autoGuessing ? 'ON' : 'OFF'}`;
  50. Object.assign(autoGuessButton.style, { padding: '5px 10px', fontSize: '12px', backgroundColor: '#333', color: '#fff' });
  51. settingsElem.appendChild(autoGuessButton);
  52.  
  53. const exportButton = document.createElement('button');
  54. exportButton.innerHTML = 'Export Answers';
  55. Object.assign(exportButton.style, { padding: '5px 10px', fontSize: '12px', backgroundColor: '#333', color: '#fff' });
  56. settingsElem.appendChild(exportButton);
  57.  
  58.  
  59. // Functions
  60. const correctAnswers = GM_getValue('correctAnswers', []);
  61.  
  62. async function fetchWords(url) {
  63. const response = await fetch(url);
  64. if (!response.ok) return [];
  65.  
  66. const data = await response.text();
  67. return data.split('\n').filter(elem => elem !== '');
  68. }
  69.  
  70. async function fetchAndStoreLatestWordlist() {
  71. const words = await fetchWords('https://raw.githubusercontent.com/zkisaboss/reorderedwordlist/main/wordlist.txt');
  72.  
  73. words.forEach(word => {
  74. if (!correctAnswers.includes(word)) correctAnswers.push(word);
  75. });
  76. }
  77.  
  78. fetchAndStoreLatestWordlist();
  79.  
  80.  
  81. let myUsername = '';
  82.  
  83. function findUsername() {
  84. const target = document.querySelector(".players-list");
  85. if (!target) return;
  86.  
  87. const observer = new MutationObserver(() => {
  88. myUsername = document.querySelector(".me").textContent.replace(" (You)", "")
  89. observer.disconnect();
  90. });
  91.  
  92. observer.observe(target, { childList: true});
  93. }
  94.  
  95. findUsername();
  96.  
  97.  
  98. function observeDrawingTurn() {
  99. const target = document.querySelector('.words');
  100. if (!target) return;
  101.  
  102. const observer = new MutationObserver(() => {
  103. target.childNodes.forEach(word => {
  104. const text = word.textContent.toLowerCase();
  105.  
  106. if (!correctAnswers.includes(text)) {
  107. correctAnswers.push(text);
  108. console.log(`New Word: ${text}`)
  109. GM_setValue('correctAnswers', correctAnswers);
  110. }
  111. });
  112. });
  113.  
  114. observer.observe(target, { childList: true });
  115. }
  116.  
  117. observeDrawingTurn();
  118.  
  119.  
  120. // Core functionality
  121. let possibleWords = [];
  122.  
  123. function renderGuesses(possibleWords) {
  124. guessElem.innerHTML = '';
  125.  
  126. possibleWords.forEach(word => {
  127. const wordElem = document.createElement('div');
  128. wordElem.textContent = word;
  129. Object.assign(wordElem.style, {
  130. fontWeight: 'bold',
  131. display: 'inline-block',
  132. padding: '5px',
  133. marginRight: '2px',
  134. color: 'white',
  135. textShadow: '2px 2px 2px black',
  136. backgroundColor: 'hsl(205, 100%, 50%)'
  137. });
  138.  
  139. wordElem.addEventListener('mouseenter', () => {
  140. if (!wordElem.classList.contains('pressed')) wordElem.style.backgroundColor = 'lightgray';
  141. wordElem.classList.add('hovered');
  142. });
  143.  
  144. wordElem.addEventListener('mouseleave', () => {
  145. if (!wordElem.classList.contains('pressed')) wordElem.style.backgroundColor = 'hsl(205, 100%, 50%)';
  146. wordElem.classList.remove('hovered');
  147. });
  148.  
  149. wordElem.addEventListener('mousedown', () => {
  150. wordElem.classList.add('pressed');
  151. wordElem.style.backgroundColor = 'gray';
  152. });
  153.  
  154. wordElem.addEventListener('mouseup', () => {
  155. wordElem.classList.remove('pressed');
  156. wordElem.style.backgroundColor = wordElem.classList.contains('hovered') ? 'lightgray' : 'hsl(205, 100%, 50%)';
  157. });
  158.  
  159. wordElem.addEventListener('click', () => {
  160. document.querySelector('#game-chat input[data-translate="placeholder"]').value = word;
  161. document.querySelector('#game-chat form').dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));
  162. });
  163.  
  164. guessElem.appendChild(wordElem);
  165. });
  166. }
  167.  
  168. function generateGuesses() {
  169. const inputElem = document.querySelector('#game-chat input[data-translate="placeholder"]');
  170. const pattern = inputElem.value.toLowerCase().trim();
  171. const filteredWords = possibleWords.filter(word => word.startsWith(pattern));
  172.  
  173. if (possibleWords.length === 1) {
  174. inputElem.value = possibleWords.shift();
  175. inputElem.closest('form').dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));
  176. }
  177.  
  178. renderGuesses(filteredWords);
  179. }
  180.  
  181. function filterHints(inputWords) {
  182. const hints = Array.from(document.querySelectorAll('.hints .hint'));
  183.  
  184. // turn into helper function or use a cleaner method to find allUncovered
  185. const allUncovered = hints.every(elem => elem.classList.contains('uncover'));
  186. if (allUncovered) {
  187. const correctAnswer = hints.map(elem => elem.textContent).join('').toLowerCase();
  188.  
  189. if (correctAnswers.includes(correctAnswer)) {
  190. const currentIndex = correctAnswers.indexOf(correctAnswer);
  191. const newIndex = Math.max(0, currentIndex - 1);
  192. correctAnswers.splice(currentIndex, 1);
  193. correctAnswers.splice(newIndex, 0, correctAnswer);
  194. } else {
  195. correctAnswers.push(correctAnswer);
  196. console.log(`New Word: ${correctAnswer}`)
  197. }
  198.  
  199. GM_setValue('correctAnswers', correctAnswers);
  200. return [];
  201. }
  202.  
  203. const hintPattern = hints.map(hint => hint.textContent === '_' ? '[a-z]' : hint.textContent).join('');
  204. const hintRegex = new RegExp(`^${hintPattern}$`, 'i');
  205. return inputWords.filter(word => hintRegex.test(word));
  206. }
  207.  
  208. function observeHints() {
  209. const target = document.querySelector('.hints .container');
  210. if (!target) return;
  211.  
  212. const observer = new MutationObserver(() => {
  213. possibleWords = filterHints(possibleWords);
  214. generateGuesses();
  215. });
  216.  
  217. observer.observe(target, { childList: true, subtree: true });
  218. }
  219.  
  220. observeHints();
  221.  
  222.  
  223. // https://youtu.be/Dd_NgYVOdLk
  224. function levenshteinDistance(a, b) {
  225. const matrix = [];
  226. for (let i = 0; i <= b.length; i++) matrix[i] = [i];
  227. for (let j = 0; j <= a.length; j++) matrix[0][j] = j;
  228.  
  229. for (let i = 1; i <= b.length; i++) {
  230. for (let j = 1; j <= a.length; j++) {
  231. if (b.charAt(i - 1) === a.charAt(j - 1)) {
  232. matrix[i][j] = matrix[i - 1][j - 1];
  233. } else {
  234. matrix[i][j] = Math.min(
  235. matrix[i - 1][j - 1] + 1,
  236. matrix[i][j - 1] + 1,
  237. matrix[i - 1][j] + 1
  238. );
  239. }
  240. }
  241. }
  242. return matrix[b.length][a.length];
  243. }
  244.  
  245. let previousWords = [];
  246.  
  247. function handleChatMessage(messageNode) {
  248. const messageColor = window.getComputedStyle(messageNode).color;
  249. const message = messageNode.textContent;
  250.  
  251. if (messageColor === 'rgb(57, 117, 206)' && message.endsWith('is drawing now!')) {
  252. possibleWords = filterHints(correctAnswers);
  253.  
  254. generateGuesses();
  255. }
  256.  
  257. if (message.includes(': ')) {
  258. const [username, guess] = message.split(': ');
  259. possibleWords = possibleWords.filter(word => word !== guess);
  260. previousWords = possibleWords;
  261.  
  262. if (username === myUsername) {
  263. possibleWords = possibleWords.filter(word => levenshteinDistance(word, guess) > 1);
  264. }
  265.  
  266. generateGuesses();
  267. }
  268.  
  269. if (messageColor === 'rgb(226, 203, 0)' && message.endsWith('is close!')) {
  270. const closeWord = message.replace(' is close!', '');
  271. possibleWords = previousWords.filter(word => levenshteinDistance(word, closeWord) === 1);
  272.  
  273. generateGuesses();
  274. }
  275. }
  276.  
  277. function observeChat() {
  278. const target = document.querySelector('.chat-content');
  279. if (!target) return;
  280.  
  281. const observer = new MutationObserver(() => {
  282. const lastMessage = target.lastElementChild;
  283. if (lastMessage) handleChatMessage(lastMessage);
  284. });
  285.  
  286. observer.observe(target, { childList: true });
  287. }
  288.  
  289. observeChat();
  290.  
  291.  
  292. function observeInput() {
  293. const inputElem = document.querySelector('#game-chat input[data-translate="placeholder"]');
  294.  
  295. inputElem.addEventListener('input', generateGuesses);
  296.  
  297. inputElem.addEventListener('keydown', ({ key }) => {
  298. if (key === 'Enter') {
  299. const guessDiv = guessElem.querySelector('div');
  300. if (guessDiv) {
  301. inputElem.value = guessDiv.innerText;
  302. inputElem.closest('form').dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));
  303. }
  304. }
  305. });
  306. }
  307.  
  308. observeInput();
  309.  
  310.  
  311. let autoGuessInterval;
  312.  
  313. function startAutoGuessing() {
  314. if (autoGuessing) {
  315. autoGuessInterval = setInterval(() => {
  316. if (possibleWords.length > 0) {
  317. document.querySelector('#game-chat input[data-translate="placeholder"]').value = possibleWords.shift();
  318. document.querySelector('#game-chat form').dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));
  319. }
  320. }, 8000);
  321. }
  322. }
  323.  
  324. startAutoGuessing();
  325.  
  326.  
  327. function toggleAutoGuessing() {
  328. autoGuessing = !autoGuessing;
  329. autoGuessButton.innerHTML = `Auto Guess: ${autoGuessing ? 'ON' : 'OFF'}`;
  330.  
  331. if (autoGuessing) {
  332. startAutoGuessing();
  333. } else {
  334. clearInterval(autoGuessInterval);
  335. autoGuessInterval = null;
  336. }
  337. }
  338.  
  339. autoGuessButton.addEventListener('click', toggleAutoGuessing);
  340.  
  341.  
  342. async function exportNewWords() {
  343. const old = await fetchWords('https://raw.githubusercontent.com/zkisaboss/reorderedwordlist/main/wordlist.txt');
  344. const newWords = correctAnswers.filter(word => !old.includes(word));
  345.  
  346. const blob = new Blob([newWords.join('\n')], { type: 'text/plain;charset=utf-8' });
  347.  
  348. const anchor = document.createElement('a');
  349. anchor.href = URL.createObjectURL(blob);
  350. anchor.download = 'newWords.txt';
  351.  
  352. document.body.appendChild(anchor);
  353. anchor.click();
  354.  
  355. document.body.removeChild(anchor);
  356. }
  357.  
  358. exportButton.addEventListener('click', exportNewWords);
  359. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址