Auto-Duolingo

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

目前為 2024-05-14 提交的版本,檢視 最新版本

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

QingJ © 2025

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