Auto-Duolingo

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

As of 2024-04-23. See the latest version.

  1. // ==UserScript==
  2. // @name Auto-Duolingo
  3. // @version 1.0.5
  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 = "autoDuolingoStorage";
  15. const { isSafeMode, isShowUI, isAnimationOff, exp, time } = getSession();
  16.  
  17. const autoDuoLite = {
  18. setup: function () {
  19. this.createStyle();
  20. this.createSignature();
  21. this.createContact();
  22. this.createPopup();
  23. this.createBtn();
  24. this.createStatistics();
  25. this.createFunctions();
  26. this.createSetting();
  27. this.createContainer();
  28. !isShowUI && this.handleShowHideUI();
  29. isAnimationOff && this.handleAnimationOff();
  30. this.renderTime();
  31. getDataSession("isBasicAuto") && this.start();
  32. },
  33.  
  34. createSignature: function () {
  35. this.signatureElm = document.createElement("div");
  36. Object.assign(this.signatureElm, {
  37. className: "signature-listening",
  38. innerHTML: `
  39. <div>
  40. Auto-Duolingo DevX
  41. <div class="autoduo-lite-version">
  42. LITE VERSION
  43. </div>
  44. </div>
  45. `,
  46. });
  47. document.body.appendChild(this.signatureElm);
  48. },
  49.  
  50. createContact: function () {
  51. this.contactWrapper = document.createElement("div");
  52. Object.assign(this.contactWrapper, {
  53. className: "contact-wrapper-listening",
  54. 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')">
  55. <p class="popup">Chat with DevX</p>
  56. </a>
  57. <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')">
  58. <p class="popup">Telegram Community</p>
  59. </a>
  60. <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')">
  61. <p class="popup">Zalo Community</p>
  62. </a>
  63. <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')">
  64. <p class="popup">Youtube Channel</p>
  65. </a>
  66. <a class="contact-item-listening" href="https://greatest.deepsurf.us/en/scripts/487867-auto-duolingo" target="_blank" style="--data-img: url('https://api.autoduolingo.click/assets/greasyfork-icon.ndx')">
  67. <p class="popup">Greasy Fork</p>
  68. </a>`,
  69. });
  70. },
  71.  
  72. createPopup: function () {
  73. this.updateGuidePopup = document.createElement("div");
  74. Object.assign(this.updateGuidePopup, {
  75. className: "update-guide-popup",
  76. innerHTML: `
  77. <div class="guide-popup-main">
  78. <h2 class="guide-popup-title">UPDATE GUIDE</h2>
  79. <div class="guide-popup-content">
  80. <p class="guide-popup-text" style="color: rgb(0,159,235)">
  81. The full version unlocks all features and receives priority updates for new features and bug fixes.
  82. To upgrade, follow the instructions below:
  83. </p>
  84. <p class="guide-popup-text">
  85. <b>Step 1:</b> Click the "Go update" button below, then proceed to click the "Update" button
  86. in the popup window to confirm the update process.
  87. </p>
  88. <p class="guide-popup-text">
  89. <b>Step 2</b>: Refresh the Duolingo page to update the interface of the full version of Auto-Duolingo.
  90. </p>
  91. <div class="guide-popup-btn">
  92. <button class="autoduo-btn popup-btn-close">Close</button>
  93. <a class="autoduo-btn btn-green popup-btn-access" href="#">Go Update</a>
  94. </div>
  95. </div>
  96. </div>
  97. `,
  98. });
  99.  
  100. const closePopupBtn = this.updateGuidePopup.querySelector(".popup-btn-close");
  101. closePopupBtn.addEventListener("click", () => {
  102. document.body.contains(this.updateGuidePopup) && this.updateGuidePopup.remove();
  103. });
  104. },
  105.  
  106. createBtn: function () {
  107. this.autoBtn = document.createElement("button");
  108. Object.assign(this.autoBtn, {
  109. className: "autoduo-btn btn-green auto-farm-btn-listening",
  110. innerText: "START FARM XP",
  111. onclick: () => {
  112. this.isAuto ? this.stop() : this.start();
  113. },
  114. });
  115.  
  116. this.updateBtn = document.createElement("button");
  117. Object.assign(this.updateBtn, {
  118. className: "autoduo-btn update-btn-listening",
  119. innerText: "Update to the full version",
  120. onclick: () => {
  121. this.isAuto && this.stop();
  122. document.body.appendChild(this.updateGuidePopup);
  123. },
  124. });
  125.  
  126. this.showHideBtn = document.createElement("button");
  127. Object.assign(this.showHideBtn, {
  128. className: "show-hide-listening",
  129. style: `--data-version: 'V${this.version}'`,
  130. innerHTML: "<i></i>",
  131. });
  132.  
  133. this.showHideBtn.addEventListener("click", () => {
  134. this.isShowUI = !this.isShowUI;
  135. this.handleShowHideUI(true);
  136. });
  137. document.body.append(this.showHideBtn);
  138.  
  139. new Promise((resolve) => {
  140. setTimeout(
  141. resolve.bind(window, notAvailable("aHR0cHM6Ly9pbnN0YWxsLmF1dG9kdW9saW5nby5jbGljaw==")),
  142. 1000
  143. );
  144. }).then((res) => {
  145. this.updateGuidePopup.querySelector(".popup-btn-access").href = res;
  146. });
  147. },
  148.  
  149. createStatistics: function () {
  150. this.statistic = document.createElement("div");
  151. this.keyTypeElm = document.createElement("p");
  152. this.expElm = document.createElement("p");
  153. this.dateElm = document.createElement("p");
  154. const statisticWrapper = document.createElement("div");
  155.  
  156. Object.assign(this.keyTypeElm, {
  157. className: "key-type-listening",
  158. style: `--data-name: "Type"`,
  159. innerHTML: "<b style='color: #009feb'>Auto-Duolingo Lite</b>",
  160. });
  161.  
  162. this.expElm.className = "total-exp-listening";
  163. this.expElm.innerText = this.exp;
  164. this.statistic.className = "statistic-listening";
  165. this.dateElm.className = "time-listening";
  166. statisticWrapper.className = "statistic-wrapper-listening";
  167.  
  168. statisticWrapper.append(this.expElm, this.dateElm);
  169. this.statistic.append(this.keyTypeElm, statisticWrapper);
  170. },
  171.  
  172. createFunctions: function () {
  173. this.animationOffWrapper = document.createElement("div");
  174. this.animationOffWrapper.style = `--data-name: "Hide Animation"`;
  175. const animationOffInfo =
  176. "HIDE ANIMATION MODE:\n" +
  177. "- When this mode is enabled, images and animations on the website will be hidden to optimize performance.";
  178. this.autoduoCreateSwitch(
  179. animationOffInfo,
  180. this.animationOffWrapper,
  181. 1,
  182. this.isAnimationOff,
  183. (setSwitch) => {
  184. this.isAnimationOff = !this.isAnimationOff;
  185. this.handleAnimationOff(true);
  186. setSwitch(this.isAnimationOff);
  187. }
  188. );
  189.  
  190. this.safeModeWrapper = document.createElement("div");
  191. this.safeModeWrapper.style = `--data-name: "Safe Mode"`;
  192. const safeModeInfo =
  193. "SAFE MODE:\n" +
  194. "- When this mode is enabled, the system will simulate user actions when using auto. The speed will be more relaxed, " +
  195. "in exchange for the completion time of lessons and the amount of experience will be the most natural, minimizing " +
  196. "the risks of REPORT and account BAN!";
  197. this.autoduoCreateSwitch(safeModeInfo, this.safeModeWrapper, 2, this.isSafeMode, () => {
  198. this.isSafeMode ? this.handleSafeModeOff() : this.handleSafeModeOn();
  199. });
  200.  
  201. this.turboModeWrapper = document.createElement("div");
  202. this.turboModeWrapper.style = `--data-name: "Turbo Mode"`;
  203. const turboModeInfo =
  204. "TURBO MODE:\n" +
  205. "- When enabled, the system will significantly boost the auto speed. It will utilize higher performance and " +
  206. "is not recommended for use on low-performance devices.\n- Turn it off and refresh the page if you encounter " +
  207. "issues while activating this mode!\n\n- Note: This is an experimental feature and requires a VIP Key to use. " +
  208. "Only enable it when you truly require speed and understand its implications!!";
  209. this.autoduoCreateSwitch(turboModeInfo, this.turboModeWrapper, 4, false);
  210.  
  211. this.legendModeWrapper = document.createElement("div");
  212. this.legendModeWrapper.style = `--data-name: "Lesson Pass Mode"`;
  213. const legendModeInfo =
  214. "LESSON PASS MODE:\n" +
  215. "- When activated, the system won't repeat exercises as in the regular mode but will engage in exercises actively selected by the user. " +
  216. "This mode is used for legendary exercises, story exercises, and most other similar exercises.\n- You need to enter the lesson you want to " +
  217. "pass in, and then the system will automatically complete that lesson for you!\n" +
  218. "- When this mode is activated, the basic auto button will be temporarily disabled.";
  219. this.autoduoCreateSwitch(legendModeInfo, this.legendModeWrapper, 5, false);
  220.  
  221. this.targetModeWrapper = document.createElement("div");
  222. this.targetModeWrapper.style = `--data-name: "XP Target Mode"`;
  223. const targetModeInfo =
  224. "EXPERIENCE POINT TARGET MODE:\n" +
  225. "- By setting an experience point target, the system will automatically stop auto mode when the total experience points " +
  226. "obtained equal or exceed the specified target.\n- This helps you better control the auto function, " +
  227. "preventing unintentional accumulation of excess experience points due to forgetting to turn off auto mode!\n\n" +
  228. "- Note: The experience point target must be greater than the current amount of experience points obtained through auto mode!";
  229. this.autoduoCreateSwitch(targetModeInfo, this.targetModeWrapper, 6, false);
  230.  
  231. this.passModeWrapper = document.createElement("div");
  232. this.passModeWrapper.style = `--data-name: "Auto Pass Mode"`;
  233. const passModeInfo =
  234. "AUTO PASS MODE:\n" +
  235. "- By setting the number of lessons you wish to pass, the system will automatically pass the corresponding " +
  236. "number of new lessons as per the value you've set!\n\n" +
  237. "- Note: the lesson value should be within the range of 1 - 1000 (Enter 0 for unlimited auto)!";
  238. this.autoduoCreateSwitch(passModeInfo, this.passModeWrapper, 7, false);
  239.  
  240. this.darkModeWrapper = document.createElement("div");
  241. this.darkModeWrapper.style = `--data-name: "Dark Mode"`;
  242. const darkModeInfo = "DARK MODE\n- Enable/disable website dark mode faster!";
  243. this.autoduoCreateSwitch(darkModeInfo, this.darkModeWrapper, 3, this.isDarkMode, (setSwitch) => {
  244. this.isDarkMode = !this.isDarkMode;
  245. const [theme, value] = this.isDarkMode ? ["dark", "on"] : ["light", "off"];
  246. document.documentElement.setAttribute("data-duo-theme", theme);
  247. localStorage.setItem("duo.darkMode", `{"1313105280":"${value}"}`);
  248. setSwitch(this.isDarkMode);
  249. });
  250.  
  251. this.farmingLocationWrapper = document.createElement("div");
  252. this.farmingLocationWrapper.style = `--data-name: "Set XP Farm Location"`;
  253. const farmingLocationInfo =
  254. "SET XP FARM LOCATION\n" +
  255. "- By default, the system can only Farm XP in practice exercises or listening practices. However, with this feature, you can Farm XP " +
  256. "in any lesson you want, even in story lessons!\n" +
  257. "- Usage: Activate the feature and enter the URL of the lesson you want, then enable the XP Farm mode to start farming.\n" +
  258. "- NOTE: The URL to the lesson must be accurate and the lesson must be repeatable. Entering an inaccurate URL may lead " +
  259. "to errors or even pose risks to your account!";
  260. this.autoduoCreateSwitch(farmingLocationInfo, this.farmingLocationWrapper, 8, false);
  261.  
  262. this.autoX2Wrapper = document.createElement("div");
  263. this.autoX2Wrapper.style = `--data-name: "Auto Collect x2 XP"`;
  264. const autoX2Info =
  265. "AUTO COLLECT X2 XP:\n" +
  266. '- This is a supplementary feature for "Auto Farm KN", helping to maintain the x2 KN status during farming. When enabled, '+
  267. "it will check and automatically do new lessons to get x2 KN rewards if it detects the current state doesn't have x2. "+
  268. 'This will help you farm more KN points than usual. \n\n- NOTE: This feature will do new lessons to maintain the x2 status, so '+
  269. 'consider before enabling it if you have constraints with these lessons.';
  270. this.autoduoCreateSwitch(autoX2Info, this.autoX2Wrapper, 8, false);
  271.  
  272. this.functionWrapper = document.createElement("div");
  273. this.functionWrapper.className = "function-wrapper-listening";
  274. this.functionWrapper.append(
  275. this.darkModeWrapper,
  276. this.animationOffWrapper,
  277. this.safeModeWrapper,
  278. this.turboModeWrapper
  279. );
  280. },
  281.  
  282. createSetting: function () {
  283. this.settingBtn = document.createElement("button");
  284. Object.assign(this.settingBtn, {
  285. className: "autoduo-btn setting-btn-listening",
  286. innerText: "Other settings",
  287. });
  288. this.settingBtn.addEventListener("click", () => {
  289. this.controlContainer.contains(this.settingOverlay) ||
  290. this.controlContainer.appendChild(this.settingOverlay);
  291. });
  292.  
  293. this.closeSettingBtn = document.createElement("button");
  294. Object.assign(this.closeSettingBtn, {
  295. className: "autoduo-btn close-setting-btn-listening",
  296. innerText: "Close",
  297. });
  298. this.closeSettingBtn.addEventListener("click", () => {
  299. this.controlContainer.contains(this.settingOverlay) &&
  300. this.controlContainer.removeChild(this.settingOverlay);
  301. });
  302.  
  303. // Create setting overlay
  304. this.settingOverlay = document.createElement("div");
  305. Object.assign(this.settingOverlay, {
  306. className: "setting-overlay-listening",
  307. innerHTML: `
  308. <h3>Other settings</h3>
  309. `,
  310. });
  311.  
  312. // Create setting functions
  313. this.settingFunction = document.createElement("div");
  314. this.settingFunction.className = "setting-function-listening";
  315. this.settingFunction.append(
  316. this.legendModeWrapper,
  317. this.passModeWrapper,
  318. this.targetModeWrapper,
  319. this.farmingLocationWrapper,
  320. this.autoX2Wrapper
  321. );
  322.  
  323. // Append the funtions and the close setting btn to the setting overlay
  324. this.settingOverlay.append(this.settingFunction, this.closeSettingBtn);
  325. },
  326.  
  327. createContainer: function () {
  328. this.autoContainer = document.createElement("div");
  329. this.autoContainer.className = "auto-container-listening";
  330. this.autoContainer.append(
  331. this.statistic,
  332. this.functionWrapper,
  333. this.settingBtn,
  334. this.autoBtn,
  335. this.updateBtn
  336. );
  337.  
  338. this.overlayContainer = document.createElement("div");
  339. this.overlayContainer.className = "overlay-listening";
  340.  
  341. this.controlContainer = document.createElement("div");
  342. this.controlContainer.className = "control-container-listening";
  343. this.controlContainer.append(this.autoContainer, this.contactWrapper);
  344. document.body.append(this.controlContainer);
  345. },
  346.  
  347. handleShowHideUI: function (isSave = false) {
  348. if (this.isShowUI) {
  349. this.showHideBtn.classList.remove("hide");
  350. document.body.append(this.controlContainer, this.signatureElm);
  351. } else {
  352. this.showHideBtn.classList.add("hide");
  353. this.controlContainer.remove();
  354. this.signatureElm.remove();
  355. }
  356.  
  357. if (isSave) {
  358. setDataSession("isShowUI", this.isShowUI);
  359. this.controlContainer.classList.contains("autoduo-animate") ||
  360. this.controlContainer.classList.add("autoduo-animate");
  361. }
  362. },
  363.  
  364. handleAnimationOff: function (isSave = false) {
  365. this.isAnimationOff
  366. ? document.head.appendChild(this.animationStyle)
  367. : document.head.removeChild(this.animationStyle);
  368. isSave && setDataSession("isAnimationOff", this.isAnimationOff);
  369. },
  370.  
  371. handleSafeModeOn: function () {
  372. this.safeModeWrapper.setAutoduoSwitch(this.setSafeMode(true));
  373. },
  374.  
  375. handleSafeModeOff: function () {
  376. this.safeModeWrapper.setAutoduoSwitch(this.setSafeMode(false));
  377. },
  378.  
  379. start: function () {
  380. if (this.isAuto || this.isAutoRunning) {
  381. return;
  382. }
  383.  
  384. document.body.appendChild(this.overlayContainer);
  385. this.isAuto = true;
  386. this.autoBtn.classList.add("running");
  387. this.autoBtn.innerText = "STOP FARM XP";
  388. setDataSession("isBasicAuto", this.isAuto);
  389. this.startTm = Date.now();
  390. this.handleLocation();
  391. },
  392.  
  393. stop: function () {
  394. if (!this.isAuto || this.isLegendMode) {
  395. return;
  396. }
  397. document.body.removeChild(this.overlayContainer);
  398. this.isAuto = false;
  399. this.autoBtn.classList.remove("running");
  400. this.autoBtn.innerText = "START FARM XP";
  401. setDataSession("isBasicAuto", this.isAuto);
  402. },
  403.  
  404. handleLocation: function () {
  405. if (!this.isAuto) {
  406. return;
  407. }
  408.  
  409. const currentPath = window.location.pathname;
  410.  
  411. switch (currentPath) {
  412. case this.practiceHubPath:
  413. this.goPracticeHubChallenge();
  414. break;
  415.  
  416. case this.listeningPacticePath:
  417. this.handlePracticeHubChallenge();
  418. break;
  419.  
  420. default:
  421. this.autoduoError(
  422. "Inappropriate location: Only enable auto when on the practice page (with the dumbbell icon) of Duolingo Super!" +
  423. "\n- Enabling auto on Duolingo Super's practice page will automatically farm listening exercises (20 XP)." +
  424. "\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!"
  425. );
  426. break;
  427. }
  428. },
  429.  
  430. goPracticeHubChallenge: function () {
  431. if (this.isAuto === false) {
  432. return;
  433. }
  434. const challengeBtn = $(
  435. 'img[src="https://d35aaqx5ub95lt.cloudfront.net/images/practiceHub/2ebe830fd55a7f2754d371bcd79faf32.svg"]'
  436. );
  437.  
  438. if (!challengeBtn) {
  439. setTimeout(this.goPracticeHubChallenge.bind(this), 1000);
  440. return;
  441. }
  442.  
  443. challengeBtn.click();
  444. setTimeout(this.handlePracticeHubChallenge.bind(this), 1000);
  445. },
  446.  
  447. handlePracticeHubChallenge: function () {
  448. if (window.location.pathname === this.practiceHubPath) {
  449. this.goPracticeHubChallenge();
  450. return;
  451. }
  452.  
  453. const challengeWrapper = this.isPreviewVersion ? $(".wqSzE") : $(".wqSzE");
  454. if (challengeWrapper) {
  455. this.getDataStateNode(challengeWrapper);
  456. this.next();
  457. return;
  458. }
  459. const nextActiveBtn = $('[data-test="player-next"][aria-disabled="false"]');
  460.  
  461. if (nextActiveBtn) {
  462. this.next();
  463. return;
  464. }
  465.  
  466. setTimeout(this.handlePracticeHubChallenge.bind(this), 1000);
  467. },
  468.  
  469. handleChallenge: async function () {
  470. if (this.isSafeMode) {
  471. await this.sleep(500);
  472. }
  473. if (!this.isAuto || this.isAutoRunning) {
  474. return;
  475. }
  476.  
  477. const challengeTypeElm = $('[data-test*="challenge challenge"]');
  478.  
  479. if (!challengeTypeElm) {
  480. return this.autoduoError("Undefined challenge!!");
  481. }
  482.  
  483. const challengeType = challengeTypeElm.dataset.test?.slice(10);
  484.  
  485. this.setAutoRunning(true);
  486. switch (challengeType) {
  487. case "challenge-listenTap":
  488. this.handleChallengeTranslate();
  489. break;
  490.  
  491. case "challenge-gapFill":
  492. case "challenge-listenIsolation":
  493. case "challenge-assist":
  494. case "challenge-selectTranscription":
  495. case "challenge-characterIntro":
  496. case "challenge-characterSelect":
  497. case "challenge-selectPronunciation":
  498. case "challenge-dialogue":
  499. case "challenge-readComprehension":
  500. case "challenge-listenComprehension":
  501. case "challenge-select":
  502. case "challenge-form":
  503. case "challenge-definition":
  504. case "challenge-sameDifferent":
  505. this.handleChallengeChoice();
  506. break;
  507.  
  508. default:
  509. this.autoduoError(
  510. "This exercise is not currently supported in this version. Try updating to the full version of Auto-Duolingo and try again!"
  511. );
  512. break;
  513. }
  514. },
  515.  
  516. handleChallengeTranslate: function () {
  517. if (this.isAuto === false) {
  518. return;
  519. }
  520.  
  521. let data = this.getData("correctTokens");
  522.  
  523. if (this.isAuto === false) {
  524. return;
  525. }
  526.  
  527. if (!data?.length) {
  528. data = this.getData(["challengeResponseTrackingProperties", "best_solution"])?.split(" ");
  529. }
  530.  
  531. if (!data) {
  532. return this.autoduoError("Lesson data not found.");
  533. }
  534.  
  535. const textArea = $('textarea[data-test="challenge-translate-input"]:not([disabled])');
  536. if (textArea) {
  537. const toggleKeyboard = $('[data-test="player-toggle-keyboard"]');
  538. if (toggleKeyboard) {
  539. toggleKeyboard.click();
  540. return setTimeout(this.handleChallengeTranslate.bind(this), 500);
  541. }
  542.  
  543. const inputEvent = new Event("input", {
  544. bubbles: true,
  545. });
  546.  
  547. let answer = "";
  548.  
  549. const inputCaseHandler = () => {
  550. setTimeout(() => {
  551. if (data.length === 0) {
  552. this.setAutoRunning(false);
  553. this.next(true);
  554. return;
  555. }
  556.  
  557. answer += " " + data.shift();
  558. this.nativeTextareaValueSetter.call(textArea, answer);
  559. textArea.dispatchEvent(inputEvent);
  560. inputCaseHandler();
  561. }, this.rmSafeDlTm());
  562. };
  563. inputCaseHandler();
  564. return;
  565. }
  566.  
  567. let options = this.isPreviewVersion
  568. ? arr($$('[class="_1v1Bd"] button[data-test*="challenge-tap-token"]'))
  569. : arr($$('[class="_1v1Bd"] button[data-test*="challenge-tap-token"]'));
  570. if (options.length === 0) {
  571. return setTimeout(this.handleChallengeTranslate.bind(this), 500);
  572. }
  573.  
  574. const getIndexOfOption = (targetData) => {
  575. const index = options.findIndex((option) => option.textContent === targetData);
  576. return index;
  577. };
  578.  
  579. const selectCaseHandler = () => {
  580. setTimeout(() => {
  581. if (data.length === 0) {
  582. this.setAutoRunning(false);
  583. this.next(true);
  584. return;
  585. }
  586.  
  587. const firstValue = data.shift();
  588. const index = getIndexOfOption(firstValue);
  589.  
  590. if (index === -1) {
  591. return this.autoduoLessonError("No suitable option found.");
  592. }
  593.  
  594. options[index].click();
  595. options.splice(index, 1);
  596. selectCaseHandler();
  597. }, this.rmSafeDlTm());
  598. };
  599. selectCaseHandler();
  600. },
  601.  
  602. handleChallengeChoice: function () {
  603. if (!this.isAuto) {
  604. return;
  605. }
  606.  
  607. const optionElm = $$('[data-test="challenge-choice"]');
  608. const correctIndex = this.getData("correctIndex");
  609.  
  610. if (correctIndex === null) {
  611. return this.autoduoError("Lesson data not found.");
  612. }
  613.  
  614. setTimeout(() => {
  615. optionElm[correctIndex].click();
  616. setTimeout(() => {
  617. this.setAutoRunning(false);
  618. this.next();
  619. }, this.rmSafeDlTm());
  620. }, this.rmSafeDlTm());
  621. },
  622.  
  623. next: function () {
  624. if (!this.isAuto) {
  625. return;
  626. }
  627.  
  628. const expWrapper = this.isPreviewVersion ? $('[class="_1XNQX"]') : $('[class="_1XNQX"]');
  629. if (expWrapper) {
  630. const exp = this.getExp(expWrapper);
  631.  
  632. if (exp !== undefined) {
  633. this.exp += exp;
  634. this.expElm.innerText = this.exp;
  635.  
  636. const timeNow = Date.now();
  637. const finishTime = timeNow - this.startTm;
  638. this.totalTime += finishTime;
  639. this.startTm = timeNow;
  640. this.renderTime();
  641.  
  642. setDataSession("exp", this.exp);
  643. setDataSession("time", this.totalTime);
  644.  
  645. const currentPath = window.location.pathname;
  646. if (currentPath === this.listeningPacticePath) {
  647. if ((this.totalReloadTime += finishTime) >= this.reloadTm) {
  648. window.location.reload();
  649. return;
  650. }
  651. }
  652. }
  653. }
  654.  
  655. const nextBtn = $('[data-test="player-next"]');
  656.  
  657. if (!nextBtn) {
  658. setTimeout(this.handleLocation.bind(this), this.goChallengeTm);
  659. return;
  660. }
  661.  
  662. const isDisable = nextBtn.getAttribute("aria-disabled");
  663. const loadingClass = this.isPreviewVersion ? "_3whsM" : "_3whsM";
  664. const isLoading = nextBtn.classList.contains(loadingClass);
  665.  
  666. if (isDisable === "true" && !isLoading) {
  667. boom(this.handleChallenge.bind(this));
  668. return;
  669. }
  670.  
  671. !isLoading && nextBtn.click();
  672. boom(this.next.bind(this));
  673. },
  674.  
  675. findReactProps: function (wrapperElm) {
  676. this.reactProps = Object.keys(wrapperElm).find((key) => key.startsWith("__reactProps"));
  677.  
  678. if (!this.reactProps) {
  679. return this.autoduoError("ERROR");
  680. }
  681. },
  682.  
  683. getDataStateNode: function (wrapperElm) {
  684. this.reactProps === null && this.findReactProps(wrapperElm);
  685. const childrenData = wrapperElm?.[this.reactProps]?.children;
  686.  
  687. if (Array.isArray(childrenData)) {
  688. this.dataStateNode = childrenData?.[0]?._owner?.stateNode;
  689. } else {
  690. this.dataStateNode = childrenData?._owner?.stateNode;
  691. }
  692. },
  693.  
  694. getData: function (subGenealogy) {
  695. const currentChallenge = this.dataStateNode?.props?.currentChallenge;
  696. if (!currentChallenge) {
  697. return this.autoduoError("There was an error while loading challenge data!");
  698. }
  699.  
  700. if (Array.isArray(subGenealogy)) {
  701. const result = subGenealogy.reduce((acc, currentKey) => {
  702. if (acc === null) {
  703. return null;
  704. }
  705.  
  706. const currentValue = acc[currentKey];
  707. return currentValue || null;
  708. }, currentChallenge);
  709.  
  710. if (result === null) {
  711. return this.autoduoError("There was an error while getting the data!");
  712. }
  713.  
  714. return Array.isArray(result) ? [...result] : result;
  715. } else {
  716. const result = currentChallenge[subGenealogy];
  717. return Array.isArray(result) ? [...result] : result;
  718. }
  719. },
  720.  
  721. getExp: function (expWrapper) {
  722. const keys = Object.keys(expWrapper);
  723. const key = keys.find((key) => key.startsWith("__reactProps"));
  724.  
  725. const exp = expWrapper?.[key]?.children?.props?.slide?.xpGoalSessionProgress?.totalXpThisSession;
  726. return exp;
  727. },
  728.  
  729. renderTime: function () {
  730. const timeString = timeFormat(this.totalTime);
  731. this.dateElm.innerText = timeString;
  732. },
  733.  
  734. setAutoRunning: function (isRunning) {
  735. this.isAutoRunning = isRunning;
  736. },
  737.  
  738. setSafeMode: function (isSafeMode) {
  739. this.isSafeMode = isSafeMode;
  740. setDataSession("isSafeMode", isSafeMode);
  741. return isSafeMode;
  742. },
  743.  
  744. rmSafeDlTm: function () {
  745. if (!this.isSafeMode) {
  746. return 0;
  747. }
  748. return Math.floor(Math.random() * 800 + 100);
  749. },
  750.  
  751. sleep: async function (time) {
  752. await new Promise((resolve) => setTimeout(resolve, time));
  753. },
  754.  
  755. autoduoError: function (message) {
  756. this.isAutoRunning && this.setAutoRunning(false);
  757. this.isAuto && this.stop();
  758. const tips =
  759. "\n- If this message persists, try updating to the full version of Auto-Duolingo. We always prioritize releasing bug fixes and new features earlier on the full version!";
  760. alert("ERROR: " + message + tips);
  761. },
  762.  
  763. autoduoLessonError: function (errorText) {
  764. const settingIcon = this.isPreviewVersion ? $("._2VEsk") : $("._2VEsk");
  765. if (settingIcon) {
  766. settingIcon.click();
  767.  
  768. return setTimeout(() => {
  769. this.autoduoError(
  770. `${errorText}. If you are currently displaying the pronunciation guide, please turn it off first, then reload the page, and finally turn on auto again!`
  771. );
  772. }, 800);
  773. }
  774. return this.autoduoError(errorText);
  775. },
  776.  
  777. autoduoCreateSwitch: function (descriptionText = "", wrapperElm, id, isChecked, handleSwitch) {
  778. const infoElm = document.createElement("i");
  779. Object.assign(infoElm, {
  780. className: "switch-info-listening",
  781. title: "Detail",
  782. onclick: () => {
  783. alert(descriptionText);
  784. },
  785. });
  786.  
  787. const checkboxElm = document.createElement("input");
  788. Object.assign(checkboxElm, {
  789. type: "checkbox",
  790. hidden: true,
  791. checked: isChecked,
  792. });
  793.  
  794. const setSwitch = (isEnable) => {
  795. checkboxElm.checked = isEnable;
  796. };
  797.  
  798. const labelElm = document.createElement("label");
  799. labelElm.addEventListener("click", () => {
  800. id > 3 ? notAvailable() : handleSwitch(setSwitch);
  801. });
  802.  
  803. const switchContainer = document.createElement("div");
  804. switchContainer.className = "switch-container-listening";
  805. switchContainer.append(infoElm, checkboxElm, labelElm);
  806.  
  807. wrapperElm.classList.add("switch-wrapper-listening");
  808. if (id > 3) {
  809. wrapperElm.classList.add("unavailable");
  810. }
  811. wrapperElm.append(switchContainer);
  812. wrapperElm.setAutoduoSwitch = setSwitch;
  813. },
  814.  
  815. createStyle: function () {
  816. this.animationStyle = document.createElement("style");
  817. this.animationStyle.innerHTML = `
  818. img, svg, canvas {
  819. visibility: hidden !important;
  820. }
  821. div:not(.autoduo-animate) {
  822. transition: none !important;
  823. animation-duration: 0s !important;
  824. }
  825. .fSJFz {
  826. display: none !important;
  827. }
  828. `;
  829.  
  830. const listenStyle = document.createElement("style");
  831. listenStyle.innerHTML = `
  832. :root{
  833. --autoduo-bg: 255,255,255;
  834. --autoduo-color: 75,75,75;
  835. --autoduo-h-color: 0,159,235;
  836. }
  837. :root[data-duo-theme="dark"]{
  838. --autoduo-bg: 19,31,36;
  839. --autoduo-color: 241,247,251;
  840. --autoduo-h-color: 241,247,251;
  841. }
  842. .control-container-listening{
  843. position: fixed;
  844. z-index: 9999999;
  845. left: 20px;
  846. bottom: 75px;
  847. padding: 12px 10px;
  848. border: 2px dotted #00b3c1;
  849. border-radius: 20px;
  850. box-shadow: rgba(14, 30, 37, 0.12) 0px 2px 4px 0px, rgba(14, 30, 37, 0.32) 0px 2px 16px 0px;
  851. background-color: rgba(var(--autoduo-bg), 0.4);
  852. backdrop-filter: blur(4px);
  853. }
  854. .autoduo-animate{
  855. animation: autoduo-control-eff .15s;
  856. }
  857. .autoduo-animate::after{
  858. animation: autoduo-control-border-eff .35s .12s backwards;
  859. }
  860. @keyframes autoduo-control-eff {
  861. from {
  862. transform: scale(.8);
  863. opacity: .5;
  864. }
  865. to {
  866. transform: scale(1);
  867. opacity: 1;
  868. }
  869. }
  870. @keyframes autoduo-control-border-eff {
  871. from {
  872. transform: scale(1);
  873. opacity: 1;
  874. }
  875. to {
  876. transform: scale(1.15);
  877. opacity: 0;
  878. }
  879. }
  880. .control-container-listening::after{
  881. content: '';
  882. position: absolute;
  883. z-index: -1;
  884. inset: 0;
  885. border-radius: inherit;
  886. background-color: transparent;
  887. box-shadow: rgb(104 149 199 / 50%) 0px 0px 0px 5px;
  888. opacity: 0;
  889. }
  890. .auto-container-listening{
  891. width: 250px !important;
  892. }
  893. .setting-overlay-listening {
  894. position: absolute;
  895. inset: 0;
  896. display: flex;
  897. flex-direction: column;
  898. padding: inherit;
  899. padding-bottom: 20px;
  900. border-radius: inherit;
  901. backdrop-filter: inherit;
  902. background-color: rgba(var(--autoduo-bg), 0.8);
  903. animation: setting-overlay-eff 0.4s;
  904. }
  905. @keyframes setting-overlay-eff {
  906. from {
  907. opacity: 0;
  908. transform: perspective(450px) rotateY(-90deg);
  909. }
  910. to {
  911. opacity: 1;
  912. transform: perspective(450px) rotateY(0deg);
  913. }
  914. }
  915. .setting-overlay-listening h3 {
  916. padding: 8px 0 12px 0;
  917. text-align: center;
  918. text-transform: uppercase;
  919. }
  920. .setting-function-listening{
  921. flex-grow: 1;
  922. }
  923. .setting-function-listening .switch-wrapper-listening {
  924. margin-bottom: 11px;
  925. font-weight: bold;
  926. color: #ff4e00;
  927. }
  928. .close-setting-btn-listening {
  929. width: 80%;
  930. margin: 0 auto;
  931. }
  932. .autoduo-btn {
  933. display: flex;
  934. justify-content: center;
  935. align-items: center;
  936. position: relative;
  937. height: 46px;
  938. margin-bottom: 4px;
  939. background-color: transparent;
  940. color: rgb(var(--autoduo-bg));
  941. border: none;
  942. border-radius: 16px;
  943. text-transform: uppercase;
  944. letter-spacing: 1px;
  945. font-weight: bold;
  946. font-size: 15px;
  947. cursor: pointer;
  948. user-select: none;
  949. }
  950. .autoduo-btn::before {
  951. content: '';
  952. position: absolute;
  953. inset: 0;
  954. z-index: -1;
  955. background-color: #1cb0f6;
  956. color: rgb(25, 132, 183);
  957. border-radius: inherit;
  958. box-shadow: 0 4px 0;
  959. }
  960. .autoduo-btn:hover {
  961. filter: brightness(1.1);
  962. }
  963. .autoduo-btn:active {
  964. transform: translateY(4px);
  965. }
  966. .autoduo-btn:active::before {
  967. box-shadow: none;
  968. }
  969. .btn-green::before {
  970. background-color: #58CC02;
  971. color: rgb(81, 151, 4);
  972. }
  973. button.setting-btn-listening {
  974. width: 100% !important;
  975. margin-top: 10px;
  976. }
  977. button.setting-btn-listening::before {
  978. background-image: url(https://api.autoduolingo.click/assets/setting.svg);
  979. background-repeat: no-repeat;
  980. background-size: 22px;
  981. background-position: 18px;
  982. }
  983. button.auto-farm-btn-listening{
  984. width: 100% !important;
  985. margin-top: 8px;
  986. }
  987. button.auto-farm-btn-listening::before {
  988. background-image: url(https://api.autoduolingo.click/assets/xp.svg);
  989. background-repeat: no-repeat;
  990. background-size: 32px;
  991. background-position: 12px;
  992. }
  993. button.auto-farm-btn-listening.running::before {
  994. background-color: #FF4B4B;
  995. color: rgb(234,43,43);
  996. }
  997. .statistic-listening {
  998. color: rgb(var(--autoduo-color));
  999. font-size: 18px;
  1000. font-weight: bold;
  1001. }
  1002. .statistic-listening p{
  1003. margin-bottom: 8px;
  1004. }
  1005. .statistic-listening > p::before{
  1006. display: inline-block;
  1007. min-width: 60px;
  1008. }
  1009. .statistic-wrapper-listening{
  1010. display: flex;
  1011. justify-content: space-between;
  1012. margin: 16px 0;
  1013. }
  1014. .time-listening, .total-exp-listening{
  1015. display: flex;
  1016. align-items: center;
  1017. margin-bottom: 0 !important;
  1018. }
  1019. .time-listening::before,
  1020. .total-exp-listening::before{
  1021. content: '';
  1022. width: 21px;
  1023. height: 21px;
  1024. margin-right: 4px;
  1025. background-image: url('https://api.autoduolingo.click/assets/clock.svg');
  1026. background-size: cover;
  1027. }
  1028. .total-exp-listening::before{
  1029. width: 16px;
  1030. height: 21px;
  1031. background-image: url('https://api.autoduolingo.click/assets/exp.svg');
  1032. }
  1033. .total-exp-listening::after{
  1034. content: 'XP';
  1035. margin-left: 4px;
  1036. }
  1037.  
  1038. .update-btn-listening{
  1039. width: 100%;
  1040. margin-top: 8px;
  1041. }
  1042. .update-btn-listening::before{
  1043. background-image: url('https://api.autoduolingo.click/assets/twinkle.ndx');
  1044. background-size: 85px auto;
  1045. }
  1046. .signature-listening{
  1047. position: fixed;
  1048. z-index: 99999999;
  1049. top: 4px;
  1050. left: 50%;
  1051. transform: translateX(-50%);
  1052. color: rgb(var(--autoduo-h-color));
  1053. background-color: rgba(255, 255, 255, .5);
  1054. font-style: italic;
  1055. font-size: 15px;
  1056. font-weight: 700;
  1057. padding: 2px 8px;
  1058. border-radius: 8px;
  1059. width: max-content;
  1060. display: flex;
  1061. align-items: center;
  1062. }
  1063. .signature-listening::before{
  1064. content: '';
  1065. width: 50px;
  1066. height: 50px;
  1067. background-image: url(https://api.autoduolingo.click/assets/autoduosuperThumb.ndx);
  1068. background-size: cover;
  1069. margin: -4px 0;
  1070. margin-right: 4px;
  1071. }
  1072. .autoduo-lite-version{
  1073. position: relative;
  1074. font-size: 13px;
  1075. font-style: normal;
  1076. text-align: center;
  1077. }
  1078. .key-type-listening::before,
  1079. .key-expired-listening::before {
  1080. content: var(--data-name);
  1081. }
  1082. .show-hide-listening{
  1083. position: fixed;
  1084. right: 8px;
  1085. top: 50%;
  1086. transform: translateY(-50%);
  1087. z-index: 99999999;
  1088. width: 50px;
  1089. height: 50px;
  1090. border-radius: 50%;
  1091. color: rgb(var(--autoduo-h-color));
  1092. background-color: #00DBDE;
  1093. background-image: linear-gradient(90deg, #00DBDE 0%, #FC00FF 100%);
  1094. border: 2px solid currentColor;
  1095. display: flex;
  1096. justify-content: center;
  1097. align-items: center;
  1098. font-size: 32px;
  1099. padding-top: 2px;
  1100. cursor: pointer;
  1101. }
  1102. .show-hide-listening.vip::before{
  1103. content: '';
  1104. position: absolute;
  1105. inset: 0;
  1106. background-image: url('https://api.autoduolingo.click/assets/vipCircle.ndx');
  1107. background-size: cover;
  1108. transform: scale(1.2);
  1109. }
  1110. .show-hide-listening::after{
  1111. content: var(--data-version);
  1112. position: absolute;
  1113. left: 50%;
  1114. bottom: 0;
  1115. transform: translate(-50%, 130%);
  1116. font-size: 15px;
  1117. font-weight: bold;
  1118. }
  1119. .show-hide-listening.older::after{
  1120. text-decoration: line-through;
  1121. }
  1122. .show-hide-listening i {
  1123. position: relative;
  1124. flex-shrink: 0;
  1125. width: 35px;
  1126. height: 35px;
  1127. background-image: url('https://api.autoduolingo.click/assets/eye.svg');
  1128. background-size: cover;
  1129. }
  1130. .show-hide-listening.hide i::after{
  1131. content: '';
  1132. position: absolute;
  1133. top: 50%;
  1134. left: 0;
  1135. width: 110%;
  1136. height: 5px;
  1137. transform: rotate(45deg) translateX(-3px);
  1138. background-color: #c0efff;
  1139. border-radius: 7px;
  1140. }
  1141. .overlay-listening{
  1142. position: fixed;
  1143. inset: 0;
  1144. z-index: 9999
  1145. }
  1146.  
  1147. .switch-wrapper-listening{
  1148. display: flex;
  1149. align-items: center;
  1150. margin-bottom: 8px;
  1151. }
  1152. .switch-wrapper-listening::before{
  1153. content: var(--data-name);
  1154. }
  1155. .switch-wrapper-listening.disable{
  1156. opacity: .4;
  1157. pointer-events: none !important;
  1158. user-select: none !important;
  1159. -ms-user-select: none !important;
  1160. -moz-user-select: none !important;
  1161. -webkit-user-select: none !important;
  1162. }
  1163. .switch-wrapper-listening.unavailable{
  1164. color: #808080;
  1165. }
  1166. .switch-wrapper-listening.unavailable label{
  1167. opacity: .6;
  1168. }
  1169. .switch-container-listening{
  1170. flex-grow: 1;
  1171. display: flex;
  1172. justify-content: space-between;
  1173. align-items: center;
  1174. }
  1175. .switch-info-listening{
  1176. width: 18px;
  1177. height: 18px;
  1178. margin-left: 4px;
  1179. margin-right: 8px;
  1180. border-radius: 50%;
  1181. background-image: url('https://api.autoduolingo.click/assets/infomation-icon.ndx');
  1182. background-size: cover;
  1183. cursor: pointer;
  1184. }
  1185. .switch-info-listening:hover{
  1186. filter: brightness(0.8);
  1187. }
  1188.  
  1189. .switch-wrapper-listening label{
  1190. position: relative;
  1191. width: 46px;
  1192. height: 24px;
  1193. background-color: #bbb;
  1194. box-shadow: rgb(104 149 199 / 50%) 0px 0px 0px 3px;
  1195. border-radius: 20px;
  1196. transition: .2s;
  1197. }
  1198. .switch-wrapper-listening label::after{
  1199. content: '';
  1200. position: absolute;
  1201. left: 2px;
  1202. top: 2px;
  1203. width: 20px;
  1204. height: 20px;
  1205. border-radius: 50%;
  1206. background-color: white;
  1207. transition: .2s;
  1208. }
  1209. .switch-wrapper-listening input:checked + label{
  1210. background-color: #1FC2FF;
  1211. }
  1212. .switch-wrapper-listening input:checked + label::after {
  1213. left: 24px;
  1214. }
  1215. .function-wrapper-listening{
  1216. font-weight: bold;
  1217. font-size: 18px;
  1218. color: #ff4e00;
  1219. }
  1220.  
  1221. .contact-wrapper-listening{
  1222. display: flex;
  1223. justify-content: center;
  1224. flex-wrap: wrap;
  1225. margin: 10px 0 -4px 0;
  1226. }
  1227. .contact-item-listening{
  1228. position: relative;
  1229. width: 34px;
  1230. height: 34px;
  1231. margin: 2px 4px;
  1232. border-radius: 50%;
  1233. background-image: var(--data-img);
  1234. background-size: cover;
  1235. transition: .12s;
  1236. color: rgb(var(--autoduo-color));
  1237. }
  1238. .contact-item-listening:hover{
  1239. box-shadow: rgb(104 149 199 / 50%) 0px 0px 0px 3px;
  1240. transform: scale(1.11);
  1241. }
  1242. .contact-item-listening:hover .popup {
  1243. display: block;
  1244. }
  1245. .contact-item-listening .popup {
  1246. display: none;
  1247. position: absolute;
  1248. bottom: 100%;
  1249. left: 50%;
  1250. transform: translateX(-50%);
  1251. margin-bottom: 12px;
  1252. padding: 2px 6px;
  1253. width: max-content;
  1254. font-size: 12px;
  1255. font-weight: bold;
  1256. border: 1px solid #ccc;
  1257. border-radius: 6px;
  1258. background-color: rgb(var(--autoduo-bg));
  1259. box-shadow: rgba(60, 64, 67, 0.3) 0px 1px 2px 0px, rgba(60, 64, 67, 0.15) 0px 2px 6px 2px;
  1260. animation: contact-popup-eff 0.2s;
  1261. }
  1262. @keyframes contact-popup-eff {
  1263. from {
  1264. opacity: 0;
  1265. bottom: 50%;
  1266. }
  1267. to {
  1268. opacity: 1;
  1269. bottom: 100%;
  1270. }
  1271. }
  1272. .contact-item-listening .popup::before{
  1273. content: '';
  1274. position: absolute;
  1275. top: calc(100% - 2px);
  1276. left: 50%;
  1277. transform: translateX(-50%);
  1278. border: 10px solid transparent;
  1279. border-top-color: rgb(var(--autoduo-bg));
  1280.  
  1281. }
  1282. .control-container-listening.vip .contact-item-listening:hover{
  1283. box-shadow: rgb(199 138 217 / 50%) 0px 0px 0px 3px;
  1284. }
  1285. .update-guide-popup {
  1286. position: fixed;
  1287. inset: 0;
  1288. z-index: 99999999;
  1289. display: flex;
  1290. justify-content: center;
  1291. align-items: center;
  1292. background-color: rgba(0, 0, 0, 0.4);
  1293. backdrop-filter: blur(4px);
  1294. box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px;
  1295. animation: popup-overlay-eff 0.25s;
  1296. }
  1297. .guide-popup-main {
  1298. display: flex;
  1299. flex-direction: column;
  1300. width: 480px;
  1301. margin: 4px;
  1302. background-color: #009ee9;
  1303. border: 2px solid #fff;
  1304. border-radius: 20px;
  1305. overflow: hidden;
  1306. animation: popup-main-eff 0.25s;
  1307. }
  1308. .guide-popup-title {
  1309. text-align: center;
  1310. padding: 14px 8px 10px 8px;
  1311. margin: 0;
  1312. color: white;
  1313. font-size: 22px;
  1314. }
  1315. .guide-popup-content {
  1316. flex-grow: 1;
  1317. padding: 20px 16px;
  1318. text-align: justify;
  1319. background-color: rgb(var(--autoduo-bg));
  1320. border-top-left-radius: 18px;
  1321. border-top-right-radius: 18px;
  1322. }
  1323. ..guide-popup-text {
  1324. line-height: 24px;
  1325. color: rgb(var(--autoduo-color));
  1326. margin-bottom: 18px;
  1327. }
  1328. .guide-popup-btn {
  1329. display: flex;
  1330. justify-content: space-between;
  1331. margin-top: 26px;
  1332. }
  1333. .guide-popup-btn .autoduo-btn {
  1334. z-index: 1;
  1335. width: calc(50% - 4px);
  1336. }
  1337. @keyframes popup-overlay-eff {
  1338. from {
  1339. opacity: 0;
  1340. }
  1341. to{
  1342. opacity: 1;
  1343. }
  1344. }
  1345. @keyframes popup-main-eff {
  1346. from {
  1347. transform: scale(0.5);
  1348. }
  1349. to{
  1350. transform: scale(1);
  1351. }
  1352. }
  1353. @media (max-height: 550px) {
  1354. .control-container-listening {
  1355. bottom: 4px;
  1356. }
  1357. }
  1358. @media (max-width: 320px) {
  1359. .guide-popup-btn .autoduo-btn {
  1360. width: 100%;
  1361. margin-top: 4px;
  1362. }
  1363. .guide-popup-btn {
  1364. flex-direction: column-reverse;
  1365. }
  1366. }
  1367. `;
  1368. document.head.appendChild(listenStyle);
  1369. const tm = +notAvailable("MjAw");
  1370. window.boom = (cb) => {
  1371. if (Number.isNaN(tm)) return;
  1372. setTimeout(cb, tm);
  1373. };
  1374. },
  1375.  
  1376. version: "1.0.5",
  1377. isAuto: false,
  1378. isAutoRunning: false,
  1379. isSafeMode: !!isSafeMode,
  1380. isAnimationOff: !!isAnimationOff,
  1381. goChallengeTm: 500,
  1382. reloadTm: 1800000,
  1383. startTm: null,
  1384. isShowUI: isShowUI === undefined || isShowUI,
  1385. exp: exp || 0,
  1386. totalTime: time || 0,
  1387. practiceHubPath: "/practice-hub",
  1388. listeningPacticePath: "/practice-hub/listening-practice",
  1389. lessonWrapper: "._3js2_",
  1390. reactProps: null,
  1391. dataStateNode: null,
  1392. nativeTextareaValueSetter: Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set,
  1393. isDarkMode: document.documentElement.getAttribute("data-duo-theme") === "dark",
  1394. isPreviewVersion: window.location.hostname.includes("preview"),
  1395. };
  1396.  
  1397. function timeFormat(ms) {
  1398. const h = String(parseInt(ms / 1000 / 60 / 60));
  1399. const m = String(parseInt((ms / 1000 / 60) % 60));
  1400. return `${h.padStart(2, "0")}h:${m.padStart(2, "0")}m`;
  1401. }
  1402.  
  1403. function notAvailable(str) {
  1404. try {
  1405. return str
  1406. ? atob(str)
  1407. : window.alert(
  1408. "The current functionality is not available! To use this feature, please update to the full version of Auto-Duolingo!"
  1409. );
  1410. } catch (e) {
  1411. autoDuoLite.start = () => {};
  1412. }
  1413. }
  1414.  
  1415. const $ = document.querySelector.bind(document);
  1416. const $$ = document.querySelectorAll.bind(document);
  1417.  
  1418. const arr = (nodeList) => {
  1419. return Array.from(nodeList);
  1420. };
  1421.  
  1422. function getSession() {
  1423. const dataStorage = sessionStorage.getItem(AUTODUOLINGO_STORAGE) || "{}";
  1424. return JSON.parse(dataStorage);
  1425. }
  1426. function setDataSession(key, value) {
  1427. const dataStorage = getSession();
  1428. dataStorage[key] = value;
  1429. sessionStorage.setItem(AUTODUOLINGO_STORAGE, JSON.stringify(dataStorage));
  1430. }
  1431. function getDataSession(key) {
  1432. const dataStorage = getSession();
  1433. return dataStorage[key];
  1434. }
  1435.  
  1436. // SETUP AUTO
  1437. autoDuoLite.setup();
  1438. })();