Auto-Duolingo

[Lite Version] Automatically farm experience points, hacking Duolingo is so easy!

目前為 2024-04-02 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Auto-Duolingo
  3. // @version 1.0.2
  4. // @author DevX
  5. // @namespace http://tampermonkey.net/
  6. // @description [Lite Version] Automatically farm experience points, hacking Duolingo is so easy!
  7. // @match https://*.duolingo.com/*
  8. // @grant none
  9. // @license MIT
  10. // @icon https://api.autoduolingo.click/assets/favicon.ico
  11. // ==/UserScript==
  12.  
  13. (() => {
  14. const AUTODUOLINGO_STORAGE_KEY = "autoDuolingoStorageKey";
  15. const { isSafeMode, isShowUI, isAnimationOff, exp, time } = getSession();
  16.  
  17. const autoDuoLite = {
  18. version: "1.0.2",
  19. isAuto: false,
  20. isAutoRunning: false,
  21. isSafeMode: !!isSafeMode,
  22. isAnimationOff: !!isAnimationOff,
  23. goChallengeTm: 500,
  24. reloadTm: 1800000,
  25. startTm: null,
  26. isShowUI: isShowUI === undefined || isShowUI,
  27. exp: exp || 0,
  28. totalTime: time || 0,
  29. practiceHubPath: "/practice-hub",
  30. listeningPacticePath: "/practice-hub/listening-practice",
  31. lessonWrapper: "._3js2_",
  32. reactProps: null,
  33. dataStateNode: null,
  34. nativeTextareaValueSetter: Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set,
  35.  
  36. setup: function () {
  37. this.createStyle();
  38. this.createSignature();
  39. this.createContact();
  40. this.createBtn();
  41. this.createStatistics();
  42. this.createFunctions();
  43. this.createContainer();
  44. !isShowUI && this.handleShowHideUI();
  45. isAnimationOff && this.handleAnimationOff();
  46. this.renderTime();
  47. getDataSession("isBasicAuto") && this.start();
  48. },
  49.  
  50. createSignature: function () {
  51. this.signatureElm = document.createElement("div");
  52. Object.assign(this.signatureElm, {
  53. className: "signature-listening",
  54. innerHTML: `
  55. <div>
  56. Auto-Duolingo DevX
  57. <div class="autoduo-lite-version">
  58. LITE VERSION
  59. </div>
  60. </div>
  61. `,
  62. });
  63. document.body.appendChild(this.signatureElm);
  64. },
  65.  
  66. createContact: function () {
  67. this.contactWrapper = document.createElement("div");
  68. Object.assign(this.contactWrapper, {
  69. className: "contact-wrapper-listening",
  70. innerHTML: `<a class="contact-item-listening" href="https://t.me/imdevx" target="_blank" style="--data-img: url('https://api.autoduolingo.click/assets/tele-icon.ndx')"></a>
  71. <a class="contact-item-listening" href="https://t.me/autoduolingo" target="_blank" style="--data-img: url('https://api.autoduolingo.click/assets/tele-gr-icon.ndx')"></a>
  72. <a class="contact-item-listening" href="https://zalo.me/g/lmhfps187" target="_blank" style="--data-img: url('https://api.autoduolingo.click/assets/zalo-icon.ndx')"></a>
  73. <a class="contact-item-listening" href="https://www.youtube.com/@autoduofamily" target="_blank" style="--data-img: url('https://api.autoduolingo.click/assets/youtube-icon.ndx')"></a>
  74. <a class="contact-item-listening" href="https://gf.qytechs.cn/en/scripts/487867-auto-duolingo" target="_blank" style="--data-img: url('https://api.autoduolingo.click/assets/greasyfork-icon.ndx')"></a>
  75. `,
  76. });
  77. },
  78.  
  79. createBtn: function () {
  80. this.autoBtn = document.createElement("button");
  81. Object.assign(this.autoBtn, {
  82. className: "_2N_A5 _36Vd3 _16r-S auto-farm-btn-listening",
  83. innerText: "START FARM XP",
  84. onclick: () => {
  85. this.isAuto ? this.stop() : this.start();
  86. },
  87. });
  88.  
  89. this.updateBtn = document.createElement("a");
  90. Object.assign(this.updateBtn, {
  91. className: "_2N_A5 _36Vd3 _16r-S update-btn-listening",
  92. innerText: "Update to the full version",
  93. target: "_blank",
  94. onclick: () => {
  95. this.isAuto && this.stop();
  96. },
  97. });
  98.  
  99. this.showHideBtn = document.createElement("button");
  100. Object.assign(this.showHideBtn, {
  101. className: "show-hide-listening",
  102. style: `--data-version: 'V${this.version}'`,
  103. innerHTML: "<i></i>",
  104. });
  105.  
  106. this.showHideBtn.addEventListener("click", () => {
  107. this.isShowUI = !this.isShowUI;
  108. this.handleShowHideUI(true);
  109. });
  110. document.body.append(this.showHideBtn);
  111.  
  112. new Promise((resolve) => {
  113. setTimeout(
  114. resolve.bind(window, notAvailable("aHR0cHM6Ly9pbnN0YWxsLmF1dG9kdW9saW5nby5jbGljaw==")),
  115. 1000
  116. );
  117. }).then((res) => (this.updateBtn.href = res));
  118. },
  119.  
  120. createStatistics: function () {
  121. this.statistic = document.createElement("div");
  122. this.keyTypeElm = document.createElement("p");
  123. this.keyExpiredElm = document.createElement("p");
  124. this.expElm = document.createElement("p");
  125. this.dateElm = document.createElement("p");
  126. const statisticWrapper = document.createElement("div");
  127.  
  128. Object.assign(this.keyTypeElm, {
  129. className: "key-type-listening",
  130. style: `--data-name: "Type"`,
  131. innerHTML: "<b style='color: #009feb'>Auto-Duolingo LITE</b>",
  132. });
  133. Object.assign(this.keyExpiredElm, {
  134. className: "key-expired-listening",
  135. style: `--data-name: "EXP"`,
  136. innerHTML: "<b style='color: #009feb'>∞</b>",
  137. });
  138.  
  139. this.expElm.className = "total-exp-listening";
  140. this.expElm.innerText = this.exp;
  141. this.statistic.className = "statistic-listening";
  142. this.dateElm.className = "time-listening";
  143. statisticWrapper.className = "statistic-wrapper-listening";
  144.  
  145. statisticWrapper.append(this.expElm, this.dateElm);
  146. this.statistic.append(this.keyTypeElm, this.keyExpiredElm, statisticWrapper);
  147. },
  148.  
  149. createFunctions: function () {
  150. this.animationOffWrapper = document.createElement("div");
  151. this.animationOffWrapper.style = `--data-name: "Hide Animation"`;
  152. const animationOffInfo =
  153. "- HIDE ANIMATION MODE:\n" +
  154. "When this mode is enabled, images and animations on the website will be hidden to optimize performance.";
  155. this.autoduoCreateSwitch(
  156. animationOffInfo,
  157. this.animationOffWrapper,
  158. 1,
  159. this.isAnimationOff,
  160. (setSwitch) => {
  161. this.isAnimationOff = !this.isAnimationOff;
  162. this.handleAnimationOff(true);
  163. setSwitch(this.isAnimationOff);
  164. }
  165. );
  166.  
  167. this.safeModeWrapper = document.createElement("div");
  168. this.safeModeWrapper.style = `--data-name: "Safe Mode"`;
  169. const safeModeInfo =
  170. "- SAFE MODE:\n" +
  171. "When this mode is enabled, the system will simulate user actions when using auto. The speed will be more relaxed, " +
  172. "in exchange for the completion time of lessons and the amount of experience will be the most natural, minimizing " +
  173. "the risks of REPORT and account BAN!";
  174. this.autoduoCreateSwitch(safeModeInfo, this.safeModeWrapper, 2, this.isSafeMode, () => {
  175. this.isSafeMode ? this.handleSafeModeOff() : this.handleSafeModeOn();
  176. });
  177.  
  178. this.turboModeWrapper = document.createElement("div");
  179. this.turboModeWrapper.style = `--data-name: "Turbo Mode"`;
  180. const turboModeInfo =
  181. "- TURBO MODE:\n" +
  182. "When enabled, the system will significantly boost the auto speed. It will utilize higher performance and " +
  183. "is not recommended for use on low-performance devices.\nTurn it off and refresh the page if you encounter " +
  184. "issues while activating this mode!\n\nNote: This is an experimental feature and requires a VIP Key to use. " +
  185. "Only enable it when you truly require speed and understand its implications!!";
  186. this.autoduoCreateSwitch(turboModeInfo, this.turboModeWrapper, 3, false);
  187.  
  188. this.legendModeWrapper = document.createElement("div");
  189. this.legendModeWrapper.style = `--data-name: "Lesson Pass Mode"`;
  190. const legendModeInfo =
  191. "- LESSON PASS MODE:\n" +
  192. "When activated, the system won't repeat exercises as in the regular mode but will engage in exercises actively selected by the user. " +
  193. "This mode is used for legendary exercises, story exercises, and most other similar exercises. You need to enter the lesson you want to " +
  194. "pass in, and then the system will automatically complete that lesson for you!\n" +
  195. "When this mode is activated, the basic auto button will be temporarily disabled.";
  196. this.autoduoCreateSwitch(legendModeInfo, this.legendModeWrapper, 4, false);
  197.  
  198. this.targetModeWrapper = document.createElement("div");
  199. this.targetModeWrapper.style = `--data-name: "XP Target Mode"`;
  200. const targetModeInfo =
  201. "- EXPERIENCE POINT TARGET MODE:\n" +
  202. "By setting an experience point target, the system will automatically stop auto mode when the total experience points " +
  203. "obtained equal or exceed the specified target.\nThis helps you better control the auto function, " +
  204. "preventing unintentional accumulation of excess experience points due to forgetting to turn off auto mode!\n\n" +
  205. "- Note: The experience point target must be greater than the current amount of experience points obtained through auto mode!";
  206. this.autoduoCreateSwitch(targetModeInfo, this.targetModeWrapper, 5, false);
  207.  
  208. this.passModeWrapper = document.createElement("div");
  209. this.passModeWrapper.style = `--data-name: "Auto Pass Mode"`;
  210. const passModeInfo =
  211. "- AUTO PASS MODE:\n" +
  212. "By setting the number of lessons you wish to pass, the system will automatically pass the corresponding " +
  213. "number of new lessons as per the value you've set!\n\n" +
  214. "- Note: the lesson value should be within the range of 1 - 1000 (Enter 0 for unlimited auto)!";
  215. this.autoduoCreateSwitch(passModeInfo, this.passModeWrapper, 6, false);
  216.  
  217. this.functionWrapper = document.createElement("div");
  218. this.functionWrapper.className = "function-wrapper-listening";
  219. this.functionWrapper.append(
  220. this.animationOffWrapper,
  221. this.safeModeWrapper,
  222. this.turboModeWrapper,
  223. this.legendModeWrapper,
  224. this.passModeWrapper,
  225. this.targetModeWrapper
  226. );
  227. },
  228.  
  229. createContainer: function () {
  230. this.autoContainer = document.createElement("div");
  231. this.autoContainer.className = "auto-container-listening";
  232. this.autoContainer.append(this.statistic, this.functionWrapper, this.autoBtn, this.updateBtn);
  233.  
  234. this.overlayContainer = document.createElement("div");
  235. this.overlayContainer.className = "overlay-listening";
  236.  
  237. this.controlContainer = document.createElement("div");
  238. this.controlContainer.className = "control-container-listening";
  239. this.controlContainer.append(this.autoContainer, this.contactWrapper);
  240. document.body.append(this.controlContainer);
  241. },
  242.  
  243. handleShowHideUI: function (isSave = false) {
  244. if (this.isShowUI) {
  245. this.showHideBtn.classList.remove("hide");
  246. document.body.append(this.controlContainer, this.signatureElm);
  247. } else {
  248. this.showHideBtn.classList.add("hide");
  249. this.controlContainer.remove();
  250. this.signatureElm.remove();
  251. }
  252.  
  253. if (isSave) {
  254. setDataSession("isShowUI", this.isShowUI);
  255. this.controlContainer.classList.contains("autoduo-animate") ||
  256. this.controlContainer.classList.add("autoduo-animate");
  257. }
  258. },
  259.  
  260. handleAnimationOff: function (isSave = false) {
  261. this.isAnimationOff
  262. ? document.head.appendChild(this.animationStyle)
  263. : document.head.removeChild(this.animationStyle);
  264. isSave && setDataSession("isAnimationOff", this.isAnimationOff);
  265. },
  266.  
  267. handleSafeModeOn: function () {
  268. this.safeModeWrapper.setAutoduoSwitch(this.setSafeMode(true));
  269. },
  270.  
  271. handleSafeModeOff: function () {
  272. this.safeModeWrapper.setAutoduoSwitch(this.setSafeMode(false));
  273. },
  274.  
  275. start: function () {
  276. if (this.isAuto || this.isAutoRunning) {
  277. return;
  278. }
  279.  
  280. document.body.appendChild(this.overlayContainer);
  281. this.isAuto = true;
  282. this.autoBtn.classList.add("running");
  283. this.autoBtn.innerText = "STOP FARM XP";
  284. setDataSession("isBasicAuto", this.isAuto);
  285. this.startTm = Date.now();
  286. this.handleLocation();
  287. },
  288.  
  289. stop: function () {
  290. if (!this.isAuto || this.isLegendMode) {
  291. return;
  292. }
  293. document.body.removeChild(this.overlayContainer);
  294. this.isAuto = false;
  295. this.autoBtn.classList.remove("running");
  296. this.autoBtn.innerText = "START FARM XP";
  297. setDataSession("isBasicAuto", this.isAuto);
  298. },
  299.  
  300. handleLocation: function () {
  301. if (!this.isAuto) {
  302. return;
  303. }
  304.  
  305. const currentPath = window.location.pathname;
  306.  
  307. switch (currentPath) {
  308. case this.practiceHubPath:
  309. this.goPracticeHubChallenge();
  310. break;
  311.  
  312. case this.listeningPacticePath:
  313. this.handlePracticeHubChallenge();
  314. break;
  315.  
  316. default:
  317. this.autoduoError(
  318. "Inappropriate location: Only enable auto when on the practice page (with the dumbbell icon) of Duolingo Super!" +
  319. "\n- Enabling auto on Duolingo Super's practice page will automatically farm listening exercises (20 XP)." +
  320. "\n- If you want to auto farm practice exercises without needing Super or automatically complete most other exercises, please update to the full version of Auto-Duolingo!"
  321. );
  322. break;
  323. }
  324. },
  325.  
  326. goPracticeHubChallenge: function () {
  327. if (this.isAuto === false) {
  328. return;
  329. }
  330. const challengeBtn = $(
  331. 'img[src="https://d35aaqx5ub95lt.cloudfront.net/images/practiceHub/2ebe830fd55a7f2754d371bcd79faf32.svg"]'
  332. );
  333.  
  334. if (!challengeBtn) {
  335. setTimeout(this.goPracticeHubChallenge.bind(this), 1000);
  336. return;
  337. }
  338.  
  339. challengeBtn.click();
  340. setTimeout(this.handlePracticeHubChallenge.bind(this), 1000);
  341. },
  342.  
  343. handlePracticeHubChallenge: function () {
  344. if (window.location.pathname === this.practiceHubPath) {
  345. this.goPracticeHubChallenge();
  346. return;
  347. }
  348.  
  349. const challengeWrapper = $(this.lessonWrapper);
  350. if (challengeWrapper) {
  351. this.getDataStateNode(challengeWrapper);
  352. this.next();
  353. return;
  354. }
  355. const nextActiveBtn = $('[data-test="player-next"][aria-disabled="false"]');
  356.  
  357. if (nextActiveBtn) {
  358. this.next();
  359. return;
  360. }
  361.  
  362. setTimeout(this.handlePracticeHubChallenge.bind(this), 1000);
  363. },
  364.  
  365. handleChallenge: async function () {
  366. if (this.isSafeMode) {
  367. await this.sleep(500);
  368. }
  369. if (!this.isAuto || this.isAutoRunning) {
  370. return;
  371. }
  372.  
  373. const challengeTypeElm = $('[data-test*="challenge challenge"]');
  374.  
  375. if (!challengeTypeElm) {
  376. return this.autoduoError("Undefined challenge!!");
  377. }
  378.  
  379. const challengeType = challengeTypeElm.dataset.test?.slice(10);
  380.  
  381. this.setAutoRunning(true);
  382. switch (challengeType) {
  383. case "challenge-listenTap":
  384. this.handleChallengeTranslate();
  385. break;
  386.  
  387. case "challenge-gapFill":
  388. case "challenge-listenIsolation":
  389. case "challenge-assist":
  390. case "challenge-selectTranscription":
  391. case "challenge-characterIntro":
  392. case "challenge-characterSelect":
  393. case "challenge-selectPronunciation":
  394. case "challenge-dialogue":
  395. case "challenge-readComprehension":
  396. case "challenge-listenComprehension":
  397. case "challenge-select":
  398. case "challenge-form":
  399. case "challenge-definition":
  400. case "challenge-sameDifferent":
  401. this.handleChallengeChoice();
  402. break;
  403.  
  404. default:
  405. this.autoduoError(
  406. "This exercise is not currently supported in this version. Try updating to the full version of Auto-Duolingo and try again!"
  407. );
  408. break;
  409. }
  410. },
  411.  
  412. handleChallengeTranslate: function () {
  413. if (this.isAuto === false) {
  414. return;
  415. }
  416.  
  417. let data = this.getData("correctTokens");
  418.  
  419. if (!data?.length) {
  420. data = this.getData(["challengeResponseTrackingProperties", "best_solution"])?.split(" ");
  421. }
  422.  
  423. if (!data) {
  424. return this.autoduoError("Lesson data not found.");
  425. }
  426.  
  427. const textArea = $('textarea[data-test="challenge-translate-input"]:not([disabled])');
  428. if (textArea) {
  429. const toggleKeyboard = $('[data-test="player-toggle-keyboard"]');
  430. if (toggleKeyboard) {
  431. toggleKeyboard.click();
  432. return setTimeout(this.handleChallengeTranslate.bind(this), 500);
  433. }
  434.  
  435. const inputEvent = new Event("input", {
  436. bubbles: true,
  437. });
  438.  
  439. let answer = "";
  440.  
  441. const inputCaseHandler = () => {
  442. setTimeout(() => {
  443. if (data.length === 0) {
  444. this.setAutoRunning(false);
  445. this.next(true);
  446. return;
  447. }
  448.  
  449. answer += " " + data.shift();
  450. this.nativeTextareaValueSetter.call(textArea, answer);
  451. textArea.dispatchEvent(inputEvent);
  452. inputCaseHandler();
  453. }, this.rmSafeDelayTm());
  454. };
  455. inputCaseHandler();
  456. return;
  457. }
  458.  
  459. let options = arr($$('[class="_1vkDo"] button[data-test*="challenge-tap-token"]'));
  460. if (options.length === 0) {
  461. return setTimeout(this.handleChallengeTranslate.bind(this), 500);
  462. }
  463.  
  464. const getIndexOfOption = (targetData) => {
  465. const index = options.findIndex((option) => option.textContent === targetData);
  466. return index;
  467. };
  468.  
  469. const selectCaseHandler = () => {
  470. setTimeout(() => {
  471. if (data.length === 0) {
  472. this.setAutoRunning(false);
  473. this.next(true);
  474. return;
  475. }
  476.  
  477. const firstValue = data.shift();
  478. const index = getIndexOfOption(firstValue);
  479.  
  480. if (index === -1) {
  481. return this.autoduoLessonError("No suitable option found.");
  482. }
  483.  
  484. options[index].click();
  485. options.splice(index, 1);
  486. selectCaseHandler();
  487. }, this.rmSafeDelayTm());
  488. };
  489. selectCaseHandler();
  490. },
  491.  
  492. handleChallengeChoice: function () {
  493. if (!this.isAuto) {
  494. return;
  495. }
  496.  
  497. const optionElm = $$('[data-test="challenge-choice"]');
  498. const correctIndex = this.getData("correctIndex");
  499.  
  500. if (correctIndex === null) {
  501. return this.autoduoError("Lesson data not found.");
  502. }
  503.  
  504. setTimeout(() => {
  505. optionElm[correctIndex].click();
  506. setTimeout(() => {
  507. this.setAutoRunning(false);
  508. this.next();
  509. }, this.rmSafeDelayTm());
  510. }, this.rmSafeDelayTm());
  511. },
  512.  
  513. next: function () {
  514. if (!this.isAuto) {
  515. return;
  516. }
  517.  
  518. const expWrapper = $('[class="i6eR4"]');
  519. if (expWrapper) {
  520. const exp = this.getExp(expWrapper);
  521.  
  522. if (exp !== undefined) {
  523. this.exp += exp;
  524. this.expElm.innerText = this.exp;
  525.  
  526. const timeNow = Date.now();
  527. const finishTime = timeNow - this.startTm;
  528. this.totalTime += finishTime;
  529. this.startTm = timeNow;
  530. this.renderTime();
  531.  
  532. setDataSession("exp", this.exp);
  533. setDataSession("time", this.totalTime);
  534.  
  535. const currentPath = window.location.pathname;
  536. if (currentPath === this.listeningPacticePath) {
  537. if ((this.totalReloadTime += finishTime) >= this.reloadTm) {
  538. window.location.reload();
  539. return;
  540. }
  541. }
  542. }
  543. }
  544.  
  545. const nextBtn = $('[data-test="player-next"]');
  546.  
  547. if (!nextBtn) {
  548. setTimeout(this.handleLocation.bind(this), this.goChallengeTm);
  549. return;
  550. }
  551.  
  552. const isDisable = nextBtn.getAttribute("aria-disabled");
  553. const isLoadingBtn = nextBtn.classList.contains("_3CBig");
  554.  
  555. if (isDisable === "true" && !isLoadingBtn) {
  556. boom(this.handleChallenge.bind(this));
  557. return;
  558. }
  559.  
  560. !isLoadingBtn && nextBtn.click();
  561. boom(this.next.bind(this));
  562. },
  563.  
  564. findReactProps: function (wrapperElm) {
  565. this.reactProps = Object.keys(wrapperElm).find((key) => key.startsWith("__reactProps"));
  566.  
  567. if (!this.reactProps) {
  568. return this.autoduoError("ERROR");
  569. }
  570. },
  571.  
  572. getDataStateNode: function (wrapperElm) {
  573. this.reactProps === null && this.findReactProps(wrapperElm);
  574. const childrenData = wrapperElm?.[this.reactProps]?.children;
  575.  
  576. if (Array.isArray(childrenData)) {
  577. this.dataStateNode = childrenData?.[0]?._owner?.stateNode;
  578. } else {
  579. this.dataStateNode = childrenData?._owner?.stateNode;
  580. }
  581. },
  582.  
  583. getData: function (subGenealogy) {
  584. const currentChallenge = this.dataStateNode?.props?.currentChallenge;
  585. if (!currentChallenge) {
  586. return this.autoduoError("There was an error while loading challenge data!");
  587. }
  588.  
  589. if (Array.isArray(subGenealogy)) {
  590. const result = subGenealogy.reduce((acc, currentKey) => {
  591. if (acc === null) {
  592. return null;
  593. }
  594.  
  595. const currentValue = acc[currentKey];
  596. return currentValue || null;
  597. }, currentChallenge);
  598.  
  599. if (result === null) {
  600. return this.autoduoError("There was an error while getting the data!");
  601. }
  602.  
  603. return Array.isArray(result) ? [...result] : result;
  604. } else {
  605. const result = currentChallenge[subGenealogy];
  606. return Array.isArray(result) ? [...result] : result;
  607. }
  608. },
  609.  
  610. getExp: function (expWrapper) {
  611. const keys = Object.keys(expWrapper);
  612. const key = keys.find((key) => key.startsWith("__reactProps"));
  613.  
  614. const exp = expWrapper?.[key]?.children?.props?.slide?.xpGoalSessionProgress?.totalXpThisSession;
  615. return exp;
  616. },
  617.  
  618. renderTime: function () {
  619. const timeString = timeFormat(this.totalTime);
  620. this.dateElm.innerText = timeString;
  621. },
  622.  
  623. setAutoRunning: function (isRunning) {
  624. this.isAutoRunning = isRunning;
  625. },
  626.  
  627. setSafeMode: function (isSafeMode) {
  628. this.isSafeMode = isSafeMode;
  629. setDataSession("isSafeMode", isSafeMode);
  630. return isSafeMode;
  631. },
  632.  
  633. rmSafeDelayTm: function () {
  634. if (!this.isSafeMode) {
  635. return 0;
  636. }
  637.  
  638. return Math.floor(Math.random() * 550 + 50);
  639. },
  640.  
  641. sleep: async function (time) {
  642. await new Promise((resolve) => setTimeout(resolve, time));
  643. },
  644.  
  645. autoduoError: function (message) {
  646. this.isAutoRunning && this.setAutoRunning(false);
  647. this.isAuto && this.stop();
  648. alert("ERROR: " + message + ". Try updating to the full version if this issue persists!");
  649. },
  650.  
  651. autoduoLessonError: function (errorText) {
  652. const settingIcon = $("._7X9XV");
  653. if (settingIcon) {
  654. settingIcon.click();
  655.  
  656. return setTimeout(() => {
  657. this.autoduoError(
  658. `${errorText}. If you are currently displaying the pronunciation guide, please turn it off first, then reload the page, and finally turn on auto again!`
  659. );
  660. }, 800);
  661. }
  662. return this.autoduoError(errorText);
  663. },
  664.  
  665. autoduoCreateSwitch: function (descriptionText = "", wrapperElm, id, isChecked, handleSwitch) {
  666. const infoElm = document.createElement("i");
  667. Object.assign(infoElm, {
  668. className: "switch-info-listening",
  669. title: "Detail",
  670. onclick: () => {
  671. alert(descriptionText);
  672. },
  673. });
  674.  
  675. const checkboxElm = document.createElement("input");
  676. Object.assign(checkboxElm, {
  677. type: "checkbox",
  678. hidden: true,
  679. checked: isChecked,
  680. });
  681.  
  682. const setSwitch = (isEnable) => {
  683. checkboxElm.checked = isEnable;
  684. };
  685.  
  686. const labelElm = document.createElement("label");
  687. labelElm.addEventListener("click", () => {
  688. id > 2 ? notAvailable() : handleSwitch(setSwitch);
  689. });
  690.  
  691. const switchContainer = document.createElement("div");
  692. switchContainer.className = "switch-container-listening";
  693. switchContainer.append(infoElm, checkboxElm, labelElm);
  694.  
  695. wrapperElm.classList.add("switch-wrapper-listening");
  696. if ([3, 4, 5, 6].includes(id)) {
  697. wrapperElm.classList.add("unavailable");
  698. }
  699. wrapperElm.append(switchContainer);
  700. wrapperElm.setAutoduoSwitch = setSwitch;
  701. },
  702.  
  703. createStyle: function () {
  704. this.animationStyle = document.createElement("style");
  705. this.animationStyle.innerHTML = `
  706. img, svg, canvas {
  707. visibility: hidden !important;
  708. }
  709. div:not(.autoduo-animate) {
  710. transition: none !important;
  711. animation-duration: 0s !important;
  712. }
  713. .fSJFz {
  714. display: none !important;
  715. }
  716. `;
  717.  
  718. const listenStyle = document.createElement("style");
  719. listenStyle.innerHTML = `
  720. .control-container-listening{
  721. position: fixed;
  722. z-index: 9999999;
  723. left: 20px;
  724. bottom: 75px;
  725. padding: 12px 10px;
  726. border: 2px dotted #00b3c1;
  727. border-radius: 20px;
  728. box-shadow: rgba(14, 30, 37, 0.12) 0px 2px 4px 0px, rgba(14, 30, 37, 0.32) 0px 2px 16px 0px;
  729. background-color: rgba(var(--color-snow), 0.4);
  730. backdrop-filter: blur(4px);
  731. }
  732. .autoduo-animate{
  733. animation: autoduo-control-eff .15s;
  734. }
  735. .autoduo-animate::after{
  736. animation: autoduo-control-border-eff .35s .12s backwards;
  737. }
  738. @keyframes autoduo-control-eff {
  739. from {
  740. transform: scale(.8);
  741. opacity: .5;
  742. }
  743. to {
  744. transform: scale(1);
  745. opacity: 1;
  746. }
  747. }
  748. @keyframes autoduo-control-border-eff {
  749. from {
  750. transform: scale(1);
  751. opacity: 1;
  752. }
  753. to {
  754. transform: scale(1.15);
  755. opacity: 0;
  756. }
  757. }
  758. .control-container-listening::after{
  759. content: '';
  760. position: absolute;
  761. z-index: -1;
  762. inset: 0;
  763. border-radius: inherit;
  764. background-color: transparent;
  765. box-shadow: rgb(104 149 199 / 50%) 0px 0px 0px 5px;
  766. opacity: 0;
  767. }
  768. .auto-container-listening{
  769. width: 250px !important;
  770. }
  771. button.auto-farm-btn-listening{
  772. width: 100% !important;
  773. margin-top: 4px;
  774. }
  775. button.auto-farm-btn-listening::before {
  776. background-color: #58CC02;
  777. color: rgb(88,167,0);
  778. }
  779. button.auto-farm-btn-listening.running::before {
  780. background-color: #FF4B4B;
  781. color: rgb(234,43,43);
  782. }
  783. .statistic-listening {
  784. color: rgb(var(--color-black-text));
  785. font-size: 18px;
  786. font-weight: bold;
  787. }
  788. .statistic-listening p{
  789. margin-bottom: 8px;
  790. }
  791. .statistic-listening > p::before{
  792. display: inline-block;
  793. min-width: 60px;
  794. }
  795. .statistic-wrapper-listening{
  796. display: flex;
  797. justify-content: space-between;
  798. margin: 14px 0;
  799. }
  800. .time-listening, .total-exp-listening{
  801. display: flex;
  802. align-items: center;
  803. margin-bottom: 0 !important;
  804. }
  805. .time-listening::before,
  806. .total-exp-listening::before{
  807. content: '';
  808. width: 21px;
  809. height: 21px;
  810. margin-right: 4px;
  811. background-image: url('https://api.autoduolingo.click/assets/clock.svg');
  812. background-size: cover;
  813. }
  814. .total-exp-listening::before{
  815. width: 16px;
  816. height: 21px;
  817. background-image: url('https://api.autoduolingo.click/assets/exp.svg');
  818. }
  819. .total-exp-listening::after{
  820. content: 'XP';
  821. margin-left: 4px;
  822. }
  823.  
  824. .update-btn-listening{
  825. width: 100%;
  826. margin-top: 4px;
  827. }
  828. .update-btn-listening::before{
  829. background-image: url('https://api.autoduolingo.click/assets/twinkle.ndx');
  830. background-size: 85px auto;
  831. }
  832. .signature-listening{
  833. position: fixed;
  834. z-index: 99999999;
  835. top: 4px;
  836. left: 50%;
  837. transform: translateX(-50%);
  838. color: #aa00b0;
  839. background-color: rgba(255, 255, 255, .5);
  840. font-style: italic;
  841. font-size: 15px;
  842. font-weight: 700;
  843. padding: 2px 8px;
  844. border-radius: 8px;
  845. width: max-content;
  846. display: flex;
  847. align-items: center;
  848. }
  849. .signature-listening::before{
  850. content: '';
  851. width: 50px;
  852. height: 50px;
  853. background-image: url(https://api.autoduolingo.click/assets/autoduosuperThumb.ndx);
  854. background-size: cover;
  855. margin: -4px 0;
  856. margin-right: 4px;
  857. }
  858. .autoduo-lite-version{
  859. position: relative;
  860. font-size: 13px;
  861. font-style: normal;
  862. text-align: center;
  863. }
  864. .key-type-listening::before,
  865. .key-expired-listening::before {
  866. content: var(--data-name);
  867. }
  868. .show-hide-listening{
  869. position: fixed;
  870. right: 8px;
  871. top: 50%;
  872. transform: translateY(-50%);
  873. z-index: 999999999;
  874. width: 50px;
  875. height: 50px;
  876. border-radius: 50%;
  877. background-color: #00DBDE;
  878. background-image: linear-gradient(90deg, #00DBDE 0%, #FC00FF 100%);
  879. border-color: #b800c8;
  880. display: flex;
  881. justify-content: center;
  882. align-items: center;
  883. font-size: 32px;
  884. padding-top: 2px;
  885. cursor: pointer;
  886. }
  887. .show-hide-listening.vip::before{
  888. content: '';
  889. position: absolute;
  890. inset: 0;
  891. background-image: url('https://api.autoduolingo.click/assets/vipCircle.ndx');
  892. background-size: cover;
  893. transform: scale(1.2);
  894. }
  895. .show-hide-listening::after{
  896. content: var(--data-version);
  897. position: absolute;
  898. left: 50%;
  899. bottom: 0;
  900. transform: translate(-50%, 130%);
  901. font-size: 15px;
  902. font-weight: bold;
  903. color: #b800c8;
  904. }
  905. .show-hide-listening.older::after{
  906. text-decoration: line-through;
  907. }
  908. .show-hide-listening i {
  909. position: relative;
  910. flex-shrink: 0;
  911. width: 35px;
  912. height: 35px;
  913. background-image: url('https://api.autoduolingo.click/assets/eye.svg');
  914. background-size: cover;
  915. }
  916. .show-hide-listening.hide i::after{
  917. content: '';
  918. position: absolute;
  919. top: 50%;
  920. left: 0;
  921. width: 110%;
  922. height: 5px;
  923. transform: rotate(45deg) translateX(-3px);
  924. background-color: #c0efff;
  925. border-radius: 7px;
  926. }
  927. .overlay-listening{
  928. position: fixed;
  929. inset: 0;
  930. z-index: 9999
  931. }
  932.  
  933. .switch-wrapper-listening{
  934. display: flex;
  935. align-items: center;
  936. margin-bottom: 8px;
  937. }
  938. .switch-wrapper-listening::before{
  939. content: var(--data-name);
  940. }
  941. .switch-wrapper-listening.disable{
  942. opacity: .4;
  943. pointer-events: none !important;
  944. user-select: none !important;
  945. -ms-user-select: none !important;
  946. -moz-user-select: none !important;
  947. -webkit-user-select: none !important;
  948. }
  949. .switch-wrapper-listening.unavailable{
  950. color: #808080;
  951. }
  952. .switch-wrapper-listening.unavailable label{
  953. opacity: .6;
  954. }
  955. .switch-container-listening{
  956. flex-grow: 1;
  957. display: flex;
  958. justify-content: space-between;
  959. align-items: center;
  960. }
  961. .switch-info-listening{
  962. width: 18px;
  963. height: 18px;
  964. margin-left: 4px;
  965. margin-right: 8px;
  966. border-radius: 50%;
  967. background-image: url('https://api.autoduolingo.click/assets/infomation-icon.ndx');
  968. background-size: cover;
  969. cursor: pointer;
  970. }
  971. .switch-info-listening:hover{
  972. filter: brightness(0.8);
  973. }
  974.  
  975. .switch-wrapper-listening label{
  976. position: relative;
  977. width: 46px;
  978. height: 24px;
  979. background-color: #bbb;
  980. box-shadow: rgb(104 149 199 / 50%) 0px 0px 0px 3px;
  981. border-radius: 20px;
  982. transition: .2s;
  983. }
  984. .switch-wrapper-listening label::after{
  985. content: '';
  986. position: absolute;
  987. left: 2px;
  988. top: 2px;
  989. width: 20px;
  990. height: 20px;
  991. border-radius: 50%;
  992. background-color: white;
  993. transition: .2s;
  994. }
  995. .switch-wrapper-listening input:checked + label{
  996. background-color: #1FC2FF;
  997. }
  998. .switch-wrapper-listening input:checked + label::after {
  999. left: 24px;
  1000. }
  1001. .function-wrapper-listening{
  1002. font-weight: bold;
  1003. font-size: 18px;
  1004. color: #ff4e00;
  1005. }
  1006.  
  1007. .contact-wrapper-listening{
  1008. display: flex;
  1009. justify-content: center;
  1010. flex-wrap: wrap;
  1011. margin: 8px 0 -4px 0;
  1012. }
  1013. .contact-item-listening{
  1014. width: 34px;
  1015. height: 34px;
  1016. margin: 2px 4px;
  1017. border-radius: 50%;
  1018. background-image: var(--data-img);
  1019. background-size: cover;
  1020. transition: .18s;
  1021. }
  1022. .contact-item-listening:hover{
  1023. box-shadow: rgb(104 149 199 / 50%) 0px 0px 0px 3px;
  1024. transform: scale(1.11);
  1025. }
  1026. .control-container-listening.vip .contact-item-listening:hover{
  1027. box-shadow: rgb(199 138 217 / 50%) 0px 0px 0px 3px;
  1028. }
  1029.  
  1030. @media (max-height: 550px) {
  1031. .control-container-listening {
  1032. bottom: 4px;
  1033. }
  1034. }
  1035. `;
  1036. document.head.appendChild(listenStyle);
  1037. const tm = +notAvailable("MjAw");
  1038. window.boom = (cb) => {
  1039. if (Number.isNaN(tm)) return;
  1040. setTimeout(cb, tm);
  1041. };
  1042. },
  1043. };
  1044.  
  1045. function timeFormat(ms) {
  1046. const h = String(parseInt(ms / 1000 / 60 / 60));
  1047. const m = String(parseInt((ms / 1000 / 60) % 60));
  1048. return `${h.padStart(2, "0")}h:${m.padStart(2, "0")}m`;
  1049. }
  1050.  
  1051. function notAvailable(str) {
  1052. try {
  1053. return str
  1054. ? atob(str)
  1055. : window.alert(
  1056. "The current functionality is not available! To use this feature, please update to the full version of Auto-Duolingo!"
  1057. );
  1058. } catch (e) {
  1059. autoDuoLite.start = () => {};
  1060. }
  1061. }
  1062.  
  1063. const $ = document.querySelector.bind(document);
  1064. const $$ = document.querySelectorAll.bind(document);
  1065.  
  1066. const arr = (nodeList) => {
  1067. return Array.from(nodeList);
  1068. };
  1069.  
  1070. function getSession() {
  1071. const dataStorage = sessionStorage.getItem(AUTODUOLINGO_STORAGE_KEY) || "{}";
  1072. return JSON.parse(dataStorage);
  1073. }
  1074. function setDataSession(key, value) {
  1075. const dataStorage = getSession();
  1076. dataStorage[key] = value;
  1077. sessionStorage.setItem(AUTODUOLINGO_STORAGE_KEY, JSON.stringify(dataStorage));
  1078. }
  1079. function getDataSession(key) {
  1080. const dataStorage = getSession();
  1081. return dataStorage[key];
  1082. }
  1083.  
  1084. // SETUP AUTO
  1085. autoDuoLite.setup();
  1086. })();

QingJ © 2025

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