Contexto Hack

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

目前为 2023-08-01 提交的版本。查看 最新版本

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