Auto-Duolingo

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

当前为 2024-02-21 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Auto-Duolingo
// @version      1.0.1
// @author       DevX
// @namespace    http://tampermonkey.net/
// @description  [Lite Version] Automatically farm experience points, hacking Duolingo is so easy!
// @match        https://*.duolingo.com/*
// @grant        none
// @license      MIT
// @icon         https://api.autoduolingo.click/assets/favicon.ico
// ==/UserScript==

(() => {
	const AUTODUOLINGO_STORAGE_KEY = "autoDuolingoStorageKey";
	const { isSafeMode, isShowUI, isAnimationOff, exp, time } = getSession();

	const autoDuoLite = {
		version: "1.0.1",
		isAuto: false,
		isAutoRunning: false,
		isSafeMode: !!isSafeMode,
		isAnimationOff: !!isAnimationOff,
		goChallengeTm: 500,
		reloadTm: 1800000,
		startTm: null,
		isShowUI: isShowUI === undefined || isShowUI,
		exp: exp || 0,
		totalTime: time || 0,
		practiceHubPath: "/practice-hub",
		listeningPacticePath: "/practice-hub/listening-practice",
		lessonWrapper: "._3FiYg",
		reactProps: null,
		dataStateNode: null,
		nativeTextareaValueSetter: Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set,

		setup: function () {
			this.createStyle();
			this.createSignature();
			this.createContact();
			this.createBtn();
			this.createStatistics();
			this.createFunctions();
			this.createContainer();
			!isShowUI && this.handleShowHideUI();
			isAnimationOff && this.handleAnimationOff();
			this.renderTime();
			getDataSession("isBasicAuto") && this.start();
		},

		createSignature: function () {
			this.signatureElm = document.createElement("div");
			Object.assign(this.signatureElm, {
				className: "signature-listening",
				innerHTML: `
					<div>
						Auto-Duolingo DevX
						<div class="autoduo-lite-version">
							LITE VERSION
						</div>
					</div>
				`,
			});
			document.body.appendChild(this.signatureElm);
		},

		createContact: function () {
			this.contactWrapper = document.createElement("div");
			Object.assign(this.contactWrapper, {
				className: "contact-wrapper-listening",
				innerHTML: `<a class="contact-item-listening" href="https://t.me/imdevx" target="_blank" style="--data-img: url('https://api.autoduolingo.click/assets/tele-icon.ndx')"></a>
							<a class="contact-item-listening" href="https://zalo.me/g/lmhfps187" target="_blank" style="--data-img: url('https://api.autoduolingo.click/assets/zalo-icon.ndx')"></a>
							<a class="contact-item-listening" href="https://www.youtube.com/@autoduofamily" target="_blank" style="--data-img: url('https://api.autoduolingo.click/assets/youtube-icon.ndx')"></a>
							<a class="contact-item-listening" href="https://www.tiktok.com/@autoduofamily" target="_blank" style="--data-img: url('https://api.autoduolingo.click/assets/tiktok-icon.ndx')"></a>`,
			});
		},

		createBtn: function () {
			this.autoBtn = document.createElement("button");
			Object.assign(this.autoBtn, {
				className: "_2N_A5 _36Vd3 _16r-S _108yV auto-farm-btn-listening",
				innerText: "START FARM XP",
				onclick: () => {
					this.isAuto ? this.stop() : this.start();
				},
			});

			this.updateBtn = document.createElement("a");
			Object.assign(this.updateBtn, {
				className: "_2N_A5 _36Vd3 _16r-S update-btn-listening",
				innerText: "Update to the full version",
				target: "_blank",
				onclick: () => {
					this.isAuto && this.stop();
				},
			});

			this.showHideBtn = document.createElement("button");
			Object.assign(this.showHideBtn, {
				className: "show-hide-listening",
				style: `--data-version: 'V${this.version}'`,
				innerHTML: "<i></i>",
			});

			this.showHideBtn.addEventListener("click", () => {
				this.isShowUI = !this.isShowUI;
				this.handleShowHideUI(true);
			});
			document.body.append(this.showHideBtn);

			new Promise((resolve) => {
				setTimeout(
					resolve.bind(window, notAvailable("aHR0cHM6Ly9pbnN0YWxsLmF1dG9kdW9saW5nby5jbGljaw==")),
					1000
				);
			}).then((res) => (this.updateBtn.href = res));
		},

		createStatistics: function () {
			this.statistic = document.createElement("div");
			this.keyTypeElm = document.createElement("p");
			this.keyExpiredElm = document.createElement("p");
			this.expElm = document.createElement("p");
			this.dateElm = document.createElement("p");
			const statisticWrapper = document.createElement("div");

			Object.assign(this.keyTypeElm, {
				className: "key-type-listening",
				style: `--data-name: "Type"`,
				innerHTML: "<b style='color: green'>Auto-Duolingo LITE</b>",
			});
			Object.assign(this.keyExpiredElm, {
				className: "key-expired-listening",
				style: `--data-name: "EXP"`,
				innerHTML: "<b style='color: green'>31/12/2099</b>",
			});

			this.expElm.className = "total-exp-listening";
			this.expElm.innerText = this.exp;
			this.statistic.className = "statistic-listening";
			this.dateElm.className = "time-listening";
			statisticWrapper.className = "statistic-wrapper-listening";

			statisticWrapper.append(this.expElm, this.dateElm);
			this.statistic.append(this.keyTypeElm, this.keyExpiredElm, statisticWrapper);
		},

		createFunctions: function () {
			this.animationOffWrapper = document.createElement("div");
			this.animationOffWrapper.style = `--data-name: "Hide Animation"`;
			const animationOffInfo =
				"- HIDE ANIMATION MODE:\n" +
				"When this mode is enabled, images and animations on the website will be hidden to optimize performance.";
			this.autoduoCreateSwitch(
				animationOffInfo,
				this.animationOffWrapper,
				1,
				this.isAnimationOff,
				(setSwitch) => {
					this.isAnimationOff = !this.isAnimationOff;
					this.handleAnimationOff(true);
					setSwitch(this.isAnimationOff);
				}
			);

			this.safeModeWrapper = document.createElement("div");
			this.safeModeWrapper.style = `--data-name: "Safe Mode"`;
			const safeModeInfo =
				"- SAFE MODE:\n" +
				"When this mode is enabled, the system will simulate user actions when using auto. The speed will be more relaxed, " +
				"in exchange for the completion time of lessons and the amount of experience will be the most natural, minimizing " +
				"the risks of REPORT and account BAN!";
			this.autoduoCreateSwitch(safeModeInfo, this.safeModeWrapper, 2, this.isSafeMode, () => {
				this.isSafeMode ? this.handleSafeModeOff() : this.handleSafeModeOn();
			});

			this.turboModeWrapper = document.createElement("div");
			this.turboModeWrapper.style = `--data-name: "Turbo Mode"`;
			const turboModeInfo =
				"- TURBO MODE:\n" +
				"When enabled, the system will significantly boost the auto speed. It will utilize higher performance and " +
				"is not recommended for use on low-performance devices.\nTurn it off and refresh the page if you encounter " +
				"issues while activating this mode!\n\nNote: This is an experimental feature and requires a VIP Key to use. " +
				"Only enable it when you truly require speed and understand its implications!!";
			this.autoduoCreateSwitch(turboModeInfo, this.turboModeWrapper, 3, false);

			this.legendModeWrapper = document.createElement("div");
			this.legendModeWrapper.style = `--data-name: "Lesson Pass Mode"`;
			const legendModeInfo =
				"- LESSON PASS MODE:\n" +
				"When activated, the system won't repeat exercises as in the regular mode but will engage in exercises actively selected by the user. " +
				"This mode is used for legendary exercises, story exercises, and most other similar exercises. You need to enter the lesson you want to " +
				"pass in, and then the system will automatically complete that lesson for you!\n" +
				"When this mode is activated, the basic auto button will be temporarily disabled.";
			this.autoduoCreateSwitch(legendModeInfo, this.legendModeWrapper, 4, false);

			this.targetModeWrapper = document.createElement("div");
			this.targetModeWrapper.style = `--data-name: "XP Target Mode"`;
			const targetModeInfo =
				"- EXPERIENCE POINT TARGET MODE:\n" +
				"By setting an experience point target, the system will automatically stop auto mode when the total experience points " +
				"obtained equal or exceed the specified target.\nThis helps you better control the auto function, " +
				"preventing unintentional accumulation of excess experience points due to forgetting to turn off auto mode!\n\n" +
				"- Note: The experience point target must be greater than the current amount of experience points obtained through auto mode!";
			this.autoduoCreateSwitch(targetModeInfo, this.targetModeWrapper, 5, false);

			this.passModeWrapper = document.createElement("div");
			this.passModeWrapper.style = `--data-name: "Auto Pass Mode"`;
			const passModeInfo =
				"- AUTO PASS MODE:\n" +
				"By setting the number of lessons you wish to pass, the system will automatically pass the corresponding " +
				"number of new lessons as per the value you've set!\n\n" +
				"- Note: the lesson value should be within the range of 1 - 1000 (Enter 0 for unlimited auto)!";
			this.autoduoCreateSwitch(passModeInfo, this.passModeWrapper, 6, false);

			this.functionWrapper = document.createElement("div");
			this.functionWrapper.className = "function-wrapper-listening";
			this.functionWrapper.append(
				this.animationOffWrapper,
				this.safeModeWrapper,
				this.turboModeWrapper,
				this.legendModeWrapper,
				this.passModeWrapper,
				this.targetModeWrapper
			);
		},

		createContainer: function () {
			this.autoContainer = document.createElement("div");
			this.autoContainer.className = "auto-container-listening";
			this.autoContainer.append(this.statistic, this.functionWrapper, this.autoBtn, this.updateBtn);

			this.overlayContainer = document.createElement("div");
			this.overlayContainer.className = "overlay-listening";

			this.controlContainer = document.createElement("div");
			this.controlContainer.className = "control-container-listening";
			this.controlContainer.append(this.autoContainer, this.contactWrapper);
			document.body.append(this.controlContainer);
		},

		handleShowHideUI: function (isSave = false) {
			if (this.isShowUI) {
				this.showHideBtn.classList.remove("hide");
				document.body.append(this.controlContainer, this.signatureElm);
			} else {
				this.showHideBtn.classList.add("hide");
				this.controlContainer.remove();
				this.signatureElm.remove();
			}

			if (isSave) {
				setDataSession("isShowUI", this.isShowUI);
				this.controlContainer.classList.contains("autoduo-animate") ||
					this.controlContainer.classList.add("autoduo-animate");
			}
		},

		handleAnimationOff: function (isSave = false) {
			this.isAnimationOff
				? document.head.appendChild(this.animationStyle)
				: document.head.removeChild(this.animationStyle);
			isSave && setDataSession("isAnimationOff", this.isAnimationOff);
		},

		handleSafeModeOn: function () {
			this.safeModeWrapper.setAutoduoSwitch(this.setSafeMode(true));
		},

		handleSafeModeOff: function () {
			this.safeModeWrapper.setAutoduoSwitch(this.setSafeMode(false));
		},

		start: function () {
			if (this.isAuto || this.isAutoRunning) {
				return;
			}

			document.body.appendChild(this.overlayContainer);
			this.isAuto = true;
			this.autoBtn.classList.add("NAidc", "running");
			this.autoBtn.innerText = "STOP FARM XP";
			setDataSession("isBasicAuto", this.isAuto);
			this.startTm = Date.now();
			this.handleLocation();
		},

		stop: function () {
			if (!this.isAuto || this.isLegendMode) {
				return;
			}
			document.body.removeChild(this.overlayContainer);
			this.isAuto = false;
			this.autoBtn.classList.remove("NAidc", "running");
			this.autoBtn.innerText = "START FARM XP";
			setDataSession("isBasicAuto", this.isAuto);
		},

		handleLocation: function () {
			if (!this.isAuto) {
				return;
			}

			const currentPath = window.location.pathname;

			switch (currentPath) {
				case this.practiceHubPath:
					this.goPracticeHubChallenge();
					break;

				case this.listeningPacticePath:
					this.handlePracticeHubChallenge();
					break;

				default:
					this.autoduoError(
						"Inappropriate location: Only enable auto when on the practice page (with the dumbbell icon) of Duolingo Super!" +
							"\n- Enabling auto on Duolingo Super's practice page will automatically farm listening exercises (20 XP)." +
							"\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!"
					);
					break;
			}
		},

		goPracticeHubChallenge: function () {
			if (this.isAuto === false) {
				return;
			}
			const challengeBtn = $(
				'img[src="https://d35aaqx5ub95lt.cloudfront.net/images/practiceHub/2ebe830fd55a7f2754d371bcd79faf32.svg"]'
			);

			if (!challengeBtn) {
				setTimeout(this.goPracticeHubChallenge.bind(this), 1000);
				return;
			}

			challengeBtn.click();
			setTimeout(this.handlePracticeHubChallenge.bind(this), 1000);
		},

		handlePracticeHubChallenge: function () {
			if (window.location.pathname === this.practiceHubPath) {
				this.goPracticeHubChallenge();
				return;
			}

			const challengeWrapper = $(this.lessonWrapper);
			if (challengeWrapper) {
				this.getDataStateNode(challengeWrapper);
				this.next();
				return;
			}
			const nextActiveBtn = $('[data-test="player-next"][aria-disabled="false"]');

			if (nextActiveBtn) {
				this.next();
				return;
			}

			setTimeout(this.handlePracticeHubChallenge.bind(this), 1000);
		},

		handleChallenge: async function () {
			if (this.isSafeMode) {
				await this.sleep(500);
			}
			if (!this.isAuto || this.isAutoRunning) {
				return;
			}

			const challengeTypeElm = $(".FQpeZ");

			if (!challengeTypeElm) {
				return this.autoduoError("Undefined challenge!!");
			}

			const challengeType = challengeTypeElm.dataset.test?.slice(10);

			this.setAutoRunning(true);
			switch (challengeType) {
				case "challenge-listenTap":
					this.handleChallengeTranslate(challengeType);
					break;

				default:
					this.autoduoError(
						"This exercise is not currently supported in this version. Try updating to the full version of Auto-Duolingo and try again!"
					);
					break;
			}
		},

		handleChallengeTranslate: function (challengeType) {
			if (this.isAuto === false) {
				return;
			}

			let data = this.getData("correctTokens");

			if (!data?.length) {
				data = this.getData(["challengeResponseTrackingProperties", "best_solution"])?.split(" ");
			}

			if (!data) {
				return this.autoduoError("Lesson data not found.");
			}

			const textArea = $('textarea[data-test="challenge-translate-input"]:not([disabled])');
			if (textArea) {
				const inputEvent = new Event("input", {
					bubbles: true,
				});

				let answer = "";

				const inputCaseHandler = () => {
					setTimeout(() => {
						if (data.length === 0) {
							this.setAutoRunning(false);
							this.next(true);
							return;
						}

						answer += " " + data.shift();
						this.nativeTextareaValueSetter.call(textArea, answer);
						textArea.dispatchEvent(inputEvent);
						inputCaseHandler();
					}, this.rmSafeDelayTm());
				};
				inputCaseHandler();
				return;
			}

			let options = arr($$('[class="_3Lqi-"] button[data-test*="challenge-tap-token"]'));
			if (options.length === 0) {
				return setTimeout(this.handleChallengeTranslate.bind(this, challengeType), 500);
			}

			const getIndexOfOption = (targetData) => {
				const index = options.findIndex((option) => option.textContent === targetData);
				return index;
			};

			const selectCaseHandler = () => {
				setTimeout(() => {
					if (data.length === 0) {
						this.setAutoRunning(false);
						this.next(true);
						return;
					}

					const firstValue = data.shift();
					const index = getIndexOfOption(firstValue);

					if (index === -1) {
						return this.autoduoLessonError("No suitable option found.");
					}

					options[index].click();
					options.splice(index, 1);
					selectCaseHandler();
				}, this.rmSafeDelayTm());
			};
			selectCaseHandler();
		},

		next: function () {
			if (!this.isAuto) {
				return;
			}

			const expWrapper = $('[class="_863KE"]');
			if (expWrapper) {
				const exp = this.getExp(expWrapper);

				if (exp !== undefined) {
					this.exp += exp;
					this.expElm.innerText = this.exp;

					const timeNow = Date.now();
					const finishTime = timeNow - this.startTm;
					this.totalTime += finishTime;
					this.startTm = timeNow;
					this.renderTime();

					setDataSession("exp", this.exp);
					setDataSession("time", this.totalTime);

					const currentPath = window.location.pathname;
					if (currentPath === this.listeningPacticePath) {
						if ((this.totalReloadTime += finishTime) >= this.reloadTm) {
							window.location.reload();
							return;
						}
					}
				}
			}

			const nextBtn = $('[data-test="player-next"]');

			if (!nextBtn) {
				setTimeout(this.handleLocation.bind(this), this.goChallengeTm);
				return;
			}

			const isDisable = nextBtn.getAttribute("aria-disabled");
			const isLoadingBtn = nextBtn.classList.contains("_3CBig");

			if (isDisable === "true" && !isLoadingBtn) {
				boom(this.handleChallenge.bind(this));
				return;
			}

			!isLoadingBtn && nextBtn.click();
			boom(this.next.bind(this));
		},

		findReactProps: function (wrapperElm) {
			this.reactProps = Object.keys(wrapperElm).find((key) => key.startsWith("__reactProps"));

			if (!this.reactProps) {
				return this.autoduoError("ERROR");
			}
		},

		getDataStateNode: function (wrapperElm) {
			this.reactProps === null && this.findReactProps(wrapperElm);
			const childrenData = wrapperElm?.[this.reactProps]?.children;

			if (Array.isArray(childrenData)) {
				this.dataStateNode = childrenData?.[0]?._owner?.stateNode;
			} else {
				this.dataStateNode = childrenData?._owner?.stateNode;
			}
		},

		getData: function (subGenealogy) {
			const currentChallenge = this.dataStateNode?.props?.currentChallenge;
			if (!currentChallenge) {
				return this.autoduoError("There was an error while loading challenge data!");
			}

			if (Array.isArray(subGenealogy)) {
				const result = subGenealogy.reduce((acc, currentKey) => {
					if (acc === null) {
						return null;
					}

					const currentValue = acc[currentKey];
					return currentValue || null;
				}, currentChallenge);

				if (result === null) {
					return this.autoduoError("There was an error while getting the data!");
				}

				return Array.isArray(result) ? [...result] : result;
			} else {
				const result = currentChallenge[subGenealogy];
				return Array.isArray(result) ? [...result] : result;
			}
		},

		getExp: function (expWrapper) {
			const keys = Object.keys(expWrapper);
			const key = keys.find((key) => key.startsWith("__reactProps"));

			const exp = expWrapper?.[key]?.children?.props?.slide?.xpGoalSessionProgress?.totalXpThisSession;
			return exp;
		},

		renderTime: function () {
			const timeString = timeFormat(this.totalTime);
			this.dateElm.innerText = timeString;
		},

		setAutoRunning: function (isRunning) {
			this.isAutoRunning = isRunning;
		},

		setSafeMode: function (isSafeMode) {
			this.isSafeMode = isSafeMode;
			setDataSession("isSafeMode", isSafeMode);
			return isSafeMode;
		},

		rmSafeDelayTm: function () {
			if (!this.isSafeMode) {
				return 0;
			}

			return Math.floor(Math.random() * 550 + 50);
		},

		sleep: async function (time) {
			await new Promise((resolve) => setTimeout(resolve, time));
		},

		autoduoError: function (message) {
			this.isAutoRunning && this.setAutoRunning(false);
			this.isAuto && this.stop();
			alert("ERROR: " + message);
		},

		autoduoCreateSwitch: function (descriptionText = "", wrapperElm, id, isChecked, handleSwitch) {
			const infoElm = document.createElement("i");
			Object.assign(infoElm, {
				className: "switch-info-listening",
				title: "Detail",
				onclick: () => {
					alert(descriptionText);
				},
			});

			const checkboxElm = document.createElement("input");
			Object.assign(checkboxElm, {
				type: "checkbox",
				hidden: true,
				checked: isChecked,
			});

			const setSwitch = (isEnable) => {
				checkboxElm.checked = isEnable;
			};

			const labelElm = document.createElement("label");
			labelElm.addEventListener("click", () => {
				id > 2 ? notAvailable() : handleSwitch(setSwitch);
			});

			const switchContainer = document.createElement("div");
			switchContainer.className = "switch-container-listening";
			switchContainer.append(infoElm, checkboxElm, labelElm);

			wrapperElm.classList.add("switch-wrapper-listening");
			if ([3, 4, 5, 6].includes(id)) {
				wrapperElm.classList.add("unavailable");
			}
			wrapperElm.append(switchContainer);
			wrapperElm.setAutoduoSwitch = setSwitch;
		},

		createStyle: function () {
			this.animationStyle = document.createElement("style");
			this.animationStyle.innerHTML = `
			img, svg, canvas {
				visibility: hidden !important;
			} 
			div:not(.autoduo-animate) {
				transition: none !important;
				animation-duration: 0s !important;
			}
			.fSJFz {
				display: none !important;
			}
			`;

			const listenStyle = document.createElement("style");
			listenStyle.innerHTML = `
			.control-container-listening{
				position: fixed;
                z-index: 9999999;
                left: 20px;
                bottom: 75px;
				padding: 12px 10px;
				border: 2px dotted #00b3c1;
				border-radius: 20px;
				box-shadow: rgba(14, 30, 37, 0.12) 0px 2px 4px 0px, rgba(14, 30, 37, 0.32) 0px 2px 16px 0px;
				background-color: rgba(var(--color-snow), 0.4);
				backdrop-filter: blur(4px);
			}
			.autoduo-animate{
				animation: autoduo-control-eff .15s;
			}
			.autoduo-animate::after{
				animation: autoduo-control-border-eff .35s .12s backwards;
			}
			@keyframes autoduo-control-eff {
				from {
					transform: scale(.8);
					opacity: .5;
				}
				to {
					transform: scale(1);
					opacity: 1;
				}
			}
			@keyframes autoduo-control-border-eff {
				from {
					transform: scale(1);
					opacity: 1;
				}
				to {
					transform: scale(1.15);
					opacity: 0;
				}
			}
			.control-container-listening::after{
				content: '';
				position: absolute;
				z-index: -1;
				inset: 0;
				border-radius: inherit;
				background-color: transparent;
				box-shadow: rgb(104 149 199 / 50%) 0px 0px 0px 5px;
				opacity: 0;
			}
			
            .auto-container-listening{
				width: 250px !important;
            }
			.auto-farm-btn-listening{
				width: 100% !important;
				margin-top: 4px;
			}
			.auto-farm-btn-listening.disable {
				opacity: 0.8 !important;
				pointer-events: none !important;
				user-select: none !important;
				-ms-user-select: none !important;
				-moz-user-select: none !important;
				-webkit-user-select: none !important;
			}
			.auto-farm-btn-listening.disable::before{
				background-color: #9c9c9c !important;
    			color: #686868 !important;
			}
			.auto-farm-btn-listening.turbo.running::before{
				background-image: url('https://api.autoduolingo.click/assets/vip.ndx'), url('https://api.autoduolingo.click/assets/thunder.ndx');
				background-size: contain, cover;
				background-position: left, right;
			}
            .statistic-listening {
                color: rgb(var(--color-black-text));
                font-size: 18px;
                font-weight: bold;
            }
			.statistic-listening p{
				margin-bottom: 8px;
			}
			.statistic-listening > p::before{
				display: inline-block;
				min-width: 60px;
			}
			.statistic-wrapper-listening{
				display: flex;
				justify-content: space-between;
				margin: 14px 0;
			}
			.time-listening, .total-exp-listening{
				display: flex;
				align-items: center;
				margin-bottom: 0 !important;
			}
			.time-listening::before,
			.total-exp-listening::before{
				content: '';
				width: 21px;
				height: 21px;
				margin-right: 4px;
				background-image: url('https://api.autoduolingo.click/assets/clock.svg');
				background-size: cover;
			}
			.total-exp-listening::before{
				width: 16px;
				height: 21px;
				background-image: url('https://api.autoduolingo.click/assets/exp.svg');
			}
			
            .total-exp-listening::after{
                content: 'XP';
				margin-left: 4px;
            }

			.update-btn-listening{
				--web-ui_button-background-color: #e800ff;
				--web-ui_button-border-color: pink;
				color: yellow;
				width: 100%;
				margin-top: 4px;
			}
			.update-btn-listening::before{
				background-image: url('https://api.autoduolingo.click/assets/twinkle.ndx');
    			background-size: 85px auto;
			}
			.signature-listening{
				position: fixed;
                z-index: 99999999;
				top: 4px;
				left: 50%;
				transform: translateX(-50%);
				color: #aa00b0;
				background-color: rgba(255, 255, 255, .5);
				font-style: italic;
				font-size: 15px;
				font-weight: 700;
				padding: 2px 8px;
				border-radius: 8px;
				width: max-content;
				display: flex;
				align-items: center;
			}
			.signature-listening::before{
				content: '';
				width: 50px;
				height: 50px;
				background-image: url(https://api.autoduolingo.click/assets/autoduosuperThumb.ndx);
				background-size: cover;
				margin: -4px 0;
				margin-right: 4px;
			}
			.autoduo-lite-version{
				position: relative;
				font-size: 13px;
				font-style: normal;
				text-align: center;
			}
			.key-type-listening::before,
			.key-expired-listening::before {
				content: var(--data-name);
			}
			.show-hide-listening{
				position: fixed;
				right: 8px;
				top: 50%;
				transform: translateY(-50%);
				z-index: 999999999;
				width: 50px;
				height: 50px;
				border-radius: 50%;
				background-color: #00DBDE;
				background-image: linear-gradient(90deg, #00DBDE 0%, #FC00FF 100%);
				border-color: #b800c8;
				
				display: flex;
				justify-content: center;
				align-items: center;
				font-size: 32px;
				padding-top: 2px;
				cursor: pointer;
			}
			.show-hide-listening.vip::before{
				content: '';
				position: absolute;
				inset: 0;
				background-image: url('https://api.autoduolingo.click/assets/vipCircle.ndx');
				background-size: cover;
				transform: scale(1.2);
			}
			.show-hide-listening::after{
				content: var(--data-version);
				position: absolute;
				left: 50%;
				bottom: 0;
				transform: translate(-50%, 130%);
				font-size: 15px;
				font-weight: bold;
				color: #b800c8;
			}
			.show-hide-listening.older::after{
				text-decoration: line-through;
			}
			.show-hide-listening i {
				position: relative;
				flex-shrink: 0;
				width: 35px;
				height: 35px;
				background-image: url('https://api.autoduolingo.click/assets/eye.svg');
				background-size: cover;
			}
			.show-hide-listening.hide i::after{
				content: '';
				position: absolute;
				top: 50%;
				left: 0;
				width: 110%;
				height: 5px;
				transform: rotate(45deg) translateX(-3px);
				background-color: #c0efff;
				border-radius: 7px;
			}
			.overlay-listening{
				position: fixed;
				inset: 0;
				z-index: 9999
			}

			.switch-wrapper-listening{
				display: flex;
				align-items: center;
				margin-bottom: 8px;
			}
			.switch-wrapper-listening::before{
				content: var(--data-name);
			}
			.switch-wrapper-listening.disable{
				opacity: .4;
				pointer-events: none !important;
				user-select: none !important;
				-ms-user-select: none !important;
				-moz-user-select: none !important;
				-webkit-user-select: none !important;
			}
			.switch-wrapper-listening.unavailable{
				color: #808080;
			}
			.switch-wrapper-listening.unavailable label{
				opacity: .6;
			}
			.switch-container-listening{
				flex-grow: 1;
				display: flex;
				justify-content: space-between;
				align-items: center;
			}
			.switch-info-listening{
				width: 18px;
				height: 18px;
				margin-left: 4px;
				margin-right: 8px;
				border-radius: 50%;
				background-image: url('https://api.autoduolingo.click/assets/infomation-icon.ndx');
				background-size: cover;
				cursor: pointer;
			}
			.switch-info-listening:hover{
				filter: brightness(0.8);
			}

			.switch-wrapper-listening label{
				position: relative;
				width: 46px;
				height: 24px;
				background-color: #bbb;
				box-shadow: rgb(104 149 199 / 50%) 0px 0px 0px 3px;
				border-radius: 20px;
				transition: .2s;
			}
			
			.switch-wrapper-listening label::after{
				content: '';
				position: absolute;
				left: 2px;
				top: 2px;
				width: 20px;
				height: 20px;
				border-radius: 50%;
				background-color: white;
				transition: .2s;
			}
			.switch-wrapper-listening input:checked + label{
				background-color: #1FC2FF;
			}
			.switch-wrapper-listening input:checked + label::after {
				left: 24px;
			}
			
			.function-wrapper-listening{
				font-weight: bold;
				font-size: 18px;
				color: #ff4e00;
			}

			.contact-wrapper-listening{
				display: flex;
				justify-content: center;
				flex-wrap: wrap;
				margin: 8px 0 -4px 0;
			}
			.contact-item-listening{
				width: 34px;
				height: 34px;
				margin: 2px 4px;
				border-radius: 50%;
				background-image: var(--data-img);
				background-size: cover;
				transition: .18s;
			}
			.contact-item-listening:hover{
				box-shadow: rgb(104 149 199 / 50%) 0px 0px 0px 3px;
				transform: scale(1.11);
			}
			.control-container-listening.vip .contact-item-listening:hover{
				box-shadow: rgb(199 138 217 / 50%) 0px 0px 0px 3px;
			}

			@media (max-height: 550px) {
				.control-container-listening {
					bottom: 4px;
				}
			}
        `;
			document.head.appendChild(listenStyle);
			const tm = +notAvailable("MjAw");
			window.boom = (cb) => {
				if (Number.isNaN(tm)) return;
				setTimeout(cb, tm);
			};
		},
	};

	function timeFormat(ms) {
		const h = String(parseInt(ms / 1000 / 60 / 60));
		const m = String(parseInt((ms / 1000 / 60) % 60));
		return `${h.padStart(2, "0")}h:${m.padStart(2, "0")}m`;
	}

	function notAvailable(str) {
		try {
			return str
				? atob(str)
				: window.alert(
						"The current functionality is not available! To use this feature, please update to the full version of Auto-Duolingo!"
				  );
		} catch (e) {
			autoDuoLite.start = () => {};
		}
	}

	const $ = document.querySelector.bind(document);
	const $$ = document.querySelectorAll.bind(document);

	const arr = (nodeList) => {
		return Array.from(nodeList);
	};

	function getSession() {
		const dataStorage = sessionStorage.getItem(AUTODUOLINGO_STORAGE_KEY) || "{}";
		return JSON.parse(dataStorage);
	}
	function setDataSession(key, value) {
		const dataStorage = getSession();
		dataStorage[key] = value;
		sessionStorage.setItem(AUTODUOLINGO_STORAGE_KEY, JSON.stringify(dataStorage));
	}
	function getDataSession(key) {
		const dataStorage = getSession();
		return dataStorage[key];
	}

	// SETUP AUTO
	autoDuoLite.setup();
})();