duohacker but for practice.

please download my other script for it to work properly. duolingo cheat, duolingo hack, duolingo farmer, duolingo xp.

  1. // ==UserScript==
  2. // @name duohacker but for practice.
  3. // @namespace https://www.duolingo.com/
  4. // @homepageURL https://github.com/smintf/duohacker
  5. // @supportURL https://github.com/smintf/duohacker/issues
  6. // @contributionURL https://github.com/smintf/duohacker/pulls
  7. // @version 2.0
  8. // @description please download my other script for it to work properly. duolingo cheat, duolingo hack, duolingo farmer, duolingo xp.
  9. // @author 0325skz
  10. // @copyright Smint
  11. // @match https://www.duolingo.com/learn*
  12. // @match https://www.duolingo.com/practice*
  13. // @match https://www.duolingo.com/checkpoint*
  14. // @license MIT
  15. // @grant none
  16. // @run-at document-end
  17. // ==/UserScript==
  18.  
  19. // ==OpenUserJS==
  20. // @author smintf
  21. // ==/OpenUserJS==
  22.  
  23. const DEBUG = false;
  24. let mainInterval;
  25. const dataTestComponentClassName = 'e4VJZ';
  26. const TIME_OUT = 250;
  27.  
  28. // Challenge types
  29. const CHARACTER_SELECT_TYPE = 'characterSelect';
  30. const CHARACTER_MATCH_TYPE = 'characterMatch';
  31. const TRANSLATE_TYPE = 'translate';
  32. const LISTEN_TAP_TYPE = 'listenTap';
  33. const NAME_TYPE = 'name';
  34. const COMPLETE_REVERSE_TRANSLATION_TYPE = 'completeReverseTranslation';
  35. const LISTEN_TYPE = 'listen';
  36. const SELECT_TYPE = 'select';
  37. const JUDGE_TYPE = 'judge';
  38. const FORM_TYPE = 'form';
  39. const LISTEN_COMPREHENSION_TYPE = 'listenComprehension';
  40. const READ_COMPREHENSION_TYPE = 'readComprehension';
  41. const CHARACTER_INTRO_TYPE = 'characterIntro';
  42. const DIALOGUE_TYPE = 'dialogue';
  43. const SELECT_TRANSCRIPTION_TYPE = 'selectTranscription';
  44. const SPEAK_TYPE = 'speak';
  45. const SELECT_PRONUNCIATION_TYPE = 'selectPronunciation';
  46.  
  47. // W.I.P
  48. const ASSIST_TYPE = 'assist'
  49. const TAP_COMPLETE_TYPE = 'tapComplete';
  50. const GAP_FILL_TYPE = 'gapFill';
  51. const CHARACTER_TRACE_TYPE = 'characterTrace';
  52. const CHALLENGE_PUZZLE_TYPE = 'characterPuzzle';
  53. const DEFINITION_TYPE = 'definition';
  54. const MATCH_TYPE = 'match';
  55. const TAP_DESCRIBE_TYPE = 'tapDescribe';
  56. const FREE_RESPONSE_TYPE = 'freeResponse';
  57.  
  58. // Query DOM keys
  59. const CHALLENGE_CHOICE_CARD = '[data-test="challenge-choice-card"]';
  60. const CHALLENGE_CHOICE = '[data-test="challenge-choice"]';
  61. const CHALLENGE_TRANSLATE_INPUT = '[data-test="challenge-translate-input"]';
  62. const CHALLENGE_LISTEN_TAP = '[data-test="challenge-listenTap"]';
  63. const CHALLENGE_JUDGE_TEXT = '[data-test="challenge-judge-text"]';
  64. const CHALLENGE_TEXT_INPUT = '[data-test="challenge-text-input"]';
  65. const CHALLENGE_TAP_TOKEN = '[data-test="challenge-tap-token"]';
  66. const PLAYER_NEXT = '[data-test="player-next"]';
  67. const PLAYER_SKIP = '[data-test="player-skip"]';
  68. const BLAME_INCORRECT = '[data-test="blame blame-incorrect"]';
  69. const CHARACTER_MATCH = '[data-test="challenge challenge-characterMatch"]';
  70.  
  71.  
  72. // Mouse Click
  73. const clickEvent = new MouseEvent('click', {
  74. view: window,
  75. bubbles: true,
  76. cancelable: true
  77. });
  78.  
  79. // Gets Challenge Object
  80. function getChallengeObj(theObject) {
  81. let result = null;
  82. if (theObject instanceof Array) {
  83. for (let i = 0; i < theObject.length; i++) {
  84. result = getChallengeObj(theObject[i]);
  85. if (result) {
  86. break;
  87. }
  88. }
  89. }
  90. else {
  91. for (let prop in theObject) {
  92. if (prop == 'challenge') {
  93. if (typeof theObject[prop] == 'object') {
  94. return theObject;
  95. }
  96. }
  97. if (theObject[prop] instanceof Object || theObject[prop] instanceof Array) {
  98. result = getChallengeObj(theObject[prop]);
  99. if (result) {
  100. break;
  101. }
  102. }
  103. }
  104. }
  105. return result;
  106. }
  107.  
  108.  
  109. // Gets the Challenge
  110. function getChallenge() {
  111. const dataTestDOM = document.getElementsByClassName(dataTestComponentClassName)[0];
  112.  
  113. if (!dataTestDOM) {
  114. document.querySelectorAll(PLAYER_NEXT)[0].dispatchEvent(clickEvent);
  115. return null;
  116. } else {
  117. const dataTestAtrr = Object.keys(dataTestDOM).filter(att => /^__reactProps/g.test(att))[0];
  118. const childDataTestProps = dataTestDOM[dataTestAtrr];
  119. const { challenge } = getChallengeObj(childDataTestProps);
  120. return challenge;
  121. }
  122. }
  123.  
  124. // Calls clickEvent()
  125. function pressEnter() {
  126. const clickEvent = new MouseEvent("click", {
  127. "view": window,
  128. "bubbles": true,
  129. "cancelable": false
  130. });
  131.  
  132. // Press the Next/Continue button automatically
  133. document.querySelector('button[data-test="player-next"]').dispatchEvent(clickEvent);
  134. }
  135.  
  136. function dynamicInput(element, msg) {
  137. let input = element;
  138. let lastValue = input.value;
  139. input.value = msg;
  140. let event = new Event('input', { bubbles: true });
  141. event.simulated = true;
  142. let tracker = input._valueTracker;
  143. if (tracker) {
  144. tracker.setValue(lastValue);
  145. }
  146. input.dispatchEvent(event);
  147. }
  148.  
  149. // Solves the Challenge
  150. function classify() {
  151. const challenge = getChallenge();
  152. if (!challenge) return;
  153. if (DEBUG) console.log(`${challenge.type}`, challenge);
  154. switch (challenge.type) {
  155. case GAP_FILL_TYPE:
  156. case SELECT_TYPE:
  157. case SELECT_PRONUNCIATION_TYPE:
  158. case READ_COMPREHENSION_TYPE:
  159. case LISTEN_COMPREHENSION_TYPE:
  160. case CHARACTER_SELECT_TYPE:
  161. case FORM_TYPE: {
  162. const { choices, correctIndex } = challenge;
  163. if (DEBUG) console.log('READ_COMPREHENSION, LISTEN_COMPREHENSION, CHARACTER_SELECT_TYPE, GAP_FILL_TYPE, SELECT_PRONUNCIATION_TYPE', { choices, correctIndex });
  164. document.querySelectorAll(CHALLENGE_CHOICE)[correctIndex].dispatchEvent(clickEvent);
  165. return { choices, correctIndex };
  166. }
  167.  
  168. case TAP_COMPLETE_TYPE: {
  169. const { choices, correctIndices } = challenge;
  170. if (DEBUG) console.log('TAP_COMPLETE_TYPE', { choices, correctIndices });
  171. return { choices, correctIndices };
  172. }
  173.  
  174. case MATCH_TYPE:
  175. case CHARACTER_MATCH_TYPE: {
  176. const { pairs } = challenge;
  177. const tokens = document.querySelectorAll(CHALLENGE_TAP_TOKEN);
  178. if (DEBUG) console.log('CHARACTER_MATCH_TYPE', { tokens });
  179. pairs.forEach((pair) => {
  180. for(let i = 0; i < tokens.length; i++) {
  181. if(tokens[i].innerText === pair.transliteration || tokens[i].innerText === pair.character) {
  182. tokens[i].dispatchEvent(clickEvent);
  183. }
  184. }
  185. })
  186. return { pairs };
  187. }
  188.  
  189. case TRANSLATE_TYPE: {
  190. const { correctTokens, correctSolutions } = challenge;
  191. if (DEBUG) console.log('TRANSLATE_TYPE', { correctTokens });
  192. if (correctTokens) {
  193. const tokens = document.querySelectorAll(CHALLENGE_TAP_TOKEN);
  194. let ignoreTokeIndexes = [];
  195. for (let correctTokenIndex in correctTokens) {
  196. for (let tokenIndex in tokens) {
  197. const token = tokens[tokenIndex];
  198. if (ignoreTokeIndexes.includes(tokenIndex)) continue;
  199. if (token.innerText === correctTokens[correctTokenIndex]) {
  200. token.dispatchEvent(clickEvent);
  201. ignoreTokeIndexes.push(tokenIndex);
  202. if(DEBUG) console.log(`correctTokenIndex [${correctTokens[correctTokenIndex]}] - tokenIndex [${token.innerText}]`);
  203. break;
  204. };
  205. }
  206. }
  207. } else if (correctSolutions) {
  208. let textInputElement = document.querySelectorAll(CHALLENGE_TRANSLATE_INPUT)[0];
  209. dynamicInput(textInputElement, correctSolutions[0]);
  210. }
  211.  
  212. return { correctTokens };
  213. }
  214.  
  215. case NAME_TYPE: {
  216. const { correctSolutions } = challenge;
  217. if (DEBUG) console.log('NAME_TYPE', { correctSolutions });
  218. let textInputElement = document.querySelectorAll(CHALLENGE_TEXT_INPUT)[0];
  219. let correctSolution = correctSolutions[0];
  220. dynamicInput(textInputElement, correctSolution);
  221. return { correctSolutions };
  222. }
  223.  
  224. case COMPLETE_REVERSE_TRANSLATION_TYPE: {
  225. const { displayTokens } = challenge;
  226. if (DEBUG) console.log('COMPLETE_REVERSE_TRANLATION_TYPE', { displayTokens });
  227. const { text } = displayTokens.filter(token => token.isBlank)[0];
  228. let textInputElement = document.querySelectorAll(CHALLENGE_TEXT_INPUT)[0];
  229. dynamicInput(textInputElement, text);
  230. return { displayTokens };
  231. }
  232.  
  233. case LISTEN_TAP_TYPE: {
  234. const { correctTokens } = challenge;
  235. if (DEBUG) console.log('LISTEN_TAP_TYPE', { correctTokens });
  236. const tokens = document.querySelectorAll(CHALLENGE_TAP_TOKEN);
  237. for (let wordIndex in correctTokens) {
  238. tokens.forEach((token) => {
  239. if (token.innerText === correctTokens[wordIndex]) {
  240. token.dispatchEvent(clickEvent);
  241. };
  242. });
  243. }
  244. return { correctTokens };
  245. }
  246.  
  247. case LISTEN_TYPE: {
  248. const { prompt } = challenge;
  249. if (DEBUG) console.log('LISTEN_TYPE', { prompt });
  250. let textInputElement = document.querySelectorAll(CHALLENGE_TRANSLATE_INPUT)[0];
  251. dynamicInput(textInputElement, prompt);
  252. return { prompt };
  253. }
  254.  
  255. case JUDGE_TYPE: {
  256. const { correctIndices } = challenge;
  257. if (DEBUG) console.log('JUDGE_TYPE', { correctIndices });
  258. document.querySelectorAll(CHALLENGE_JUDGE_TEXT)[correctIndices[0]].dispatchEvent(clickEvent);
  259. return { correctIndices };
  260. }
  261.  
  262. case DIALOGUE_TYPE:
  263. case CHARACTER_INTRO_TYPE: {
  264. const { choices, correctIndex } = challenge;
  265. if (DEBUG) console.log('CHARACTER_INTRO_TYPE, DIALOGUE_TYPE', { choices, correctIndex });
  266. document.querySelectorAll(CHALLENGE_JUDGE_TEXT)[correctIndex].dispatchEvent(clickEvent);
  267. return { choices, correctIndex };
  268. }
  269.  
  270. case SELECT_TRANSCRIPTION_TYPE: {
  271. const { choices, correctIndex } = challenge;
  272. if (DEBUG) console.log('SELECT_TRANSCRIPTION_TYPE', { choices, correctIndex });
  273. document.querySelectorAll(CHALLENGE_JUDGE_TEXT)[correctIndex].dispatchEvent(clickEvent);
  274. return { choices, correctIndex };
  275. }
  276.  
  277. case SPEAK_TYPE: {
  278. const { prompt } = challenge;
  279. if (DEBUG) console.log('SPEAK_TYPE', { prompt });
  280. document.querySelectorAll(PLAYER_SKIP)[0].dispatchEvent(clickEvent);
  281. return { prompt };
  282. }
  283.  
  284. default:
  285. break;
  286. }
  287. }
  288.  
  289. // Stops the userscript when an answer is incorrect
  290. function breakWhenIncorrect() {
  291. const isBreak = document.querySelectorAll(BLAME_INCORRECT).length > 0;
  292. if (isBreak) {
  293. console.log('Incorrect, stopped');
  294. clearInterval(mainInterval);
  295. };
  296. }
  297.  
  298. // Main Function
  299. function main() {
  300. try {
  301. let isPlayerNext = document.querySelectorAll(PLAYER_NEXT)[0].textContent.toUpperCase();
  302. if (isPlayerNext.valueOf() !== 'CONTINUE') {
  303. classify();
  304. breakWhenIncorrect()
  305. pressEnter();
  306. }
  307. setTimeout(pressEnter, 150);
  308. } catch (e) {
  309. console.log(e);
  310. }
  311. }
  312.  
  313. // Calls main()
  314. function solveChallenge() {
  315. mainInterval = setInterval(main, TIME_OUT);
  316. // Logs command to stop the Userscript
  317. console.log(`to stop the script run "clearInterval(${mainInterval})"`);
  318. }
  319.  
  320. (solveChallenge)();
  321. 1

QingJ © 2025

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