Created
April 28, 2023 21:19
-
-
Save quacksire/35b591b10efa6e316cfe1d6503d8ab2d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name DuoPower | |
// @namespace Violentmonkey Scripts | |
// @match https://*.duolingo.com/* | |
// @grant none | |
// @version 2.3 | |
// @author Mikail Deniz Cayoglu | |
// @description Duolingo Auto-Solver cheat [WORKING APRIL 2023] | |
// Feel free to donate something at: https://www.patreon.com/MikailCayoglu/membership | |
// Published on Greasyfork: https://greasyfork.org/de/scripts/463285 | |
// ==/UserScript== | |
let solvingIntervalId; | |
let isAutoMode = false; | |
const debug = false; | |
function addButtons() { | |
if (window.location.pathname === '/learn') { | |
let button = document.querySelector('a[data-test="global-practice"]'); | |
if (button) { | |
return; | |
} | |
} | |
const solveAllButton = document.getElementById("solveAllButton"); | |
if (solveAllButton !== null) { | |
return; | |
} | |
const original = document.querySelectorAll('[data-test="player-next"]')[0]; | |
if (original === undefined) { | |
const startButton = document.querySelector('[data-test="start-button"]'); | |
console.log(`Wrapper line: ${startButton}`); | |
if (startButton === null) { | |
return; | |
} | |
const wrapper = startButton.parentNode; | |
const solveAllButton = document.createElement('a'); | |
solveAllButton.className = startButton.className; | |
solveAllButton.id = "solveAllButton"; | |
solveAllButton.innerText = "COMPLETE SKILL"; | |
solveAllButton.removeAttribute('href'); | |
solveAllButton.addEventListener('click', () => { | |
solving(); | |
setInterval(() => { | |
const startButton = document.querySelector('[data-test="start-button"]'); | |
if (startButton && startButton.innerText.startsWith("START")) { | |
startButton.click(); | |
} | |
}, 3000); | |
startButton.click(); | |
}); | |
wrapper.appendChild(solveAllButton); | |
} else { | |
const wrapper = document.getElementsByClassName('_10vOG')[0]; | |
wrapper.style.display = "flex"; | |
const solveCopy = document.createElement('button'); | |
const pauseCopy = document.createElement('button'); | |
solveCopy.id = 'solveAllButton'; | |
solveCopy.innerHTML = solvingIntervalId ? 'PAUSE SOLVE' : 'SOLVE ALL'; | |
solveCopy.disabled = false; | |
pauseCopy.innerHTML = 'SOLVE'; | |
const buttonStyle = ` | |
min-width: 150px; | |
font-size: 17px; | |
border:none; | |
border-bottom: 4px solid #58a700; | |
border-radius: 18px; | |
padding: 13px 16px; | |
transform: translateZ(0); | |
transition: filter .2s; | |
font-weight: 700; | |
letter-spacing: .8px; | |
background: #55CD2E; | |
color:#fff; | |
margin-left:20px; | |
cursor:pointer; | |
`; | |
solveCopy.style.cssText = buttonStyle; | |
pauseCopy.style.cssText = buttonStyle; | |
[solveCopy, pauseCopy].forEach(button => { | |
button.addEventListener("mousemove", () => { | |
button.style.filter = "brightness(1.1)"; | |
}); | |
}); | |
[solveCopy, pauseCopy].forEach(button => { | |
button.addEventListener("mouseleave", () => { | |
button.style.filter = "none"; | |
}); | |
}); | |
original.parentElement.appendChild(pauseCopy); | |
original.parentElement.appendChild(solveCopy); | |
solveCopy.addEventListener('click', solving); | |
pauseCopy.addEventListener('click', solve); | |
} | |
} | |
setInterval(addButtons, 3000); | |
function solving() { | |
if (solvingIntervalId) { | |
clearInterval(solvingIntervalId); | |
solvingIntervalId = undefined; | |
document.getElementById("solveAllButton").innerText = "SOLVE ALL"; | |
isAutoMode = false; | |
} else { | |
document.getElementById("solveAllButton").innerText = "PAUSE SOLVE"; | |
isAutoMode = true; | |
solvingIntervalId = setInterval(solve, 500); | |
} | |
} | |
function solve() { | |
const selAgain = document.querySelectorAll('[data-test="player-practice-again"]'); | |
const practiceAgain = document.querySelector('[data-test="player-practice-again"]'); | |
if (selAgain.length === 1 && isAutoMode) { | |
// Make sure it's the `practice again` button | |
//if (selAgain[0].innerHTML.toLowerCase() === 'practice again') { | |
// Click the `practice again` button | |
selAgain[0].click(); | |
// Terminate | |
return; | |
//} | |
} | |
if (practiceAgain !== null && isAutoMode) { | |
practiceAgain.click(); | |
return; | |
} | |
try { | |
window.sol = findReact(document.getElementsByClassName('_3FiYg')[0]).props.currentChallenge; | |
} catch { | |
let next = document.querySelector('[data-test="player-next"]'); | |
if (next) { | |
next.click(); | |
} | |
return; | |
} | |
if (!window.sol) { | |
return; | |
} | |
let nextButton = document.querySelector('[data-test="player-next"]'); | |
if (!nextButton) { | |
return; | |
} | |
if (document.querySelectorAll('[data-test*="challenge-speak"]').length > 0) { | |
if (debug) | |
document.getElementById("solveAllButton").innerText = 'Challenge Speak'; | |
const buttonSkip = document.querySelector('button[data-test="player-skip"]'); | |
if (buttonSkip) { | |
buttonSkip.click(); | |
} | |
} else if (window.sol.type === 'listenMatch') { | |
if (debug) | |
document.getElementById("solveAllButton").innerText = 'Listen Match'; | |
const nl = document.querySelectorAll('[data-test$="challenge-tap-token"]'); | |
window.sol.pairs?.forEach((pair) => { | |
for (let i = 0; i < nl.length; i++) { | |
let nlInnerText; | |
if (nl[i].querySelectorAll('[data-test="challenge-tap-token-text"]').length > 1) { | |
nlInnerText = nl[i].querySelector('[data-test="challenge-tap-token-text"]').innerText.toLowerCase().trim(); | |
} else { | |
nlInnerText = findSubReact(nl[i]).text.toLowerCase().trim(); | |
} | |
if ( | |
( | |
nlInnerText === pair.learningWord.toLowerCase().trim() || | |
nlInnerText === pair.translation.toLowerCase().trim() | |
) && | |
!nl[i].disabled | |
) { | |
nl[i].click(); | |
} | |
} | |
}); | |
} else if (document.querySelectorAll('[data-test="challenge-choice"]').length > 0) { | |
// choice challenge | |
if (debug) | |
document.getElementById("solveAllButton").innerText = 'Challenge Choice'; | |
if (window.sol.correctTokens !== undefined) { | |
correctTokensRun(); | |
nextButton.click() | |
} else if (window.sol.correctIndex !== undefined) { | |
document.querySelectorAll('[data-test="challenge-choice"]')[window.sol.correctIndex].click(); | |
nextButton.click(); | |
} | |
} else if (document.querySelectorAll('[data-test$="challenge-tap-token"]').length > 0) { | |
// match correct pairs challenge | |
if (window.sol.pairs !== undefined) { | |
if (debug) | |
document.getElementById("solveAllButton").innerText = 'Pairs'; | |
let nl = document.querySelectorAll('[data-test$="challenge-tap-token"]'); | |
if (document.querySelectorAll('[data-test="challenge-tap-token-text"]').length | |
=== nl.length) { | |
window.sol.pairs?.forEach((pair) => { | |
for (let i = 0; i < nl.length; i++) { | |
const nlInnerText = nl[i].querySelector('[data-test="challenge-tap-token-text"]').innerText.toLowerCase().trim(); | |
try { | |
if ( | |
( | |
nlInnerText === pair.transliteration.toLowerCase().trim() || | |
nlInnerText === pair.character.toLowerCase().trim() | |
) | |
&& !nl[i].disabled | |
) { | |
nl[i].click() | |
} | |
} catch (TypeError) { | |
if ( | |
( | |
nlInnerText === pair.learningToken.toLowerCase().trim() || | |
nlInnerText === pair.fromToken.toLowerCase().trim() | |
) | |
&& !nl[i].disabled | |
) { | |
nl[i].click() | |
} | |
} | |
} | |
}) | |
} | |
} else if (window.sol.correctTokens !== undefined) { | |
if (debug) | |
document.getElementById("solveAllButton").innerText = 'Token Run'; | |
correctTokensRun(); | |
nextButton.click() | |
} else if (window.sol.correctIndices !== undefined) { | |
if (debug) | |
document.getElementById("solveAllButton").innerText = 'Indices Run'; | |
correctIndicesRun(); | |
} | |
} else if (document.querySelectorAll('[data-test="challenge-tap-token-text"]').length > 0) { | |
if (debug) | |
document.getElementById("solveAllButton").innerText = 'Challenge Tap Token Text'; | |
// fill the gap challenge | |
correctIndicesRun(); | |
} else if (document.querySelectorAll('[data-test="challenge-text-input"]').length > 0) { | |
if (debug) | |
document.getElementById("solveAllButton").innerText = 'Challenge Text Input'; | |
let elm = document.querySelectorAll('[data-test="challenge-text-input"]')[0]; | |
let nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set; | |
nativeInputValueSetter.call(elm, window.sol.correctSolutions ? window.sol.correctSolutions[0] : (window.sol.displayTokens ? window.sol.displayTokens.find(t => t.isBlank).text : window.sol.prompt)); | |
let inputEvent = new Event('input', { | |
bubbles: true | |
}); | |
elm.dispatchEvent(inputEvent); | |
} else if (document.querySelectorAll('[data-test*="challenge-partialReverseTranslate"]').length > 0) { | |
if (debug) | |
document.getElementById("solveAllButton").innerText = 'Partial Reverse'; | |
let elm = document.querySelector('[data-test*="challenge-partialReverseTranslate"]')?.querySelector("span[contenteditable]"); | |
let nativeInputNodeTextSetter = Object.getOwnPropertyDescriptor(Node.prototype, "textContent").set | |
nativeInputNodeTextSetter.call(elm, '"' + window.sol?.displayTokens?.filter(t => t.isBlank)?.map(t => t.text)?.join()?.replaceAll(',', '') + '"'); | |
let inputEvent = new Event('input', { | |
bubbles: true | |
}); | |
elm.dispatchEvent(inputEvent); | |
} else if (document.querySelectorAll('textarea[data-test="challenge-translate-input"]').length > 0) { | |
if (debug) | |
document.getElementById("solveAllButton").innerText = 'Challenge Translate Input'; | |
const elm = document.querySelector('textarea[data-test="challenge-translate-input"]'); | |
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); | |
} | |
nextButton.click() | |
} | |
function correctTokensRun() { | |
const all_tokens = document.querySelectorAll('[data-test$="challenge-tap-token"]'); | |
const correct_tokens = window.sol.correctTokens; | |
const clicked_tokens = []; | |
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 = clicked_tokens.filter(token => token.textContent.trim() === correct_token.trim()).length; | |
if (match_index < matching_elements.length) { | |
matching_elements[match_index].click(); | |
clicked_tokens.push(matching_elements[match_index]); | |
} else { | |
clicked_tokens.push(matching_elements[0]); | |
} | |
} | |
}); | |
} | |
function correctIndicesRun() { | |
if (window.sol.correctIndices) { | |
window.sol.correctIndices?.forEach(index => { | |
document.querySelectorAll('div[data-test="word-bank"] [data-test="challenge-tap-token-text"]')[index].click(); | |
}); | |
// nextButton.click(); | |
} | |
} | |
function findSubReact(dom, traverseUp = 0) { | |
const key = Object.keys(dom).find(key => key.startsWith("__reactProps$")); | |
return dom.parentElement[key].children.props; | |
} | |
function findReact(dom, traverseUp = 0) { | |
let reactProps = Object.keys(dom.parentElement).find((key) => key.startsWith("__reactProps$")); | |
while (traverseUp-- > 0 && dom.parentElement) { | |
dom = dom.parentElement; | |
reactProps = Object.keys(dom.parentElement).find((key) => key.startsWith("__reactProps$")); | |
} | |
return dom?.parentElement?.[reactProps]?.children[0]?._owner?.stateNode; | |
} | |
window.findReact = findReact; | |
window.ss = solving; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment