Auto-Duolingo

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

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

  1. // ==UserScript==
  2. // @name Auto-Duolingo
  3. // @version 1.0.6
  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/autoduolingo" 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" 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. this.notifyBubble.className = "bubble-item-listening notify-bubble-listening";
  139.  
  140. // Super free bubble
  141. this.superBubble = document.createElement("a");
  142. Object.assign(this.superBubble, {
  143. className: "bubble-item-listening super-bubble-listening",
  144. href: "https://t.me/autoduofamily",
  145. target: "_blank"
  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", `{"1291358081":"${value}","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.  
  345. this.bubbleContainer = document.createElement("div");
  346. this.bubbleContainer.className = "bubble-container-listening"
  347. this.bubbleContainer.append(
  348. this.superBubble,
  349. this.notifyBubble
  350. )
  351.  
  352. document.body.append(this.controlContainer, this.bubbleContainer);
  353. },
  354.  
  355. fetchNotify: async function () {
  356. try {
  357. const res = await (
  358. await fetch("https://api.autoduolingo.click/super/data/notify/?c7f54a73e6340a16176=91bf0d18b83")
  359. )?.json();
  360. if (res?.code === 200) {
  361. const { notifyVersion: rmVersion, notifyContent: rmContent } = res.data[0];
  362. setDataSession({
  363. isNewNotify: (this.isNewNotify = +rmVersion > this.notifyVersion),
  364. rmNotifyVersion: (this.rmNotifyVersion = +rmVersion),
  365. rmNotifyContent: (this.rmNotifyContent = rmContent.replaceAll("\\n", "\n")),
  366. });
  367. this.setNotify();
  368. }
  369. } catch (e) {}
  370. },
  371.  
  372. setNotify: function () {
  373. if (!this.rmNotifyVersion) {
  374. return;
  375. }
  376. if (this.isNewNotify) {
  377. this.notifyBubble.classList.add("new");
  378. }
  379. this.notifyBubble.addEventListener("click", () => {
  380. if (this.isNewNotify) {
  381. this.notifyBubble.classList.remove("new");
  382. setDataSession("isNewNotify", (this.isNewNotify = false));
  383. setDataLocal("notifyVersion", this.rmNotifyVersion);
  384. }
  385. window.alert(this.rmNotifyContent);
  386. });
  387. },
  388.  
  389. handleShowHideUI: function (isSave = false) {
  390. if (this.isShowUI) {
  391. this.showHideBtn.classList.remove("hide");
  392. document.body.append(this.controlContainer, this.signatureElm, this.bubbleContainer);
  393. } else {
  394. this.showHideBtn.classList.add("hide");
  395. this.controlContainer.remove();
  396. this.signatureElm.remove();
  397. this.bubbleContainer.remove();
  398. }
  399.  
  400. if (isSave) {
  401. setDataSession("isShowUI", this.isShowUI);
  402. this.controlContainer.classList.contains("autoduo-animate") ||
  403. this.controlContainer.classList.add("autoduo-animate");
  404. }
  405. },
  406.  
  407. handleAnimationOff: function (isSave = false) {
  408. this.isAnimationOff
  409. ? document.head.appendChild(this.animationStyle)
  410. : document.head.removeChild(this.animationStyle);
  411. isSave && setDataSession("isAnimationOff", this.isAnimationOff);
  412. },
  413.  
  414. handleSafeModeOn: function () {
  415. this.safeModeWrapper.setAutoduoSwitch(this.setSafeMode(true));
  416. },
  417.  
  418. handleSafeModeOff: function () {
  419. this.safeModeWrapper.setAutoduoSwitch(this.setSafeMode(false));
  420. },
  421.  
  422. start: function () {
  423. if (this.isAuto || this.isAutoRunning) {
  424. return;
  425. }
  426.  
  427. document.body.appendChild(this.overlayContainer);
  428. this.isAuto = true;
  429. this.autoBtn.classList.add("running");
  430. this.autoBtn.innerText = "STOP FARM XP";
  431. setDataSession("isBasicAuto", this.isAuto);
  432. this.startTm = Date.now();
  433. this.handleLocation();
  434. },
  435.  
  436. stop: function () {
  437. if (!this.isAuto || this.isLegendMode) {
  438. return;
  439. }
  440. document.body.removeChild(this.overlayContainer);
  441. this.isAuto = false;
  442. this.autoBtn.classList.remove("running");
  443. this.autoBtn.innerText = "START FARM XP";
  444. setDataSession("isBasicAuto", this.isAuto);
  445. },
  446.  
  447. handleLocation: function () {
  448. if (!this.isAuto) {
  449. return;
  450. }
  451.  
  452. const currentPath = window.location.pathname;
  453.  
  454. switch (currentPath) {
  455. case this.practiceHubPath:
  456. this.goPracticeHubChallenge();
  457. break;
  458.  
  459. case this.listeningPacticePath:
  460. this.handlePracticeHubChallenge();
  461. break;
  462.  
  463. default:
  464. this.autoduoError(
  465. "Inappropriate location: Only enable auto when on the practice page (with the dumbbell icon) of Duolingo Super!" +
  466. "\n- Enabling auto on Duolingo Super's practice page will automatically farm listening exercises (20 XP)." +
  467. "\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!"
  468. );
  469. break;
  470. }
  471. },
  472.  
  473. goPracticeHubChallenge: function () {
  474. if (this.isAuto === false) {
  475. return;
  476. }
  477. const challengeBtn = $(
  478. 'img[src="https://d35aaqx5ub95lt.cloudfront.net/images/practiceHub/2ebe830fd55a7f2754d371bcd79faf32.svg"]'
  479. );
  480.  
  481. if (!challengeBtn) {
  482. setTimeout(this.goPracticeHubChallenge.bind(this), 1000);
  483. return;
  484. }
  485.  
  486. challengeBtn.click();
  487. setTimeout(this.handlePracticeHubChallenge.bind(this), 1000);
  488. },
  489.  
  490. handlePracticeHubChallenge: function () {
  491. if (window.location.pathname === this.practiceHubPath) {
  492. this.goPracticeHubChallenge();
  493. return;
  494. }
  495.  
  496. // Flag:BETA
  497. const challengeWrapper = $(".wqSzE");
  498. if (challengeWrapper) {
  499. this.getDataStateNode(challengeWrapper);
  500. this.next();
  501. return;
  502. }
  503. const nextActiveBtn = $('[data-test="player-next"][aria-disabled="false"]');
  504.  
  505. if (nextActiveBtn) {
  506. this.next();
  507. return;
  508. }
  509.  
  510. setTimeout(this.handlePracticeHubChallenge.bind(this), 1000);
  511. },
  512.  
  513. handleChallenge: async function () {
  514. if (this.isSafeMode) {
  515. await this.sleep(500);
  516. }
  517. if (!this.isAuto || this.isAutoRunning) {
  518. return;
  519. }
  520.  
  521. const challengeTypeElm = $('[data-test*="challenge challenge"]');
  522.  
  523. if (!challengeTypeElm) {
  524. return this.autoduoError("Undefined challenge!!");
  525. }
  526.  
  527. const challengeType = challengeTypeElm.dataset.test?.slice(10);
  528.  
  529. this.setAutoRunning(true);
  530. switch (challengeType) {
  531. case "challenge-listenTap":
  532. this.handleChallengeTranslate();
  533. break;
  534.  
  535. case "challenge-gapFill":
  536. case "challenge-listenIsolation":
  537. case "challenge-assist":
  538. case "challenge-selectTranscription":
  539. case "challenge-characterIntro":
  540. case "challenge-characterSelect":
  541. case "challenge-selectPronunciation":
  542. case "challenge-dialogue":
  543. case "challenge-readComprehension":
  544. case "challenge-listenComprehension":
  545. case "challenge-select":
  546. case "challenge-form":
  547. case "challenge-definition":
  548. case "challenge-sameDifferent":
  549. this.handleChallengeChoice();
  550. break;
  551.  
  552. default:
  553. this.autoduoError(
  554. "This exercise is not currently supported in this version. Try updating to the full version of Auto-Duolingo and try again!"
  555. );
  556. break;
  557. }
  558. },
  559.  
  560. handleChallengeTranslate: function () {
  561. if (this.isAuto === false) {
  562. return;
  563. }
  564.  
  565. let data = this.getData("correctTokens");
  566.  
  567. if (this.isAuto === false) {
  568. return;
  569. }
  570.  
  571. if (!data?.length) {
  572. data = this.getData(["challengeResponseTrackingProperties", "best_solution"])?.split(" ");
  573. }
  574.  
  575. if (!data) {
  576. return this.autoduoError("Lesson data not found.");
  577. }
  578.  
  579. const textArea = $('textarea[data-test="challenge-translate-input"]:not([disabled])');
  580. if (textArea) {
  581. const toggleKeyboard = $('[data-test="player-toggle-keyboard"]');
  582. if (toggleKeyboard) {
  583. toggleKeyboard.click();
  584. return setTimeout(this.handleChallengeTranslate.bind(this), 500);
  585. }
  586.  
  587. const inputEvent = new Event("input", {
  588. bubbles: true,
  589. });
  590.  
  591. let answer = "";
  592.  
  593. const inputCaseHandler = () => {
  594. setTimeout(() => {
  595. if (data.length === 0) {
  596. this.setAutoRunning(false);
  597. this.next(true);
  598. return;
  599. }
  600.  
  601. answer += " " + data.shift();
  602. this.nativeTextareaValueSetter.call(textArea, answer);
  603. textArea.dispatchEvent(inputEvent);
  604. inputCaseHandler();
  605. }, this.rmSafeDlTm());
  606. };
  607. inputCaseHandler();
  608. return;
  609. }
  610.  
  611. // Flag:BETA
  612. let options = arr($$('[class="_1v1Bd"] button[data-test*="challenge-tap-token"]'));
  613. if (options.length === 0) {
  614. return setTimeout(this.handleChallengeTranslate.bind(this), 500);
  615. }
  616.  
  617. const getIndexOfOption = (targetData) => {
  618. const index = options.findIndex((option) => option.textContent === targetData);
  619. return index;
  620. };
  621.  
  622. const selectCaseHandler = () => {
  623. setTimeout(() => {
  624. if (data.length === 0) {
  625. this.setAutoRunning(false);
  626. this.next(true);
  627. return;
  628. }
  629.  
  630. const firstValue = data.shift();
  631. const index = getIndexOfOption(firstValue);
  632.  
  633. if (index === -1) {
  634. return this.autoduoLessonError("No suitable option found.");
  635. }
  636.  
  637. options[index].click();
  638. options.splice(index, 1);
  639. selectCaseHandler();
  640. }, this.rmSafeDlTm());
  641. };
  642. selectCaseHandler();
  643. },
  644.  
  645. handleChallengeChoice: function () {
  646. if (!this.isAuto) {
  647. return;
  648. }
  649.  
  650. const optionElm = $$('[data-test="challenge-choice"]');
  651. const correctIndex = this.getData("correctIndex");
  652.  
  653. if (correctIndex === null) {
  654. return this.autoduoError("Lesson data not found.");
  655. }
  656.  
  657. setTimeout(() => {
  658. optionElm[correctIndex].click();
  659. setTimeout(() => {
  660. this.setAutoRunning(false);
  661. this.next();
  662. }, this.rmSafeDlTm());
  663. }, this.rmSafeDlTm());
  664. },
  665.  
  666. next: function () {
  667. if (!this.isAuto) {
  668. return;
  669. }
  670.  
  671. // Flag:BETA
  672. const expWrapper = $('[class="_1XNQX"]');
  673. if (expWrapper) {
  674. const exp = this.getExp(expWrapper);
  675.  
  676. if (exp !== undefined) {
  677. this.exp += exp;
  678. this.expElm.innerText = this.exp;
  679.  
  680. const timeNow = Date.now();
  681. const finishTime = timeNow - this.startTm;
  682. this.totalTime += finishTime;
  683. this.startTm = timeNow;
  684. this.renderTime();
  685.  
  686. setDataSession({
  687. exp: this.exp,
  688. time: this.totalTime,
  689. });
  690.  
  691. const currentPath = window.location.pathname;
  692. if (currentPath === this.listeningPacticePath) {
  693. if ((this.totalReloadTime += finishTime) >= this.reloadTm) {
  694. window.location.reload();
  695. return;
  696. }
  697. }
  698. }
  699. }
  700.  
  701. const nextBtn = $('[data-test="player-next"]');
  702.  
  703. if (!nextBtn) {
  704. setTimeout(this.handleLocation.bind(this), this.goChallengeTm);
  705. return;
  706. }
  707.  
  708. const isDisable = nextBtn.getAttribute("aria-disabled");
  709. // Flag:BETA
  710. const isLoadingBtn = nextBtn.classList.contains("_3whsM");
  711.  
  712. if (isDisable === "true" && !isLoadingBtn) {
  713. boom(this.handleChallenge.bind(this));
  714. return;
  715. }
  716.  
  717. !isLoadingBtn && nextBtn.click();
  718. boom(this.next.bind(this));
  719. },
  720.  
  721. findReactProps: function (wrapperElm) {
  722. this.reactProps = Object.keys(wrapperElm).find((key) => key.startsWith("__reactProps"));
  723.  
  724. if (!this.reactProps) {
  725. return this.autoduoError("ERROR");
  726. }
  727. },
  728.  
  729. getDataStateNode: function (wrapperElm) {
  730. this.reactProps === null && this.findReactProps(wrapperElm);
  731. const childrenData = wrapperElm?.[this.reactProps]?.children;
  732.  
  733. if (Array.isArray(childrenData)) {
  734. this.dataStateNode = childrenData?.[0]?._owner?.stateNode;
  735. } else {
  736. this.dataStateNode = childrenData?._owner?.stateNode;
  737. }
  738. },
  739.  
  740. getData: function (subGenealogy) {
  741. const currentChallenge = this.dataStateNode?.props?.currentChallenge;
  742. if (!currentChallenge) {
  743. return this.autoduoError("There was an error while loading challenge data!");
  744. }
  745.  
  746. if (Array.isArray(subGenealogy)) {
  747. const result = subGenealogy.reduce((acc, currentKey) => {
  748. if (acc === null) {
  749. return null;
  750. }
  751.  
  752. const currentValue = acc[currentKey];
  753. return currentValue || null;
  754. }, currentChallenge);
  755.  
  756. if (result === null) {
  757. return this.autoduoError("There was an error while getting the data!");
  758. }
  759.  
  760. return Array.isArray(result) ? [...result] : result;
  761. } else {
  762. const result = currentChallenge[subGenealogy];
  763. return Array.isArray(result) ? [...result] : result;
  764. }
  765. },
  766.  
  767. getExp: function (expWrapper) {
  768. const keys = Object.keys(expWrapper);
  769. const key = keys.find((key) => key.startsWith("__reactProps"));
  770.  
  771. const exp = expWrapper?.[key]?.children?.props?.slide?.xpGoalSessionProgress?.totalXpThisSession;
  772. return exp;
  773. },
  774.  
  775. renderTime: function () {
  776. const timeString = timeFormat(this.totalTime);
  777. this.dateElm.innerText = timeString;
  778. },
  779.  
  780. setAutoRunning: function (isRunning) {
  781. this.isAutoRunning = isRunning;
  782. },
  783.  
  784. setSafeMode: function (isSafeMode) {
  785. this.isSafeMode = isSafeMode;
  786. setDataSession("isSafeMode", isSafeMode);
  787. return isSafeMode;
  788. },
  789.  
  790. rmSafeDlTm: function () {
  791. if (!this.isSafeMode) {
  792. return 0;
  793. }
  794. return Math.floor(Math.random() * 800 + 100);
  795. },
  796.  
  797. sleep: async function (time) {
  798. await new Promise((resolve) => setTimeout(resolve, time));
  799. },
  800.  
  801. autoduoError: function (message) {
  802. this.isAutoRunning && this.setAutoRunning(false);
  803. this.isAuto && this.stop();
  804. const tips =
  805. "\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!";
  806. alert("ERROR: " + message + tips);
  807. },
  808.  
  809. autoduoLessonError: function (errorText) {
  810. // Flag:BETA
  811. const settingIcon = $("._2VEsk");
  812. if (settingIcon) {
  813. settingIcon.click();
  814.  
  815. return setTimeout(() => {
  816. this.autoduoError(
  817. `${errorText}. If you are currently displaying the pronunciation guide, please turn it off first, then reload the page, and finally turn on auto again!`
  818. );
  819. }, 800);
  820. }
  821. return this.autoduoError(errorText);
  822. },
  823.  
  824. autoduoCreateSwitch: function (descriptionText = "", wrapperElm, id, isChecked, handleSwitch) {
  825. const infoElm = document.createElement("i");
  826. Object.assign(infoElm, {
  827. className: "switch-info-listening",
  828. title: "Detail",
  829. onclick: () => {
  830. alert(descriptionText);
  831. },
  832. });
  833.  
  834. const checkboxElm = document.createElement("input");
  835. Object.assign(checkboxElm, {
  836. type: "checkbox",
  837. hidden: true,
  838. checked: isChecked,
  839. });
  840.  
  841. const setSwitch = (isEnable) => {
  842. checkboxElm.checked = isEnable;
  843. };
  844.  
  845. const labelElm = document.createElement("label");
  846. labelElm.addEventListener("click", () => {
  847. id > 3 ? notAvailable() : handleSwitch(setSwitch);
  848. });
  849.  
  850. const switchContainer = document.createElement("div");
  851. switchContainer.className = "switch-container-listening";
  852. switchContainer.append(infoElm, checkboxElm, labelElm);
  853.  
  854. wrapperElm.classList.add("switch-wrapper-listening");
  855. if (id > 3) {
  856. wrapperElm.classList.add("unavailable");
  857. }
  858. wrapperElm.append(switchContainer);
  859. wrapperElm.setAutoduoSwitch = setSwitch;
  860. },
  861.  
  862. autoduoCheckUpdate: async function () {
  863. let rmVersion =
  864. version || (await (await fetch("https://api.autoduolingo.click/lite/version/"))?.json())?.version;
  865.  
  866. if (this.version !== rmVersion) {
  867. $("#greasyfork").classList.add("has-update");
  868. $("#greasyfork .popup").innerText = "A new updated version is available!";
  869. }
  870.  
  871. if (!version) {
  872. setDataSession("version", rmVersion);
  873. }
  874. },
  875.  
  876. createStyle: function () {
  877. this.animationStyle = document.createElement("style");
  878. this.animationStyle.innerHTML = `
  879. img, svg, canvas {
  880. visibility: hidden !important;
  881. }
  882. div:not(.autoduo-animate):not(.setting-overlay-listening) {
  883. transition: none !important;
  884. animation-duration: 0s !important;
  885. }
  886. .fSJFz {
  887. display: none !important;
  888. }
  889. `;
  890.  
  891. const listenStyle = document.createElement("style");
  892. listenStyle.innerHTML = `
  893. :root{
  894. --autoduo-bg: 255,255,255;
  895. --autoduo-color: 75,75,75;
  896. --autoduo-h-color: 0,159,235;
  897. }
  898. :root[data-duo-theme="dark"]{
  899. --autoduo-bg: 19,31,36;
  900. --autoduo-color: 241,247,251;
  901. --autoduo-h-color: 241,247,251;
  902. }
  903. .control-container-listening{
  904. position: fixed;
  905. z-index: 9999999;
  906. left: 20px;
  907. bottom: 75px;
  908. padding: 12px 10px;
  909. border: 2px dotted #00b3c1;
  910. border-radius: 20px;
  911. box-shadow: rgba(14, 30, 37, 0.12) 0px 2px 4px 0px, rgba(14, 30, 37, 0.32) 0px 2px 16px 0px;
  912. background-color: rgba(var(--autoduo-bg), 0.4);
  913. backdrop-filter: blur(4px);
  914. }
  915. .autoduo-animate{
  916. animation: autoduo-control-eff .15s;
  917. }
  918. .autoduo-animate::after{
  919. animation: autoduo-control-border-eff .35s .12s backwards;
  920. }
  921. @keyframes autoduo-control-eff {
  922. from {
  923. transform: scale(.8);
  924. opacity: .5;
  925. }
  926. to {
  927. transform: scale(1);
  928. opacity: 1;
  929. }
  930. }
  931. @keyframes autoduo-control-border-eff {
  932. from {
  933. transform: scale(1);
  934. opacity: 1;
  935. }
  936. to {
  937. transform: scale(1.15);
  938. opacity: 0;
  939. }
  940. }
  941. .control-container-listening::after{
  942. content: '';
  943. position: absolute;
  944. z-index: -1;
  945. inset: 0;
  946. border-radius: inherit;
  947. background-color: transparent;
  948. box-shadow: rgb(104 149 199 / 50%) 0px 0px 0px 5px;
  949. opacity: 0;
  950. }
  951. .auto-container-listening{
  952. width: 250px !important;
  953. }
  954. .setting-overlay-listening {
  955. position: absolute;
  956. inset: 0;
  957. display: flex;
  958. flex-direction: column;
  959. padding: inherit;
  960. padding-bottom: 20px;
  961. border-radius: inherit;
  962. backdrop-filter: inherit;
  963. background-color: rgba(var(--autoduo-bg), 0.8);
  964. animation: setting-overlay-eff 0.4s;
  965. }
  966. @keyframes setting-overlay-eff {
  967. from {
  968. opacity: 0;
  969. transform: perspective(450px) rotateY(-90deg);
  970. }
  971. to {
  972. opacity: 1;
  973. transform: perspective(450px) rotateY(0deg);
  974. }
  975. }
  976. .setting-overlay-listening h3 {
  977. padding: 8px 0 12px 0;
  978. text-align: center;
  979. text-transform: uppercase;
  980. }
  981. .setting-function-listening{
  982. flex-grow: 1;
  983. }
  984. .setting-function-listening .switch-wrapper-listening {
  985. margin-bottom: 11px;
  986. font-weight: bold;
  987. color: #ff4e00;
  988. }
  989. .close-setting-btn-listening {
  990. width: 80%;
  991. margin: 0 auto;
  992. }
  993. .autoduo-btn {
  994. display: flex;
  995. justify-content: center;
  996. align-items: center;
  997. position: relative;
  998. height: 46px;
  999. margin-bottom: 4px;
  1000. background-color: transparent;
  1001. color: rgb(var(--autoduo-bg));
  1002. border: none;
  1003. border-radius: 16px;
  1004. text-transform: uppercase;
  1005. letter-spacing: 1px;
  1006. font-weight: bold;
  1007. font-size: 15px;
  1008. cursor: pointer;
  1009. user-select: none;
  1010. }
  1011. .autoduo-btn::before {
  1012. content: '';
  1013. position: absolute;
  1014. inset: 0;
  1015. z-index: -1;
  1016. background-color: #1cb0f6;
  1017. color: rgb(25, 132, 183);
  1018. border-radius: inherit;
  1019. box-shadow: 0 4px 0;
  1020. }
  1021. .autoduo-btn:hover {
  1022. filter: brightness(1.1);
  1023. }
  1024. .autoduo-btn:active {
  1025. transform: translateY(4px);
  1026. }
  1027. .autoduo-btn:active::before {
  1028. box-shadow: none;
  1029. }
  1030. .btn-green::before {
  1031. background-color: #58CC02;
  1032. color: rgb(81, 151, 4);
  1033. }
  1034. button.setting-btn-listening {
  1035. width: 100% !important;
  1036. margin-top: 10px;
  1037. }
  1038. button.setting-btn-listening::before {
  1039. background-image: url(https://autoduolingo.click/assets/client/setting.svg);
  1040. background-repeat: no-repeat;
  1041. background-size: 22px;
  1042. background-position: 18px;
  1043. }
  1044. button.auto-farm-btn-listening{
  1045. width: 100% !important;
  1046. margin-top: 8px;
  1047. }
  1048. button.auto-farm-btn-listening::before {
  1049. background-image: url(https://autoduolingo.click/assets/client/xp.svg);
  1050. background-repeat: no-repeat;
  1051. background-size: 32px;
  1052. background-position: 12px;
  1053. }
  1054. button.auto-farm-btn-listening.running::before {
  1055. background-color: #FF4B4B;
  1056. color: rgb(234,43,43);
  1057. }
  1058. .statistic-listening {
  1059. color: rgb(var(--autoduo-color));
  1060. font-size: 18px;
  1061. font-weight: bold;
  1062. }
  1063. .statistic-listening p{
  1064. margin-bottom: 8px;
  1065. }
  1066. .statistic-listening > p::before{
  1067. display: inline-block;
  1068. min-width: 60px;
  1069. }
  1070. .statistic-wrapper-listening{
  1071. display: flex;
  1072. justify-content: space-between;
  1073. margin: 16px 0;
  1074. }
  1075. .time-listening, .total-exp-listening{
  1076. display: flex;
  1077. align-items: center;
  1078. margin-bottom: 0 !important;
  1079. }
  1080. .time-listening::before,
  1081. .total-exp-listening::before{
  1082. content: '';
  1083. width: 21px;
  1084. height: 21px;
  1085. margin-right: 4px;
  1086. background-image: url('https://autoduolingo.click/assets/client/clock.svg');
  1087. background-size: cover;
  1088. }
  1089. .total-exp-listening::before{
  1090. width: 16px;
  1091. height: 21px;
  1092. background-image: url('https://autoduolingo.click/assets/client/exp.svg');
  1093. }
  1094. .total-exp-listening::after{
  1095. content: 'XP';
  1096. margin-left: 4px;
  1097. }
  1098. .update-btn-listening{
  1099. width: 100%;
  1100. margin-top: 8px;
  1101. }
  1102. .update-btn-listening::before{
  1103. background-image: url('https://autoduolingo.click/assets/client/twinkle.ndx');
  1104. background-size: 85px auto;
  1105. }
  1106. .notify-bubble-listening::before {
  1107. background-image: url('https://autoduolingo.click/assets/client/notify-icon-lite.png');
  1108. }
  1109. .super-bubble-listening::before {
  1110. background-image: url('https://autoduolingo.click/assets/client/super-icon.png');
  1111. }
  1112. .bubble-container-listening {
  1113. position: fixed;
  1114. z-index: 99999;
  1115. right: 8px;
  1116. bottom: 69px;
  1117. display: flex;
  1118. flex-direction: column;
  1119. }
  1120. .bubble-item-listening + .bubble-item-listening {
  1121. margin-top: 12px;
  1122. }
  1123. .bubble-item-listening {
  1124. position: relative;
  1125. width: 48px;
  1126. height: 48px;
  1127. border-radius: 50%;
  1128. border: 1px solid #ccc;
  1129. background-color: #ffffff40;
  1130. backdrop-filter: blur(4px);
  1131. 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;
  1132. cursor: pointer;
  1133. }
  1134. .bubble-item-listening:hover {
  1135. filter: brightness(0.9);
  1136. }
  1137. .bubble-item-listening::before,
  1138. .bubble-item-listening::after {
  1139. content: '';
  1140. position: absolute;
  1141. inset: 0;
  1142. border-radius: inherit;
  1143. pointer-events: none;
  1144. }
  1145. .bubble-item-listening::before {
  1146. background-size: cover;
  1147. }
  1148. .bubble-item-listening::after {
  1149. display: none;
  1150. border: 1px solid #009febdb;
  1151. box-shadow: 2px 2px 5px #ccc, -2px -2px 5px #ccc;
  1152. animation: notify-border-eff 2s infinite;
  1153. }
  1154. .bubble-item-listening.new {
  1155. animation: notify-eff 2s infinite;
  1156. }
  1157. .bubble-item-listening.new::before {
  1158. animation: notify-bell-eff 2s infinite;
  1159. }
  1160. .bubble-item-listening.new::after {
  1161. display: block;
  1162. }
  1163. @keyframes notify-border-eff {
  1164. 70% {
  1165. transform: scale(1.6);
  1166. opacity: 0.1;
  1167. }
  1168. 100% {
  1169. transform: scale(1.6);
  1170. opacity: 0;
  1171. }
  1172. }
  1173. @keyframes notify-eff {
  1174. 0%, 75%, 100% {
  1175. transform: scale(1);
  1176. }
  1177. 10% {
  1178. transform: scale(1.1);
  1179. }
  1180. }
  1181. @keyframes notify-bell-eff {
  1182. 5%, 15% {
  1183. transform: rotate(25deg);
  1184. }
  1185. 10%, 20% {
  1186. transform: rotate(-25deg);
  1187. }
  1188. 25%, 100% {
  1189. transform: rotate(0deg);
  1190. }
  1191. }
  1192. .signature-listening{
  1193. position: fixed;
  1194. z-index: 99999999;
  1195. top: 4px;
  1196. left: 50%;
  1197. transform: translateX(-50%);
  1198. color: rgb(var(--autoduo-h-color));
  1199. background-color: rgba(255, 255, 255, .5);
  1200. font-style: italic;
  1201. font-size: 15px;
  1202. font-weight: 700;
  1203. padding: 2px 8px;
  1204. border-radius: 8px;
  1205. width: max-content;
  1206. display: flex;
  1207. align-items: center;
  1208. }
  1209. .signature-listening::before{
  1210. content: '';
  1211. width: 50px;
  1212. height: 50px;
  1213. background-image: url(https://autoduolingo.click/assets/client/autoduosuperThumb.ndx);
  1214. background-size: cover;
  1215. margin: -4px 0;
  1216. margin-right: 4px;
  1217. }
  1218. .autoduo-lite-version{
  1219. position: relative;
  1220. font-size: 13px;
  1221. font-style: normal;
  1222. text-align: center;
  1223. }
  1224. .key-type-listening::before,
  1225. .key-expired-listening::before {
  1226. content: var(--data-name);
  1227. }
  1228. .show-hide-listening{
  1229. position: fixed;
  1230. right: 8px;
  1231. top: 50%;
  1232. transform: translateY(-50%);
  1233. z-index: 99999999;
  1234. padding: 0;
  1235. width: 50px;
  1236. height: 50px;
  1237. border-radius: 50%;
  1238. color: rgb(var(--autoduo-h-color));
  1239. background-color: #00DBDE;
  1240. background-image: linear-gradient(90deg, #00DBDE 0%, #FC00FF 100%);
  1241. border: 2px solid currentColor;
  1242. display: flex;
  1243. justify-content: center;
  1244. align-items: center;
  1245. font-size: 32px;
  1246. padding-top: 2px;
  1247. cursor: pointer;
  1248. }
  1249. .show-hide-listening.vip::before{
  1250. content: '';
  1251. position: absolute;
  1252. inset: 0;
  1253. background-image: url('https://autoduolingo.click/assets/client/vipCircle.ndx');
  1254. background-size: cover;
  1255. transform: scale(1.2);
  1256. }
  1257. .show-hide-listening::after{
  1258. content: var(--data-version);
  1259. position: absolute;
  1260. left: 50%;
  1261. bottom: 0;
  1262. transform: translate(-50%, 130%);
  1263. font-size: 15px;
  1264. font-weight: bold;
  1265. }
  1266. .show-hide-listening.older::after{
  1267. text-decoration: line-through;
  1268. }
  1269. .show-hide-listening i {
  1270. position: relative;
  1271. flex-shrink: 0;
  1272. width: 35px;
  1273. height: 35px;
  1274. background-image: url('https://autoduolingo.click/assets/client/eye.svg');
  1275. background-size: cover;
  1276. }
  1277. .show-hide-listening.hide i::after{
  1278. content: '';
  1279. position: absolute;
  1280. top: 50%;
  1281. left: 0;
  1282. width: 110%;
  1283. height: 5px;
  1284. transform: rotate(45deg) translateX(-3px);
  1285. background-color: #c0efff;
  1286. border-radius: 7px;
  1287. }
  1288. .overlay-listening{
  1289. position: fixed;
  1290. inset: 0;
  1291. z-index: 9999
  1292. }
  1293.  
  1294. .switch-wrapper-listening{
  1295. display: flex;
  1296. align-items: center;
  1297. margin-bottom: 8px;
  1298. }
  1299. .switch-wrapper-listening::before{
  1300. content: var(--data-name);
  1301. }
  1302. .switch-wrapper-listening.disable{
  1303. opacity: .4;
  1304. pointer-events: none !important;
  1305. user-select: none !important;
  1306. -ms-user-select: none !important;
  1307. -moz-user-select: none !important;
  1308. -webkit-user-select: none !important;
  1309. }
  1310. .switch-wrapper-listening.unavailable{
  1311. color: #808080;
  1312. }
  1313. .switch-wrapper-listening.unavailable label{
  1314. opacity: .6;
  1315. }
  1316. .switch-container-listening{
  1317. flex-grow: 1;
  1318. display: flex;
  1319. justify-content: space-between;
  1320. align-items: center;
  1321. }
  1322. .switch-info-listening{
  1323. width: 18px;
  1324. height: 18px;
  1325. margin-left: 4px;
  1326. margin-right: 8px;
  1327. border-radius: 50%;
  1328. background-image: url('https://autoduolingo.click/assets/client/infomation-icon.ndx');
  1329. background-size: cover;
  1330. cursor: pointer;
  1331. }
  1332. .switch-info-listening:hover{
  1333. filter: brightness(0.8);
  1334. }
  1335.  
  1336. .switch-wrapper-listening label{
  1337. position: relative;
  1338. width: 46px;
  1339. height: 24px;
  1340. background-color: #bbb;
  1341. box-shadow: rgb(104 149 199 / 50%) 0px 0px 0px 3px;
  1342. border-radius: 20px;
  1343. transition: .2s;
  1344. }
  1345. .switch-wrapper-listening label::after{
  1346. content: '';
  1347. position: absolute;
  1348. left: 2px;
  1349. top: 2px;
  1350. width: 20px;
  1351. height: 20px;
  1352. border-radius: 50%;
  1353. background-color: white;
  1354. transition: .2s;
  1355. }
  1356. .switch-wrapper-listening input:checked + label{
  1357. background-color: #1FC2FF;
  1358. }
  1359. .switch-wrapper-listening input:checked + label::after {
  1360. left: 24px;
  1361. }
  1362. .function-wrapper-listening{
  1363. font-weight: bold;
  1364. font-size: 18px;
  1365. color: #ff4e00;
  1366. }
  1367.  
  1368. .contact-wrapper-listening{
  1369. display: flex;
  1370. justify-content: center;
  1371. flex-wrap: wrap;
  1372. margin: 10px 0 -4px 0;
  1373. }
  1374. .contact-item-listening{
  1375. position: relative;
  1376. width: 34px;
  1377. height: 34px;
  1378. margin: 2px 4px;
  1379. border-radius: 50%;
  1380. background-image: var(--data-img);
  1381. background-size: cover;
  1382. transition: .12s;
  1383. color: rgb(var(--autoduo-color));
  1384. }
  1385. .contact-item-listening:hover{
  1386. box-shadow: rgb(104 149 199 / 50%) 0px 0px 0px 3px;
  1387. transform: scale(1.11);
  1388. }
  1389. .contact-item-listening:hover .popup {
  1390. display: block;
  1391. }
  1392. .contact-item-listening .popup {
  1393. display: none;
  1394. position: absolute;
  1395. bottom: 100%;
  1396. left: 50%;
  1397. transform: translateX(-50%);
  1398. margin-bottom: 12px;
  1399. padding: 2px 6px;
  1400. width: max-content;
  1401. font-size: 12px;
  1402. font-weight: bold;
  1403. border: 1px solid #ccc;
  1404. border-radius: 6px;
  1405. background-color: rgb(var(--autoduo-bg));
  1406. box-shadow: rgba(60, 64, 67, 0.3) 0px 1px 2px 0px, rgba(60, 64, 67, 0.15) 0px 2px 6px 2px;
  1407. animation: contact-popup-eff 0.2s;
  1408. }
  1409. @keyframes contact-popup-eff {
  1410. from {
  1411. opacity: 0;
  1412. bottom: 50%;
  1413. }
  1414. to {
  1415. opacity: 1;
  1416. bottom: 100%;
  1417. }
  1418. }
  1419. @keyframes contact-popup-update-eff {
  1420. 0%, 100% {
  1421. transform: translateY(3px)
  1422. }
  1423. 50% {
  1424. transform: translateY(-3px)
  1425. }
  1426. }
  1427. @keyframes spin-eff {
  1428. 0% {
  1429. transform: scale(var(--scale)) rotate(0deg);
  1430. }
  1431. 100% {
  1432. transform: scale(var(--scale)) rotate(360deg);
  1433. }
  1434. }
  1435. .contact-item-listening .popup::before{
  1436. content: '';
  1437. position: absolute;
  1438. top: calc(100% - 2px);
  1439. left: 50%;
  1440. transform: translateX(-50%);
  1441. border: 10px solid transparent;
  1442. border-top-color: rgb(var(--autoduo-bg));
  1443.  
  1444. }
  1445. .contact-item-listening.has-update {
  1446. transform: scale(1.11) !important;
  1447. box-shadow: rgb(117 117 117 / 50%) 0px 0px 0px 3px;
  1448. }
  1449. .contact-item-listening.has-update::before {
  1450. content: '';
  1451. --scale: 1.05;
  1452. position: absolute;
  1453. inset: 0;
  1454. border-radius: 50%;
  1455. border: 2px solid white;
  1456. border-top-color: transparent;
  1457. border-bottom-color: transparent;
  1458. animation: spin-eff 1.1s linear infinite;
  1459. }
  1460. .contact-item-listening.has-update .popup{
  1461. display: block !important;
  1462. transform: unset;
  1463. right: -70%;
  1464. left: unset;
  1465. animation: contact-popup-update-eff 1.2s infinite;
  1466. }
  1467. .contact-item-listening.has-update .popup::before {
  1468. left: 75%;
  1469. transform: unset;
  1470. }
  1471. .control-container-listening.vip .contact-item-listening:hover{
  1472. box-shadow: rgb(199 138 217 / 50%) 0px 0px 0px 3px;
  1473. }
  1474. .update-guide-popup {
  1475. position: fixed;
  1476. inset: 0;
  1477. z-index: 99999999;
  1478. display: flex;
  1479. justify-content: center;
  1480. align-items: center;
  1481. background-color: rgba(0, 0, 0, 0.4);
  1482. backdrop-filter: blur(4px);
  1483. box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px;
  1484. animation: popup-overlay-eff 0.25s;
  1485. }
  1486. .guide-popup-main {
  1487. display: flex;
  1488. flex-direction: column;
  1489. width: 480px;
  1490. margin: 4px;
  1491. background-color: #009ee9;
  1492. border: 2px solid #fff;
  1493. border-radius: 20px;
  1494. overflow: hidden;
  1495. animation: popup-main-eff 0.25s;
  1496. }
  1497. .guide-popup-title {
  1498. text-align: center;
  1499. padding: 14px 8px 10px 8px;
  1500. margin: 0;
  1501. color: white;
  1502. font-size: 22px;
  1503. }
  1504. .guide-popup-content {
  1505. flex-grow: 1;
  1506. padding: 20px 16px;
  1507. text-align: justify;
  1508. background-color: rgb(var(--autoduo-bg));
  1509. border-top-left-radius: 18px;
  1510. border-top-right-radius: 18px;
  1511. }
  1512. ..guide-popup-text {
  1513. line-height: 24px;
  1514. color: rgb(var(--autoduo-color));
  1515. margin-bottom: 18px;
  1516. }
  1517. .guide-popup-btn {
  1518. display: flex;
  1519. justify-content: space-between;
  1520. margin-top: 26px;
  1521. }
  1522. .guide-popup-btn .autoduo-btn {
  1523. z-index: 1;
  1524. width: calc(50% - 4px);
  1525. }
  1526. @keyframes popup-overlay-eff {
  1527. from {
  1528. opacity: 0;
  1529. }
  1530. to{
  1531. opacity: 1;
  1532. }
  1533. }
  1534. @keyframes popup-main-eff {
  1535. from {
  1536. transform: scale(0.5);
  1537. }
  1538. to{
  1539. transform: scale(1);
  1540. }
  1541. }
  1542. @media (max-height: 550px) {
  1543. .control-container-listening {
  1544. bottom: 4px;
  1545. }
  1546. }
  1547. @media (max-width: 320px) {
  1548. .guide-popup-btn .autoduo-btn {
  1549. width: 100%;
  1550. margin-top: 4px;
  1551. }
  1552. .guide-popup-btn {
  1553. flex-direction: column-reverse;
  1554. }
  1555. }
  1556. `;
  1557. document.head.appendChild(listenStyle);
  1558. const tm = +notAvailable("MjAw");
  1559. window.boom = (cb) => {
  1560. if (Number.isNaN(tm)) return;
  1561. setTimeout(cb, tm);
  1562. };
  1563. },
  1564.  
  1565. setup: function () {
  1566. this.createStyle();
  1567. this.createSignature();
  1568. this.createContact();
  1569. this.createPopup();
  1570. this.createBtn();
  1571. this.createBubbles();
  1572. this.createStatistics();
  1573. this.createFunctions();
  1574. this.createSetting();
  1575. this.createContainer();
  1576. !isShowUI && this.handleShowHideUI();
  1577. isAnimationOff && this.handleAnimationOff();
  1578. this.renderTime();
  1579. getDataSession("isBasicAuto") && this.start();
  1580. this.autoduoCheckUpdate();
  1581. this.rmNotifyVersion ? this.setNotify() : this.fetchNotify();
  1582. },
  1583.  
  1584. version: "1.0.6",
  1585. isAuto: false,
  1586. isAutoRunning: false,
  1587. isSafeMode: !!isSafeMode,
  1588. isAnimationOff: !!isAnimationOff,
  1589. goChallengeTm: 500,
  1590. reloadTm: 1800000,
  1591. startTm: null,
  1592. isShowUI: isShowUI === undefined || isShowUI,
  1593. exp: exp || 0,
  1594. totalTime: time || 0,
  1595. practiceHubPath: "/practice-hub",
  1596. listeningPacticePath: "/practice-hub/listening-practice",
  1597. lessonWrapper: "._3js2_",
  1598. reactProps: null,
  1599. dataStateNode: null,
  1600. nativeTextareaValueSetter: Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set,
  1601. isDarkMode: document.documentElement.getAttribute("data-duo-theme") === "dark",
  1602. notifyVersion: +notifyVersion || 0,
  1603. isNewNotify: isNewNotify,
  1604. rmNotifyVersion: rmNotifyVersion,
  1605. rmNotifyContent: rmNotifyContent,
  1606. };
  1607.  
  1608. function timeFormat(ms) {
  1609. const h = String(parseInt(ms / 1000 / 60 / 60));
  1610. const m = String(parseInt((ms / 1000 / 60) % 60));
  1611. return `${h.padStart(2, "0")}h:${m.padStart(2, "0")}m`;
  1612. }
  1613.  
  1614. function notAvailable(str) {
  1615. try {
  1616. return str
  1617. ? atob(str)
  1618. : window.alert(
  1619. "The current functionality is not available! To use this feature, please update to the full version of Auto-Duolingo!"
  1620. );
  1621. } catch (e) {
  1622. autoDuoLite.start = () => {};
  1623. }
  1624. }
  1625.  
  1626. const $ = document.querySelector.bind(document);
  1627. const $$ = document.querySelectorAll.bind(document);
  1628.  
  1629. const arr = (nodeList) => {
  1630. return Array.from(nodeList);
  1631. };
  1632.  
  1633. function getSession() {
  1634. const dataStorage = sessionStorage.getItem(AUTODUOLINGO_STORAGE) || "{}";
  1635. return JSON.parse(dataStorage);
  1636. }
  1637. function setDataSession(key, value) {
  1638. const dataStorage = getSession();
  1639. if (key instanceof Object) {
  1640. Object.assign(dataStorage, key);
  1641. } else {
  1642. dataStorage[key] = value;
  1643. }
  1644. sessionStorage.setItem(AUTODUOLINGO_STORAGE, JSON.stringify(dataStorage));
  1645. }
  1646. function getDataSession(key) {
  1647. const dataStorage = getSession();
  1648. return dataStorage[key];
  1649. }
  1650. function getLocal() {
  1651. const dataStorage = localStorage.getItem(AUTODUOLINGO_STORAGE) || "{}";
  1652. try {
  1653. return JSON.parse(dataStorage);
  1654. } catch (e) {
  1655. return {};
  1656. }
  1657. }
  1658. function setDataLocal(key, value) {
  1659. const dataStorage = getLocal();
  1660. dataStorage[key] = value;
  1661. localStorage.setItem(AUTODUOLINGO_STORAGE, JSON.stringify(dataStorage));
  1662. }
  1663.  
  1664. // SETUP AUTO
  1665. autoDuoLite.setup();
  1666. })();

QingJ © 2025

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