Hệ thống tự động giải bài Duolingo chạy ngầm hoàn toàn, đã lược bỏ UI.
// ==UserScript==
// @name DuoSolver - Core Auto (No UI)
// @namespace Violentmonkey Scripts
// @match https://*.duolingo.com/*
// @grant GM_getValue
// @grant GM_setValue
// @version 1.1.3-Core
// @author DuoSolverGrinder (Cleaned by Gemini)
// @description Hệ thống tự động giải bài Duolingo chạy ngầm hoàn toàn, đã lược bỏ UI.
// @license MIT2
// ==/UserScript==
// Tốc độ giải (thời gian chờ giữa các hành động - tính bằng mili-giây)
// speedSlow: 2000, speedMedium: 1000, speedFast: 500, speedFastest: 0
const SOLVE_DELAY = 1000;
let solveTimerId;
let tokens_clicked = [];
const mainLessonFormClass = "[id='root'] > div > div > div > div > div:first-child._3v4ux";
// Vòng lặp kiểm tra trang trạng thái để kích hoạt auto
setInterval(() => {
if (window.location.pathname === '/lesson' || window.location.pathname === '/practice') {
const checkBttn = document.querySelectorAll('[data-test="player-next"]')[0];
if (checkBttn && !solveTimerId) {
// Kích hoạt chu kỳ tự động giải nếu tìm thấy nút Next/Check
resetTimerAutoMode();
}
}
}, 1000);
function solveOne() {
const practiceAgain = document.querySelector('[data-test="player-practice-again"]');
if (practiceAgain !== null) {
practiceAgain.click();
solveTimerId = null;
return;
}
if (document.querySelector('[data-test="session-complete-slide"]') && !practiceAgain && window.location.pathname === '/practice') {
window.location.assign('/practice');
solveTimerId = null;
return;
}
let subType = "";
try {
window.sol = findReact(document.querySelectorAll(mainLessonFormClass)[0]).props.currentChallenge;
subType = window.sol.challengeGeneratorIdentifier.specificType;
} catch {
let next = document.querySelector('[data-test="player-next"]');
if (next) {
next.click();
}
resetTimerAutoMode();
return;
}
if (!window.sol) {
resetTimerAutoMode();
return;
}
let nextButton = document.querySelector('[data-test="player-next"]');
if (!nextButton) {
resetTimerAutoMode();
return;
}
// --- CORE LOGIC: XỬ LÝ CÁC DẠNG BÀI TẬP ---
switch(window.sol.type) {
case "listenMatch":
case "listenIsolation":
case "listenTap":
case "speak":
const buttonSkip = document.querySelector('button[data-test="player-skip"]');
if (buttonSkip) {
buttonSkip.click();
}
break;
case "translate":
switch(subType) {
case "reverse_translate":
const elm = document.querySelector('textarea[data-test="challenge-translate-input"]');
if (elm) {
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set;
nativeInputValueSetter.call(elm, window.sol.correctSolutions ? window.sol.correctSolutions[0] : window.sol.prompt);
let inputEvent = new Event('input', { bubbles: true });
elm.dispatchEvent(inputEvent);
}
break;
case "tap":
case "reverse_tap":
let tokens_to_click = translateTapReverseTapSolve();
tokens_to_click.forEach((clicked_token) => {
clicked_token.click();
});
break;
default:
null;
}
break;
case "assist":
case "gapFill":
document.querySelectorAll('[data-test="challenge-choice"]')[window.sol.correctIndex]?.click();
break;
case "name":
let textInput = document.querySelector('[data-test="challenge-text-input"]');
if (textInput && window.sol.correctSolutions) {
let nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
nativeInputValueSetter.call(textInput, window.sol.correctSolutions[0]);
let inputEvent = new Event('input', { bubbles: true });
textInput.dispatchEvent(inputEvent);
}
break;
case "partialReverseTranslate":
let partialElm = document.querySelector('[data-test*="challenge-partialReverseTranslate"]')?.querySelector("span[contenteditable]");
if (partialElm) {
let nativeInputNodeTextSetter = Object.getOwnPropertyDescriptor(Node.prototype, "textContent").set;
nativeInputNodeTextSetter.call(partialElm, window.sol?.displayTokens?.filter(t => t.isBlank)?.map(t => t.text)?.join()?.replaceAll(',', '') );
let inputEvent = new Event('input', { bubbles: true });
partialElm.dispatchEvent(inputEvent);
}
break;
default:
null;
}
// Bấm Check/Next sau khi chọn đáp án
nextButton.click();
resetTimerAutoMode();
}
function translateTapReverseTapSolve() {
const all_tokens = document.querySelectorAll('[data-test$="challenge-tap-token"]');
const correct_tokens = window.sol.correctTokens;
const tokens_to_click = [];
correct_tokens.forEach(correct_token => {
const matching_elements = Array.from(all_tokens).filter(element => element.textContent.trim() === correct_token.trim());
if (matching_elements.length > 0) {
const match_index = tokens_to_click.filter(token => token.textContent.trim() === correct_token.trim()).length;
if (match_index < matching_elements.length) {
tokens_to_click.push(matching_elements[match_index]);
} else {
tokens_to_click.push(matching_elements[0]);
}
}
});
return tokens_to_click;
}
function resetTimerAutoMode() {
clearTimeout(solveTimerId);
solveTimerId = setTimeout(solveOne, SOLVE_DELAY);
}
function findReact(dom) {
if (!dom || !dom.parentElement) return null;
let reactProps = Object.keys(dom.parentElement).find((key) => key.startsWith("__reactProps$"));
let child = dom?.parentElement?.[reactProps]?.children;
return child?.props?.children?._owner?.stateNode ?? child?._owner?.stateNode;
}