Duolingo-Cheat-Tool

Auto answer Duolingo script!

当前为 2021-09-05 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Duolingo-Cheat-Tool
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1
  5. // @description Auto answer Duolingo script!
  6. // @author Tran Quy <tranphuquy19@gmail.com>
  7. // @match https://www.duolingo.com/skill*
  8. // @icon https://www.google.com/s2/favicons?domain=duolingo.com
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. const DEBUG = true;
  13. let mainInterval;
  14.  
  15. const dataTestComponentClassName = 'e4VJZ';
  16.  
  17. const TIME_OUT = 1000;
  18.  
  19. // Challenge types
  20. const CHARACTER_SELECT_TYPE = 'characterSelect';
  21. const CHARACTER_MATCH_TYPE = 'characterMatch'; // not yet
  22. const TRANSLATE_TYPE = 'translate';
  23. const LISTEN_TAP_TYPE = 'listenTap';
  24. const NAME_TYPE = 'name';
  25. const COMPLETE_REVERSE_TRANSLATION_TYPE = 'completeReverseTranslation';
  26. const LISTEN_TYPE = 'listen';
  27. const SELECT_TYPE = 'select';
  28. const JUDGE_TYPE = 'judge';
  29. const FORM_TYPE = 'form';
  30. const LISTEN_COMPREHENSION_TYPE = 'listenComprehension';
  31. const READ_COMPREHENSION_TYPE = 'readComprehension';
  32. const CHARACTER_INTRO_TYPE = 'characterIntro';
  33. const DIALOGUE_TYPE = 'dialogue';
  34. const SELECT_TRANSCRIPTION_TYPE = 'selectTranscription';
  35. const SPEAK_TYPE = 'speak';
  36. const SELECT_PRONUNCIATION_TYPE = 'selectPronunciation';
  37.  
  38. // Query DOM keys
  39. const CHALLENGE_CHOICE_CARD = '[data-test="challenge-choice-card"]';
  40. const CHALLENGE_CHOICE = '[data-test="challenge-choice"]';
  41. const CHALLENGE_TRANSLATE_INPUT = '[data-test="challenge-translate-input"]';
  42. const CHALLENGE_LISTEN_TAP = '[data-test="challenge-listenTap"]';
  43. const CHALLENGE_JUDGE_TEXT = '[data-test="challenge-judge-text"]';
  44. const CHALLENGE_TEXT_INPUT = '[data-test="challenge-text-input"]';
  45. const CHALLENGE_TAP_TOKEN = '[data-test="challenge-tap-token"]';
  46. const PLAYER_NEXT = '[data-test="player-next"]';
  47. const PLAYER_SKIP = '[data-test="player-skip"]';
  48. const BLAME_INCORRECT = '[data-test="blame blame-incorrect"]';
  49. const CHARACTER_MATCH = '[data-test="challenge challenge-characterMatch"]';
  50.  
  51. const clickEvent = new MouseEvent('click', {
  52. view: window,
  53. bubbles: true,
  54. cancelable: true
  55. });
  56.  
  57. function getChallengeObj(theObject) {
  58. let result = null;
  59. if (theObject instanceof Array) {
  60. for (let i = 0; i < theObject.length; i++) {
  61. result = getChallengeObj(theObject[i]);
  62. if (result) {
  63. break;
  64. }
  65. }
  66. }
  67. else {
  68. for (let prop in theObject) {
  69. if (prop == 'challenge') {
  70. if (typeof theObject[prop] == 'object') {
  71. return theObject;
  72. }
  73. }
  74. if (theObject[prop] instanceof Object || theObject[prop] instanceof Array) {
  75. result = getChallengeObj(theObject[prop]);
  76. if (result) {
  77. break;
  78. }
  79. }
  80. }
  81. }
  82. return result;
  83. }
  84.  
  85. function getChallenge() {
  86. // const dataTestComponentClassName = 'e4VJZ';
  87. const dataTestDOM = document.getElementsByClassName(dataTestComponentClassName)[0];
  88.  
  89. if (!dataTestDOM) {
  90. document.querySelectorAll(PLAYER_NEXT)[0].dispatchEvent(clickEvent);
  91. return null;
  92. } else {
  93. const dataTestAtrr = Object.keys(dataTestDOM).filter(att => /^__reactProps/g.test(att))[0];
  94. const childDataTestProps = dataTestDOM[dataTestAtrr];
  95. const { challenge } = getChallengeObj(childDataTestProps);
  96. return challenge;
  97. }
  98. }
  99.  
  100. function pressEnter() {
  101. document.dispatchEvent(new KeyboardEvent('keydown', { 'keyCode': 13, 'which': 13 }));
  102. }
  103.  
  104. function dynamicInput(element, msg) {
  105. let input = element;
  106. let lastValue = input.value;
  107. input.value = msg;
  108. let event = new Event('input', { bubbles: true });
  109. // hack React15
  110. event.simulated = true;
  111. // hack React16 内部定义了descriptor拦截value,此处重置状态
  112. let tracker = input._valueTracker;
  113. if (tracker) {
  114. tracker.setValue(lastValue);
  115. }
  116. input.dispatchEvent(event);
  117. }
  118.  
  119. function classify() {
  120. const challenge = getChallenge();
  121. if (!challenge) return;
  122. if (DEBUG) console.log(`${challenge.type}`, challenge);
  123. switch (challenge.type) {
  124. case SELECT_PRONUNCIATION_TYPE:
  125. case READ_COMPREHENSION_TYPE:
  126. case LISTEN_COMPREHENSION_TYPE:
  127. case FORM_TYPE: { // trắc nghiệm 1 đáp án
  128. const { choices, correctIndex } = challenge;
  129. if (DEBUG) console.log('READ_COMPREHENSION LISTEN_COMPREHENSION FORM', { choices, correctIndex });
  130. document.querySelectorAll(CHALLENGE_CHOICE)[correctIndex].dispatchEvent(clickEvent);
  131. return { choices, correctIndex };
  132. }
  133.  
  134. case SELECT_TYPE:
  135. case CHARACTER_SELECT_TYPE: { // trắc nghiệm 1 đáp án
  136. const { choices, correctIndex } = challenge;
  137. if (DEBUG) console.log('SELECT CHARACTER_SELECT', { choices, correctIndex });
  138. document.querySelectorAll(CHALLENGE_CHOICE_CARD)[correctIndex].dispatchEvent(clickEvent);
  139. return { choices, correctIndex };
  140. }
  141.  
  142. case CHARACTER_MATCH_TYPE: { // tập hợp các cặp thẻ
  143. const { pairs } = challenge;
  144. const tokens = document.querySelectorAll(CHALLENGE_TAP_TOKEN);
  145. pairs.forEach((pair) => {
  146. for(let i = 0; i < tokens.length; i++) {
  147. if(tokens[i].innerText === pair.transliteration || tokens[i].innerText === pair.character) {
  148. tokens[i].dispatchEvent(clickEvent);
  149. }
  150. }
  151. })
  152. return { pairs };
  153. }
  154.  
  155. case TRANSLATE_TYPE: {
  156. const { correctTokens, correctSolutions } = challenge;
  157. if (DEBUG) console.log('TRANSLATE', { correctTokens });
  158. if (correctTokens) {
  159. const tokens = document.querySelectorAll(CHALLENGE_TAP_TOKEN);
  160. let ignoreTokeIndexes = [];
  161. for (let correctTokenIndex in correctTokens) {
  162. for (let tokenIndex in tokens) {
  163. const token = tokens[tokenIndex];
  164. if (ignoreTokeIndexes.includes(tokenIndex)) continue;
  165. if (token.innerText === correctTokens[correctTokenIndex]) {
  166. token.dispatchEvent(clickEvent);
  167. ignoreTokeIndexes.push(tokenIndex);
  168. if(DEBUG) console.log(`correctTokenIndex [${correctTokens[correctTokenIndex]}] - tokenIndex [${token.innerText}]`);
  169. break;
  170. };
  171. }
  172. }
  173. } else if (correctSolutions) {
  174. let textInputElement = document.querySelectorAll(CHALLENGE_TRANSLATE_INPUT)[0];
  175. dynamicInput(textInputElement, correctSolutions[0]);
  176. }
  177.  
  178. return { correctTokens };
  179. }
  180.  
  181. case NAME_TYPE: { // nhập đán án
  182. const { correctSolutions } = challenge;
  183. if (DEBUG) console.log('NAME', { correctSolutions });
  184. let textInputElement = document.querySelectorAll(CHALLENGE_TEXT_INPUT)[0];
  185. let correctSolution = correctSolutions[0];
  186. dynamicInput(textInputElement, correctSolution);
  187. return { correctSolutions };
  188. }
  189.  
  190. case COMPLETE_REVERSE_TRANSLATION_TYPE: { // điền vào từ còn thiếu
  191. const { displayTokens } = challenge;
  192. if (DEBUG) console.log('COMPLETE_REVERSE_TRANLATION', { displayTokens });
  193. const { text } = displayTokens.filter(token => token.isBlank)[0];
  194. let textInputElement = document.querySelectorAll(CHALLENGE_TEXT_INPUT)[0];
  195. dynamicInput(textInputElement, text);
  196. return { displayTokens };
  197. }
  198.  
  199. case LISTEN_TAP_TYPE: {
  200. const { correctTokens } = challenge;
  201. if (DEBUG) console.log('LISTEN_TAP', { correctTokens });
  202. const tokens = document.querySelectorAll(CHALLENGE_TAP_TOKEN);
  203. for (let wordIndex in correctTokens) {
  204. tokens.forEach((token) => {
  205. if (token.innerText === correctTokens[wordIndex]) {
  206. token.dispatchEvent(clickEvent);
  207. };
  208. });
  209. }
  210. return { correctTokens };
  211. }
  212.  
  213. case LISTEN_TYPE: { // nghe và điền vào ô input
  214. const { prompt } = challenge;
  215. if (DEBUG) console.log('LISTEN', { prompt });
  216. let textInputElement = document.querySelectorAll(CHALLENGE_TRANSLATE_INPUT)[0];
  217. dynamicInput(textInputElement, prompt);
  218. return { prompt };
  219. }
  220.  
  221. case JUDGE_TYPE: { // trắc nghiệm 1 đáp án
  222. const { correctIndices } = challenge;
  223. if (DEBUG) console.log('JUDGE', { correctIndices });
  224. document.querySelectorAll(CHALLENGE_JUDGE_TEXT)[correctIndices[0]].dispatchEvent(clickEvent);
  225. return { correctIndices };
  226. }
  227.  
  228. case DIALOGUE_TYPE:
  229. case CHARACTER_INTRO_TYPE: { // trắc nghiệm 1 đáp án
  230. const { choices, correctIndex } = challenge;
  231. if (DEBUG) console.log('DIALOGUE CHARACTER_INTRO', { choices, correctIndex });
  232. document.querySelectorAll(CHALLENGE_JUDGE_TEXT)[correctIndex].dispatchEvent(clickEvent);
  233. return { choices, correctIndex };
  234. }
  235.  
  236. case SELECT_TRANSCRIPTION_TYPE: {
  237. const { choices, correctIndex } = challenge;
  238. if (DEBUG) console.log('DIALOGUE CHARACTER_INTRO', { choices, correctIndex });
  239. document.querySelectorAll(CHALLENGE_JUDGE_TEXT)[correctIndex].dispatchEvent(clickEvent);
  240. return { choices, correctIndex };
  241. }
  242.  
  243. case SPEAK_TYPE: {
  244. const { prompt } = challenge;
  245. if (DEBUG) console.log('SPEAK', { prompt });
  246. document.querySelectorAll(PLAYER_SKIP)[0].dispatchEvent(clickEvent);
  247. return { prompt };
  248. }
  249.  
  250. default:
  251. break;
  252. }
  253. }
  254.  
  255. function breakWhenIncorrect() {
  256. const isBreak = document.querySelectorAll(BLAME_INCORRECT).length > 0;
  257. if (isBreak) {
  258. console.log('Incorrect, stopped');
  259. clearInterval(mainInterval);
  260. };
  261. }
  262.  
  263. function main() {
  264. try {
  265. let isPlayerNext = document.querySelectorAll(PLAYER_NEXT)[0].textContent.toUpperCase();
  266. if (isPlayerNext.valueOf() !== 'CONTINUE') {
  267. classify();
  268. breakWhenIncorrect()
  269. pressEnter();
  270. }
  271. setTimeout(pressEnter, 150);
  272. } catch (e) {
  273. console.log(e);
  274. }
  275. }
  276.  
  277. function solveChallenge() {
  278. mainInterval = setInterval(main, TIME_OUT);
  279. console.log(`to stop run this command clearInterval(${mainInterval})`);
  280. }
  281.  
  282. // solveChallenge();
  283. (solveChallenge)();
  284. 1