Contexto Hack

This is a Contexto Hack that Gives you the word of the day for everyday.

  1. // ==UserScript==
  2. // @name Contexto Hack
  3. // @namespace your-namespace-here
  4. // @version 4.0
  5. // @author longkidkoolstar
  6. // @description This is a Contexto Hack that Gives you the word of the day for everyday.
  7. // @icon https://www.google.com/s2/favicons?sz=64&domain=contexto.me
  8. // @match https://contexto.me/*
  9. // @license CC BY-NC-ND 4.0
  10. // @grant GM_setValue
  11. // @grant GM_getValue
  12. // @grant GM_xmlhttpRequest
  13. // ==/UserScript==
  14. (function() {
  15. 'use strict';
  16. // Function to change the world list mode
  17. function changeWorldListMode() {
  18. const htmlElement = document.querySelector("html");
  19. const worldListElement = document.querySelector('.info-bar');
  20. if (htmlElement && worldListElement) {
  21. const isDarkMode = htmlElement.getAttribute('data-theme') === 'dark';
  22. // Adjust the background color for a slightly darker effect
  23. const darkModeBackgroundColor = 'rgb(15, 24, 32)';
  24. const lightModeBackgroundColor = 'white';
  25. worldListElement.style.backgroundColor = isDarkMode ? darkModeBackgroundColor : lightModeBackgroundColor;
  26. worldListElement.style.color = isDarkMode ? 'white' : 'black';
  27. // Adjust the saved words GUI mode for a slightly darker effect
  28. const savedWordsGUI = document.querySelector('#saved-words-list');
  29. if (savedWordsGUI) {
  30. savedWordsGUI.style.backgroundColor = isDarkMode ? darkModeBackgroundColor : lightModeBackgroundColor;
  31. savedWordsGUI.style.color = isDarkMode ? 'white' : 'black';
  32. }
  33. }
  34. }
  35. // Function to check if a string contains the number 1 by itself
  36. function containsOne(str) {
  37. return /^\D*1\D*$/.test(str);
  38. }
  39. // Retrieve saved words and game numbers from JSONBin.io
  40. let savedWords = {};
  41. let gameNumbers = {};
  42. // JSONStorage.net configurations
  43. const userId = 'd206ce58-9543-48db-a5e4-997cfc745ef3';
  44. const apiKey = 'e8dccf1d-81f7-4ecc-b99d-dc7401df192d';
  45.  
  46. // Function to fetch saved words from JSONStorage.net
  47. function fetchSavedWords() {
  48. // Check if saved words exist in the Tampermonkey storage
  49. const savedWordsData = GM_getValue('savedWordsData');
  50. if (savedWordsData) {
  51. savedWords = savedWordsData.savedWords;
  52. gameNumbers = savedWordsData.gameNumbers;
  53. console.log('Loaded saved words from Tampermonkey storage');
  54. updateSavedWordsGUI();
  55. }
  56.  
  57. // Fetch saved words from JSONStorage.net
  58. GM_xmlhttpRequest({
  59. method: 'GET',
  60. url: `https://api.jsonstorage.net/v1/json/${userId}/7d74d2ca-952e-4812-8a5c-76ec64d66a28?apiKey=${apiKey}`,
  61. headers: {
  62. 'Content-Type': 'application/json',
  63. },
  64. onload: function (response) {
  65. if (response.status === 200) {
  66. const responseData = JSON.parse(response.responseText);
  67. if (responseData.savedWords) {
  68. savedWords = responseData.savedWords;
  69. }
  70. if (responseData.gameNumbers) {
  71. gameNumbers = responseData.gameNumbers;
  72. }
  73. console.log('Read saved words successfully');
  74. updateSavedWordsGUI();
  75.  
  76. // Save fetched words to Tampermonkey storage
  77. const savedWordsData = {
  78. savedWords: savedWords,
  79. gameNumbers: gameNumbers,
  80. };
  81. GM_setValue('savedWordsData', savedWordsData);
  82. console.log('Saved fetched words to Tampermonkey storage');
  83.  
  84. // Call the searchForWordsAndGameNumbers function if it doesn't have the word for the current game number
  85. if (!Object.values(gameNumbers).includes(currentGameNumber)) {
  86. searchForWordsAndGameNumbers();
  87. }
  88. }
  89. },
  90. });
  91. }
  92.  
  93. // Function to save words and game numbers to JSONStorage.net
  94. function saveWordsToJSONStorage() {
  95. GM_xmlhttpRequest({
  96. method: 'PUT',
  97. url: `https://api.jsonstorage.net/v1/json/${userId}/7d74d2ca-952e-4812-8a5c-76ec64d66a28?apiKey=${apiKey}`,
  98. headers: {
  99. 'Content-Type': 'application/json',
  100. },
  101. data: JSON.stringify({
  102. savedWords: savedWords,
  103. gameNumbers: gameNumbers,
  104. }),
  105. onload: function (response) {
  106. if (response.status === 200) {
  107. console.log('Words and game numbers saved successfully');
  108. }
  109. },
  110. });
  111. }
  112.  
  113. // Function to search for words and game numbers to save on the page
  114. let currentGameNumber = '';
  115.  
  116. function searchForWordsAndGameNumbers() {
  117. // Find the game number element on the page
  118. const gameNumberElement = document.querySelector('.info-bar span:nth-child(2)');
  119. currentGameNumber = gameNumberElement ? gameNumberElement.textContent.trim().replace('#', '') : '';
  120.  
  121. if (currentGameNumber && !Object.values(gameNumbers).includes(currentGameNumber)) {
  122. // Find all the div elements with class "row" on the page
  123. const rows = document.querySelectorAll('.row');
  124. for (let i = 0; i < rows.length; i++) {
  125. const row = rows[i];
  126. // Find all the span elements within the row
  127. const spans = row.querySelectorAll('span');
  128. let hasOne = false;
  129. let word = '';
  130. for (let j = 0; j < spans.length; j++) {
  131. const span = spans[j];
  132. if (containsOne(span.innerHTML)) {
  133. hasOne = true;
  134. } else {
  135. word = span.innerHTML;
  136. }
  137. }
  138. // Save the word and game number to the objects if the word has the number 1 by itself and it's not already saved
  139. // Save the updated objects to JSONStorage.net only if a new word is saved
  140. if (hasOne && word && !savedWords.hasOwnProperty(word)) {
  141. savedWords[word] = true;
  142. gameNumbers[word] = currentGameNumber; // Save the current game number instead of searching for it again
  143. // Log the game number for the saved word
  144. console.log(`Game number for ${word}: ${currentGameNumber}`);
  145.  
  146. // Save the updated objects to JSONStorage.net
  147. saveWordsToJSONStorage();
  148. }
  149. }
  150. }
  151.  
  152. // Update the GUI with the saved words and game numbers
  153. updateSavedWordsGUI();
  154. }
  155.  
  156. // Function to reveal the word for the current game number
  157. function revealWordForCurrentGameNumber() {
  158. currentGameNumber = ''; // Clear the current game number
  159.  
  160. // Find the game number element on the page
  161. const gameNumberElement = document.querySelector('.info-bar span:nth-child(2)');
  162. currentGameNumber = gameNumberElement ? gameNumberElement.textContent.trim().replace('#', '') : '';
  163.  
  164. // Find the saved word for the current game number
  165. const savedWordsForCurrentGameNumber = Object.keys(savedWords).filter((word) => {
  166. return gameNumbers[word] === currentGameNumber;
  167. });
  168.  
  169. // Display the saved word in an alert box
  170. if (savedWordsForCurrentGameNumber.length > 0) {
  171. alert(`The word for game number ${currentGameNumber} is: ${savedWordsForCurrentGameNumber[0]}`);
  172. } else {
  173. alert(`No saved words for game number ${currentGameNumber}. Trying to find the word in the library...`);
  174. fetchWordFromAPI(currentGameNumber);
  175. }
  176. }
  177.  
  178. // Function to fetch the word from the API
  179. function fetchWordFromAPI(gameNumber) {
  180. const apiUrl = `https://api.contexto.me/machado/en/giveup/${gameNumber}`;
  181.  
  182. // Make an HTTP request to the API endpoint
  183. fetch(apiUrl)
  184. .then(response => response.json())
  185. .then(data => {
  186. const word = data.word;
  187. if (word) {
  188. alert(`The word for game number ${gameNumber} is: ${word}`);
  189. // Save the word and game number to the objects
  190. savedWords[word] = true;
  191. gameNumbers[word] = gameNumber;
  192. // Save the updated objects to JSONStorage.net
  193. saveWordsToJSONStorage();
  194. // Save the updated objects to GM_setValue storage
  195. const savedWordsData = {
  196. savedWords: savedWords,
  197. gameNumbers: gameNumbers,
  198. };
  199. GM_setValue('savedWordsData', savedWordsData);
  200. } else {
  201. alert(`No word found for game number ${gameNumber}`);
  202. }
  203. })
  204. .catch(error => {
  205. console.error('Error fetching word from API:', error);
  206. });
  207. }
  208. const buttoncss = `
  209. .theme-button {
  210. background-color: #29a19c; /* Contexto primary color - Change this to the actual primary color */
  211. color: #ffffff; /* White text to contrast with the primary color */
  212. border: none;
  213. padding: 5px 13px; /* Reduce padding to make the button smaller */
  214. font-size: 11px; /* Reduce font size to make the button smaller */
  215. border-radius: 3px; /* Adjust border radius for less rounded corners */
  216. cursor: pointer;
  217. margin: 5px;
  218. /* Add any additional styles or adjustments you want */
  219. }
  220. .theme-button:hover {
  221. background-color: #45c0b5; /* Lighter shade on hover - Change this to the actual hover color */
  222. }
  223. `;
  224. // Add the CSS styles to the page
  225. const buttonstyle = document.createElement('style');
  226. buttonstyle.innerHTML = buttoncss;
  227. document.head.appendChild(buttonstyle);
  228. // Create a button to show the saved words GUI
  229. const showSavedWordsButton = document.createElement('button');
  230. showSavedWordsButton.textContent = 'Saved Words';
  231. showSavedWordsButton.classList.add('theme-button'); // Add a custom class for styling
  232. showSavedWordsButton.addEventListener('click', () => {
  233. savedWordsGUI.classList.add('open');
  234. });
  235. document.body.appendChild(showSavedWordsButton);
  236. // Create a button to reveal the word for the current game number
  237. const revealButton = document.createElement('button');
  238. revealButton.textContent = 'Reveal Word';
  239. revealButton.classList.add('theme-button'); // Add a custom class for styling
  240. revealButton.addEventListener('click', revealWordForCurrentGameNumber);
  241. document.body.appendChild(revealButton);
  242. // Create a div element to hold the saved words GUI
  243. const savedWordsGUI = document.createElement('div');
  244. savedWordsGUI.id = 'saved-words-list';
  245. document.body.appendChild(savedWordsGUI);
  246. // Create a button to minimize the saved words GUI
  247. const minimizeSavedWordsButton = document.createElement('button');
  248. minimizeSavedWordsButton.innerHTML = '<img src="https://th.bing.com/th/id/R.6a6eda3ee63c80ebc02dc830b395324e?rik=t2E%2fYYP3IGbSsQ&pid=ImgRaw&r=0" alt="Close">';
  249. minimizeSavedWordsButton.addEventListener('click', () => {
  250. savedWordsGUI.classList.remove('open');
  251. });
  252. savedWordsGUI.appendChild(minimizeSavedWordsButton);
  253. // Create a list element to display the saved words
  254. const savedWordsList = document.createElement('ul');
  255. savedWordsGUI.appendChild(savedWordsList);
  256. // Function to update the saved words GUI with the saved words and game numbers
  257. function updateSavedWordsGUI() {
  258. // Clear the current saved words list
  259. savedWordsList.innerHTML = '';
  260. // Get all saved words sorted by game number
  261. const savedWordsSorted = Object.keys(gameNumbers).sort((a, b) => {
  262. return gameNumbers[a] - gameNumbers[b];
  263. });
  264. // Add each saved word to the list
  265. for (let i = 0; i < savedWordsSorted.length; i++) {
  266. const word = savedWordsSorted[i];
  267. const gameNumber = gameNumbers[word];
  268. const listItem = document.createElement('li');
  269. listItem.textContent = `${word} (Game ${gameNumber})`;
  270. savedWordsList.appendChild(listItem);
  271. }
  272. }
  273. // Update the saved words GUI with the saved words and game numbers
  274. updateSavedWordsGUI();
  275. // Function to clear the saved words and game numbers from JSONBin.io
  276. function clearSavedWords() {
  277. savedWords = {};
  278. gameNumbers = {};
  279. //saveWordsToJSONBin();
  280. updateSavedWordsGUI();
  281. alert('Saved words cleared');
  282. }
  283. // Create a button to clear the saved words and game numbers
  284. const clearSavedWordsButton = document.createElement('button');
  285. clearSavedWordsButton.textContent = 'Clear Saved Words';
  286. clearSavedWordsButton.addEventListener('click', clearSavedWords);
  287. savedWordsGUI.appendChild(clearSavedWordsButton);
  288. // Function to export the saved words and game numbers as JSON
  289. function exportSavedWords() {
  290. const savedWordsData = {
  291. savedWords: savedWords,
  292. gameNumbers: gameNumbers
  293. };
  294. const dataStr = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(savedWordsData));
  295. const downloadAnchorNode = document.createElement('a');
  296. downloadAnchorNode.setAttribute('href', dataStr);
  297. downloadAnchorNode.setAttribute('download', 'contexto_saved_words.json');
  298. document.body.appendChild(downloadAnchorNode); // required for firefox
  299. downloadAnchorNode.click();
  300. downloadAnchorNode.remove();
  301. }
  302. // Create a button to export the saved words and game numbers
  303. const exportSavedWordsButton = document.createElement('button');
  304. exportSavedWordsButton.textContent = 'Export Saved Words';
  305. exportSavedWordsButton.addEventListener('click', exportSavedWords);
  306. savedWordsGUI.appendChild(exportSavedWordsButton);
  307. // Function to import saved words and game numbers from JSON
  308. function importSavedWords() {
  309. const fileInput = document.createElement('input');
  310. fileInput.type = 'file';
  311. fileInput.accept = '.json';
  312. fileInput.addEventListener('change', () => {
  313. const file = fileInput.files[0];
  314. const reader = new FileReader();
  315. reader.onload = (e) => {
  316. try {
  317. const savedWordsData = JSON.parse(e.target.result);
  318. savedWords = savedWordsData.savedWords;
  319. gameNumbers = savedWordsData.gameNumbers;
  320. saveWordsToJSONBin();
  321. updateSavedWordsGUI();
  322. alert('Saved words imported');
  323. } catch (err) {
  324. alert('Error importing saved words');
  325. }
  326. };
  327. reader.readAsText(file);
  328. });
  329. fileInput.click();
  330. }
  331. // Create a button to import saved words and game numbers
  332. const importSavedWordsButton = document.createElement('button');
  333. importSavedWordsButton.textContent = 'Import Saved Words';
  334. importSavedWordsButton.addEventListener('click', importSavedWords);
  335. savedWordsGUI.appendChild(importSavedWordsButton);
  336. // Define CSS styles for the saved words GUI
  337. const css = `
  338. #saved-words-list {
  339. position: fixed;
  340. bottom: 0;
  341. right: 0;
  342. background-color: white;
  343. border: 2px solid black;
  344. border-radius: 5px 0 0 0;
  345. padding: 10px;
  346. max-height: 300px;
  347. overflow-y: auto;
  348. display: none;
  349. }
  350. #saved-words-list.open {
  351. display: block;
  352. }
  353. #saved-words-list button {
  354. margin: 5px;
  355. padding: 0;
  356. background: none;
  357. border: none;
  358. cursor: pointer;
  359. }
  360. #saved-words-list img {
  361. width: 20px;
  362. height: 20px;
  363. }
  364. `;
  365. // Add the CSS styles to the page
  366. const style = document.createElement('style');
  367. style.innerHTML = css;
  368. document.head.appendChild(style);
  369. // Fetch saved words and game numbers from JSONBin.io on page load
  370. fetchSavedWords();
  371. // Search for words and game numbers to save on page load and every 5 seconds
  372. searchForWordsAndGameNumbers();
  373. setInterval(searchForWordsAndGameNumbers, 17000);//17 seconds
  374. // Change the world list mode on page load and whenever the data-theme changes
  375. changeWorldListMode();
  376. const htmlElement = document.querySelector("html");
  377. const observer = new MutationObserver(changeWorldListMode);
  378. observer.observe(htmlElement, { attributes: true, attributeFilter: ['data-theme'] });
  379. })();