Greasy Fork is available in English.

TrioLingo

Duolingo cheat script hack. Based on DuoPower

  1. // ==UserScript==
  2. // @name TrioLingo
  3. // @namespace Violentmonkey Scripts
  4. // @match https://*.duolingo.com/*
  5. // @grant GM_log
  6. // @version 1.0
  7. // @author Cerberus
  8. // @description Duolingo cheat script hack. Based on DuoPower
  9. // ==/UserScript==
  10.  
  11. let solvingIntervalId;
  12. let isAutoMode = true;
  13. const debug = true;
  14.  
  15. function addButtons() {
  16. if (window.location.pathname === '/learn') {
  17. let button = document.querySelector('a[data-test="global-practice"]');
  18. if (button) {
  19. button.click();
  20. }
  21. }
  22.  
  23. const solveAllButton = document.getElementById("solveAllButton");
  24. if (solveAllButton !== null) {
  25. solving();
  26. return;
  27. }
  28.  
  29. const original = document.querySelectorAll('[data-test="player-next"]')[0];
  30. if (original === undefined) {
  31. const startButton = document.querySelector('[data-test="start-button"]');
  32. console.log(`Wrapper line: ${startButton}`);
  33. if (startButton === null) {
  34. return;
  35. }
  36. const wrapper = startButton.parentNode;
  37. const solveAllButton = document.createElement('a');
  38. solveAllButton.className = startButton.className;
  39. solveAllButton.id = "solveAllButton";
  40. solveAllButton.innerText = "COMPLETE SKILL";
  41. solveAllButton.removeAttribute('href');
  42. solveAllButton.addEventListener('click', () => {
  43. solving();
  44. setInterval(() => {
  45. const startButton = document.querySelector('[data-test="start-button"]');
  46. if (startButton && startButton.innerText.startsWith("START")) {
  47. startButton.click();
  48. }
  49. }, 3000);
  50. startButton.click();
  51. });
  52. wrapper.appendChild(solveAllButton);
  53. } else {
  54. const wrapper = document.getElementsByClassName('_10vOG')[0];
  55. wrapper.style.display = "flex";
  56.  
  57. const solveCopy = document.createElement('button');
  58. const pauseCopy = document.createElement('button');
  59.  
  60. solveCopy.id = 'solveAllButton';
  61. solveCopy.innerHTML = solvingIntervalId ? 'PAUSE SOLVE' : 'SOLVE ALL';
  62. solveCopy.disabled = false;
  63. pauseCopy.innerHTML = 'SOLVE';
  64.  
  65. const buttonStyle = `
  66. min-width: 150px;
  67. font-size: 17px;
  68. border:none;
  69. border-bottom: 4px solid #58a700;
  70. border-radius: 18px;
  71. padding: 13px 16px;
  72. transform: translateZ(0);
  73. transition: filter .2s;
  74. font-weight: 700;
  75. letter-spacing: .8px;
  76. background: #55CD2E;
  77. color:#fff;
  78. margin-left:20px;
  79. cursor:pointer;
  80. `;
  81.  
  82. solveCopy.style.cssText = buttonStyle;
  83. pauseCopy.style.cssText = buttonStyle;
  84.  
  85. [solveCopy, pauseCopy].forEach(button => {
  86. button.addEventListener("mousemove", () => {
  87. button.style.filter = "brightness(1.1)";
  88. });
  89. });
  90.  
  91. [solveCopy, pauseCopy].forEach(button => {
  92. button.addEventListener("mouseleave", () => {
  93. button.style.filter = "none";
  94. });
  95. });
  96.  
  97. original.parentElement.appendChild(pauseCopy);
  98. original.parentElement.appendChild(solveCopy);
  99.  
  100. solveCopy.addEventListener('click', solving);
  101. pauseCopy.addEventListener('click', solve);
  102.  
  103. solving();
  104. }
  105. }
  106.  
  107.  
  108. setInterval(addButtons, 3000);
  109.  
  110. function solving() {
  111. if (solvingIntervalId) {
  112. //clearInterval(solvingIntervalId);
  113. //solvingIntervalId = undefined;
  114. //document.getElementById("solveAllButton").innerText = "SOLVE ALL";
  115. //isAutoMode = false;
  116. } else {
  117. document.getElementById("solveAllButton").innerText = "PAUSE SOLVE";
  118. isAutoMode = true;
  119. solvingIntervalId = setInterval(solve, 500);
  120. }
  121. }
  122.  
  123. function solve() {
  124. const selAgain = document.querySelectorAll('[data-test="player-practice-again"]');
  125. const practiceAgain = document.querySelector('[data-test="player-practice-again"]');
  126. if (selAgain.length === 1 && isAutoMode) {
  127. // Make sure it's the `practice again` button
  128. //if (selAgain[0].innerHTML.toLowerCase() === 'practice again') {
  129. // Click the `practice again` button
  130. selAgain[0].click();
  131. // Terminate
  132. return;
  133. //}
  134. }
  135. if (practiceAgain !== null && isAutoMode) {
  136. practiceAgain.click();
  137. return;
  138. }
  139. try {
  140. window.sol = findReact(document.getElementsByClassName('_3FiYg')[0]).props.currentChallenge;
  141. } catch {
  142. let next = document.querySelector('[data-test="player-next"]');
  143. if (next) {
  144. next.click();
  145. }
  146. return;
  147. }
  148. if (!window.sol) {
  149. return;
  150. }
  151. let nextButton = document.querySelector('[data-test="player-next"]');
  152. if (!nextButton) {
  153. return;
  154. }
  155. if (document.querySelectorAll('[data-test*="challenge-speak"]').length > 0) {
  156. if (debug)
  157. document.getElementById("solveAllButton").innerText = 'Challenge Speak';
  158. const buttonSkip = document.querySelector('button[data-test="player-skip"]');
  159. if (buttonSkip) {
  160. buttonSkip.click();
  161. }
  162. } else if (window.sol.type === 'listenMatch') {
  163. if (debug)
  164. document.getElementById("solveAllButton").innerText = 'Listen Match';
  165.  
  166. console.log('hello');
  167.  
  168. const nl = document.querySelectorAll('[data-test$="challenge-tap-token"]');
  169. window.sol.pairs?.forEach((pair) => {
  170. for (let i = 0; i < nl.length; i++) {
  171. let nlInnerText;
  172. if (nl[i].querySelectorAll('[data-test="challenge-tap-token-text"]').length > 1) {
  173. nlInnerText = nl[i].querySelector('[data-test="challenge-tap-token-text"]').innerText.toLowerCase().trim();
  174. } else {
  175. //nlInnerText = findSubReact(nl[i]).textContent.toLowerCase().trim();
  176. nlInnerText = nl[i].getAttribute('data-test').split('-')[0].toLowerCase().trim();
  177. console.log(nlInnerText);
  178. }
  179. if (
  180. (
  181. nlInnerText === pair.learningWord.toLowerCase().trim() ||
  182. nlInnerText === pair.translation.toLowerCase().trim()
  183. ) &&
  184. !nl[i].disabled
  185. ) {
  186. nl[i].click();
  187. }
  188. }
  189. });
  190. } else if (document.querySelectorAll('[data-test="challenge-choice"]').length > 0) {
  191. // choice challenge
  192. if (debug)
  193. document.getElementById("solveAllButton").innerText = 'Challenge Choice';
  194. if (window.sol.correctTokens !== undefined) {
  195. correctTokensRun();
  196. nextButton.click()
  197. } else if (window.sol.correctIndex !== undefined) {
  198. document.querySelectorAll('[data-test="challenge-choice"]')[window.sol.correctIndex].click();
  199. nextButton.click();
  200. }
  201. } else if (document.querySelectorAll('[data-test$="challenge-tap-token"]').length > 0) {
  202. // match correct pairs challenge
  203. if (window.sol.pairs !== undefined) {
  204. if (debug)
  205. document.getElementById("solveAllButton").innerText = 'Pairs';
  206. let nl = document.querySelectorAll('[data-test$="challenge-tap-token"]');
  207. if (document.querySelectorAll('[data-test="challenge-tap-token-text"]').length
  208. === nl.length) {
  209. window.sol.pairs?.forEach((pair) => {
  210. for (let i = 0; i < nl.length; i++) {
  211. const nlInnerText = nl[i].querySelector('[data-test="challenge-tap-token-text"]').innerText.toLowerCase().trim();
  212. try {
  213. if (
  214. (
  215. nlInnerText === pair.transliteration.toLowerCase().trim() ||
  216. nlInnerText === pair.character.toLowerCase().trim()
  217. )
  218. && !nl[i].disabled
  219. ) {
  220. nl[i].click()
  221. }
  222. } catch (TypeError) {
  223. if (
  224. (
  225. nlInnerText === pair.learningToken.toLowerCase().trim() ||
  226. nlInnerText === pair.fromToken.toLowerCase().trim()
  227. )
  228. && !nl[i].disabled
  229. ) {
  230. nl[i].click()
  231. }
  232. }
  233. }
  234. })
  235. }
  236. } else if (window.sol.correctTokens !== undefined) {
  237. if (debug)
  238. document.getElementById("solveAllButton").innerText = 'Token Run';
  239. correctTokensRun();
  240. nextButton.click()
  241. } else if (window.sol.correctIndices !== undefined) {
  242. if (debug)
  243. document.getElementById("solveAllButton").innerText = 'Indices Run';
  244. correctIndicesRun();
  245. }
  246. } else if (document.querySelectorAll('[data-test="challenge-tap-token-text"]').length > 0) {
  247. if (debug)
  248. document.getElementById("solveAllButton").innerText = 'Challenge Tap Token Text';
  249. // fill the gap challenge
  250. correctIndicesRun();
  251. } else if (document.querySelectorAll('[data-test="challenge-text-input"]').length > 0) {
  252. if (debug)
  253. document.getElementById("solveAllButton").innerText = 'Challenge Text Input';
  254. let elm = document.querySelectorAll('[data-test="challenge-text-input"]')[0];
  255. let nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
  256. nativeInputValueSetter.call(elm, window.sol.correctSolutions ? window.sol.correctSolutions[0] : (window.sol.displayTokens ? window.sol.displayTokens.find(t => t.isBlank).text : window.sol.prompt));
  257. let inputEvent = new Event('input', {
  258. bubbles: true
  259. });
  260.  
  261. elm.dispatchEvent(inputEvent);
  262. } else if (document.querySelectorAll('[data-test*="challenge-partialReverseTranslate"]').length > 0) {
  263. if (debug)
  264. document.getElementById("solveAllButton").innerText = 'Partial Reverse';
  265. let elm = document.querySelector('[data-test*="challenge-partialReverseTranslate"]')?.querySelector("span[contenteditable]");
  266. let nativeInputNodeTextSetter = Object.getOwnPropertyDescriptor(Node.prototype, "textContent").set
  267. nativeInputNodeTextSetter.call(elm, '"' + window.sol?.displayTokens?.filter(t => t.isBlank)?.map(t => t.text)?.join()?.replaceAll(',', '') + '"');
  268. let inputEvent = new Event('input', {
  269. bubbles: true
  270. });
  271.  
  272. elm.dispatchEvent(inputEvent);
  273. } else if (document.querySelectorAll('textarea[data-test="challenge-translate-input"]').length > 0) {
  274. if (debug)
  275. document.getElementById("solveAllButton").innerText = 'Challenge Translate Input';
  276. const elm = document.querySelector('textarea[data-test="challenge-translate-input"]');
  277. const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set;
  278. nativeInputValueSetter.call(elm, window.sol.correctSolutions ? window.sol.correctSolutions[0] : window.sol.prompt);
  279.  
  280. let inputEvent = new Event('input', {
  281. bubbles: true
  282. });
  283.  
  284. elm.dispatchEvent(inputEvent);
  285. }
  286. nextButton.click()
  287. }
  288.  
  289. function correctTokensRun() {
  290. const all_tokens = document.querySelectorAll('[data-test$="challenge-tap-token"]');
  291. const correct_tokens = window.sol.correctTokens;
  292. const clicked_tokens = [];
  293. correct_tokens.forEach(correct_token => {
  294. const matching_elements = Array.from(all_tokens).filter(element => element.textContent.trim() === correct_token.trim());
  295. if (matching_elements.length > 0) {
  296. const match_index = clicked_tokens.filter(token => token.textContent.trim() === correct_token.trim()).length;
  297. if (match_index < matching_elements.length) {
  298. matching_elements[match_index].click();
  299. clicked_tokens.push(matching_elements[match_index]);
  300. } else {
  301. clicked_tokens.push(matching_elements[0]);
  302. }
  303. }
  304. });
  305. }
  306.  
  307. function correctIndicesRun() {
  308. if (window.sol.correctIndices) {
  309. window.sol.correctIndices?.forEach(index => {
  310. document.querySelectorAll('div[data-test="word-bank"] [data-test="challenge-tap-token-text"]')[index].click();
  311. });
  312. // nextButton.click();
  313. }
  314. }
  315.  
  316. function findSubReact(dom, traverseUp = 0) {
  317. const key = Object.keys(dom).find(key => key.startsWith("__reactProps$"));
  318. return dom.parentElement[key].children.props;
  319. }
  320.  
  321. function findReact(dom, traverseUp = 0) {
  322. let reactProps = Object.keys(dom.parentElement).find((key) => key.startsWith("__reactProps$"));
  323. while (traverseUp-- > 0 && dom.parentElement) {
  324. dom = dom.parentElement;
  325. reactProps = Object.keys(dom.parentElement).find((key) => key.startsWith("__reactProps$"));
  326. }
  327. if(dom?.parentElement?.[reactProps]?.children[0] == null){
  328. return dom?.parentElement?.[reactProps]?.children[1]?._owner?.stateNode;
  329. }
  330. else{
  331. return dom?.parentElement?.[reactProps]?.children[0]?._owner?.stateNode;
  332. }
  333. //return dom?.parentElement?.[reactProps]?.children[0]?._owner?.stateNode;
  334. }
  335.  
  336.  
  337. window.findReact = findReact;
  338.  
  339. window.ss = solving;