Greasy Fork镜像 支持简体中文。

PWNEDuo (based on DuoHacker).

Duolingo automation auto answer script. Include skill leveling up

目前為 2023-01-10 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name PWNEDuo (based on DuoHacker).
  3. // @description Duolingo automation auto answer script. Include skill leveling up
  4. // @namespace Violentmonkey Scripts
  5. // @match https://*.duolingo.com/*
  6. // @grant none
  7. // @version 1.0
  8. // @author hprnv
  9. // @description 11-1-2023
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. var intervalId;
  14.  
  15. function addButtons() {
  16. if (document.getElementById("solveAllButton") !== null) {
  17. return;
  18. }
  19.  
  20. let original = document.querySelectorAll('[data-test="player-next"]')[0];
  21. let wrapper = document.getElementsByClassName('_10vOG')[0];
  22. if (original == undefined) {
  23. let startButton = document.querySelector('[data-test="start-button"]');
  24. if (startButton == undefined) {
  25. return;
  26. }
  27. let wrapper = startButton.parentNode;
  28. let autoComplete = document.createElement('a');
  29. autoComplete.className = startButton.className;
  30. autoComplete.id = "solveAllButton";
  31. autoComplete.innerText = "COMPLETE SKILL";
  32. autoComplete.removeAttribute('href');
  33. autoComplete.onclick = function () {
  34. startSolving();
  35. setInterval(function () {
  36. let startButton = document.querySelector('[data-test="start-button"]');
  37. if (startButton && startButton.innerText.startsWith("START")) {
  38. startButton.click();
  39. }
  40. }, 3000);
  41. startButton.click();
  42. };
  43. wrapper.appendChild(autoComplete);
  44. } else {
  45.  
  46. wrapper.style.display = "flex";
  47.  
  48. let solveCopy = document.createElement('button');
  49. let pauseCopy = document.createElement('button');
  50.  
  51. solveCopy.id = 'solveAllButton';
  52. if (intervalId) {
  53. solveCopy.innerHTML = 'PAUSE SOLVE';
  54. } else {
  55. solveCopy.innerHTML = 'SOLVE ALL';
  56. }
  57. solveCopy.disabled = false;
  58. pauseCopy.innerHTML = 'SOLVE';
  59.  
  60. const buttonStyle = `
  61. min-width: 150px;
  62. font-size: 17px;
  63. border:none;
  64. border-bottom: 4px solid #58a700;
  65. border-radius: 18px;
  66. padding: 13px 16px;
  67. transform: translateZ(0);
  68. transition: filter .2s;
  69. font-weight: 700;
  70. letter-spacing: .8px;
  71. background: #55CD2E;
  72. color:#fff;
  73. margin-left:20px;
  74. cursor:pointer;
  75. `;
  76.  
  77. solveCopy.style.cssText = buttonStyle;
  78. pauseCopy.style.cssText = buttonStyle;
  79.  
  80. //Hover effect for buttons
  81.  
  82. function mouseOver(x) {
  83. x.style.filter = "brightness(1.1)";
  84. }
  85.  
  86. function mouseLeave(x) {
  87. x.style.filter = "none";
  88. }
  89.  
  90. let buttons = [solveCopy, pauseCopy]
  91.  
  92. buttons.forEach(button => {
  93. button.addEventListener("mousemove", () => {
  94. mouseOver(button);
  95. });
  96. });
  97.  
  98. buttons.forEach(button => {
  99. button.addEventListener("mouseleave", () => {
  100. mouseLeave(button);
  101. });
  102. });
  103.  
  104.  
  105.  
  106. original.parentElement.appendChild(pauseCopy);
  107. original.parentElement.appendChild(solveCopy);
  108.  
  109.  
  110. solveCopy.addEventListener('click', solving);
  111. pauseCopy.addEventListener('click', solve);
  112. }
  113. }
  114.  
  115. setInterval(addButtons, 3000);
  116.  
  117. function solving() {
  118. if (intervalId) {
  119. pauseSolving();
  120. } else {
  121. startSolving();
  122. }
  123. }
  124.  
  125. function startSolving() {
  126. if (intervalId) {
  127. return;
  128. }
  129. document.getElementById("solveAllButton").innerText = "PAUSE SOLVE";
  130. intervalId = setInterval(solve, 500);
  131. }
  132.  
  133. function pauseSolving() {
  134. if (!intervalId) {
  135. return;
  136. }
  137. document.getElementById("solveAllButton").innerText = "SOLVE ALL";
  138. clearInterval(intervalId);
  139. intervalId = undefined;
  140. }
  141.  
  142. function solve() {
  143. try {
  144. window.sol = FindReact(document.getElementsByClassName('_3FiYg')[0]).props.currentChallenge;
  145. } catch {
  146. let next = document.querySelector('[data-test="player-next"]');
  147. if (next) {
  148. next.click();
  149. }
  150. return;
  151. }
  152. if (!window.sol) {
  153. return;
  154. }
  155. let btn = null;
  156.  
  157. let selNext = document.querySelectorAll('[data-test="player-next"]');
  158. let selAgain = document.getElementsByClassName('_3_pD1 _2ESN4 _2arQ0 _2vmUZ _2Zh2S _1X3l0 eJd0I _3yrdh _2wXoR _1AM95 _1dlWz _2gnHr _2L5kw _3Ry1f')
  159.  
  160. if (selAgain.length === 1) {
  161. // Make sure it's the `practice again` button
  162. if (selAgain[0].innerHTML.toLowerCase() === 'practice again') {
  163. // Click the `practice again` button
  164. selAgain[0].click();
  165.  
  166. // Terminate
  167. return;
  168. }
  169. }
  170.  
  171. if (selNext.length === 1) {
  172. // Save the button element
  173. btn = selNext[0];
  174.  
  175. if (document.querySelectorAll('[data-test="challenge-choice"]').length > 0) {
  176. if (window.sol.correctIndices) {
  177. window.sol.correctIndices?.forEach(index => {
  178. document.querySelectorAll('[data-test="challenge-choice"]')[index].children[0].click();
  179. });
  180. // Click the first element
  181. } else if (window.sol.articles) {
  182. var article = '';
  183. for (var i = 0; i < window.sol.articles.length; i++) {
  184. if (window.sol.correctSolutions[0].startsWith(window.sol.articles[i])) {
  185. Array.from(document.querySelectorAll('[data-test="challenge-choice"]'))
  186. .find((elm) =>
  187. elm.querySelector('[data-test="challenge-judge-text"]').innerText == window.sol.articles[i]
  188. ).click();
  189. article = window.sol.articles[i];
  190. break;
  191. }
  192. }
  193. let elm = document.querySelectorAll('[data-test="challenge-text-input"]')[0];
  194. let nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
  195. nativeInputValueSetter.call(elm, window.sol.correctSolutions ? window.sol.correctSolutions[0].replace(article + ' ', '') : (window.sol.displayTokens ? window.sol.displayTokens.find(t => t.isBlank).text : window.sol.prompt));
  196. let inputEvent = new Event('input', {
  197. bubbles: true
  198. });
  199.  
  200. elm.dispatchEvent(inputEvent);
  201. } else {
  202. document.querySelectorAll('[data-test="challenge-choice"]')[window.sol.correctIndex].click();
  203. }
  204. // Click the solve button
  205. btn.click();
  206. }
  207.  
  208. if (document.querySelectorAll('[data-test="challenge-choice-card"]').length > 0) {
  209. // Click the first element
  210. if (window.sol.correctIndices) {
  211. window.sol.correctIndices?.forEach(index => {
  212. document.querySelectorAll('[data-test="challenge-choice-card"]')[index].children[0].click();
  213. });
  214. } else {
  215. document.querySelectorAll('[data-test="challenge-choice-card"]')[window.sol.correctIndex].click();
  216. }
  217. // Click the solve button
  218. btn.click();
  219. }
  220.  
  221. if (window.sol.type == 'listenMatch') {
  222. let nl = document.querySelectorAll('[data-test="challenge-tap-token"]');
  223. window.sol.pairs?.forEach((pair) => {
  224. for (let i = 0; i < nl.length; i++) {
  225. let nlInnerText;
  226. if (nl[i].querySelectorAll('[data-test="challenge-tap-token-text"]').length > 1) {
  227. nlInnerText = nl[i].querySelector('[data-test="challenge-tap-token-text"]').innerText.toLowerCase().trim();
  228. } else {
  229. nlInnerText = FindSubReact(nl[i]).text.toLowerCase().trim();
  230. }
  231. if (
  232. (
  233. nlInnerText == pair.learningWord.toLowerCase().trim() ||
  234. nlInnerText == pair.translation.toLowerCase().trim()
  235. ) &&
  236. !nl[i].disabled
  237. ) {
  238. nl[i].click();
  239. }
  240. }
  241. });
  242. }
  243.  
  244. if (window.sol.type == 'listenSpell') {
  245. let tokens = window.sol.displayTokens.filter(x => x.damageStart !== undefined);
  246. let elms = document.querySelectorAll('._2cjP3._2IKiF');
  247. let nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
  248.  
  249. var solutionCharacters = [];
  250. for (let tok of tokens) {
  251. for (let i = tok.damageStart; i < tok.damageEnd; i++) {
  252. solutionCharacters.push(tok.text[i]);
  253. }
  254. }
  255.  
  256. for (var elmIndex = 0; elmIndex < elms.length; elmIndex++) {
  257. nativeInputValueSetter.call(elms[elmIndex], solutionCharacters[elmIndex]);
  258.  
  259. let inputEvent = new Event('input', {
  260. bubbles: true
  261. });
  262.  
  263. elms[elmIndex].dispatchEvent(inputEvent);
  264. }
  265. }
  266.  
  267. if (document.querySelectorAll('[data-test="challenge-tap-token"]').length > 0) {
  268. // Click the first element
  269. if (window.sol.pairs) {
  270. let nl = document.querySelectorAll('[data-test="challenge-tap-token"]');
  271. if (document.querySelectorAll('[data-test="challenge-tap-token-text"]').length == document.querySelectorAll('[data-test="challenge-tap-token"]').length) {
  272. window.sol.pairs?.forEach((pair) => {
  273. for (let i = 0; i < nl.length; i++) {
  274. const nlInnerText = nl[i].querySelector('[data-test="challenge-tap-token-text"]').innerText.toLowerCase().trim();
  275. if (
  276. (
  277. nlInnerText == pair.learningToken.toLowerCase().trim() ||
  278. nlInnerText == pair.fromToken.toLowerCase().trim()
  279. ) &&
  280. !nl[i].disabled
  281. ) {
  282. nl[i].click();
  283. }
  284. }
  285. });
  286. }
  287. } else if(!window.sol.correctTokens){
  288. let clicked = {}
  289. let nl = document.querySelectorAll('[data-test="challenge-tap-token"]');
  290. window.sol.correctIndices?.forEach(index => {
  291. let correctAnswer = window.sol.choices[index];
  292. for (let i = 0; i < nl.length; i++) {
  293. if ((nl[i].innerText).toLowerCase().trim() == correctAnswer.text.toLowerCase().trim() && !nl[i].disabled && !clicked[i]) {
  294. clicked[i] = 1;
  295. nl[i].click();
  296. break;
  297. }
  298. }
  299. });
  300. } else {
  301. let clicked = {}
  302. let nl = document.querySelectorAll('[data-test="challenge-tap-token"]');
  303. window.sol.correctIndices?.forEach(index => {
  304. let correctAnswer = window.sol.correctTokens[index];
  305. for (let i = 0; i < nl.length; i++) {
  306. if ((nl[i].innerText).toLowerCase().trim() == correctAnswer.toLowerCase().trim() && !nl[i].disabled && !clicked[i]) {
  307. clicked[i] = 1;
  308. nl[i].click();
  309. break;
  310. }
  311. }
  312. });
  313. }
  314. // Click the solve button
  315. btn.click();
  316. }
  317.  
  318. if (document.querySelectorAll('[data-test="challenge-text-input"]').length > 0) {
  319.  
  320. let elm = document.querySelectorAll('[data-test="challenge-text-input"]')[0];
  321. let nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
  322. nativeInputValueSetter.call(elm, window.sol.correctSolutions ? window.sol.correctSolutions[0] : (window.sol.displayTokens ? window.sol.displayTokens.find(t => t.isBlank).text : window.sol.prompt));
  323. let inputEvent = new Event('input', {
  324. bubbles: true
  325. });
  326.  
  327. elm.dispatchEvent(inputEvent);
  328. }
  329.  
  330.  
  331. if (document.querySelectorAll('[data-test*="challenge-partialReverseTranslate"]').length > 0) {
  332. let elm = document.querySelector('[data-test*="challenge-partialReverseTranslate"]')?.querySelector("span[contenteditable]");
  333. let nativeInputNodeTextSetter = Object.getOwnPropertyDescriptor(Node.prototype, "textContent").set
  334. nativeInputNodeTextSetter.call(elm, '"' + window.sol.displayTokens.filter(t => t.isBlank).map(t=>t.text).join().replaceAll(',', '') + '"');
  335. let inputEvent = new Event('input', {
  336. bubbles: true
  337. });
  338.  
  339. elm.dispatchEvent(inputEvent);
  340. }
  341.  
  342. if (document.getElementsByTagName('textarea').length > 0) {
  343. let elm = document.getElementsByTagName('textarea')[0]
  344.  
  345. let nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set;
  346. nativeInputValueSetter.call(elm, window.sol.correctSolutions ? window.sol.correctSolutions[0] : window.sol.prompt);
  347.  
  348. let inputEvent = new Event('input', {
  349. bubbles: true
  350. });
  351.  
  352. elm.dispatchEvent(inputEvent);
  353. }
  354.  
  355. // Continue
  356. btn.click();
  357. }
  358. }
  359.  
  360. function FindSubReact(dom, traverseUp = 0) {
  361. const key = Object.keys(dom).find(key => key.startsWith("__reactProps$"));
  362. return dom.parentElement[key].children.props;
  363. }
  364.  
  365. function FindReact(dom, traverseUp = 0) {
  366. const key = Object.keys(dom.parentElement).find(key => key.startsWith("__reactProps$"));
  367. return dom.parentElement[key].children[0]._owner.stateNode;
  368. }
  369.  
  370. window.findReact = FindReact;
  371.  
  372. window.ss = startSolving;

QingJ © 2025

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