Copy Wardle Guesses

Copies a list of your Wardle guesses (spoilered for Discord) to your clipboard

  1. // ==UserScript==
  2. // @name Copy Wardle Guesses
  3. // @namespace https://github.com/w4tchdoge
  4. // @version 1.0.0-20240924_214547
  5. // @description Copies a list of your Wardle guesses (spoilered for Discord) to your clipboard
  6. // @author w4tchdoge
  7. // @homepage https://github.com/w4tchdoge/MISC-UserScripts
  8. // @match *://wardlegame.com/*
  9. // @grant GM_setClipboard
  10. // @grant GM.setClipboard
  11. // @grant GM_registerMenuCommand
  12. // @grant GM.registerMenuCommand
  13. // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
  14. // @license AGPL-3.0-or-later
  15. // @history 1.0.0 — Add button to the share widget that lets you copy your guesses spoilered for discord. Attempt to make the script more compatible with the Single Page Application-ness of Wardle
  16. // @history 0.0.3 — cleanup/redo guesses_arr and guesses_spoilers
  17. // @history 0.0.2 — Initial git commit
  18. // ==/UserScript==
  19.  
  20. (async function () {
  21. `use strict`;
  22.  
  23. // modified from https://stackoverflow.com/a/61511955/11750206
  24. function waitForElmXPATH(xpathstr) {
  25. return new Promise(resolve => {
  26. if (document.evaluate(xpathstr, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue) {
  27. return resolve(document.evaluate(xpathstr, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue);
  28. }
  29.  
  30. const observer = new MutationObserver(mutations => {
  31. if (document.evaluate(xpathstr, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue) {
  32. observer.disconnect();
  33. resolve(document.evaluate(xpathstr, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue);
  34. }
  35. });
  36.  
  37. // If you get "parameter 1 is not of type 'Node'" error, see https://stackoverflow.com/a/77855838/492336
  38. observer.observe(document.documentElement, {
  39. childList: true,
  40. subtree: true
  41. });
  42. });
  43. }
  44.  
  45. function MAINfunk() {
  46. const guess_table = (() => {
  47.  
  48. const elm_xp = `.//div[contains(concat(" ",normalize-space(@class)," ")," mb-2 ")]/parent::div[contains(concat(" ",normalize-space(@class)," ")," mt-2 ")][contains(@class,"w-11/12")]`;
  49. const elm = document.evaluate(elm_xp, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  50. return elm;
  51.  
  52. })();
  53.  
  54. // guess_table;
  55.  
  56. // let names_elm_arr = Array.from(guess_table.querySelectorAll(`div.vehicle-name`)).reverse();
  57. const names_txt_arr = Array.from(guess_table.querySelectorAll(`div.vehicle-name`), elm => elm.textContent.trim()).reverse();
  58.  
  59. const nations_txt_arr = (() => {
  60.  
  61. let txt_arr = [];
  62. const elm_arr = Array.from(guess_table.querySelectorAll(`span[class^="fi"]`)).reverse();
  63.  
  64. elm_arr.forEach((elm, index, arr) => {
  65.  
  66. const nation_code = elm.classList[1].slice(3);
  67. let nation_name;
  68.  
  69. // ISO 3166-1 alpha3 codes from https://en.wikipedia.org/wiki/ISO_3166-1
  70. switch (nation_code) {
  71. case `it`:
  72. nation_name = `ITY`;
  73. break;
  74.  
  75. case `jp`:
  76. nation_name = `JPN`;
  77. break;
  78.  
  79. case `gb`:
  80. nation_name = `GBR`;
  81. break;
  82.  
  83. case `de`:
  84. nation_name = `DEU`;
  85. break;
  86.  
  87. case `se`:
  88. nation_name = `SWE`;
  89. break;
  90.  
  91. case `us`:
  92. nation_name = `USA`;
  93. break;
  94.  
  95. case `fr`:
  96. nation_name = `FRA`;
  97. break;
  98.  
  99. case `ru`:
  100. nation_name = `RUS`;
  101. break;
  102.  
  103. case `cn`:
  104. nation_name = `CHN`;
  105. break;
  106.  
  107. case `il`:
  108. nation_name = `ISR`;
  109. break;
  110.  
  111. default:
  112. throw new Error("ERROR: Unknown Nation Code!");
  113. }
  114.  
  115. txt_arr.push(nation_name);
  116.  
  117. });
  118.  
  119. return txt_arr;
  120.  
  121. })();
  122.  
  123. if (names_txt_arr.length != nations_txt_arr.length) {
  124. throw new Error("ERROR: Size of nations array and names array do not match!");
  125. }
  126.  
  127. // console.log(`Names arr length: ${names_txt_arr.length}\nNations arr length: ${nations_txt_arr.length}`);
  128.  
  129. const guesses_arr = (() => {
  130.  
  131. let output_arr = [];
  132.  
  133. [...Array(names_txt_arr.length)].forEach((_, index) => {
  134.  
  135. const input_obj = { guess_no: (index + 1), name: names_txt_arr.at(index), nation: nations_txt_arr.at(index) };
  136. output_arr.push(input_obj);
  137.  
  138. });
  139.  
  140. return output_arr;
  141.  
  142. })();
  143.  
  144. return guesses_arr;
  145.  
  146. }
  147.  
  148. function DiscordSpoilerGuessesToClipboard() {
  149.  
  150. const guesses_arr = MAINfunk();
  151. const guesses_spoilers = (() => {
  152.  
  153. let output_arr = [];
  154.  
  155. guesses_arr.forEach((elm, index, arr) => {
  156.  
  157. const spoiler_text = `${elm.guess_no}. ||${elm.name} **(${elm.nation})**||`;
  158. output_arr.push(spoiler_text);
  159.  
  160. });
  161.  
  162. return output_arr;
  163.  
  164. })();
  165. const cliptext = `My guesses:\n${guesses_spoilers.join(`\n`)}`;
  166. GM.setClipboard(cliptext);
  167. console.log(`
  168. Wardle Guesses have been copied to clipboard!
  169. Guesses were copied in the Discord Spoilers format.
  170. Clipboard should now be:
  171.  
  172. ${cliptext}`);
  173.  
  174. }
  175.  
  176. const share_row = await waitForElmXPATH(`.//div[contains(concat(" ",normalize-space(@class)," ")," shareWidget ")]/div[contains(concat(" ",normalize-space(@class)," ")," flex-row ")][count(.//button[count(.//p[contains(concat(" ",normalize-space(@class)," ")," font-bold ")]) > 0][count(preceding-sibling::a) > 0 or count(following-sibling::a) > 0]) > 0]`);
  177.  
  178. const userscript_share_row = Object.assign(share_row.cloneNode(false), {
  179. innerHTML: `<button class="flex flex-row items-center justify-center rounded-2xl bg-secondary-background p-3 text-text-color transition delay-100 duration-300 ease-in-out hover:scale-110 hover:bg-blue-600 md:p-4"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" class="mr-3 h-6 w-6 md:h-9 md:w-9 iconify iconify--mdi" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M19 3h-4.18C14.4 1.84 13.3 1 12 1s-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2m-7 0a1 1 0 0 1 1 1a1 1 0 0 1-1 1a1 1 0 0 1-1-1a1 1 0 0 1 1-1M7 7h10V5h2v14H5V5h2z"></path></svg><p class="font-bold tracking-wider md:text-lg">COPY GUESSES\n(DISCORD)</p></button>`
  180. });
  181. userscript_share_row.addEventListener(`click`, DiscordSpoilerGuessesToClipboard);
  182.  
  183. share_row.after(userscript_share_row);
  184.  
  185. GM.registerMenuCommand(`Copy Guesses to Clipboard`, DiscordSpoilerGuessesToClipboard);
  186.  
  187. })();