// ==UserScript==
// @name Duolingo DuoHacker
// @name:vi Duolingo DuoHacker
// @name:zh-CN 多邻国 DuoHacker
// @name:zh-TW 多鄰國 DuoHacker
// @name:ja Duolingo DuoHacker
// @name:ko 듀오링고 DuoHacker
// @name:fr Duolingo DuoHacker
// @name:de Duolingo DuoHacker
// @name:es Duolingo DuoHacker
// @name:pt-BR Duolingo DuoHacker
// @name:pt-PT Duolingo DuoHacker
// @name:ru Дуолинго DuoHacker
// @name:ar Duolingo DuoHacker دوهاكر
// @name:tr Duolingo DuoHacker
// @name:id Duolingo DuoHacker
// @name:th Duolingo DuoHacker
// @name:pl Duolingo DuoHacker
// @name:nl Duolingo DuoHacker
// @name:it Duolingo DuoHacker
// @name:sv Duolingo DuoHacker
// @name:da Duolingo DuoHacker
// @name:fi Duolingo DuoHacker
// @name:nb Duolingo DuoHacker
// @name:cs Duolingo DuoHacker
// @name:hu Duolingo DuoHacker
// @name:ro Duolingo DuoHacker
// @name:uk Дуолінго DuoHacker
// @name:hi Duolingo DuoHacker
// @name:bn Duolingo DuoHacker
// @name:fa Duolingo DuoHacker دوهاکر
// @name:he Duolingo DuoHacker
// @name:ms Duolingo DuoHacker
// @name:fil Duolingo DuoHacker
// @name:el Duolingo DuoHacker
// @name:hr Duolingo DuoHacker
// @name:sk Duolingo DuoHacker
// @name:bg Дуолинго DuoHacker
// @name:sr Дуолинго DuoHacker
// @name:lt Duolingo DuoHacker
// @name:lv Duolingo DuoHacker
// @name:et Duolingo DuoHacker
// @name:sl Duolingo DuoHacker
// @name:ca Duolingo DuoHacker
// @name:af Duolingo DuoHacker
// @name:sw Duolingo DuoHacker
// @name:zu Duolingo DuoHacker
// @name:mn Duolingo DuoHacker
// @name:my Duolingo DuoHacker
// @name:km Duolingo DuoHacker
// @name:lo Duolingo DuoHacker
// @name:ur Duolingo DuoHacker
// @namespace https://github.com/not2pixel/DuoHacker
// @version 2026.04.21
// @description The #1 Duolingo hack - Farm XP, Gems, Streaks and unlock Duolingo Max for free.
// @description:vi Công cụ hack Duolingo #1 - Farm XP, Gems, Streaks và mở khóa Duolingo Max miễn phí.
// @description:zh-CN 最强 Duolingo 辅助工具 - 自动刷 XP、宝石、连胜,免费解锁 Duolingo Max。
// @description:zh-TW 最強 Duolingo 輔助工具 - 自動刷 XP、寶石、連勝,免費解鎖 Duolingo Max。
// @description:ja Duolingo最強ハックツール - XP・ジェム・連続記録を自動取得、Duolingo Maxを無料解放。
// @description:ko 최고의 Duolingo 핵 툴 - XP, 젬, 스트릭 자동 획득 및 Duolingo Max 무료 해제.
// @description:fr Le hack Duolingo #1 - Farmez XP, Gemmes, Séries et débloquez Duolingo Max gratuitement.
// @description:de Der #1 Duolingo-Hack - XP, Gems, Serien farmen und Duolingo Max kostenlos freischalten.
// @description:es El hack #1 de Duolingo - Farmea XP, Gemas, Rachas y desbloquea Duolingo Max gratis.
// @description:pt-BR O hack #1 do Duolingo - Farme XP, Gemas, Sequências e desbloqueie o Duolingo Max de graça.
// @description:pt-PT O hack #1 do Duolingo - Faça farm de XP, Gemas, Sequências e desbloqueie o Duolingo Max gratuitamente.
// @description:ru Лучший хак для Duolingo - Фармите XP, Гемы, Серии и разблокируйте Duolingo Max бесплатно.
// @description:ar أفضل هاك لـ Duolingo - اجمع XP والجواهر والسلاسل وافتح Duolingo Max مجاناً.
// @description:tr Duolingo için #1 hack - XP, Gem, Seri farm'la ve Duolingo Max'ı ücretsiz aç.
// @description:id Hack Duolingo #1 - Farm XP, Gems, Streak dan buka kunci Duolingo Max gratis.
// @description:th แฮก Duolingo อันดับ 1 - ฟาร์ม XP, เพชร, สตรีคและปลดล็อก Duolingo Max ฟรี.
// @description:pl Najlepszy hack na Duolingo - Farmuj XP, Klejnoty, Serie i odblokuj Duolingo Max za darmo.
// @description:nl De #1 Duolingo-hack - Farm XP, Edelstenen, Reeksen en ontgrendel Duolingo Max gratis.
// @description:it Il miglior hack per Duolingo - Fai farm di XP, Gemme, Streak e sblocca Duolingo Max gratis.
// @description:sv Bästa Duolingo-hacket - Farma XP, Ädelstenar, Streaks och lås upp Duolingo Max gratis.
// @description:da Den bedste Duolingo-hack - Farm XP, Ædelstene, Striber og lås Duolingo Max op gratis.
// @description:fi Paras Duolingo-hakki - Farmmaa XP, Jalokivet, Putket ja avaa Duolingo Max ilmaiseksi.
// @description:nb Den beste Duolingo-hacken - Farm XP, Edelstener, Rekker og lås opp Duolingo Max gratis.
// @description:cs Nejlepší hack na Duolingo - Farmujte XP, Drahokamy, Série a odemkněte Duolingo Max zdarma.
// @description:hu A legjobb Duolingo-hack - Farmolj XP-t, Drágaköveket, Sorozatokat és oldd fel a Duolingo Maxot ingyen.
// @description:ro Cel mai bun hack pentru Duolingo - Farmează XP, Pietre, Serii și deblochează Duolingo Max gratuit.
// @description:uk Найкращий хак для Duolingo - Фармте XP, Самоцвіти, Серії та розблокуйте Duolingo Max безкоштовно.
// @description:hi Duolingo का #1 हैक - XP, Gems, Streaks फार्म करें और Duolingo Max मुफ्त में अनलॉक करें।
// @description:bn সেরা Duolingo হ্যাক - XP, Gems, Streaks ফার্ম করুন এবং বিনামূল্যে Duolingo Max আনলক করুন।
// @description:fa بهترین هک Duolingo - XP، جواهر و رکورد را فارم کنید و Duolingo Max را رایگان باز کنید.
// @description:he ההאק הטוב ביותר ל-Duolingo - צבור XP, אבנים יקרות, רצפים ופתח את Duolingo Max בחינם.
// @description:ms Hack Duolingo terbaik - Farm XP, Permata, Streak dan buka kunci Duolingo Max secara percuma.
// @description:fil Ang pinakamahusay na Duolingo hack - Mag-farm ng XP, Gems, Streaks at i-unlock ang Duolingo Max nang libre.
// @description:el Το καλύτερο hack για το Duolingo - Κάντε farm XP, Πετράδια, Σειρές και ξεκλειδώστε το Duolingo Max δωρεάν.
// @description:hr Najbolji Duolingo hack - Farmajte XP, Dragulje, Nizove i otključajte Duolingo Max besplatno.
// @description:sk Najlepší hack na Duolingo - Farmujte XP, Drahokamy, Série a odomknite Duolingo Max zadarmo.
// @description:bg Най-добрият хак за Duolingo - Фармете XP, Скъпоценни камъни, Серии и отключете Duolingo Max безплатно.
// @description:sr Najbolji Duolingo hak - Farmujte XP, Dragulje, Serije i otključajte Duolingo Max besplatno.
// @description:lt Geriausias Duolingo įsilaužimas - Farminkite XP, Brangakmenius, Serijas ir atrakinkite Duolingo Max nemokamai.
// @description:lv Labākais Duolingo hack - Farmējiet XP, Dārgakmeņus, Sērijas un atbloķējiet Duolingo Max bez maksas.
// @description:et Parim Duolingo häkk - Farmige XP, Kalliskive, Seeriad ja avage Duolingo Max tasuta.
// @description:sl Najboljši Duolingo hack - Farmajte XP, Dragulji, Serije in odklenite Duolingo Max brezplačno.
// @description:ca El millor hack per a Duolingo - Fes farm de XP, Gemmes, Ratxes i desbloqueja Duolingo Max gratis.
// @description:af Die beste Duolingo-hack - Boer XP, Edelstene, Reekse en ontsluit Duolingo Max gratis.
// @description:sw Hack bora ya Duolingo - Fanya farm ya XP, Vito, Mifululizo na fungua Duolingo Max bila malipo.
// @description:zu I-hack engcono kakhulu ye-Duolingo - Fama i-XP, Amagugu, Izindawo futhi vula i-Duolingo Max mahhala.
// @description:mn Duolingo-н хамгийн шилдэг хак - XP, Эрдэнийн чулуу, Цуваа фарм хийж Duolingo Max-ийг үнэгүй нээ.
// @description:my Duolingo hack အကောင်းဆုံး - XP, Gems, Streaks farm လုပ်ပြီး Duolingo Max ကို အခမဲ့ ဖွင့်ပါ။
// @description:km ហេគ Duolingo ល្អបំផុត - ដាំ XP, Gems, Streaks និងដោះសោ Duolingo Max ដោយឥតគិតថ្លៃ។
// @description:lo ແຮັກ Duolingo ອັນດັບໜຶ່ງ - ຟາມ XP, ເພັດ, ສາຍຕໍ່ເນື່ອງ ແລະ ປົດລັອກ Duolingo Max ຟຣີ.
// @description:ur Duolingo کا بہترین ہیک - XP، Gems، Streaks فارم کریں اور Duolingo Max مفت میں انلاک کریں۔
// @author DuoHacker Team
// @match https://*.duolingo.com/*
// @match https://*.duolingo.cn/*
// @icon https://assets.twisk.fun/images/DuoHacker_Logo_NoBG_PNG.png
// @run-at document-end
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @connect duolingo.com
// @connect stories.duolingo.com
// @connect goals-api.duolingo.com
// @connect duolingo-leaderboards-prod.duolingo.com
// @connect raw.githubusercontent.com
// @connect avatars.githubusercontent.com
// @connect fonts.googleapis.com
// @connect greasyfork.org
// @compatible chrome Tested on Chrome 120+ with Tampermonkey
// @compatible firefox Tested on Firefox 120+ with Tampermonkey / Violentmonkey
// @compatible edge Tested on Edge 120+ with Tampermonkey
// @compatible opera Supported via Tampermonkey / Violentmonkey
// @compatible safari Supported via Userscripts app
// @compatible brave Supported via Tampermonkey
// @homepageURL https://github.com/not2pixel/DuoHacker
// @supportURL https://discord.com/invite/Gvmd7deFtS
// @copyright 2026, DuoHacker (https://github.com/not2pixel)
// @license BY-NC-ND 4.0
// ==/UserScript==
(function () {
'use strict';
const _fontLink = document.createElement('link');
_fontLink.rel = 'stylesheet';
_fontLink.href = 'https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700;800;900&display=swap';
document.head.appendChild(_fontLink);
GM_addStyle(`
:root {
--DH-blue: 0, 122, 255;
--DH-green: 52, 199, 89;
--DH-red: 255, 59, 48;
--DH-orange: 255, 149, 0;
}
@media (prefers-color-scheme: dark) {
:root {
--DH-blue: 10, 132, 255;
--DH-green: 48, 209, 88;
--DH-red: 255, 69, 58;
}
}
#DH_Root * { box-sizing: border-box; }
#DH_Root p, #DH_Root span, #DH_Root button, #DH_Root input, #DH_Root label, #DH_Root div {
font-family: 'Nunito', 'din-round', -apple-system, sans-serif !important;
}
#DH_Root p, #DH_Root span { margin: 0; padding: 0; }
#DH_Root svg { flex-shrink: 0; }
.DH_Main {
display: inline-flex; flex-direction: column;
justify-content: flex-end; align-items: flex-end;
gap: 8px; position: fixed; right: 16px; bottom: 16px;
z-index: 2147483647;
}
@media (max-width: 699px) {
.DH_Main { margin-bottom: 80px; }
}
.DH_Main_Box {
display: flex; width: 312px; padding: 16px;
box-sizing: border-box;
flex-direction: column; justify-content: center; align-items: center; gap: 8px;
overflow: hidden; border-radius: 20px;
outline: 2px solid rgb(var(--color-eel, 117,117,117), 0.10); outline-offset: -2px;
background: rgb(var(--color-snow), 0.90);
backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
}
.DH_HStack_Auto { display:flex; align-items:center; justify-content:space-between; align-self:stretch; }
.DH_HStack_8 { display:flex; align-items:center; gap:8px; align-self:stretch; }
.DH_HStack_4 { display:flex; align-items:center; gap:4px; align-self:stretch; }
.DH_VStack_8 { display:flex; flex-direction:column; justify-content:center; align-items:center; gap:8px; align-self:stretch; }
.DH_VStack_4 { display:flex; flex-direction:column; justify-content:center; align-items:center; gap:4px; align-self:stretch; }
.DH_NoSel { user-select:none; -webkit-user-select:none; }
.DH_Divider { align-self:stretch; height:1px; background:rgb(var(--color-eel,117,117,117), 0.10); flex-shrink:0; }
.DH_T1 { font-size:15px; font-weight:700; line-height:normal; color:rgb(var(--color-wolf,60,60,67), 0.80); margin:0; }
.DH_T2 { font-size:13px; font-weight:600; line-height:normal; color:rgb(var(--color-wolf,60,60,67), 0.50); margin:0; }
.DH_Btn {
display:flex; height:40px; padding:10px 12px 10px 10px; box-sizing:border-box;
align-items:center; gap:6px; flex:1 0 0;
border-radius:8px; border:none; cursor:pointer;
user-select:none; -webkit-user-select:none;
transition: filter 0.4s cubic-bezier(0.16,1,0.32,1), transform 0.4s cubic-bezier(0.16,1,0.32,1);
}
.DH_Btn:hover { filter:brightness(0.88); transform:scale(1.04); }
.DH_Btn:active { filter:brightness(0.82); transform:scale(0.96); }
.DH_Btn_Blue_Ghost {
outline:2px solid rgba(var(--DH-blue),0.20); outline-offset:-2px;
background:linear-gradient(0deg,rgba(var(--DH-blue),0.10),rgba(var(--DH-blue),0.10)),rgb(var(--color-snow),0.80);
backdrop-filter:blur(16px);
}
.DH_Btn_Eel {
outline:2px solid rgb(var(--color-eel,117,117,117),0.20); outline-offset:-2px;
background:rgb(var(--color-eel,117,117,117),0.10);
}
.DH_Btn_Icon { flex:none!important; width:40px; padding:10px!important; justify-content:center; }
.DH_Input_Wrap {
display:flex; height:48px; padding:16px; box-sizing:border-box;
align-items:center; flex:1 0 0; gap:6px;
border-radius:8px;
outline:2px solid rgba(var(--DH-blue),0.20); outline-offset:-2px;
background:rgba(var(--DH-blue),0.10);
position:relative; overflow:hidden;
}
.DH_Input {
border:none!important; outline:none!important; background:none!important;
text-align:right; font-size:16px!important; font-weight:600!important;
color:rgb(var(--DH-blue))!important; font-family:inherit!important; width:100%;
-moz-appearance:textfield;
}
.DH_Input::placeholder { color:rgba(var(--DH-blue),0.50)!important; }
.DH_Input::-webkit-outer-spin-button,
.DH_Input::-webkit-inner-spin-button { -webkit-appearance:none; margin:0; }
.DH_Input_Btn {
display:flex; height:48px; padding:12px 14px; box-sizing:border-box;
justify-content:center; align-items:center;
border-radius:8px; border:none; cursor:pointer;
user-select:none; -webkit-user-select:none;
outline:2px solid rgba(0,0,0,0.20); outline-offset:-2px;
background:rgb(var(--DH-blue));
white-space:nowrap; flex-shrink:0;
transition:
width 0.8s cubic-bezier(0.77,0,0.18,1),
background 0.8s cubic-bezier(0.16,1,0.32,1),
outline 0.8s cubic-bezier(0.16,1,0.32,1),
filter 0.4s cubic-bezier(0.16,1,0.32,1),
transform 0.4s cubic-bezier(0.16,1,0.32,1);
}
.DH_Input_Btn:hover { filter:brightness(0.88); transform:scale(1.04); }
.DH_Input_Btn:active { filter:brightness(0.82); transform:scale(0.96); }
.DH_Input_Btn:disabled { opacity:0.38; pointer-events:none; }
.DH_Btn_Label {
font-size:15px; font-weight:800; letter-spacing:0.2px;
transition:opacity 0.4s, filter 0.4s, color 0.4s;
}
.DH_Sm_Btn {
display:flex; height:38px; padding:0 16px;
justify-content:center; align-items:center;
border-radius:8px; border:none; cursor:pointer;
user-select:none; flex-shrink:0;
outline:2px solid rgba(0,0,0,0.20); outline-offset:-2px;
background:rgb(var(--DH-blue));
white-space:nowrap;
transition:
width 0.8s cubic-bezier(0.77,0,0.18,1),
background 0.8s cubic-bezier(0.16,1,0.32,1),
outline 0.8s cubic-bezier(0.16,1,0.32,1),
filter 0.4s cubic-bezier(0.16,1,0.32,1),
transform 0.4s cubic-bezier(0.16,1,0.32,1);
}
.DH_Sm_Btn:hover { filter:brightness(0.88); transform:scale(1.04); }
.DH_Sm_Btn:active { filter:brightness(0.82); transform:scale(0.96); }
.DH_Sm_Btn:disabled { opacity:0.38; pointer-events:none; }
.DH_Sm_Btn_Label { font-size:13px; font-weight:800; transition:opacity 0.4s, filter 0.4s, color 0.4s; }
.DH_Prog_Wrap {
align-self:stretch; height:0; border-radius:3px;
background:rgba(var(--DH-blue),0.10); overflow:hidden;
transition:height 0.4s cubic-bezier(0.16,1,0.32,1);
}
.DH_Prog_Wrap.on { height:4px; }
.DH_Prog_Fill {
height:100%; border-radius:3px; background:rgb(var(--DH-blue));
width:0%; transition:width 0.5s cubic-bezier(0.16,1,0.32,1);
box-shadow:0 0 6px rgba(var(--DH-blue),0.35);
}
.DH_Dot {
width:8px; height:8px; border-radius:50%;
background:rgb(var(--color-eel,117,117,117),0.30); flex-shrink:0;
transition:background 0.4s, box-shadow 0.4s;
}
.DH_Dot.ok { background:rgb(var(--DH-green)); box-shadow:0 0 7px rgba(var(--DH-green),0.5); }
.DH_Dot.err { background:rgb(var(--DH-red)); box-shadow:0 0 7px rgba(var(--DH-red),0.4); }
.DH_Dot.spin { animation:DH_Spin 1.5s linear infinite; }
.DH_Avatar {
width:32px; height:32px; border-radius:50%;
background:rgba(var(--DH-blue),0.10);
overflow:hidden; flex-shrink:0;
display:flex; align-items:center; justify-content:center; font-size:16px;
}
.DH_Stat_Ico { width:15px; height:15px; display:block; flex-shrink:0; }
.DH_Stat_Val { font-size:13px!important; font-weight:700!important; color:rgb(var(--color-wolf,60,60,67),0.60)!important; }
.DH_Toggle { position:relative; width:44px; height:26px; flex-shrink:0; cursor:pointer; }
.DH_Toggle input { opacity:0; width:0; height:0; }
.DH_Toggle_Slider {
position:absolute; cursor:pointer; inset:0;
background:rgb(var(--color-eel,117,117,117),0.22); border-radius:26px; transition:background 0.3s;
}
.DH_Toggle_Slider:before {
content:''; position:absolute; width:20px; height:20px; left:3px; bottom:3px;
background:#fff; border-radius:50%; transition:transform 0.3s; box-shadow:0 2px 4px rgba(0,0,0,0.2);
}
.DH_Toggle input:checked + .DH_Toggle_Slider { background:rgb(var(--DH-blue)); }
.DH_Toggle input:checked + .DH_Toggle_Slider:before { transform:translateX(18px); }
.DH_Shimmer {
position:absolute; pointer-events:none; top:0; left:0;
transform:translateX(-150px);
animation:DH_Shimmer 5s ease-in-out infinite 1.5s;
}
@keyframes DH_Shimmer {
0% { transform:translateX(-150px); }
18% { transform:translateX(340px); }
100%{ transform:translateX(340px); }
}
@keyframes DH_Spin { from{transform:rotate(0deg)} to{transform:rotate(360deg)} }
.DH_Spin_Ico { animation:DH_Spin 1.5s linear infinite; display:inline-block; }
.DH_Page { display:none; }
.DH_Page.active { display:flex; flex-direction:column; gap:8px; align-self:stretch; align-items:center; }
.DH_Notif_Main {
display:flex; justify-content:center; align-items:center;
width:300px; position:fixed; left:calc(50% - 150px);
z-index:2147483647; bottom:16px; border-radius:16px;
transition:0.8s cubic-bezier(0.16,1,0.32,1); pointer-events:none;
}
.DH_Notif_Box {
display:flex; width:300px; padding:14px 16px;
flex-direction:column; gap:3px;
border-radius:16px;
outline:2px solid rgb(var(--color-eel,117,117,117),0.10); outline-offset:-2px;
background:rgb(var(--color-snow),0.90);
backdrop-filter:blur(16px); -webkit-backdrop-filter:blur(16px);
box-shadow:0 8px 32px rgba(0,0,0,0.12);
transition:0.8s cubic-bezier(0.16,1,0.32,1); filter:blur(16px); opacity:0;
pointer-events:auto;
}
.DH_Notif_Box.show { filter:blur(0px); opacity:1; }
.DH_Shop_Grid { display:grid; grid-template-columns:repeat(2,1fr); gap:8px; align-self:stretch; }
.DH_Shop_Card {
display:flex; flex-direction:column; align-items:center;
gap:6px; padding:12px 8px; border-radius:12px;
outline:1.5px solid rgb(var(--color-eel,117,117,117),0.13); outline-offset:-1px;
background:rgb(var(--color-eel,117,117,117),0.05);
transition:outline-color 0.2s, background 0.2s; text-align:center;
}
.DH_Shop_Card:hover { outline-color:rgba(var(--DH-blue),0.30); background:rgba(var(--DH-blue),0.04); }
.DH_Shop_Ico { width:36px; height:36px; object-fit:contain; }
.DH_Shop_Name { font-size:11px; font-weight:700; color:rgb(var(--color-wolf,60,60,67),0.75); line-height:1.3; flex:1; }
.DH_Shop_Btn {
width:100%; height:28px; border-radius:7px; border:none; cursor:pointer;
font-size:11px; font-weight:800; color:#fff;
background:rgb(var(--DH-blue));
outline:2px solid rgba(0,0,0,0.20); outline-offset:-2px;
transition:filter 0.4s cubic-bezier(0.16,1,0.32,1), transform 0.4s cubic-bezier(0.16,1,0.32,1), background 0.4s;
}
.DH_Shop_Btn:hover { filter:brightness(0.88); transform:scale(1.03); }
.DH_Shop_Btn:active { transform:scale(0.97); }
.DH_Shop_Btn.loading { background:rgb(var(--color-eel,117,117,117),0.12); color:rgb(var(--color-eel,117,117,117),0.50); outline-color:rgb(var(--color-eel,117,117,117),0.18); pointer-events:none; }
.DH_Shop_Btn.got { background:rgba(var(--DH-green),0.12); color:rgb(var(--DH-green)); outline-color:rgba(var(--DH-green),0.25); pointer-events:none; }
.DH_Shop_Btn.fail { background:rgba(var(--DH-red),0.10); color:rgb(var(--DH-red)); outline-color:rgba(var(--DH-red),0.22); pointer-events:none; }
.DH_Cat_Header {
align-self:stretch; font-size:11px; font-weight:800; text-transform:uppercase;
letter-spacing:0.6px; color:rgb(var(--color-wolf,60,60,67),0.40);
padding:4px 0 2px; text-align:center;
border-bottom:1px solid rgb(var(--color-eel,117,117,117),0.10); margin-bottom:2px;
}
.DH_Scroll_Inner {
align-self:stretch; overflow-y:auto;
max-height:220px; display:flex; flex-direction:column; gap:6px; padding-right:2px;
}
.DH_Scroll_Inner::-webkit-scrollbar { width:3px; }
.DH_Scroll_Inner::-webkit-scrollbar-thumb { background:rgba(var(--DH-blue),0.2); border-radius:3px; }
.DH_Scroll_Inner { scrollbar-width:thin; scrollbar-color:rgba(var(--DH-blue),0.2) transparent; }
.DH_Credit_Card { display:flex; flex-direction:column; gap:6px; padding:10px 12px; border-radius:12px; background:rgba(var(--DH-blue),0.06); outline:1.5px solid rgba(var(--DH-blue),0.12); outline-offset:-1.5px; align-self:stretch; }
.DH_Credit_Card_Header { display:flex; align-items:center; gap:8px; }
.DH_Credit_Thumb { width:28px; height:28px; border-radius:8px; object-fit:cover; flex-shrink:0; background:rgba(var(--DH-blue),0.1); }
.DH_Credit_Script { font-size:13px; font-weight:800; color:rgb(var(--color-wolf,60,60,67)); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.DH_Credit_Author { font-size:11px; color:rgba(var(--color-wolf,60,60,67),0.55); }
.DH_Credit_Task { font-size:11px; color:rgba(var(--color-wolf,60,60,67),0.7); line-height:1.4; }
.DH_Credit_Link { font-size:11px; font-weight:700; color:rgb(var(--DH-blue)); text-decoration:none; }
.DH_Credit_Link:hover { opacity:0.75; }
.DH_Search {
align-self:stretch; height:40px; padding:0 14px;
border-radius:8px; border:none;
outline:2px solid rgb(var(--color-eel,117,117,117),0.15); outline-offset:-2px;
background:rgb(var(--color-eel,117,117,117),0.06);
font-size:14px; font-weight:600;
color:rgb(var(--color-wolf,60,60,67),0.80); transition:outline-color 0.2s;
}
.DH_Search:focus { outline-color:rgba(var(--DH-blue),0.35); }
.DH_Search::placeholder { color:rgb(var(--color-wolf,60,60,67),0.35); }
#DH_Extra_Panel { overflow:hidden; }
.DH_Btn_Ico {
width:28px; height:28px; border-radius:7px; flex-shrink:0;
background:rgba(var(--DH-blue),0.12);
display:flex; align-items:center; justify-content:center;
}
.DH_Acc_Card {
display:flex; align-items:center; gap:10px; align-self:stretch;
padding:10px 12px; border-radius:12px;
outline:1.5px solid rgb(var(--color-eel,117,117,117),0.13); outline-offset:-1px;
background:rgb(var(--color-eel,117,117,117),0.05);
transition:outline-color 0.2s, background 0.2s;
position:relative; overflow:hidden;
}
.DH_Acc_Card:hover { outline-color:rgba(var(--DH-blue),0.30); background:rgba(var(--DH-blue),0.04); }
.DH_Acc_Avatar {
width:36px; height:36px; border-radius:50%; flex-shrink:0;
background:rgba(var(--DH-blue),0.10); overflow:hidden;
display:flex; align-items:center; justify-content:center; font-size:16px;
}
.DH_Acc_Info { flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; }
.DH_Acc_Name { font-size:13px!important; font-weight:700!important; color:rgb(var(--color-wolf,60,60,67),0.85)!important; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.DH_Acc_Sub { font-size:11px!important; font-weight:600!important; color:rgb(var(--color-wolf,60,60,67),0.45)!important; }
.DH_Acc_Sub.active { color:rgb(var(--DH-green))!important; display:flex; align-items:center; gap:4px; }
.DH_Acc_Action_Row { display:flex; gap:5px; flex-shrink:0; }
.DH_Acc_Btn {
height:26px; padding:0 8px; border-radius:7px; border:none; cursor:pointer;
font-size:10px; font-weight:800; color:#fff;
background:rgb(var(--DH-blue));
outline:2px solid rgba(0,0,0,0.15); outline-offset:-2px;
transition:filter 0.3s,transform 0.3s;
}
.DH_Acc_Btn:hover { filter:brightness(0.88); transform:scale(1.06); }
.DH_Acc_Btn:active { transform:scale(0.95); }
.DH_Acc_Btn.del { background:rgba(var(--DH-red),0.10); color:rgb(var(--DH-red)); outline-color:rgba(var(--DH-red),0.20); }
.DH_Acc_Btn.del:hover { background:rgb(var(--DH-red)); color:#fff; }
.DH_Quest_Item {
display:flex; align-items:center; gap:10px; align-self:stretch;
padding:10px 12px; border-radius:12px;
outline:1.5px solid rgb(var(--color-eel,117,117,117),0.13); outline-offset:-1px;
background:rgb(var(--color-eel,117,117,117),0.05);
transition:outline-color 0.2s, background 0.2s;
}
.DH_Quest_Item.done { outline-color:rgba(var(--DH-green),0.25); background:rgba(var(--DH-green),0.04); }
.DH_Quest_Icon { width:40px; height:40px; object-fit:contain; flex-shrink:0; }
.DH_Quest_Info { flex:1; min-width:0; display:flex; flex-direction:column; gap:3px; }
.DH_Quest_Title { font-size:12px!important; font-weight:700!important; color:rgb(var(--color-wolf,60,60,67),0.85)!important; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.DH_Quest_Meta { font-size:10px!important; font-weight:600!important; color:rgb(var(--color-wolf,60,60,67),0.45)!important; }
.DH_Quest_Bar_Bg { height:4px; border-radius:2px; background:rgba(var(--DH-blue),0.10); overflow:hidden; align-self:stretch; }
.DH_Quest_Bar_Fill { height:100%; background:rgb(var(--DH-blue)); border-radius:2px; transition:width 0.5s; }
.DH_Quest_Item.done .DH_Quest_Bar_Fill { background:rgb(var(--DH-green)); }
.DH_Quest_Get_Btn {
height:26px; padding:0 8px; flex-shrink:0; border-radius:7px; border:none; cursor:pointer;
font-size:10px; font-weight:800; white-space:nowrap;
background:rgb(var(--DH-blue)); color:#fff;
outline:2px solid rgba(0,0,0,0.15); outline-offset:-2px;
transition:filter 0.3s,transform 0.3s;
}
.DH_Quest_Get_Btn:hover { filter:brightness(0.88); transform:scale(1.06); }
.DH_Quest_Get_Btn:active { transform:scale(0.95); }
.DH_Quest_Get_Btn.done { background:rgba(var(--DH-green),0.10); color:rgb(var(--DH-green)); outline-color:rgba(var(--DH-green),0.20); pointer-events:none; }
#DH_AccSettings_Btn { transform-origin: right center; }
#DH_AccSettings_Btn:hover { filter:brightness(0.85); transform:scale(1.1); }
#DH_AccSettings_Btn:active { transform:scale(0.92); }
/* PAGE 9: License */
#DH_Page_9 { flex:1; min-height:0; }
.DH_License_Scroll {
flex:1;
min-height:0;
overflow-y:auto;
overflow-x:hidden;
padding:14px 16px;
border-radius:12px;
outline:2px solid rgba(var(--color-eel,117,117,117),0.10);
outline-offset:-2px;
background:rgba(var(--color-eel,117,117,117),0.04);
align-self:stretch;
max-height:320px;
}
.DH_License_Scroll::-webkit-scrollbar { width:3px; }
.DH_License_Scroll::-webkit-scrollbar-thumb { background:rgba(var(--color-eel,117,117,117),0.20); border-radius:2px; }
#DH_License_Text {
font-size:11.5px;
font-weight:600;
line-height:1.65;
color:rgb(var(--color-wolf,60,60,67),0.75);
white-space:pre-wrap;
margin:0;
word-break:break-word;
}
@media (max-width:699px) {
.DH_License_Scroll { max-height:calc(100svh - 240px); }
.DH_Main { margin-bottom:80px; }
}
`);
const _wrap = document.createElement('div');
_wrap.id = 'DH_Root';
_wrap.innerHTML = `
<div class="DH_Notif_Main" id="DH_Notif_Main">
<div class="DH_Notif_Box" id="DH_Notif_Box">
<div class="DH_HStack_4" style="align-items:center;">
<p class="DH_T1 DH_NoSel" id="DH_Notif_Icon" style="font-size:18px;flex-shrink:0;"></p>
<p class="DH_T1 DH_NoSel" id="DH_Notif_Title" style="flex:1 0 0;"></p>
</div>
<p class="DH_T2 DH_NoSel" id="DH_Notif_Body" style="align-self:stretch;overflow-wrap:break-word;"></p>
</div>
</div>
<div class="DH_Main" id="DH_Main">
<div class="DH_HStack_8" style="align-self:flex-end;">
<div class="DH_Btn DH_Btn_Blue_Ghost DH_NoSel" id="DH_SwitchV1_Btn"
style="flex:none; display:none; order:-1;">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17 1l4 4-4 4" stroke="rgb(var(--DH-blue))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3 11V9a4 4 0 0 1 4-4h14" stroke="rgb(var(--DH-blue))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7 23l-4-4 4-4" stroke="rgb(var(--DH-blue))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M21 13v2a4 4 0 0 1-4 4H3" stroke="rgb(var(--DH-blue))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<p class="DH_T1 DH_NoSel" style="color:rgb(var(--DH-blue));font-size:13px;">Switch to V1</p>
</div>
<div class="DH_Btn DH_Btn_Blue_Ghost DH_NoSel" id="DH_SwitchV2_Btn"
style="flex:none; display:none; order:-1;">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17 1l4 4-4 4" stroke="rgb(var(--DH-blue))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3 11V9a4 4 0 0 1 4-4h14" stroke="rgb(var(--DH-blue))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7 23l-4-4 4-4" stroke="rgb(var(--DH-blue))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M21 13v2a4 4 0 0 1-4 4H3" stroke="rgb(var(--DH-blue))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<p class="DH_T1 DH_NoSel" style="color:rgb(var(--DH-blue));font-size:13px;">Switch to V2</p>
</div>
<div class="DH_Btn DH_NoSel" id="DH_Hide_Btn"
style="flex:none; outline:2px solid rgba(0,0,0,0.20); outline-offset:-2px; background:rgb(var(--DH-blue)); backdrop-filter:blur(16px);">
<svg id="DH_Ico_Visible" width="18" height="12" viewBox="0 0 18 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 0C5 0 1.73 2.5 0 6c1.73 3.5 5 6 9 6s7.27-2.5 9-6c-1.73-3.5-5-6-9-6zm0 10a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-6.4a2.4 2.4 0 1 0 0 4.8 2.4 2.4 0 0 0 0-4.8z" fill="#FFF"/>
<path d="M1 1l16 10" stroke="#FFF" stroke-width="1.5" stroke-linecap="round"/>
</svg>
<svg id="DH_Ico_Hidden" width="18" height="12" viewBox="0 0 18 12" fill="none" xmlns="http://www.w3.org/2000/svg" style="display:none;">
<path d="M9 0C5 0 1.73 2.5 0 6c1.73 3.5 5 6 9 6s7.27-2.5 9-6c-1.73-3.5-5-6-9-6zm0 10a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-6.4a2.4 2.4 0 1 0 0 4.8 2.4 2.4 0 0 0 0-4.8z" fill="rgb(var(--DH-blue))"/>
</svg>
<p class="DH_T1 DH_NoSel" id="DH_Hide_Txt" style="color:#fff;">Hide</p>
</div>
</div>
<div class="DH_Main_Box" id="DH_Main_Box">
<div class="DH_Page active" id="DH_Page_1">
<div class="DH_HStack_8">
<div class="DH_Btn DH_Btn_Eel DH_NoSel" id="DH_Conn_Btn" style="padding:10px 0 10px 10px; transition:background 0.8s cubic-bezier(0.16,1,0.32,1), outline 0.8s cubic-bezier(0.16,1,0.32,1), filter 0.4s cubic-bezier(0.16,1,0.32,1), transform 0.4s cubic-bezier(0.16,1,0.32,1);">
<p class="DH_T1 DH_NoSel DH_Spin_Ico" id="DH_Conn_Ico" style="color:rgb(var(--color-eel,117,117,117),0.70);">⟳</p>
<p class="DH_T1 DH_NoSel" id="DH_Conn_Txt" style="color:rgb(var(--color-eel,117,117,117),0.70);">Connecting</p>
</div>
<div class="DH_Btn DH_Btn_Icon DH_NoSel" id="DH_Discord_Btn" style="background:rgb(88,101,242);outline:2px solid rgba(0,0,0,.18);outline-offset:-2px;">
<svg width="18" height="14" viewBox="0 0 22 16" fill="#FFF"><path d="M18.289 1.34C16.9296.714 15.4761.259 13.9565 0c-.1866.332-.4046.779-.5549 1.134-1.6154-.239-3.2159-.239-4.8016 0C8.4497.779 8.2267.332 8.0384 0 6.5172.259 5.062.716 3.7027 1.343.9608 5.421.2175 9.398.5892 13.318c1.8185 1.337 3.5809 2.149 5.3136 2.68.4278-.579.8093-1.195 1.138-1.845-.6259-.234-1.2255-.523-1.7921-.858.1503-.11.2973-.225.4393-.307 3.4554 1.591 7.2098 1.591 10.624 0 .1437.118.2907.233.4393.342-.6262.337-1.2274.626-1.8534.86.3287.648.7086 1.265 1.138 1.845 1.7343-.531 3.4983-1.343 5.3168-2.681.4361-4.545-.7449-8.484-3.121-11.978ZM7.5115 10.908c-1.0373 0-1.8879-.954-1.8879-2.114 0-1.161.8325-2.115 1.8879-2.115 1.0555 0 1.9061.954 1.8879 2.115.0016 1.16-.8325 2.114-1.8879 2.114Zm6.9769 0c-1.0373 0-1.8879-.954-1.8879-2.114 0-1.161.8324-2.115 1.8879-2.115 1.0554 0 1.9061.954 1.8879 2.115 0 1.16-.8325 2.114-1.8879 2.114Z"/></svg>
</div>
<div class="DH_Btn DH_Btn_Icon DH_NoSel" id="DH_GitHub_Btn" style="background:#24292e;outline:2px solid rgba(255,255,255,.18);outline-offset:-2px;">
<svg width="18" height="18" viewBox="0 0 22 22" fill="#FFF"><path fill-rule="evenodd" clip-rule="evenodd" d="M11.009.5C5.198.5.5 5.313.5 11.266c0 4.759 3.01 8.788 7.186 10.214.522.107.713-.232.713-.517 0-.25-.017-1.105-.017-1.997-2.923.642-3.532-1.283-3.532-1.283-.47-1.248-1.166-1.568-1.166-1.568-.957-.659.07-.659.07-.659 1.062.071 1.619 1.105 1.619 1.105.94 1.64 2.453 1.176 3.062.891.087-.695.366-1.176.661-1.444-2.332-.25-4.785-1.176-4.785-5.312 0-1.176.418-2.139 1.08-2.887-.106-.267-.461-1.373.105-2.852 0 0 .888-.285 2.899 1.09a9.847 9.847 0 0 1 2.636-.356c.888 0 1.793.125 2.628.356 2.01-1.375 2.898-1.09 2.898-1.09.566 1.479.21 2.585.105 2.852.662.748 1.08 1.711 1.08 2.887 0 4.136-2.453 5.045-4.803 5.312.383.338.714.98.714 2.004 0 1.444-.018 2.606-.018 2.963 0 .285.192.624.714.48C18.49 20.054 21.5 16.025 21.5 11.266 21.517 5.313 16.802.5 11.009.5Z"/></svg>
</div>
</div>
<div class="DH_HStack_8" id="DH_User_Row" style="display:none;gap:10px;">
<div class="DH_Avatar" id="DH_Avatar">👤</div>
<div class="DH_VStack_4" style="flex:1 0 0;min-width:0;align-items:flex-start;">
<p class="DH_T1 DH_NoSel" id="DH_UName" style="font-size:14px;align-self:stretch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;"></p>
<div class="DH_HStack_4" style="gap:10px;">
<div class="DH_HStack_4" style="gap:3px;">
<img class="DH_Stat_Ico" src="https://d35aaqx5ub95lt.cloudfront.net/images/profile/01ce3a817dd01842581c3d18debcbc46.svg">
<span class="DH_Stat_Val DH_NoSel" id="DH_UXP">0</span>
</div>
<div class="DH_HStack_4" style="gap:3px;">
<img class="DH_Stat_Ico" src="https://d35aaqx5ub95lt.cloudfront.net/images/gems/45c14e05be9c1af1d7d0b54c6eed7eee.svg">
<span class="DH_Stat_Val DH_NoSel" id="DH_UGems">0</span>
</div>
<div class="DH_HStack_4" style="gap:3px;">
<img class="DH_Stat_Ico" src="https://d35aaqx5ub95lt.cloudfront.net/images/icons/398e4298a3b39ce566050e5c041949ef.svg">
<span class="DH_Stat_Val DH_NoSel" id="DH_UStreak">0</span>
</div>
</div>
</div>
<svg id="DH_AccSettings_Btn" width="24" height="24" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg" title="Account Manager"
style="cursor:pointer;flex-shrink:0;transition:filter 0.3s,transform 0.3s;transform-origin:center;">
<path d="M18.1,11c-3.9,0-7,3.1-7,7s3.1,7,7,7c3.9,0,7-3.1,7-7S22,11,18.1,11z M18.1,23c-2.8,0-5-2.2-5-5s2.2-5,5-5c2.8,0,5,2.2,5,5S20.9,23,18.1,23z" fill="rgb(var(--DH-blue))" stroke="rgb(var(--DH-blue))" stroke-width="1.2" stroke-linejoin="round" paint-order="stroke fill"/>
<path d="M32.8,14.7L30,13.8l-0.6-1.5l1.4-2.6c0.3-0.6,0.2-1.4-0.3-1.9l-2.4-2.4c-0.5-0.5-1.3-0.6-1.9-0.3l-2.6,1.4l-1.5-0.6l-0.9-2.8C21,2.5,20.4,2,19.7,2h-3.4c-0.7,0-1.3,0.5-1.4,1.2L14,6c-0.6,0.1-1.1,0.3-1.6,0.6L9.8,5.2C9.2,4.9,8.4,5,7.9,5.5L5.5,7.9C5,8.4,4.9,9.2,5.2,9.8l1.3,2.5c-0.2,0.5-0.4,1.1-0.6,1.6l-2.8,0.9C2.5,15,2,15.6,2,16.3v3.4c0,0.7,0.5,1.3,1.2,1.5L6,22.1l0.6,1.5l-1.4,2.6c-0.3,0.6-0.2,1.4,0.3,1.9l2.4,2.4c0.5,0.5,1.3,0.6,1.9,0.3l2.6-1.4l1.5,0.6l0.9,2.9c0.2,0.6,0.8,1.1,1.5,1.1h3.4c0.7,0,1.3-0.5,1.5-1.1l0.9-2.9l1.5-0.6l2.6,1.4c0.6,0.3,1.4,0.2,1.9-0.3l2.4-2.4c0.5-0.5,0.6-1.3,0.3-1.9l-1.4-2.6l0.6-1.5l2.9-0.9c0.6-0.2,1.1-0.8,1.1-1.5v-3.4C34,15.6,33.5,14.9,32.8,14.7z M32,19.4l-3.6,1.1L28.3,21c-0.3,0.7-0.6,1.4-0.9,2.1l-0.3,0.5l1.8,3.3l-2,2l-3.3-1.8l-0.5,0.3c-0.7,0.4-1.4,0.7-2.1,0.9l-0.5,0.1L19.4,32h-2.8l-1.1-3.6L15,28.3c-0.7-0.3-1.4-0.6-2.1-0.9l-0.5-0.3l-3.3,1.8l-2-2l1.8-3.3l-0.3-0.5c-0.4-0.7-0.7-1.4-0.9-2.1l-0.1-0.5L4,19.4v-2.8l3.4-1l0.2-0.5c0.2-0.8,0.5-1.5,0.9-2.2l0.3-0.5L7.1,9.1l2-2l3.2,1.8l0.5-0.3c0.7-0.4,1.4-0.7,2.2-0.9l0.5-0.2L16.6,4h2.8l1.1,3.5L21,7.7c0.7,0.2,1.4,0.5,2.1,0.9l0.5,0.3l3.3-1.8l2,2l-1.8,3.3l0.3,0.5c0.4,0.7,0.7,1.4,0.9,2.1l0.1,0.5l3.6,1.1V19.4z" fill="rgb(var(--DH-blue))" stroke="rgb(var(--DH-blue))" stroke-width="1.2" stroke-linejoin="round" paint-order="stroke fill"/>
</svg>
</div>
<div class="DH_Divider"></div>
<div class="DH_VStack_8">
<p class="DH_T1 DH_NoSel" style="align-self:stretch;">How much XP would you like to gain?</p>
<div class="DH_HStack_8">
<div class="DH_Input_Wrap">
<p class="DH_T1 DH_NoSel" style="color:rgba(var(--DH-blue),0.38);font-size:13px;flex-shrink:0;">#</p>
<input type="number" class="DH_Input DH_NoSel" id="DH_XP_Input" placeholder="0" min="30" max="500000">
</div>
<button class="DH_Input_Btn DH_NoSel" id="DH_XP_Btn" disabled>
<span class="DH_Btn_Label" id="DH_XP_Lbl" style="color:#fff;">GET</span>
</button>
</div>
<div class="DH_Prog_Wrap" id="DH_XP_Prog"><div class="DH_Prog_Fill" id="DH_XP_Fill"></div></div>
</div>
<div class="DH_VStack_8">
<p class="DH_T1 DH_NoSel" style="align-self:stretch;">How many Gems would you like to gain?</p>
<div class="DH_HStack_8">
<div class="DH_Input_Wrap">
<svg class="DH_Shimmer" width="120" height="48" viewBox="0 0 120 48" fill="none">
<path opacity="0.4" d="M72 0H96L72 48H48L72 0Z" fill="rgb(var(--DH-blue))"/>
<path opacity="0.4" d="M24 0H60L36 48H0L24 0Z" fill="rgb(var(--DH-blue))"/>
<path opacity="0.4" d="M108 0H120L96 48H84L108 0Z" fill="rgb(var(--DH-blue))"/>
</svg>
<p class="DH_T1 DH_NoSel" style="color:rgba(var(--DH-blue),0.38);font-size:14px;flex-shrink:0;">#</p>
<input type="number" class="DH_Input DH_NoSel" id="DH_Gem_Input" placeholder="0" min="1" max="500000">
</div>
<button class="DH_Input_Btn DH_NoSel" id="DH_Gem_Btn" disabled>
<span class="DH_Btn_Label" id="DH_Gem_Lbl" style="color:#fff;">GET</span>
</button>
</div>
<div class="DH_Prog_Wrap" id="DH_Gem_Prog"><div class="DH_Prog_Fill" id="DH_Gem_Fill"></div></div>
</div>
<div class="DH_VStack_8">
<p class="DH_T1 DH_NoSel" style="align-self:stretch;">How many Streak days to restore?</p>
<div class="DH_HStack_8">
<div class="DH_Input_Wrap">
<p class="DH_T1 DH_NoSel" style="color:rgba(var(--DH-blue),0.38);font-size:14px;flex-shrink:0;">#</p>
<input type="number" class="DH_Input DH_NoSel" id="DH_Streak_Input" placeholder="0" min="1" max="3650">
</div>
<button class="DH_Input_Btn DH_NoSel" id="DH_Streak_Btn" disabled>
<span class="DH_Btn_Label" id="DH_Streak_Lbl" style="color:#fff;">RUN</span>
</button>
</div>
<div class="DH_Prog_Wrap" id="DH_Streak_Prog"><div class="DH_Prog_Fill" id="DH_Streak_Fill"></div></div>
</div>
<div class="DH_Divider"></div>
<div class="DH_Btn DH_Btn_Blue_Ghost DH_NoSel" id="DH_Settings_Btn" style="align-self:stretch; justify-content:space-between; padding:10px 12px;">
<div style="display:flex; align-items:center; gap:8px;">
<div class="DH_Btn_Ico">
<svg width="18" height="18" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path fill="rgb(var(--DH-blue))" d="M39,15c0-2.2-1.8-4-4-4h-6c-0.7,0-1.1-0.8-0.7-1.4c0.6-1,0.9-2.2,0.6-3.5c-0.4-2-1.9-3.6-3.8-4C21.8,1.4,19,3.9,19,7c0,1,0.3,1.8,0.7,2.6c0.4,0.6,0,1.4-0.8,1.4h-6c-2.2,0-4,1.8-4,4v7c0,0.7,0.8,1.1,1.4,0.7c1-0.6,2.2-0.9,3.5-0.6c2,0.4,3.6,1.9,4,3.8c0.7,3.2-1.8,6.1-4.9,6.1c-1,0-1.8-0.3-2.6-0.7C9.8,30.9,9,31.3,9,32v6c0,2.2,1.8,4,4,4h22c2.2,0,4-1.8,4-4V15z"/>
</svg>
</div>
<p class="DH_T1 DH_NoSel" style="color:rgb(var(--DH-blue));">Extra Features</p>
</div>
<svg width="8" height="13" viewBox="0 0 8 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 1l6 5.5L1 12" stroke="rgb(var(--DH-blue))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div class="DH_Btn DH_Btn_Blue_Ghost DH_NoSel" id="DH_Page4_Btn" style="align-self:stretch; justify-content:space-between; padding:10px 12px;">
<div style="display:flex; align-items:center; gap:8px;">
<div class="DH_Btn_Ico">
<svg width="16" height="16" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18.1,11c-3.9,0-7,3.1-7,7s3.1,7,7,7c3.9,0,7-3.1,7-7S22,11,18.1,11z M18.1,23c-2.8,0-5-2.2-5-5s2.2-5,5-5c2.8,0,5,2.2,5,5S20.9,23,18.1,23z" fill="rgb(var(--DH-blue))" stroke="rgb(var(--DH-blue))" stroke-width="1.2" stroke-linejoin="round" paint-order="stroke fill"/>
<path d="M32.8,14.7L30,13.8l-0.6-1.5l1.4-2.6c0.3-0.6,0.2-1.4-0.3-1.9l-2.4-2.4c-0.5-0.5-1.3-0.6-1.9-0.3l-2.6,1.4l-1.5-0.6l-0.9-2.8C21,2.5,20.4,2,19.7,2h-3.4c-0.7,0-1.3,0.5-1.4,1.2L14,6c-0.6,0.1-1.1,0.3-1.6,0.6L9.8,5.2C9.2,4.9,8.4,5,7.9,5.5L5.5,7.9C5,8.4,4.9,9.2,5.2,9.8l1.3,2.5c-0.2,0.5-0.4,1.1-0.6,1.6l-2.8,0.9C2.5,15,2,15.6,2,16.3v3.4c0,0.7,0.5,1.3,1.2,1.5L6,22.1l0.6,1.5l-1.4,2.6c-0.3,0.6-0.2,1.4,0.3,1.9l2.4,2.4c0.5,0.5,1.3,0.6,1.9,0.3l2.6-1.4l1.5,0.6l0.9,2.9c0.2,0.6,0.8,1.1,1.5,1.1h3.4c0.7,0,1.3-0.5,1.5-1.1l0.9-2.9l1.5-0.6l2.6,1.4c0.6,0.3,1.4,0.2,1.9-0.3l2.4-2.4c0.5-0.5,0.6-1.3,0.3-1.9l-1.4-2.6l0.6-1.5l2.9-0.9c0.6-0.2,1.1-0.8,1.1-1.5v-3.4C34,15.6,33.5,14.9,32.8,14.7z M32,19.4l-3.6,1.1L28.3,21c-0.3,0.7-0.6,1.4-0.9,2.1l-0.3,0.5l1.8,3.3l-2,2l-3.3-1.8l-0.5,0.3c-0.7,0.4-1.4,0.7-2.1,0.9l-0.5,0.1L19.4,32h-2.8l-1.1-3.6L15,28.3c-0.7-0.3-1.4-0.6-2.1-0.9l-0.5-0.3l-3.3,1.8l-2-2l1.8-3.3l-0.3-0.5c-0.4-0.7-0.7-1.4-0.9-2.1l-0.1-0.5L4,19.4v-2.8l3.4-1l0.2-0.5c0.2-0.8,0.5-1.5,0.9-2.2l0.3-0.5L7.1,9.1l2-2l3.2,1.8l0.5-0.3c0.7-0.4,1.4-0.7,2.2-0.9l0.5-0.2L16.6,4h2.8l1.1,3.5L21,7.7c0.7,0.2,1.4,0.5,2.1,0.9l0.5,0.3l3.3-1.8l2,2l-1.8,3.3l0.3,0.5c0.4,0.7,0.7,1.4,0.9,2.1l0.1,0.5l3.6,1.1V19.4z" fill="rgb(var(--DH-blue))" stroke="rgb(var(--DH-blue))" stroke-width="1.2" stroke-linejoin="round" paint-order="stroke fill"/>
</svg>
</div>
<p class="DH_T1 DH_NoSel" style="color:rgb(var(--DH-blue));">Settings</p>
</div>
<svg width="8" height="13" viewBox="0 0 8 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 1l6 5.5L1 12" stroke="rgb(var(--DH-blue))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div class="DH_HStack_Auto">
<p class="DH_T2 DH_NoSel" style="color:rgba(var(--DH-blue),0.45);">twisk.fun</p>
<p class="DH_T2 DH_NoSel" style="color:rgba(var(--DH-blue),0.45);">v2026.04.21</p>
</div>
</div>
<div class="DH_Page" id="DH_Page_2">
<div class="DH_HStack_4 DH_NoSel" id="DH_Back_Btn" style="align-self:flex-start;cursor:pointer;opacity:0.55;">
<svg width="8" height="14" viewBox="0 0 9 16" fill="none"><path d="M8 1L2 8l6 7" stroke="rgb(var(--color-wolf,60,60,67))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
<p class="DH_T1">Back</p>
</div>
<div class="DH_HStack_Auto" style="align-self:stretch;">
<div style="display:flex;flex-direction:column;gap:2px;flex:1;min-width:0;">
<p class="DH_T1 DH_NoSel">Farm Practice</p>
<p class="DH_T2 DH_NoSel" style="font-size:11px;">0 = unlimited practice sessions</p>
</div>
<button class="DH_Sm_Btn DH_NoSel" id="DH_Practice_Btn" disabled>
<span class="DH_Sm_Btn_Label" id="DH_Practice_Lbl" style="color:#fff;">RUN</span>
</button>
</div>
<!-- Practice input inline below button -->
<div style="display:flex;align-items:center;gap:8px;align-self:stretch;">
<div class="DH_Input_Wrap" style="flex:1;">
<p class="DH_T1 DH_NoSel" style="color:rgba(var(--DH-blue),0.38);font-size:14px;flex-shrink:0;">#</p>
<input type="number" class="DH_Input DH_NoSel" id="DH_Practice_Input" placeholder="0" min="0" max="9999">
</div>
</div>
<div class="DH_Divider"></div>
<!-- Shop Items nav -->
<div class="DH_Btn DH_Btn_Blue_Ghost DH_NoSel" id="DH_Shop_Btn" style="align-self:stretch;justify-content:space-between;padding:10px 12px;">
<div style="display:flex;align-items:center;gap:8px;">
<div class="DH_Btn_Ico">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M22.36 8.27L22.07 5.5C21.65 2.48 20.28 1.25 17.35 1.25H14.99H13.51H10.47H8.99H6.59C3.65 1.25 2.29 2.48 1.86 5.53L1.59 8.28C1.49 9.35 1.78 10.39 2.41 11.2C3.17 12.19 4.34 12.75 5.64 12.75C6.9 12.75 8.11 12.12 8.87 11.11C9.55 12.12 10.71 12.75 12 12.75C13.29 12.75 14.42 12.15 15.11 11.15C15.88 12.14 17.07 12.75 18.31 12.75C19.64 12.75 20.84 12.16 21.59 11.12C22.19 10.32 22.46 9.31 22.36 8.27Z" fill="rgb(var(--DH-blue))"/>
<path d="M11.35 16.66C10.08 16.79 9.12 17.87 9.12 19.15V21.89C9.12 22.16 9.34 22.38 9.61 22.38H14.38C14.65 22.38 14.87 22.16 14.87 21.89V19.5C14.88 17.41 13.65 16.42 11.35 16.66Z" fill="rgb(var(--DH-blue))"/>
<path d="M21.37 14.4V17.38C21.37 20.14 19.13 22.38 16.37 22.38C16.1 22.38 15.88 22.16 15.88 21.89V19.5C15.88 18.22 15.49 17.22 14.73 16.54C14.06 15.93 13.15 15.63 12.02 15.63C11.77 15.63 11.52 15.64 11.25 15.67C9.47 15.85 8.12 17.35 8.12 19.15V21.89C8.12 22.16 7.9 22.38 7.63 22.38C4.87 22.38 2.63 20.14 2.63 17.38V14.42C2.63 13.72 3.32 13.25 3.97 13.48C4.24 13.57 4.51 13.64 4.79 13.68C4.91 13.7 5.04 13.72 5.16 13.72C5.32 13.74 5.48 13.75 5.64 13.75C6.8 13.75 7.94 13.32 8.84 12.58C9.7 13.32 10.82 13.75 12 13.75C13.19 13.75 14.29 13.34 15.15 12.6C16.05 13.33 17.17 13.75 18.31 13.75C18.49 13.75 18.67 13.74 18.84 13.72C18.96 13.71 19.07 13.7 19.18 13.68C19.49 13.64 19.77 13.55 20.05 13.46C20.7 13.24 21.37 13.72 21.37 14.4Z" fill="rgb(var(--DH-blue))"/>
</svg>
</div>
<p class="DH_T1 DH_NoSel" style="color:rgb(var(--DH-blue));">Shop Items</p>
</div>
<svg width="8" height="13" viewBox="0 0 8 13" fill="none"><path d="M1 1l6 5.5L1 12" stroke="rgb(var(--DH-blue))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
<!-- Auto League -->
<div class="DH_HStack_Auto" style="align-self:stretch;">
<div style="display:flex;flex-direction:column;gap:2px;flex:1;min-width:0;">
<p class="DH_T1 DH_NoSel">Auto League #1</p>
<p class="DH_T2 DH_NoSel" style="font-size:11px;">Farm XP until rank #1</p>
</div>
<button class="DH_Sm_Btn DH_NoSel" id="DH_League_Btn" disabled>
<span class="DH_Sm_Btn_Label" id="DH_League_Lbl" style="color:#fff;">RUN</span>
</button>
</div>
<div class="DH_Prog_Wrap" id="DH_League_Prog" style="align-self:stretch;"><div class="DH_Prog_Fill" id="DH_League_Fill"></div></div>
<!-- Auto Daily Quest - SIMPLIFIED -->
<div class="DH_HStack_Auto" style="align-self:stretch;">
<div style="display:flex;flex-direction:column;gap:2px;flex:1;min-width:0;">
<p class="DH_T1 DH_NoSel">Auto Daily Quest</p>
<p class="DH_T2 DH_NoSel" style="font-size:11px;">Complete all daily quests</p>
</div>
<button class="DH_Sm_Btn DH_NoSel" id="DH_Quest_Btn" disabled>
<span class="DH_Sm_Btn_Label" id="DH_Quest_Lbl" style="color:#fff;">RUN</span>
</button>
</div>
<!-- Monthly Quest -->
<div class="DH_HStack_Auto" style="align-self:stretch;cursor:pointer;" id="DH_MonthlyQuest_Nav_Btn">
<div style="display:flex;flex-direction:column;gap:2px;flex:1;min-width:0;">
<p class="DH_T1 DH_NoSel">Claim Monthly Quest</p>
<p class="DH_T2 DH_NoSel" style="font-size:11px;">View & claim monthly quests</p>
</div>
<button class="DH_Sm_Btn DH_NoSel" id="DH_MonthlyQuest_Claim_Btn" disabled>
<span class="DH_Sm_Btn_Label" style="color:#fff;">GET</span>
</button>
</div>
<div class="DH_Divider"></div>
</div>
<!-- PAGE 4: Settings -->
<div class="DH_Page" id="DH_Page_4">
<div class="DH_HStack_4 DH_NoSel" id="DH_Settings_Back_Btn" style="align-self:flex-start;cursor:pointer;opacity:0.55;">
<svg width="8" height="14" viewBox="0 0 9 16" fill="none"><path d="M8 1L2 8l6 7" stroke="rgb(var(--color-wolf,60,60,67))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
<p class="DH_T1">Back</p>
</div>
<div style="width:100%;gap:8px;display:flex;flex-direction:column;align-items:flex-start;overflow-y:auto;max-height:360px;padding-right:2px;" class="DH_Scroll_Inner">
<!-- Loop Delay -->
<div class="DH_VStack_8" style="align-self:stretch;">
<p class="DH_T1 DH_NoSel" style="align-self:stretch;">Loop delay (ms)</p>
<div class="DH_HStack_8">
<div class="DH_Input_Wrap">
<p class="DH_T1 DH_NoSel" style="color:rgba(var(--DH-blue),0.50);font-size:13px;flex-shrink:0;">ms</p>
<input type="number" class="DH_Input DH_NoSel" id="DH_Delay_Input" placeholder="500" min="0" max="10000">
</div>
<button class="DH_Input_Btn DH_NoSel" id="DH_Delay_Btn">
<span class="DH_Btn_Label" id="DH_Delay_Lbl" style="color:#fff;">SAVE</span>
</button>
</div>
</div>
<div class="DH_Divider"></div>
<!-- Free Duolingo Max toggle -->
<div class="DH_HStack_Auto" style="align-self:stretch;padding:4px 0;">
<div style="display:flex;flex-direction:column;gap:2px;">
<p class="DH_T1 DH_NoSel">Free Duolingo Max</p>
<p class="DH_T2 DH_NoSel" style="font-size:11px;">Client-side only, reload to apply</p>
</div>
<label class="DH_Toggle">
<input type="checkbox" id="DH_Super_Toggle">
<span class="DH_Toggle_Slider"></span>
</label>
</div>
<!-- Hide Profile toggle -->
<div class="DH_HStack_Auto" style="align-self:stretch;padding:4px 0;">
<div style="display:flex;flex-direction:column;gap:2px;">
<p class="DH_T1 DH_NoSel">Hide Profile</p>
<p class="DH_T2 DH_NoSel" id="DH_HideProfile_Status" style="font-size:11px;">Loading…</p>
</div>
<label class="DH_Toggle">
<input type="checkbox" id="DH_HideProfile_Toggle" disabled>
<span class="DH_Toggle_Slider"></span>
</label>
</div>
<!-- Inject Solver toggle -->
<div class="DH_HStack_Auto" style="align-self:stretch;padding:4px 0;">
<div style="display:flex;flex-direction:column;gap:2px;">
<p class="DH_T1 DH_NoSel">Auto Solver</p>
<p class="DH_T2 DH_NoSel" style="font-size:11px;">Show Auto Solver buttons during lessons</p>
</div>
<label class="DH_Toggle">
<input type="checkbox" id="DH_Solver_Toggle">
<span class="DH_Toggle_Slider"></span>
</label>
</div>
<!-- Hide Animation toggle -->
<div class="DH_HStack_Auto" style="align-self:stretch;padding:4px 0;">
<div style="display:flex;flex-direction:column;gap:2px;">
<p class="DH_T1 DH_NoSel">Hide Animation</p>
<p class="DH_T2 DH_NoSel" style="font-size:11px;">Hide Duolingo images & animations</p>
</div>
<label class="DH_Toggle">
<input type="checkbox" id="DH_HideAnim_Toggle">
<span class="DH_Toggle_Slider"></span>
</label>
</div>
<div class="DH_Divider"></div>
<div class="DH_HStack_Auto" style="align-self:stretch;padding:2px 0;">
<p class="DH_T2 DH_NoSel" id="DH_License_Open_Btn" style="color:rgba(var(--DH-blue),0.45);cursor:pointer;text-decoration:underline;text-underline-offset:2px;">DuoHacker V2</p>
<p class="DH_T2 DH_NoSel" id="DH_Credits_Btn" style="color:rgb(var(--DH-blue));cursor:pointer;font-weight:700;text-decoration:underline;text-underline-offset:2px;">View Credits</p>
</div>
</div>
</div>
<!-- PAGE 9: License -->
<div class="DH_Page" id="DH_Page_9" style="flex:1;min-height:0;">
<div class="DH_HStack_4 DH_NoSel" id="DH_License_Back_Btn" style="align-self:flex-start;cursor:pointer;opacity:0.55;">
<svg width="8" height="14" viewBox="0 0 9 16" fill="none"><path d="M8 1L2 8l6 7" stroke="rgb(var(--color-wolf,60,60,67))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
<p class="DH_T1">Back</p>
</div>
<div class="DH_HStack_Auto DH_NoSel" style="align-self:stretch;">
<p class="DH_T1" style="font-size:15px;">BY-NC-ND 4.0 License</p>
<p class="DH_T2" style="font-size:11px;color:rgba(var(--DH-blue),0.50);">DuoHacker</p>
</div>
<div class="DH_License_Scroll">
<p id="DH_License_Text" class="DH_NoSel">Loading…</p>
</div>
</div>
<!-- PAGE 3: Shop -->
<div class="DH_Page" id="DH_Page_3">
<div class="DH_HStack_4 DH_NoSel" id="DH_Shop_Back_Btn" style="align-self:flex-start;cursor:pointer;opacity:0.55;">
<svg width="8" height="14" viewBox="0 0 9 16" fill="none"><path d="M8 1L2 8l6 7" stroke="rgb(var(--color-wolf,60,60,67))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
<p class="DH_T1">Back</p>
</div>
<input type="text" class="DH_Search DH_NoSel" id="DH_Shop_Search" placeholder="Search items...">
<!-- Shop scrollable container -->
<div class="DH_Scroll_Inner" id="DH_Shop_Container" style="max-height:300px;">
<p class="DH_T2 DH_NoSel" style="text-align:center;padding:8px 0;">Loading shop...</p>
</div>
</div>
<!-- PAGE 5: Account Manager -->
<div class="DH_Page" id="DH_Page_5">
<div class="DH_HStack_4 DH_NoSel" id="DH_AccMgr_Back_Btn" style="align-self:flex-start;cursor:pointer;opacity:0.55;">
<svg width="8" height="14" viewBox="0 0 9 16" fill="none"><path d="M8 1L2 8l6 7" stroke="rgb(var(--color-wolf,60,60,67))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
<p class="DH_T1">Back</p>
</div>
<div class="DH_HStack_Auto" style="align-self:stretch;">
<p class="DH_T1 DH_NoSel" style="font-size:14px;font-weight:800;">Account Manager</p>
<button class="DH_Sm_Btn DH_NoSel" id="DH_AccSave_Btn" disabled style="height:30px;padding:0 12px;">
<span class="DH_Sm_Btn_Label" style="color:#fff;font-size:11px;">SAVE CURRENT</span>
</button>
</div>
<div class="DH_Divider"></div>
<div id="DH_AccList_Wrap" class="DH_Scroll_Inner" style="max-height:260px;width:100%;gap:6px;">
<p class="DH_T2 DH_NoSel" style="text-align:center;padding:8px 0;">No saved accounts.</p>
</div>
</div>
<!-- PAGE 7: Credits -->
<div class="DH_Page" id="DH_Page_7">
<div class="DH_HStack_4 DH_NoSel" id="DH_Credits_Back_Btn" style="align-self:flex-start;cursor:pointer;opacity:0.55;">
<svg width="8" height="14" viewBox="0 0 9 16" fill="none"><path d="M8 1L2 8l6 7" stroke="rgb(var(--color-wolf,60,60,67))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
<p class="DH_T1">Back</p>
</div>
<p class="DH_T1 DH_NoSel" style="font-size:14px;font-weight:800;align-self:stretch;">Credits</p>
<div class="DH_Divider"></div>
<div id="DH_Credits_Container" class="DH_Scroll_Inner" style="max-height:280px;width:100%;gap:10px;">
</div>
</div>
<!-- PAGE 6: Monthly Quests -->
<div class="DH_Page" id="DH_Page_6">
<div class="DH_HStack_4 DH_NoSel" id="DH_MQ_Back_Btn" style="align-self:flex-start;cursor:pointer;opacity:0.55;">
<svg width="8" height="14" viewBox="0 0 9 16" fill="none"><path d="M8 1L2 8l6 7" stroke="rgb(var(--color-wolf,60,60,67))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
<p class="DH_T1">Back</p>
</div>
<div class="DH_HStack_Auto" style="align-self:stretch;">
<p class="DH_T1 DH_NoSel" style="font-size:14px;font-weight:800;">Monthly Quests</p>
<button class="DH_Sm_Btn DH_NoSel" id="DH_MQ_ClaimAll_Btn" disabled style="height:30px;padding:0 12px;">
<span class="DH_Sm_Btn_Label" style="color:#fff;font-size:11px;">CLAIM</span>
</button>
</div>
<div class="DH_Divider"></div>
<div id="DH_MQ_Container" class="DH_Scroll_Inner" style="max-height:300px;width:100%;">
<p class="DH_T2 DH_NoSel" style="text-align:center;padding:8px 0;">Loading quests...</p>
</div>
</div>
<!-- PAGE 8: V1 Mode — simple farm with live counters, no extra features -->
<div class="DH_Page" id="DH_Page_V1">
<div class="DH_Divider"></div>
<!-- User row (reused from V2 data) -->
<div class="DH_HStack_8" id="DH_V1_User_Row" style="display:none;gap:10px;">
<div class="DH_Avatar" id="DH_V1_Avatar">👤</div>
<div class="DH_VStack_4" style="flex:1 0 0;min-width:0;align-items:flex-start;">
<p class="DH_T1 DH_NoSel" id="DH_V1_UName" style="font-size:14px;align-self:stretch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;"></p>
<div class="DH_HStack_4" style="gap:10px;">
<div class="DH_HStack_4" style="gap:3px;">
<img class="DH_Stat_Ico" src="https://d35aaqx5ub95lt.cloudfront.net/images/profile/01ce3a817dd01842581c3d18debcbc46.svg">
<span class="DH_Stat_Val DH_NoSel" id="DH_V1_UXP">0</span>
</div>
<div class="DH_HStack_4" style="gap:3px;">
<img class="DH_Stat_Ico" src="https://d35aaqx5ub95lt.cloudfront.net/images/gems/45c14e05be9c1af1d7d0b54c6eed7eee.svg">
<span class="DH_Stat_Val DH_NoSel" id="DH_V1_UGems">0</span>
</div>
<div class="DH_HStack_4" style="gap:3px;">
<img class="DH_Stat_Ico" src="https://d35aaqx5ub95lt.cloudfront.net/images/icons/398e4298a3b39ce566050e5c041949ef.svg">
<span class="DH_Stat_Val DH_NoSel" id="DH_V1_UStreak">0</span>
</div>
</div>
</div>
</div>
<div class="DH_Divider" id="DH_V1_User_Divider" style="display:none;"></div>
<!-- XP Farm -->
<div class="DH_VStack_8" style="align-self:stretch;">
<p class="DH_T1 DH_NoSel" style="align-self:stretch;">XP Farming</p>
<div class="DH_HStack_8">
<div class="DH_Input_Wrap">
<img class="DH_Stat_Ico" src="https://d35aaqx5ub95lt.cloudfront.net/images/profile/01ce3a817dd01842581c3d18debcbc46.svg" style="flex-shrink:0;">
<input type="number" class="DH_Input DH_NoSel" id="DH_V1_XP_Input" placeholder="0" readonly style="pointer-events:none;">
</div>
<button class="DH_Input_Btn DH_NoSel" id="DH_V1_XP_Btn" disabled>
<span class="DH_Btn_Label" id="DH_V1_XP_Lbl" style="color:#fff;">RUN</span>
</button>
</div>
<div class="DH_Prog_Wrap" id="DH_V1_XP_Prog"><div class="DH_Prog_Fill" id="DH_V1_XP_Fill"></div></div>
</div>
<!-- Gems Farm -->
<div class="DH_VStack_8" style="align-self:stretch;">
<p class="DH_T1 DH_NoSel" style="align-self:stretch;">Gems Farming</p>
<div class="DH_HStack_8">
<div class="DH_Input_Wrap">
<img class="DH_Stat_Ico" src="https://d35aaqx5ub95lt.cloudfront.net/images/gems/45c14e05be9c1af1d7d0b54c6eed7eee.svg" style="flex-shrink:0;">
<input type="number" class="DH_Input DH_NoSel" id="DH_V1_Gem_Input" placeholder="0" readonly style="pointer-events:none;">
</div>
<button class="DH_Input_Btn DH_NoSel" id="DH_V1_Gem_Btn" disabled>
<span class="DH_Btn_Label" id="DH_V1_Gem_Lbl" style="color:#fff;">RUN</span>
</button>
</div>
<div class="DH_Prog_Wrap" id="DH_V1_Gem_Prog"><div class="DH_Prog_Fill" id="DH_V1_Gem_Fill"></div></div>
</div>
<!-- Streak Farm -->
<div class="DH_VStack_8" style="align-self:stretch;">
<p class="DH_T1 DH_NoSel" style="align-self:stretch;">Streak Farming</p>
<div class="DH_HStack_8">
<div class="DH_Input_Wrap">
<img class="DH_Stat_Ico" src="https://d35aaqx5ub95lt.cloudfront.net/images/icons/398e4298a3b39ce566050e5c041949ef.svg" style="flex-shrink:0;">
<input type="number" class="DH_Input DH_NoSel" id="DH_V1_Streak_Input" placeholder="0" readonly style="pointer-events:none;">
</div>
<button class="DH_Input_Btn DH_NoSel" id="DH_V1_Streak_Btn" disabled>
<span class="DH_Btn_Label" id="DH_V1_Streak_Lbl" style="color:#fff;">RUN</span>
</button>
</div>
<div class="DH_Prog_Wrap" id="DH_V1_Streak_Prog"><div class="DH_Prog_Fill" id="DH_V1_Streak_Fill"></div></div>
</div>
<div class="DH_Divider"></div>
<!-- Settings (same as V2 settings page) -->
<div class="DH_Btn DH_Btn_Blue_Ghost DH_NoSel" id="DH_V1_Settings_Btn" style="align-self:stretch; justify-content:space-between; padding:10px 12px;">
<div style="display:flex; align-items:center; gap:8px;">
<div class="DH_Btn_Ico">
<svg width="16" height="16" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18.1,11c-3.9,0-7,3.1-7,7s3.1,7,7,7c3.9,0,7-3.1,7-7S22,11,18.1,11z M18.1,23c-2.8,0-5-2.2-5-5s2.2-5,5-5c2.8,0,5,2.2,5,5S20.9,23,18.1,23z" fill="rgb(var(--DH-blue))" stroke="rgb(var(--DH-blue))" stroke-width="1.2" stroke-linejoin="round" paint-order="stroke fill"/>
<path d="M32.8,14.7L30,13.8l-0.6-1.5l1.4-2.6c0.3-0.6,0.2-1.4-0.3-1.9l-2.4-2.4c-0.5-0.5-1.3-0.6-1.9-0.3l-2.6,1.4l-1.5-0.6l-0.9-2.8C21,2.5,20.4,2,19.7,2h-3.4c-0.7,0-1.3,0.5-1.4,1.2L14,6c-0.6,0.1-1.1,0.3-1.6,0.6L9.8,5.2C9.2,4.9,8.4,5,7.9,5.5L5.5,7.9C5,8.4,4.9,9.2,5.2,9.8l1.3,2.5c-0.2,0.5-0.4,1.1-0.6,1.6l-2.8,0.9C2.5,15,2,15.6,2,16.3v3.4c0,0.7,0.5,1.3,1.2,1.5L6,22.1l0.6,1.5l-1.4,2.6c-0.3,0.6-0.2,1.4,0.3,1.9l2.4,2.4c0.5,0.5,1.3,0.6,1.9,0.3l2.6-1.4l1.5,0.6l0.9,2.9c0.2,0.6,0.8,1.1,1.5,1.1h3.4c0.7,0,1.3-0.5,1.5-1.1l0.9-2.9l1.5-0.6l2.6,1.4c0.6,0.3,1.4,0.2,1.9-0.3l2.4-2.4c0.5-0.5,0.6-1.3,0.3-1.9l-1.4-2.6l0.6-1.5l2.9-0.9c0.6-0.2,1.1-0.8,1.1-1.5v-3.4C34,15.6,33.5,14.9,32.8,14.7z M32,19.4l-3.6,1.1L28.3,21c-0.3,0.7-0.6,1.4-0.9,2.1l-0.3,0.5l1.8,3.3l-2,2l-3.3-1.8l-0.5,0.3c-0.7,0.4-1.4,0.7-2.1,0.9l-0.5,0.1L19.4,32h-2.8l-1.1-3.6L15,28.3c-0.7-0.3-1.4-0.6-2.1-0.9l-0.5-0.3l-3.3,1.8l-2-2l1.8-3.3l-0.3-0.5c-0.4-0.7-0.7-1.4-0.9-2.1l-0.1-0.5L4,19.4v-2.8l3.4-1l0.2-0.5c0.2-0.8,0.5-1.5,0.9-2.2l0.3-0.5L7.1,9.1l2-2l3.2,1.8l0.5-0.3c0.7-0.4,1.4-0.7,2.2-0.9l0.5-0.2L16.6,4h2.8l1.1,3.5L21,7.7c0.7,0.2,1.4,0.5,2.1,0.9l0.5,0.3l3.3-1.8l2,2l-1.8,3.3l0.3,0.5c0.4,0.7,0.7,1.4,0.9,2.1l0.1,0.5l3.6,1.1V19.4z" fill="rgb(var(--DH-blue))" stroke="rgb(var(--DH-blue))" stroke-width="1.2" stroke-linejoin="round" paint-order="stroke fill"/>
</svg>
</div>
<p class="DH_T1 DH_NoSel" style="color:rgb(var(--DH-blue));">Settings</p>
</div>
<svg width="8" height="13" viewBox="0 0 8 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 1l6 5.5L1 12" stroke="rgb(var(--DH-blue))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div class="DH_HStack_Auto">
<p class="DH_T2 DH_NoSel" style="color:rgba(var(--DH-blue),0.45);">twisk.fun</p>
<p class="DH_T2 DH_NoSel" style="color:rgba(var(--DH-blue),0.45);">V1 Mode</p>
</div>
</div>
</div>
</div>
`;
document.body.appendChild(_wrap);
let _jwt=null, _sub=null, _hdrs=null, _user=null, _privacy=null;
let _v1Mode=false;
let _v1Running=false, _v1Task=null;
const _v1Earned={xp:0,gems:0,streak:0};
function _v1UpdateDisplay(){
requestAnimationFrame(()=>{
const xi=document.getElementById('DH_V1_XP_Input');
const gi=document.getElementById('DH_V1_Gem_Input');
const si=document.getElementById('DH_V1_Streak_Input');
if(xi) xi.value=_v1Earned.xp>0?String(_v1Earned.xp):'';
if(gi) gi.value=_v1Earned.gems>0?String(_v1Earned.gems):'';
if(si) si.value=_v1Earned.streak>0?String(_v1Earned.streak):'';
});
}
function _v1SyncUser(){
if(!_user) return;
const row=document.getElementById('DH_V1_User_Row');
const div=document.getElementById('DH_V1_User_Divider');
if(row) row.style.display='flex';
if(div) div.style.display='';
document.getElementById('DH_V1_UName').textContent=_user.username||'';
document.getElementById('DH_V1_UXP').textContent=(_user.totalXp||0).toLocaleString();
document.getElementById('DH_V1_UGems').textContent=(_user.gems||0).toLocaleString();
document.getElementById('DH_V1_UStreak').textContent=(_user.streak||0).toLocaleString();
if(_user.picture){
let hq=_user.picture.replace(/\/(medium|large|small)$/,'/xlarge');
if(!hq.endsWith('/xlarge')&&hq.includes('duolingo.com/ssr-avatars')) hq+='/xlarge';
const av=document.getElementById('DH_V1_Avatar');
const img=document.createElement('img');
img.src=hq;
img.style.cssText='width:100%;height:100%;object-fit:cover;border-radius:50%;display:block;';
img.onerror=function(){av.innerHTML='👤';};
av.innerHTML=''; av.appendChild(img);
}
}
let _isOutdated = false, _remoteVersion = '';
let _running=false, _task=null, _hidden=false;
let _delay=parseInt(localStorage.getItem('dh2_delay')||'500',10);
let _shopItems=[];
const _sleep=ms=>new Promise(r=>setTimeout(r,ms));
const GOALS_API='https://goals-api.duolingo.com';
let _pageHistory=[1];
let _questState=null;
let _solverUI = null;
let _isAutoMode = false;
let _solvingIntervalId = null;
let _isInLesson = false;
const _SOLVE_SPEED = 0.1;
let _INJECT_SOLVER_ENABLED = localStorage.getItem('duohacker_inject_solver') === 'true';
const _autoSolver = {
findReact: (dom, traverseUp = 1) => {
const key = Object.keys(dom).find(key => {
return key.startsWith("__reactFiber$") || key.startsWith("__reactInternalInstance$");
});
const domFiber = dom[key];
if (!domFiber) return null;
const GetCompFiber = fiber => {
let parentFiber = fiber.return;
while (typeof parentFiber.type == "string") {
parentFiber = parentFiber.return;
}
return parentFiber;
};
let compFiber = GetCompFiber(domFiber);
for (let i = 0; i < traverseUp; i++) {
compFiber = GetCompFiber(compFiber);
}
return compFiber.stateNode;
},
determineChallengeType: () => {
try {
const t = window.sol?.type;
if (!t) return false;
if (t === 'speak' || t === 'listenSpeak' ||
document.querySelector('[data-test="challenge challenge-listenSpeak"]') ||
document.querySelectorAll('[data-test*="challenge-speak"]').length > 0) return 'Challenge Speak';
if (t === 'listenMatch') return 'Listen Match';
if (document.querySelector('.FmlUF')) {
if (t === 'arrange') return 'Story Arrange';
if (t === 'multiple-choice' || t === 'select-phrases') return 'Story Multiple Choice';
if (t === 'point-to-phrase') return 'Story Point to Phrase';
if (t === 'match') return 'Story Pairs';
}
if (t === 'typeCloze') return 'Type Cloze';
if (t === 'typeClozeTable') return 'Type Cloze Table';
if (t === 'tapClozeTable') return 'Tap Cloze Table';
if (t === 'typeCompleteTable') return 'Type Complete Table';
if (t === 'tapCompleteTable') return 'Tap Complete Table';
if (t === 'patternTapComplete') return 'Pattern Tap Complete';
if (t === 'listenTap') return 'Listen Tap';
if (t === 'listen') return 'Listen Type';
if (t === 'translate') return 'Translate';
if (t === 'completeReverseTranslation') return 'Complete Reverse';
if (document.querySelectorAll('[data-test*="challenge-partialReverseTranslate"]').length > 0) return 'Partial Reverse';
if (t === 'judge') return 'Judge';
if (t === 'dialogue' || t === 'characterIntro' || t === 'selectTranscription') return 'Dialogue';
if (t === 'characterMatch' || t === 'match') {
if (document.querySelectorAll('[data-test$="challenge-tap-token"]').length > 0) return 'Pairs';
}
if (t === 'select' || t === 'characterSelect' || t === 'form' ||
t === 'readComprehension' || t === 'listenComprehension' ||
t === 'selectPronunciation') {
return 'Select Card';
}
if (document.querySelectorAll('[data-test*="challenge-name"]').length > 0 &&
document.querySelectorAll('[data-test="challenge-choice"]').length > 0) return 'Challenge Name';
if (document.querySelectorAll('[data-test="challenge-choice"]').length > 0) {
if (document.querySelectorAll('[data-test="challenge-text-input"]').length > 0) return 'Challenge Choice with Text Input';
return 'Challenge Choice';
}
if (document.querySelectorAll('[data-test$="challenge-tap-token"]').length > 0) {
if (window.sol?.pairs !== undefined) return 'Pairs';
if (window.sol?.correctTokens !== undefined) return 'Tokens Run';
if (window.sol?.correctIndices !== undefined) return 'Indices Run';
}
if (document.querySelectorAll('[data-test="challenge-tap-token-text"]').length > 0) return 'Fill in the Gap';
if (document.querySelectorAll('[data-test="challenge-text-input"]').length > 0) return 'Challenge Text Input';
if (document.querySelectorAll('textarea[data-test="challenge-translate-input"]').length > 0) return 'Challenge Translate Input';
return false;
} catch (error) {
return false;
}
},
setInputValue: (element, value) => {
const isTextarea = element.tagName === 'TEXTAREA';
const prototype = isTextarea ? window.HTMLTextAreaElement : window.HTMLInputElement;
const setter = Object.getOwnPropertyDescriptor(prototype.prototype, "value").set;
setter.call(element, value);
element.dispatchEvent(new Event('input', {
bubbles: true
}));
},
delay: ms => new Promise(resolve => setTimeout(resolve, ms)),
handleChallengeName: async () => {
const articles = window.sol.articles;
const correctSolution = window.sol.correctSolutions[0];
const matchingArticle = articles.find(article => correctSolution.startsWith(article));
if (matchingArticle !== undefined) {
const matchingIndex = articles.indexOf(matchingArticle);
const remainingValue = correctSolution.substring(matchingArticle.length).trim();
const selectedElement = document.querySelector(`[data-test="challenge-choice"]:nth-child(${matchingIndex + 1})`);
if (selectedElement) {
selectedElement.click();
await _autoSolver.delay(50);
}
const input = document.querySelector('[data-test="challenge-text-input"]');
if (input) _autoSolver.setInputValue(input, remainingValue);
}
},
handlePairs: async () => {
const buttons = document.querySelectorAll('[data-test*="challenge-tap-token"]:not(span)');
const texts = document.querySelectorAll('[data-test="challenge-tap-token-text"]');
if (texts.length !== buttons.length || buttons.length === 0) return;
for (const pair of window.sol.pairs || []) {
for (let i = 0; i < buttons.length; i++) {
const button = buttons[i];
if (button.disabled) continue;
const text = texts[i].innerText.toLowerCase().trim();
const matches = text === pair.transliteration?.toLowerCase().trim() ||
text === pair.character?.toLowerCase().trim() ||
text === pair.learningToken?.toLowerCase().trim() ||
text === pair.fromToken?.toLowerCase().trim();
if (matches) {
button.click();
await _autoSolver.delay(50);
}
}
}
},
handleTokensRun: async () => {
const allTokens = document.querySelectorAll('[data-test$="challenge-tap-token"]');
const clickedTokens = [];
const tokensToClick = [];
for (const correctToken of window.sol.correctTokens) {
const matchingElements = Array.from(allTokens).filter(el => el.textContent.trim() === correctToken.trim());
if (matchingElements.length > 0) {
const matchIndex = clickedTokens.filter(token => token.textContent.trim() === correctToken.trim()).length;
const elementToClick = matchingElements[matchIndex] || matchingElements[0];
if (!elementToClick.disabled) {
tokensToClick.push(elementToClick);
clickedTokens.push(elementToClick);
}
}
}
tokensToClick.forEach(token => token.click());
},
handleIndicesRun: async () => {
if (!window.sol.correctIndices) return;
const wordBank = document.querySelector('div[data-test="word-bank"]') || document.querySelector('.eSgkc');
if (!wordBank) return;
const bankButtons = Array.from(wordBank.querySelectorAll('button[data-test*="challenge-tap-token"]:not(span)'));
for (const index of window.sol.correctIndices) {
if (index >= 0 && index < bankButtons.length) {
const button = bankButtons[index];
if (!button.disabled && button.getAttribute('aria-disabled') !== 'true') {
button.click();
await _autoSolver.delay(50);
}
}
}
},
handleTapCompleteTable: async () => {
const solutionRows = window.sol.displayTableTokens.slice(1);
const tableRowElements = document.querySelectorAll('tbody tr');
const wordBank = document.querySelector('div[data-test="word-bank"]');
const wordBankButtons = wordBank ? wordBank.querySelectorAll('button[data-test*="-challenge-tap-token"]') : [];
const usedWordBankIndexes = new Set();
for (let rowIndex = 0; rowIndex < solutionRows.length; rowIndex++) {
const solutionRow = solutionRows[rowIndex];
const answerCellData = solutionRow[1];
const correctToken = answerCellData.find(token => token.isBlank);
if (correctToken) {
const correctAnswerText = correctToken.text;
const currentRowElement = tableRowElements[rowIndex];
let clicked = false;
const buttons = currentRowElement.querySelectorAll('button[data-test*="-challenge-tap-token"]');
for (const button of buttons) {
const buttonTextElm = button.querySelector('[data-test="challenge-tap-token-text"]');
if (buttonTextElm && buttonTextElm.innerText.trim() === correctAnswerText && !button.disabled) {
button.click();
await _autoSolver.delay(50);
clicked = true;
break;
}
}
if (!clicked && wordBankButtons.length > 0) {
for (let i = 0; i < wordBankButtons.length; i++) {
if (usedWordBankIndexes.has(i)) continue;
const button = wordBankButtons[i];
const buttonTextElm = button.querySelector('[data-test="challenge-tap-token-text"]');
if (buttonTextElm && buttonTextElm.innerText.trim() === correctAnswerText && !button.disabled) {
button.click();
await _autoSolver.delay(50);
usedWordBankIndexes.add(i);
break;
}
}
}
}
}
},
handleChallenge: async (type) => {
try {
switch (type) {
case 'Challenge Speak':
case 'Listen Match':
case 'Listen Speak':
document.querySelector('button[data-test="player-skip"]')?.click();
break;
case 'Select Card': {
const idx = window.sol.correctIndex ?? 0;
const cards = document.querySelectorAll('[data-test="challenge-choice-card"]');
if (cards.length > 0) {
cards[idx]?.click();
} else {
document.querySelectorAll('[data-test="challenge-choice"]')[idx]?.click();
}
break;
}
case 'Judge': {
const ci = window.sol.correctIndices?.[0] ?? 0;
document.querySelectorAll('[data-test="challenge-judge-text"]')[ci]?.click();
break;
}
case 'Dialogue': {
const idx = window.sol.correctIndex ?? 0;
const judgeItems = document.querySelectorAll('[data-test="challenge-judge-text"]');
if (judgeItems.length > 0) {
judgeItems[idx]?.click();
} else {
document.querySelectorAll('[data-test="challenge-choice"]')[idx]?.click();
}
break;
}
case 'Translate': {
const { correctTokens, correctSolutions } = window.sol;
if (correctTokens && correctTokens.length > 0) {
const tokens = document.querySelectorAll('[data-test$="challenge-tap-token"]');
const usedIndexes = [];
for (const word of correctTokens) {
for (let i = 0; i < tokens.length; i++) {
if (usedIndexes.includes(i)) continue;
if (tokens[i].innerText.trim() === word.trim() && !tokens[i].disabled) {
tokens[i].click();
usedIndexes.push(i);
await _autoSolver.delay(40);
break;
}
}
}
} else if (correctSolutions) {
const ta = document.querySelector('textarea[data-test="challenge-translate-input"]');
if (ta) _autoSolver.setInputValue(ta, correctSolutions[0]);
}
break;
}
case 'Listen Tap': {
const tokens = document.querySelectorAll('[data-test$="challenge-tap-token"]');
const usedIdx = [];
for (const word of (window.sol.correctTokens || [])) {
for (let i = 0; i < tokens.length; i++) {
if (usedIdx.includes(i)) continue;
if (tokens[i].innerText.trim() === word.trim() && !tokens[i].disabled) {
tokens[i].click();
usedIdx.push(i);
await _autoSolver.delay(40);
break;
}
}
}
break;
}
case 'Listen Type': {
const answer = window.sol.prompt || window.sol.correctSolutions?.[0] || '';
const ta = document.querySelector('textarea[data-test="challenge-translate-input"]') ||
document.querySelector('[data-test="challenge-text-input"]');
if (ta) _autoSolver.setInputValue(ta, answer);
break;
}
case 'Complete Reverse': {
const blank = window.sol.displayTokens?.find(t => t.isBlank);
const answer = blank?.text || window.sol.correctSolutions?.[0] || '';
const input = document.querySelector('[data-test="challenge-text-input"]');
if (input) _autoSolver.setInputValue(input, answer);
break;
}
case 'Challenge Choice':
document.querySelectorAll("[data-test='challenge-choice']")[window.sol.correctIndex]?.click();
break;
case 'Challenge Choice with Text Input': {
const choiceInput = document.querySelector('[data-test="challenge-text-input"]');
if (choiceInput) {
const answer = window.sol.correctSolutions ? window.sol.correctSolutions[0].split(/(?<=^\S+)\s/)[1] : (window.sol.displayTokens ? window.sol.displayTokens.find(t => t.isBlank)?.text : window.sol.prompt);
_autoSolver.setInputValue(choiceInput, answer);
}
break;
}
case 'Challenge Text Input': {
const input = document.querySelector('[data-test="challenge-text-input"]');
if (input) {
const answer = window.sol.correctSolutions?.[0] || (window.sol.displayTokens ? window.sol.displayTokens.find(t => t.isBlank)?.text : window.sol.prompt);
_autoSolver.setInputValue(input, answer);
}
break;
}
case 'Challenge Translate Input': {
const textarea = document.querySelector('textarea[data-test="challenge-translate-input"]');
if (textarea) _autoSolver.setInputValue(textarea, window.sol.correctSolutions?.[0] || window.sol.prompt);
break;
}
case 'Partial Reverse': {
const partialElm = document.querySelector('[data-test*="challenge-partialReverseTranslate"]')?.querySelector("span[contenteditable]");
if (partialElm) {
const text = window.sol?.displayTokens?.filter(t => t.isBlank)?.map(t => t.text)?.join('')?.trim();
const setter = Object.getOwnPropertyDescriptor(Node.prototype, "textContent").set;
setter.call(partialElm, text);
partialElm.dispatchEvent(new Event('input', {
bubbles: true
}));
}
break;
}
case 'Type Cloze': {
const clozeInput = document.querySelector('input[type="text"].b4jqk');
if (clozeInput) {
const targetToken = window.sol.displayTokens.find(t => t.damageStart !== undefined);
if (targetToken) {
const correctEnding = targetToken.text.slice(targetToken.damageStart);
_autoSolver.setInputValue(clozeInput, correctEnding);
}
}
break;
}
case 'Type Cloze Table': {
const tableRows = document.querySelectorAll('tbody tr');
window.sol.displayTableTokens.slice(1).forEach((rowTokens, i) => {
const answerCell = rowTokens[1]?.find(t => typeof t.damageStart === "number");
if (answerCell && tableRows[i]) {
const input = tableRows[i].querySelector('input[type="text"].b4jqk');
if (input) {
const correctEnding = answerCell.text.slice(answerCell.damageStart);
_autoSolver.setInputValue(input, correctEnding);
}
}
});
break;
}
case 'Tap Cloze Table': {
const tapTableRows = document.querySelectorAll('tbody tr');
window.sol.displayTableTokens.slice(1).forEach((rowTokens, i) => {
const answerCell = rowTokens[1]?.find(t => typeof t.damageStart === "number");
if (!answerCell || !tapTableRows[i]) return;
const wordBank = document.querySelector('[data-test="word-bank"]');
const wordButtons = wordBank ? Array.from(wordBank.querySelectorAll('button[data-test*="challenge-tap-token"]:not([aria-disabled="true"])')) : [];
const correctWord = answerCell.text;
const correctEnding = correctWord.slice(answerCell.damageStart);
let endingMatched = "";
for (let btn of wordButtons) {
if (!correctEnding.startsWith(endingMatched + btn.innerText)) continue;
btn.click();
endingMatched += btn.innerText;
if (endingMatched === correctEnding) break;
}
});
break;
}
case 'Type Complete Table': {
const completeTableRows = document.querySelectorAll('tbody tr');
window.sol.displayTableTokens.slice(1).forEach((rowTokens, i) => {
const answerCell = rowTokens[1]?.find(t => t.isBlank);
if (!answerCell || !completeTableRows[i]) return;
const input = completeTableRows[i].querySelector('input[type="text"].b4jqk');
if (input) _autoSolver.setInputValue(input, answerCell.text);
});
break;
}
case 'Pattern Tap Complete': {
const patternWordBank = document.querySelector('[data-test="word-bank"]');
if (!patternWordBank) return;
const correctIndex = window.sol.correctIndex ?? 0;
const correctText = window.sol.choices[correctIndex];
const patternButtons = Array.from(patternWordBank.querySelectorAll('button[data-test*="challenge-tap-token"]:not([aria-disabled="true"])'));
const targetButton = patternButtons.find(btn => btn.innerText.trim() === correctText);
if (targetButton) targetButton.click();
break;
}
case 'Story Arrange': {
const arrangeChoices = document.querySelectorAll('[data-test*="challenge-tap-token"]:not(span)');
for (let i = 0; i < window.sol.phraseOrder.length; i++) {
arrangeChoices[window.sol.phraseOrder[i]].click();
await _autoSolver.delay(50);
}
break;
}
case 'Story Multiple Choice': {
const storyChoices = document.querySelectorAll('[data-test="stories-choice"]');
storyChoices[window.sol.correctAnswerIndex]?.click();
break;
}
case 'Story Point to Phrase': {
const phraseChoices = document.querySelectorAll('[data-test="challenge-tap-token-text"]');
let phraseCorrectIndex = -1;
for (let i = 0; i < window.sol.parts.length; i++) {
if (window.sol.parts[i].selectable === true) {
phraseCorrectIndex += 1;
if (window.sol.correctAnswerIndex === i) {
phraseChoices[phraseCorrectIndex]?.parentElement.click();
break;
}
}
}
break;
}
case 'Story Pairs': {
const storyButtons = document.querySelectorAll('[data-test*="challenge-tap-token"]:not(span)');
const storyTexts = document.querySelectorAll('[data-test="challenge-tap-token-text"]');
const textToElementMap = new Map();
for (let i = 0; i < storyButtons.length; i++) {
const text = storyTexts[i].innerText.toLowerCase().trim();
textToElementMap.set(text, storyButtons[i]);
}
for (const key in window.sol.dictionary) {
if (window.sol.dictionary.hasOwnProperty(key)) {
const value = window.sol.dictionary[key];
const keyPart = key.split(":")[1].toLowerCase().trim();
const normalizedValue = value.toLowerCase().trim();
const element1 = textToElementMap.get(keyPart);
const element2 = textToElementMap.get(normalizedValue);
if (element1 && !element1.disabled) {
element1.click();
await _autoSolver.delay(50);
}
if (element2 && !element2.disabled) {
element2.click();
await _autoSolver.delay(50);
}
}
}
break;
}
case 'Challenge Name':
await _autoSolver.handleChallengeName();
break;
case 'Pairs':
await _autoSolver.handlePairs();
break;
case 'Tokens Run':
await _autoSolver.handleTokensRun();
break;
case 'Indices Run':
case 'Fill in the Gap':
await _autoSolver.handleIndicesRun();
break;
case 'Tap Complete Table':
await _autoSolver.handleTapCompleteTable();
break;
}
} catch (error) {
}
},
clickNext: () => {
const nextBtn = document.querySelector('[data-test="player-next"]') ||
document.querySelector('[data-test="stories-player-continue"]') ||
document.querySelector('[data-test="stories-player-done"]');
if (!nextBtn) return;
const isDisabled = nextBtn.getAttribute('aria-disabled') === 'true' || nextBtn.disabled;
if (!isDisabled) nextBtn.click();
},
solve: async () => {
if (_autoSolver._isBusy) return;
_autoSolver._isBusy = true;
const sleep = ms => new Promise(r => setTimeout(r, ms));
const skipSelectors = ['[data-test="practice-hub-ad-no-thanks-button"]', '[data-test="plus-no-thanks"]', '[data-test="story-start"]', '.vpDIE', '._1N-oo._36Vd3._16r-S._1ZBYz._23KDq._1S2uf.HakPM'];
skipSelectors.forEach(sel => document.querySelector(sel)?.click());
try {
let mainElement = document.querySelector('._3yE3H');
if (!mainElement) mainElement = document.querySelector('.RMEuZ._1GVfY') || document.querySelector('[data-test="challenge"]') || document.querySelector('[class*="challenge"]');
if (!mainElement) {
_autoSolver.clickNext();
_autoSolver._isBusy = false;
return;
}
const reactInstance = _autoSolver.findReact(mainElement);
window.sol = reactInstance?.props?.currentChallenge;
if (!window.sol) {
_autoSolver.clickNext();
_autoSolver._isBusy = false;
return;
}
const challengeType = _autoSolver.determineChallengeType();
if (challengeType && challengeType !== 'Challenge Speak' && challengeType !== 'Listen Match' && challengeType !== 'Listen Speak') {
await Promise.race([
_autoSolver.handleChallenge(challengeType),
new Promise(r => setTimeout(r, 2000))
]);
await sleep(80);
}
_autoSolver.clickNext();
await sleep(120);
} catch (error) {
_autoSolver.clickNext();
}
_autoSolver._isBusy = false;
},
_isBusy: false,
_solveLoopRunning: false,
toggleAutoMode: () => {
_isAutoMode = !_isAutoMode;
_autoSolver.updateUI();
if (_isAutoMode && !_autoSolver._solveLoopRunning) {
_autoSolver._solveLoopRunning = true;
const initialUrl = window.location.href;
(async function loop() {
while (_isAutoMode) {
if (window.location.href !== initialUrl) {
_isAutoMode = false;
_autoSolver.updateUI();
break;
}
const t0 = Date.now();
await _autoSolver.solve();
await new Promise(r => setTimeout(r, 100));
if (!_isAutoMode) break;
const elapsed = Date.now() - t0;
const wait = Math.max(0, 400 - elapsed);
if (wait > 0) await new Promise(r => setTimeout(r, wait));
}
_autoSolver._solveLoopRunning = false;
})();
} else if (!_isAutoMode) {
_autoSolver._solveLoopRunning = false;
}
},
createUI: () => {
if (_solverUI) return;
_solverUI = document.createElement('div');
_solverUI.id = 'nightware-solver-ui';
_solverUI.style.cssText = `position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); z-index: 2147483647; display: flex; gap: 12px; animation: slideUp 0.3s ease-out; pointer-events: auto;`;
_solverUI.innerHTML = `
<button class="nw-solver-btn" id="nw-solve-single" style="padding: 12px 24px; background: #89e219; border: none; border-bottom: 4px solid #58cc02; border-radius: 12px; color: white; font-weight: 700; font-size: 14px; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 4px 12px rgba(0,0,0,0.15); pointer-events: auto;">SOLVE</button>
<button class="nw-solver-btn" id="nw-solve-all" style="padding: 12px 24px; background: #ffc800; border: none; border-bottom: 4px solid #ff9600; border-radius: 12px; color: white; font-weight: 700; font-size: 14px; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 4px 12px rgba(0,0,0,0.15); pointer-events: auto;">SOLVE ALL</button>
`;
const style = document.createElement('style');
style.textContent = `@keyframes slideUp { from { opacity: 0; transform: translateX(-50%) translateY(20px); } to { opacity: 1; transform: translateX(-50%) translateY(0); } } .nw-solver-btn:hover { filter: brightness(1.1); transform: translateY(-2px); } .nw-solver-btn:active { border-bottom: 0px; transform: translateY(2px); }`;
document.head.appendChild(style);
document.body.appendChild(_solverUI);
document.getElementById('nw-solve-single').addEventListener('click', () => _autoSolver.solve());
document.getElementById('nw-solve-all').addEventListener('click', () => _autoSolver.toggleAutoMode());
document.addEventListener('keydown', (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
if (e.shiftKey) _autoSolver.toggleAutoMode();
else _autoSolver.solve();
}
});
},
removeUI: () => {
if (_solverUI) {
_solverUI.remove();
_solverUI = null;
}
if (_solvingIntervalId) {
clearInterval(_solvingIntervalId);
_solvingIntervalId = null;
}
_isAutoMode = false;
},
updateUI: () => {
const btn = document.getElementById('nw-solve-all');
if (btn) {
btn.textContent = _isAutoMode ? 'PAUSE' : 'SOLVE ALL';
btn.style.background = _isAutoMode ? '#ff4b4b' : '#1cb0f6';
btn.style.borderBottomColor = _isAutoMode ? '#cc0000' : '#2b70c9';
}
},
checkAndToggle: () => {
const currentIsInLesson = window.location.pathname.includes('/lesson') || window.location.pathname.includes('/practice');
if (currentIsInLesson !== _isInLesson) {
_isInLesson = currentIsInLesson;
if (_isInLesson && _INJECT_SOLVER_ENABLED) {
setTimeout(() => _autoSolver.createUI(), 500);
} else {
_autoSolver.removeUI();
}
}
}
}
setInterval(() => _autoSolver.checkAndToggle(), 1000);
let _lessonSolving=false;
let _currentLessonCount=0;
let _lessonsToSolve=0;
function _getJwt(){
const m=document.cookie.match(/(^| )jwt_token=([^;]+)/);
return m?m[2]:null;
}
function _decodeJwt(t){
try{
const b=t.split('.')[1].replace(/-/g,'+').replace(/_/g,'/');
const pad=b.padEnd(b.length+(4-b.length%4)%4,'=');
return JSON.parse(decodeURIComponent(atob(pad).split('').map(c=>'%'+('00'+c.charCodeAt(0).toString(16)).slice(-2)).join('')));
}catch{return null;}
}
function _buildHdrs(jwt){
return {'Content-Type':'application/json','Authorization':'Bearer '+jwt,'User-Agent':navigator.userAgent};
}
function _goalHdrs(jwt){
return {'Content-Type':'application/json','x-requested-with':'XMLHttpRequest','accept':'application/json; charset=UTF-8','Authorization':'Bearer '+jwt};
}
function _gm(method,url,data,hdrs){
return new Promise((res,rej)=>GM_xmlhttpRequest({
method,url,headers:hdrs||_hdrs,
data:data?JSON.stringify(data):null,
onload:r=>res(r),onerror:()=>rej(new Error('Network')),
timeout:15000,ontimeout:()=>rej(new Error('Timeout'))
}));
}
function _setBtnState(btnId, cfg, labelText) {
const btn = document.getElementById(btnId);
if(!btn) return;
const lbl = btn.querySelector('.DH_Btn_Label') || btn.querySelector('.DH_Sm_Btn_Label');
if(!lbl) return;
const prevTxt = lbl.textContent;
lbl.textContent = labelText;
const newW = btn.offsetWidth;
lbl.textContent = prevTxt;
btn.style.width = btn.offsetWidth+'px';
requestAnimationFrame(()=>{
lbl.style.opacity='0'; lbl.style.filter='blur(4px)';
btn.style.width = newW+'px';
btn.style.background = cfg.bg;
btn.style.outline = `solid 2px ${cfg.outline}`;
btn.style.outlineOffset = '-2px';
});
setTimeout(()=>{
lbl.style.transition='0s';
lbl.style.color = cfg.tc;
void lbl.offsetWidth;
lbl.style.transition='0.4s';
lbl.textContent = labelText;
requestAnimationFrame(()=>{ lbl.style.opacity='1'; lbl.style.filter='blur(0)'; });
setTimeout(()=>{ btn.style.width=''; }, 400);
},400);
}
const _C_BLUE = {bg:'rgb(var(--DH-blue))', outline:'rgba(0,0,0,0.18)', tc:'#fff'};
const _C_GREEN = {bg:'rgba(var(--DH-green),0.10)', outline:'rgba(var(--DH-green),0.22)', tc:'rgb(var(--DH-green))'};
const _C_RED = {bg:'rgba(var(--DH-red),0.10)', outline:'rgba(var(--DH-red),0.22)', tc:'rgb(var(--DH-red))'};
const _C_GRAY = {bg:'rgb(var(--color-eel,117,117,117),0.10)', outline:'rgb(var(--color-eel,117,117,117),0.20)', tc:'rgb(var(--color-eel,117,117,117),0.60)'};
function _resetBtn(btnId, label){
const btn=document.getElementById(btnId); if(!btn) return;
btn.disabled=false;
_setBtnState(btnId, _C_BLUE, label);
const prog=document.getElementById(btnId.replace('_Btn','_Prog'));
if(prog) setTimeout(()=>prog.classList.remove('on'),2000);
}
function _setBtnProgress(btnId, pct){
const btn=document.getElementById(btnId); if(!btn) return;
const lbl=btn.querySelector('.DH_Btn_Label')||btn.querySelector('.DH_Sm_Btn_Label');
if(lbl) lbl.textContent=pct+'%';
const fill=document.getElementById(btnId.replace('_Btn','_Fill'));
if(fill) fill.style.width=pct+'%';
}
function _setBtnRunning(btnId){
const btn=document.getElementById(btnId); if(!btn) return;
btn.disabled=false;
_setBtnState(btnId, _C_RED, '0%');
const prog=document.getElementById(btnId.replace('_Btn','_Prog'));
if(prog) prog.classList.add('on');
}
function _setBtnDone(btnId, label){
const btn=document.getElementById(btnId); if(!btn) return;
_setBtnState(btnId, _C_GREEN, label||'DONE ✓');
const fill=document.getElementById(btnId.replace('_Btn','_Fill'));
if(fill) fill.style.width='100%';
}
const _GF_SCRIPT_URL='https://greasyfork.org/en/scripts/561041-duolingo-duohacker';
const _CURRENT_VER='2026.04.21';
function _setConn(state, label){
if (state === 'connected' && _isOutdated) {
state = 'outdated';
label = `v${_remoteVersion} available — click to update`;
}
const btn=document.getElementById('DH_Conn_Btn');
const ico=document.getElementById('DH_Conn_Ico');
const txt=document.getElementById('DH_Conn_Txt');
ico.classList.remove('DH_Spin_Ico');
if(state==='outdated'){
const newBtn=btn.cloneNode(true);
btn.parentNode.replaceChild(newBtn,btn);
const nb=document.getElementById('DH_Conn_Btn');
const ni=document.getElementById('DH_Conn_Ico');
const nt=document.getElementById('DH_Conn_Txt');
nb.style.background=`linear-gradient(0deg,rgba(var(--DH-orange),0.10),rgba(var(--DH-orange),0.10)),rgb(var(--color-snow),0.90)`;
nb.style.outline=`2px solid rgba(var(--DH-orange),0.30)`;
nb.style.outlineOffset='-2px';
nb.style.cursor='pointer';
nb.style.display='flex';
nb.style.alignItems='center';
nt.textContent='Outdated';
nt.style.color=`rgb(var(--DH-orange))`;
ni.textContent='';
ni.style.display='flex';
ni.style.alignItems='center';
ni.style.justifyContent='center';
ni.innerHTML=`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="flex-shrink:0; display:block;"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" fill="rgba(var(--DH-orange),0.18)" stroke="rgb(var(--DH-orange))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><line x1="12" y1="9" x2="12" y2="13" stroke="rgb(var(--DH-orange))" stroke-width="2" stroke-linecap="round"/><circle cx="12" cy="17" r="1" fill="rgb(var(--DH-orange))"/></svg>`;
nb.title=label;
nb.onclick = () => window.open(_GF_SCRIPT_URL, '_blank');
document.getElementById('DH_User_Row').style.display='flex';
return;
}
const S={
connecting:{bg:`rgb(var(--color-eel,117,117,117),0.10)`,outline:`rgb(var(--color-eel,117,117,117),0.20)`,tc:`rgb(var(--color-eel,117,117,117),0.70)`,t:'Connecting',i:'⟳',spin:true},
connected: {bg:`linear-gradient(0deg,rgba(var(--DH-green),0.10),rgba(var(--DH-green),0.10)),rgb(var(--color-snow),0.90)`,outline:`rgba(var(--DH-green),0.22)`,tc:`rgb(var(--DH-green))`,t:'Connected',i:'✓',spin:false},
error: {bg:`rgba(var(--DH-red),0.08)`,outline:`rgba(var(--DH-red),0.20)`,tc:`rgb(var(--DH-red))`,t:label||'Error',i:'✕',spin:false},
}[state];
btn.style.background=S.bg; btn.style.outline=`2px solid ${S.outline}`; btn.style.outlineOffset='-2px';
txt.textContent=S.t; txt.style.color=S.tc;
ico.textContent=S.i; ico.style.color=S.tc;
if(S.spin) ico.classList.add('DH_Spin_Ico');
if(state==='connected') document.getElementById('DH_User_Row').style.display='flex';
}
async function _checkVersionOnLoad(){
try{
const r=await new Promise((res,rej)=>GM_xmlhttpRequest({
method:'GET',
url:`https://greasyfork.org/scripts/561041.json`,
headers:{'Accept':'application/json'},
onload:r=>res(r),onerror:()=>rej(),timeout:5000
}));
if(r.status!==200) return;
const data=JSON.parse(r.responseText);
const remoteVer=(data.version||'').trim();
const cmp = (v1, v2) => {
const a = v1.split('.').map(Number);
const b = v2.split('.').map(Number);
for (let i = 0; i < Math.max(a.length, b.length); i++) {
if ((a[i] || 0) > (b[i] || 0)) return 1;
if ((a[i] || 0) < (b[i] || 0)) return -1;
}
return 0;
};
if(cmp(remoteVer, _CURRENT_VER) !== 0){
_isOutdated = true;
_remoteVersion = remoteVer;
const txt = document.getElementById('DH_Conn_Txt');
if(txt && txt.textContent === 'Connected') {
_setConn('outdated', `v${remoteVer} available`);
}
}
}catch(e){}
}
setTimeout(_checkVersionOnLoad, 2000);
let _animBusy=false;
function _doHide(val){
if(_animBusy) return; _animBusy=true; _hidden=val;
const main=document.getElementById('DH_Main');
const box=document.getElementById('DH_Main_Box');
const h=box.offsetHeight;
const icoVisible=document.getElementById('DH_Ico_Visible');
const icoHidden=document.getElementById('DH_Ico_Hidden');
const hideTxt=document.getElementById('DH_Hide_Txt');
const hideBtn=document.getElementById('DH_Hide_Btn');
main.style.transition='0.8s cubic-bezier(0.16,1,0.32,1)';
box.style.transition='0.8s cubic-bezier(0.16,1,0.32,1)';
const switchV1Btn=document.getElementById('DH_SwitchV1_Btn');
const switchV2Btn=document.getElementById('DH_SwitchV2_Btn');
if(val){
if(switchV1Btn) switchV1Btn.style.display='none';
if(switchV2Btn) switchV2Btn.style.display='none';
hideBtn.style.background=`linear-gradient(0deg,rgba(var(--DH-blue),0.10),rgba(var(--DH-blue),0.10)),rgb(var(--color-snow),0.80)`;
hideBtn.style.outline=`2px solid rgba(var(--DH-blue),0.20)`;
if(icoVisible) icoVisible.style.display='none';
if(icoHidden){ icoHidden.style.display=''; icoHidden.querySelector('path').setAttribute('fill','rgb(var(--DH-blue))'); }
hideTxt.textContent='Show'; hideTxt.style.color='rgb(var(--DH-blue))';
main.style.bottom=`-${h-8}px`; box.style.filter='blur(8px)'; box.style.opacity='0';
} else {
if(switchV1Btn) switchV1Btn.style.display=(!_v1Mode)?'':'none';
if(switchV2Btn) switchV2Btn.style.display=(_v1Mode)?'':'none';
hideBtn.style.background=`rgb(var(--DH-blue))`;
hideBtn.style.outline=`2px solid rgba(0,0,0,0.20)`;
if(icoHidden) icoHidden.style.display='none';
if(icoVisible){ icoVisible.style.display=''; }
hideTxt.textContent='Hide'; hideTxt.style.color='#fff';
main.style.bottom='16px'; box.style.filter=''; box.style.opacity='';
}
setTimeout(()=>{ main.style.transition=''; box.style.transition=''; _animBusy=false; },800);
}
let _curPage=1, _pageBusy=false;
function _goPage(to){
if(_pageBusy||_curPage===to) return; _pageBusy=true;
const box=document.getElementById('DH_Main_Box');
const fromEl=document.getElementById(`DH_Page_${_curPage}`);
const toEl=document.getElementById(`DH_Page_${to}`);
if(!fromEl||!toEl){_pageBusy=false;return;}
const oldH=box.offsetHeight;
fromEl.style.display='none'; toEl.style.display='flex';
const newH=box.offsetHeight;
fromEl.style.display='flex'; toEl.style.display='none';
box.style.height=oldH+'px'; box.style.transition='height 0.8s cubic-bezier(0.16,1,0.32,1)';
fromEl.style.transition='opacity 0.3s,filter 0.3s';
fromEl.style.opacity='0'; fromEl.style.filter='blur(4px)';
requestAnimationFrame(()=>{ box.style.height=newH+'px'; });
setTimeout(()=>{
fromEl.classList.remove('active'); fromEl.style.cssText='';
toEl.classList.add('active');
toEl.style.opacity='0'; toEl.style.filter='blur(4px)';
void toEl.offsetWidth;
toEl.style.transition='opacity 0.3s,filter 0.3s';
toEl.style.opacity='1'; toEl.style.filter='blur(0)';
_pageHistory.push(to);
_curPage=to;
if(to===2){
const lgB=document.getElementById('DH_League_Btn');
const qB=document.getElementById('DH_Quest_Btn');
if(_user&&lgB) lgB.disabled=false;
if(_user&&qB) qB.disabled=false;
}
if(to===4){
const delI=document.getElementById('DH_Delay_Input');
if(delI) delI.value=_delay;
}
if(to===5){ _renderAccounts(); }
if(to===6){ _loadMonthlyQuests(); }
if(to===9){ _loadLicense(); }
setTimeout(()=>{ box.style.height=''; box.style.transition=''; toEl.style.cssText=''; _pageBusy=false; },300);
},350);
}
function _goBack(){
if(_pageHistory.length>1) _pageHistory.pop();
const prev=_pageHistory[_pageHistory.length-1]||1;
_pageHistory.pop();
_goPage(prev);
}
let _nTimer;
function _notif(icon,title,body,dur=5){
const box=document.getElementById('DH_Notif_Box');
document.getElementById('DH_Notif_Icon').textContent=icon;
document.getElementById('DH_Notif_Title').textContent=title;
document.getElementById('DH_Notif_Body').textContent=body;
box.classList.add('show'); clearTimeout(_nTimer);
_nTimer=setTimeout(()=>box.classList.remove('show'),dur*1000);
}
async function _connect(){
_setConn('connecting');
_jwt=_getJwt();
if(!_jwt){_setConn('error','Not logged in');return;}
const dec=_decodeJwt(_jwt);
if(!dec){_setConn('error','Invalid token');return;}
_sub=dec.sub; _hdrs=_buildHdrs(_jwt);
try{
const r=await _gm('GET',`https://www.duolingo.com/2017-06-30/users/${_sub}?fields=id,username,fromLanguage,learningLanguage,streak,totalXp,gems,picture,streakData`);
if(r.status!==200) throw new Error(r.status);
_user=JSON.parse(r.responseText);
_setConn('connected'); _renderUser(_user);
_getPrivacy().then(v=>{ _privacy=v; _applyHideProfileToggle(); });
_v1FetchSkillId();
['DH_XP_Btn','DH_Gem_Btn','DH_Streak_Btn','DH_League_Btn','DH_Quest_Btn','DH_Practice_Btn','DH_V1_XP_Btn','DH_V1_Gem_Btn','DH_V1_Streak_Btn'].forEach(id=>{
const b=document.getElementById(id); if(b) b.disabled=false;
});
const saveBtn=document.getElementById('DH_AccSave_Btn');
if(saveBtn) saveBtn.disabled=false;
const mqClaimNav=document.getElementById('DH_MonthlyQuest_Claim_Btn');
if(mqClaimNav) mqClaimNav.disabled=false;
}catch(e){
_setConn('error','Failed — retrying');
setTimeout(_connect,8000);
}
}
function _renderUser(u){
if(!u) return;
document.getElementById('DH_UName').textContent=u.username||'';
_v1SyncUser();
document.getElementById('DH_UXP').textContent=(u.totalXp||0).toLocaleString();
document.getElementById('DH_UGems').textContent=(u.gems||0).toLocaleString();
document.getElementById('DH_UStreak').textContent=(u.streak||0).toLocaleString();
if(u.picture){
let hq=u.picture.replace(/\/(medium|large|small)$/,'/xlarge');
if(!hq.endsWith('/xlarge')&&hq.includes('duolingo.com/ssr-avatars')) hq+='/xlarge';
const av=document.getElementById('DH_Avatar');
const avImg=document.createElement('img');
avImg.src=hq;
avImg.style.cssText='width:100%;height:100%;object-fit:cover;border-radius:50%;display:block;';
avImg.draggable=false;
avImg.onerror=function(){av.innerHTML='👤';};
av.innerHTML=''; av.appendChild(avImg);
}
}
async function _farmXP(txp){
const MIN=30,MAX=499;
let loops=Math.floor(txp/MAX),rem=txp%MAX;
if(rem>0&&rem<MIN&&loops>0){loops--;rem+=MAX;}
const total=loops+(rem>=MIN?1:0);
let cur=0,earned=0;
_setBtnRunning('DH_XP_Btn');
for(let i=0;i<loops;i++){
if(!_running) break;
const ok=await _storyXP(469);
if(ok){earned+=MAX;cur++;}
_setBtnProgress('DH_XP_Btn',Math.floor((cur/total)*100));
await _sleep(_delay);
}
if(rem>=MIN&&_running){
const ok=await _storyXP(Math.min(rem-MIN,469));
if(ok){earned+=rem;cur++;}
_setBtnProgress('DH_XP_Btn',100);
}
if(_running){
_setBtnDone('DH_XP_Btn','DONE ✓');
_notif('✅','XP Farm Done!',`Farmed ${earned} XP in ${cur} loops.`);
setTimeout(_connect,1500);
}
}
async function _storyXP(hh){
try{
const now=Math.floor(Date.now()/1000),dur=Math.floor(Math.random()*121+300);
const r=await _gm('POST','https://stories.duolingo.com/api2/stories/fr-en-le-passeport/complete',{
awardXp:true,completedBonusChallenge:true,
fromLanguage:'fr',learningLanguage:'en',
hasXpBoost:false,illustrationFormat:'svg',
isFeaturedStoryInPracticeHub:true,isLegendaryMode:true,
isV2Redo:false,isV2Story:false,masterVersion:true,
maxScore:0,score:0,happyHourBonusXp:hh,
startTime:now,endTime:now+dur
});
return r.status===200;
}catch{return false;}
}
// ── Slug probe — used only by V1 Mode infinite farm (fallback detection) ──
let _workingSlug=null,_workingSlugFrom=null,_workingSlugLearn=null;
let _probingSlugPromise=null;
const _SLUG_CANDIDATES=()=>[
['vi-en-le-passeport','vi','en'],
['fr-en-le-passeport','fr','en'],
['en-fr-le-passeport','en','fr'],
['es-en-le-passeport','es','en'],
['de-en-le-passeport','de','en'],
['pt-en-le-passeport','pt','en'],
['it-en-le-passeport','it','en'],
];
async function _probeSlug(){
if(_workingSlug) return _workingSlug;
if(_probingSlugPromise) return _probingSlugPromise;
_probingSlugPromise=(async()=>{
const now=Math.floor(Date.now()/1000);
const tryCandidate=([slug,from,learn])=>_gm('POST',`https://stories.duolingo.com/api2/stories/${slug}/complete`,{
awardXp:false,completedBonusChallenge:false,
fromLanguage:from,learningLanguage:learn,
hasXpBoost:false,illustrationFormat:'svg',
isFeaturedStoryInPracticeHub:true,isLegendaryMode:true,
isV2Redo:false,isV2Story:false,masterVersion:true,
maxScore:0,score:0,happyHourBonusXp:0,
startTime:now,endTime:now+300
}).then(r=>{
if(r.status===200||r.status===429) return [slug,from,learn];
return null;
}).catch(()=>null);
const winner=await new Promise(resolve=>{
let settled=false;
let pending=_SLUG_CANDIDATES().length;
_SLUG_CANDIDATES().forEach(c=>{
tryCandidate(c).then(result=>{
pending--;
if(result&&!settled){settled=true;resolve(result);}
else if(pending===0&&!settled) resolve(null);
});
});
});
if(winner){
_workingSlug=winner[0];
_workingSlugFrom=winner[1];
_workingSlugLearn=winner[2];
}
_probingSlugPromise=null;
return winner?winner[0]:null;
})();
return _probingSlugPromise;
}
async function _farmGems(tgt){
// [PATCHED 2026.04.21] Farm Gems method is currently unavailable.
_running=false; _task=null;
_resetBtn('DH_Gem_Btn','GET');
document.getElementById('DH_Gem_Btn').disabled=true;
document.getElementById('DH_Gem_Input').disabled=true;
_notif('🔒','Farm Gems method patched','Please wait until our developers fixed that.',6);
}
async function _farmStreak(days){
const CH=["assist","characterIntro","characterMatch","characterPuzzle","characterSelect","characterTrace","characterWrite","completeReverseTranslation","definition","dialogue","extendedMatch","extendedListenMatch","form","freeResponse","gapFill","judge","listen","listenComplete","listenMatch","match","name","listenComprehension","listenIsolation","listenSpeak","listenTap","orderTapComplete","partialListen","partialReverseTranslate","patternTapComplete","radioBinary","radioImageSelect","radioListenMatch","radioListenRecognize","radioSelect","readComprehension","reverseAssist","sameDifferent","select","selectPronunciation","selectTranscription","svgPuzzle","syllableTap","syllableListenTap","speak","tapCloze","tapClozeTable","tapComplete","tapCompleteTable","tapDescribe","translate","transliterate","transliterationAssist","typeCloze","typeClozeTable","typeComplete","typeCompleteTable","writeComprehension"];
let farmStart;
try{
const s=new Date(_user.streakData?.currentStreak?.startDate||Date.now());
s.setDate(s.getDate()-1);farmStart=s;
}catch{const n=new Date();n.setDate(n.getDate()-1);farmStart=n;}
_setBtnRunning('DH_Streak_Btn');
for(let i=0;i<days;i++){
if(!_running) break;
const simDay=new Date(farmStart);
simDay.setDate(simDay.getDate()-i);
const end=Math.floor(simDay.getTime()/1000);
try{
const sr=await _gm('POST','https://www.duolingo.com/2023-05-23/sessions',{
challengeTypes:CH,fromLanguage:_user.fromLanguage,isFinalLevel:false,isV2:true,
juicy:true,learningLanguage:_user.learningLanguage,smartTipsVersion:2,type:'GLOBAL_PRACTICE'
});
if(sr.status===200){
const sess=JSON.parse(sr.responseText);
await new Promise((res,rej)=>GM_xmlhttpRequest({
method:'PUT',url:`https://www.duolingo.com/2023-05-23/sessions/${sess.id}`,
headers:_hdrs,data:JSON.stringify({
...sess,heartsLeft:5,startTime:end-1,endTime:end,
enableBonusPoints:false,failed:false,maxInLessonStreak:9,shouldLearnThings:true
}),
onload:r=>res(r),onerror:()=>rej(),timeout:15000,ontimeout:()=>rej()
}));
}
}catch{}
_setBtnProgress('DH_Streak_Btn',Math.floor(((i+1)/days)*100));
await _sleep(_delay);
}
if(_running){
_setBtnDone('DH_Streak_Btn','DONE ✓');
_notif('🔥','Streak Farm Done!',`Restored ${days} streak days.`);
setTimeout(_connect,1500);
}
}
async function _farmPractice(count){
_lessonsToSolve=count;
_currentLessonCount=0;
if(_lessonSolving){_notif('⚠️','Busy','Practice farm already running.');return;}
_lessonSolving=true;
const btn=document.getElementById('DH_Practice_Btn');
_setBtnState('DH_Practice_Btn',_C_RED,'STOP');
_notif('📚','Farm Practice','Navigating to practice...',3);
if(!window.location.pathname.startsWith('/practice')){
window.location.assign('/practice');
return;
}
await _solveCurrentLesson();
}
async function _solveCurrentLesson(){
let waited=0;
while(!document.querySelector('[data-test="challenge"]')&&!document.querySelector('._3yE3H')&&waited<10000&&_lessonSolving){
await _sleep(500); waited+=500;
}
if(!_lessonSolving) return;
await new Promise(resolve=>{
let lastId=null, solving=false, ticks=0;
const MAX=240;
const clickNext=()=>{
const nb=document.querySelector('[data-test="player-next"]')||document.querySelector('[data-test="stories-player-continue"]')||document.querySelector('[data-test="stories-player-done"]');
if(!nb||nb.getAttribute('aria-disabled')==='true'||nb.disabled) return;
nb.click(); setTimeout(()=>{if(!nb.disabled) nb.click();},5);
};
const iv=setInterval(async()=>{
try{
if(!_lessonSolving){clearInterval(iv);resolve();return;}
if(++ticks>MAX){clearInterval(iv);resolve();return;}
const done=document.querySelector('[data-test="session-over"]')||document.querySelector('[data-test="session-complete-slide"]')||document.querySelector('[data-test="session-complete"]');
if(done){clearInterval(iv);_currentLessonCount++;
try{const s=JSON.parse(sessionStorage.getItem('dh2_practice')||'{}');s.done=_currentLessonCount;sessionStorage.setItem('dh2_practice',JSON.stringify(s));}catch{}
await _sleep(500);resolve();return;}
if(solving) return;
let el=document.querySelector('._3yE3H')||document.querySelector('[data-test="challenge"]')||document.querySelector('[class*="challenge"]');
if(!el){clickNext();return;}
const ri=_autoSolver.findReact(el);
window.sol=ri?.props?.currentChallenge;
if(!window.sol){clickNext();return;}
const cid=`${window.sol.type}:${window.sol.id||JSON.stringify(window.sol.correctIndex??window.sol.correctTokens??window.sol.correctSolutions?.[0]??'')}`;
if(cid===lastId){clickNext();return;}
const type=_autoSolver.determineChallengeType();
if(!type){clickNext();return;}
solving=true; lastId=cid;
try{await _autoSolver.handleChallenge(type);}catch{}
await _sleep(350); clickNext(); await _sleep(600); solving=false;
}catch{solving=false;}
},500);
setTimeout(()=>{clearInterval(iv);resolve();},180000);
});
if(!_lessonSolving) return;
if(_lessonsToSolve>0&&_currentLessonCount>=_lessonsToSolve){
_notif('✅','Farm Practice Done!',`Completed ${_currentLessonCount} practice(s).`);
_stopPractice();
return;
}
_notif('📚','Farm Practice',`Done ${_currentLessonCount}${_lessonsToSolve>0?' / '+_lessonsToSolve:''} — loading next...`,2);
await _sleep(800);
if(_lessonSolving) window.location.assign('/practice');
}
function _stopPractice(){
_lessonSolving=false;
_setBtnState('DH_Practice_Btn',_C_BLUE,'RUN');
const btn=document.getElementById('DH_Practice_Btn');
if(btn) btn.disabled=!_user;
}
function _resumePracticeIfNeeded(){
const saved=sessionStorage.getItem('dh2_practice');
if(!saved) return;
try{
const {active,count,done}=JSON.parse(saved);
if(!active) return;
_lessonsToSolve=count; _currentLessonCount=done;
sessionStorage.setItem('dh2_practice',JSON.stringify({active:true,count,done}));
if(window.location.pathname.startsWith('/practice')){
_lessonSolving=true;
if(_user){
_setBtnState('DH_Practice_Btn',_C_RED,'STOP');
_solveCurrentLesson();
} else {
const orig=_setConn.bind(null);
const check=setInterval(()=>{if(_user){clearInterval(check);_setBtnState('DH_Practice_Btn',_C_RED,'STOP');_solveCurrentLesson();}},500);
}
}
}catch{}
}
async function _farmLeague(){
const LB='https://duolingo-leaderboards-prod.duolingo.com/leaderboards/7d9f5dd1-8423-491a-91f2-2532052038ce';
const prog=document.getElementById('DH_League_Prog');
const fill=document.getElementById('DH_League_Fill');
if(prog) prog.classList.add('on');
_setBtnState('DH_League_Btn',_C_RED,'STOP');
while(_running){
try{
const r=await _gm('GET',`${LB}/users/${_sub}?client_unlocked=true&_=${Date.now()}`);
if(r.status!==200){await _sleep(3000);continue;}
const data=JSON.parse(r.responseText);
const ranks=data?.active?.cohort?.rankings||[];
const me=ranks.find(u=>u.user_id==_sub);
if(!me){_notif('⚠️','Not in league!','Join a league first.');break;}
const rank=ranks.indexOf(me)+1;
const top1=ranks[0];
const gap=top1.score-me.score;
if(rank===1&&gap<=0){_notif('🏆','League #1!','You reached Rank #1!');break;}
if(fill) fill.style.width=Math.min(95,Math.floor((me.score/Math.max(top1.score,1))*100))+'%';
if(gap+100>0){const ok=await _storyXP(469);if(!ok)await _sleep(3000);}
await _sleep(_delay);
}catch{await _sleep(5000);}
}
if(prog) setTimeout(()=>prog.classList.remove('on'),2000);
if(fill) fill.style.width='0%';
}
function _getQuestTimestamp(goalId){
const m=goalId.match(/^(\d{4})_(\d{2})_monthly/);
if(m){return new Date(Date.UTC(parseInt(m[1]),parseInt(m[2])-1,15,12,0,0)).toISOString();}
return new Date().toISOString();
}
async function _getGoals(){
return new Promise(r=>GM_xmlhttpRequest({
method:'GET',url:`${GOALS_API}/schema?ui_language=en&_=${Date.now()}`,
headers:_goalHdrs(_jwt),
onload:res=>r(res.status===200?JSON.parse(res.responseText):null),
onerror:()=>r(null)
}));
}
async function _getProgress(){
const tz=Intl.DateTimeFormat().resolvedOptions().timeZone;
return new Promise(r=>GM_xmlhttpRequest({
method:'GET',url:`${GOALS_API}/users/${_sub}/progress?timezone=${encodeURIComponent(tz)}&ui_language=en`,
headers:_goalHdrs(_jwt),
onload:res=>r(res.status===200?JSON.parse(res.responseText):null),
onerror:()=>r(null)
}));
}
async function _bruteForceGoals(metrics){
const updates=metrics.map(m=>({metric:m,quantity:2000}));
updates.push({metric:'QUESTS',quantity:1});
return new Promise(r=>GM_xmlhttpRequest({
method:'POST',url:`${GOALS_API}/users/${_sub}/progress/batch`,
headers:_goalHdrs(_jwt),
data:JSON.stringify({metric_updates:updates,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,timestamp:new Date().toISOString()}),
onload:res=>r(res.status===200),onerror:()=>r(false)
}));
}
async function _updateGoal(metric,amount,goalId){
return new Promise(r=>GM_xmlhttpRequest({
method:'POST',url:`${GOALS_API}/users/${_sub}/progress/batch`,
headers:_goalHdrs(_jwt),
data:JSON.stringify({metric_updates:[{metric,quantity:amount}],timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,timestamp:_getQuestTimestamp(goalId)}),
onload:res=>r(res.status===200),onerror:()=>r(false)
}));
}
async function _farmDailyQuest(){
_setBtnState('DH_Quest_Btn',_C_GRAY,'Loading...');
const [schema,progress]=await Promise.all([_getGoals(),_getProgress()]);
if(!schema||!progress){_notif('❌','Error','Could not load quest data.');_resetBtn('DH_Quest_Btn','RUN');return;}
const earned=new Set(progress.badges?.earned||[]);
const daily=(schema.goals||[]).filter(g=>g.category&&g.category.includes('DAILY'));
const metrics=new Set(daily.filter(g=>!earned.has(g.badgeId)&&!earned.has(g.goalId)&&g.metric).map(g=>g.metric));
if(!metrics.size){
_notif('✅','All Done!','All daily quests completed.');
_setBtnDone('DH_Quest_Btn','DONE ✓');
setTimeout(()=>_resetBtn('DH_Quest_Btn','RUN'),3000);return;
}
_setBtnState('DH_Quest_Btn',_C_RED,'Running...');
const ok=await _bruteForceGoals(Array.from(metrics));
if(ok){
_notif('✅','Daily Quests Done!',`Completed ${metrics.size} metric(s).`);
_setBtnDone('DH_Quest_Btn','DONE ✓');
setTimeout(()=>_resetBtn('DH_Quest_Btn','RUN'),3000);
} else {
_notif('❌','Error','Quest completion failed.');
_resetBtn('DH_Quest_Btn','RUN');
}
}
function _formatItem(id){
return id.replace(/_/g,' ').replace(/\b\w/g,c=>c.toUpperCase());
}
function _categorizeItem(item){
const id=item.id||'';
if(id.includes('streak_freeze')) return {cat:'Streak Freezes',icon:'https://d35aaqx5ub95lt.cloudfront.net/images/icons/216ddc11afcbb98f44e53d565ccf479e.svg'};
if(id.includes('xp_boost')) return {cat:'XP Boosts', icon:'https://d35aaqx5ub95lt.cloudfront.net/images/icons/68c1fd0f467456a4c607ecc0ac040533.svg'};
if(id.includes('health')||id.includes('heart')) return {cat:'Hearts', icon:'https://d35aaqx5ub95lt.cloudfront.net/images/hearts/547ffcf0e6256af421ad1a32c26b8f1a.svg'};
if(id.includes('gem')) return {cat:'Gems', icon:'https://d35aaqx5ub95lt.cloudfront.net/images/gems/45c14e05be9c1af1d7d0b54c6eed7eee.svg'};
if(item.type==='outfit') return {cat:'Outfits', icon:'https://d35aaqx5ub95lt.cloudfront.net/vendor/0cecd302cf0bcd0f73d51768feff75fe.svg'};
if(id.includes('free_taste')) return {cat:'Free Taste', icon:'https://d35aaqx5ub95lt.cloudfront.net/images/super/11db6cd6f69cb2e3c5046b915be8e669.svg'};
return {cat:'Misc', icon:'https://d35aaqx5ub95lt.cloudfront.net/images/leagues/9fadb349c2ece257386a0e576359c867.svg'};
}
async function _getShopItems(){
return new Promise(r=>GM_xmlhttpRequest({
method:'GET',url:'https://www.duolingo.com/2023-05-23/shop-items',
headers:_hdrs,
onload:res=>{
try{if(res.status===200)r(JSON.parse(res.responseText).shopItems||[]);else r([]);}
catch{r([]);}
},
onerror:()=>r([])
}));
}
async function _buyShopItem(itemId){
const payload={itemName:itemId,isFree:true,consumed:true,fromLanguage:_user.fromLanguage,learningLanguage:_user.learningLanguage};
return new Promise(r=>GM_xmlhttpRequest({
method:'POST',url:`https://www.duolingo.com/2017-06-30/users/${_sub}/shop-items`,
headers:_hdrs,data:JSON.stringify(payload),
onload:res=>r(res.status===200),onerror:()=>r(false)
}));
}
let _allShopItems=[];
function _renderShop(items,filter=''){
const container=document.getElementById('DH_Shop_Container');
container.innerHTML='';
const valid=items.filter(i=>i.currencyType==='XGM'&&!i.id.includes('gift'));
const f=filter.trim().toLowerCase();
const filtered=f?valid.filter(i=>(i.name||_formatItem(i.id)).toLowerCase().includes(f)):valid;
if(!filtered.length){
const p=document.createElement('p');
p.className='DH_T2 DH_NoSel'; p.style.cssText='text-align:center;padding:8px 0;';
p.textContent=f?'No items found.':'No items available.';
container.appendChild(p); return;
}
const ORDER=['Streak Freezes','XP Boosts','Hearts','Gems','Outfits','Free Taste','Misc'];
const grouped={};
filtered.forEach(i=>{
const {cat,icon}=_categorizeItem(i);
if(!grouped[cat]) grouped[cat]=[];
let name=i.name||_formatItem(i.id);
if(i.id.includes('xp_boost')&&i.id.match(/\d+$/)) name+=' Mins';
grouped[cat].push({...i,displayName:name,icon,cat});
});
ORDER.forEach(cat=>{
if(!grouped[cat]) return;
const header=document.createElement('div');
header.className='DH_Cat_Header DH_NoSel'; header.textContent=cat;
container.appendChild(header);
const grid=document.createElement('div');
grid.className='DH_Shop_Grid';
grouped[cat].forEach(item=>{
const card=document.createElement('div');
card.className='DH_Shop_Card';
card.innerHTML=`
<img src="${item.icon}" class="DH_Shop_Ico">
<div class="DH_Shop_Name DH_NoSel">${item.displayName}</div>
<button class="DH_Shop_Btn" data-id="${item.id}">GET</button>`;
const ico=card.querySelector('.DH_Shop_Ico');
if(ico) ico.onerror=function(){this.style.display='none';const fb=document.createElement('div');fb.style.cssText='width:36px;height:36px;display:flex;align-items:center;justify-content:center;font-size:22px;';fb.textContent='🎁';this.parentNode.insertBefore(fb,this);};
const btn=card.querySelector('.DH_Shop_Btn');
btn.onclick=async()=>{
btn.className='DH_Shop_Btn loading'; btn.textContent='...';
setTimeout(()=>{if(btn.textContent==='...')btn.textContent='50%';},300);
const ok=await _buyShopItem(item.id);
btn.textContent='100%';
setTimeout(()=>{
if(ok){btn.className='DH_Shop_Btn got';btn.textContent='GOT ✓';_notif('🛒','Shop','Got '+item.displayName+'!');
setTimeout(()=>{btn.className='DH_Shop_Btn';btn.textContent='GET';},3000);
} else {
btn.className='DH_Shop_Btn fail';btn.textContent='FAILED';
setTimeout(()=>{btn.className='DH_Shop_Btn';btn.textContent='GET';},2000);
_notif('❌','Shop','Failed to get item.');
}
},300);
};
grid.appendChild(card);
});
container.appendChild(grid);
});
}
async function _loadShop(){
const container=document.getElementById('DH_Shop_Container');
const cached=localStorage.getItem('dh2_shop');
if(cached){
try{const items=JSON.parse(cached);if(items&&items.length){_allShopItems=items;_renderShop(items);return;}}
catch{localStorage.removeItem('dh2_shop');}
}
container.innerHTML='<p class="DH_T2 DH_NoSel" style="text-align:center;padding:8px 0;">Loading shop...</p>';
const items=await _getShopItems();
if(items.length) localStorage.setItem('dh2_shop',JSON.stringify(items));
_allShopItems=items;
_renderShop(items);
}
function _installFakeSuper(){
if(localStorage.getItem('dh2_super')!=='true') return;
const RX=/https:\/\/www\.duolingo\.com\/\d{4}-\d{2}-\d{2}\/users\/.+/;
const ITEMS={gold_subscription:{itemName:'gold_subscription',subscriptionInfo:{vendor:'STRIPE',renewing:true,isFamilyPlan:true,expectedExpiration:9999999999000}}};
function mod(j){try{const d=JSON.parse(j);d.hasPlus=true;if(!d.trackingProperties)d.trackingProperties={};d.trackingProperties.has_item_gold_subscription=true;d.shopItems={...d.shopItems,...ITEMS};return JSON.stringify(d);}catch{return j;}}
const uw=(typeof unsafeWindow!=='undefined')?unsafeWindow:window;
const orig=uw.fetch;
uw.fetch=function(resource,options){
const url=resource instanceof Request?resource.url:resource;
const method=resource instanceof Request?resource.method:(options?.method||'GET');
if(method.toUpperCase()==='GET'&&RX.test(url)&&!url.includes('/shop-items')){
return orig.apply(this,arguments).then(async r=>{
const text=await r.clone().text();
return new Response(mod(text),{status:r.status,statusText:r.statusText,headers:new Headers(r.headers)});
});
}
return orig.apply(this,arguments);
};
}
_installFakeSuper();
function _resetLeague(){
const btn=document.getElementById('DH_League_Btn'); if(!btn) return;
btn.disabled=false; _setBtnState('DH_League_Btn',_C_BLUE,'RUN');
const fill=document.getElementById('DH_League_Fill');
if(fill) fill.style.width='0%';
const prog=document.getElementById('DH_League_Prog');
if(prog) setTimeout(()=>prog.classList.remove('on'),2000);
}
function _v1UpdateDisplayNow(){
requestAnimationFrame(()=>{
const xi=document.getElementById('DH_V1_XP_Input');
const gi=document.getElementById('DH_V1_Gem_Input');
const si=document.getElementById('DH_V1_Streak_Input');
if(xi){ xi.value=_v1Earned.xp>0?String(_v1Earned.xp):''; }
if(gi){ gi.value=_v1Earned.gems>0?String(_v1Earned.gems):''; }
if(si){ si.value=_v1Earned.streak>0?String(_v1Earned.streak):''; }
});
}
function _v1SetBtnState(btnId,cfg,label){
const btn=document.getElementById(btnId); if(!btn) return;
const lbl=btn.querySelector('.DH_Btn_Label'); if(!lbl) return;
btn.style.background=cfg.bg;
btn.style.outline=`2px solid ${cfg.outline}`;
btn.style.outlineOffset='-2px';
lbl.style.color=cfg.tc;
lbl.textContent=label;
}
function _v1SetProg(id, pct){
const prog=document.getElementById(id+'_Prog');
const fill=document.getElementById(id+'_Fill');
if(prog&&!prog.classList.contains('on')) prog.classList.add('on');
if(fill) fill.style.width=Math.min(100,Math.max(1,pct))+'%';
}
function _v1ClearProg(id){
const prog=document.getElementById(id+'_Prog');
const fill=document.getElementById(id+'_Fill');
setTimeout(()=>{ if(prog) prog.classList.remove('on'); },2000);
if(fill) fill.style.width='0%';
}
let _v1SkillId=null;
async function _v1FetchSkillId(){
if(_v1SkillId) return _v1SkillId;
try{
const r=await _gm('GET',
`https://www.duolingo.com/2017-06-30/users/${_sub}?fields=currentCourse{pathSectioned{units{levels{pathLevelMetadata{skillId},pathLevelClientData{skillId}}}}}`
);
if(r.status!==200) return null;
const d=JSON.parse(r.responseText);
const sections=d.currentCourse?.pathSectioned||[];
for(const sec of sections)
for(const unit of (sec.units||[]))
for(const lvl of (unit.levels||[])){
const sid=lvl.pathLevelMetadata?.skillId||lvl.pathLevelClientData?.skillId;
if(sid){ _v1SkillId=sid; return sid; }
}
}catch{}
return null;
}
async function _v1XP110Once(){
const sid=await _v1FetchSkillId();
if(!sid) return false;
try{
const sr=await _gm('POST','https://www.duolingo.com/2017-06-30/sessions',{
challengeTypes:[],
fromLanguage:_user.fromLanguage,
learningLanguage:_user.learningLanguage,
type:'UNIT_TEST',
skillIds:[sid]
});
if(!sr||sr.status!==200) return false;
const sess=JSON.parse(sr.responseText);
const now=Math.floor(Date.now()/1000);
const ur=await _gm('PUT',`https://www.duolingo.com/2017-06-30/sessions/${sess.id}`,{
id:sess.id,
metadata:sess.metadata,
type:'UNIT_TEST',
fromLanguage:_user.fromLanguage,
learningLanguage:_user.learningLanguage,
challenges:[],
adaptiveChallenges:[],
sessionExperimentRecord:[],
experiments_with_treatment_contexts:[],
adaptiveInterleavedChallenges:[],
sessionStartExperiments:[],
trackingProperties:[],
ttsAnnotations:[],
heartsLeft:0,
startTime:now,
enableBonusPoints:true,
endTime:now+60,
failed:false,
maxInLessonStreak:9,
shouldLearnThings:true,
hasBoost:true,
happyHourBonusXp:10,
pathLevelSpecifics:{unitIndex:0}
});
if(ur&&ur.status===200){
const d=JSON.parse(ur.responseText);
return d?.awardedXp||d?.xpGain||110;
}
}catch{}
return false;
}
async function _v1FarmXP(){
_v1Earned.xp=0; _v1UpdateDisplayNow();
_v1SetBtnState('DH_V1_XP_Btn',_C_RED,'STOP');
_v1SetProg('DH_V1_XP',1);
// Always start with story API (499 XP), silently fallback to global API (110 XP) on failure
let use499 = true;
let cons429 = 0;
const MAX_429 = 2;
let fallbackErrors = 0;
const MAX_FALLBACK = 5;
let loopPct = 0;
let fallbackLoops = 0; // count loops in fallback mode, re-probe every 10
// Slug is fixed (fr-en-le-passeport), no need to probe before starting
_workingSlug = 'fr-en-le-passeport';
_workingSlugFrom = 'fr';
_workingSlugLearn = 'en';
_v1FetchSkillId();
while(_v1Running && _v1Task==='xp'){
if(use499){
let status = 0;
try{
const now = Math.floor(Date.now()/1000);
const dur = Math.floor(Math.random()*121+300);
const r = await _gm('POST',`https://stories.duolingo.com/api2/stories/${_workingSlug}/complete`,{
awardXp:true, completedBonusChallenge:true,
fromLanguage:_workingSlugFrom, learningLanguage:_workingSlugLearn,
hasXpBoost:false, illustrationFormat:'svg',
isFeaturedStoryInPracticeHub:true, isLegendaryMode:true,
isV2Redo:false, isV2Story:false, masterVersion:true,
maxScore:0, score:0, happyHourBonusXp:469,
startTime:now, endTime:now+dur
});
status = r.status;
}catch{}
if(status===200){
cons429=0; fallbackErrors=0;
_v1Earned.xp += 499;
_v1UpdateDisplayNow();
loopPct = (loopPct+2)%99+1;
_v1SetProg('DH_V1_XP', loopPct);
} else if(status===429){
cons429++;
if(cons429>=MAX_429){
use499=false; fallbackLoops=0;
}
await _sleep(_delay*2);
continue;
} else {
use499=false; fallbackLoops=0;
continue;
}
} else {
// Global API — DuoFarmer-style UNIT_TEST session (110 XP)
// Every 10 fallback loops, try story API again
if(fallbackLoops>0 && fallbackLoops%10===0){
use499=true; cons429=0;
}
fallbackLoops++;
const earned = await _v1XP110Once();
if(earned){
fallbackErrors=0;
_v1Earned.xp += earned;
_v1UpdateDisplayNow();
loopPct = (loopPct+1)%99+1;
_v1SetProg('DH_V1_XP', loopPct);
} else {
fallbackErrors++;
if(fallbackErrors>=MAX_FALLBACK){
_notif('❌','V1 XP','Too many errors, stopping.');
break;
}
await _sleep(_delay*3);
continue;
}
}
await _sleep(_delay);
}
_v1ClearProg('DH_V1_XP');
_v1SetBtnState('DH_V1_XP_Btn',_C_BLUE,'RUN');
document.getElementById('DH_V1_XP_Btn').disabled=!_user;
_v1Running=false; _v1Task=null;
if(_v1Earned.xp>0){ _notif('✅','XP Farm Done!',`Farmed ${_v1Earned.xp.toLocaleString()} XP.`); setTimeout(_connect,1500); }
}
async function _v1FarmGems(){
// [PATCHED 2026.04.21] Farm Gems method is currently unavailable.
_v1Running=false; _v1Task=null;
_v1SetBtnState('DH_V1_Gem_Btn',_C_BLUE,'RUN');
document.getElementById('DH_V1_Gem_Btn').disabled=true;
_v1ClearProg('DH_V1_Gem');
_notif('🔒','Farm Gems method patched','Please wait until our developers fixed that.',6);
}
async function _v1FarmStreak(){
_v1Earned.streak=0; _v1UpdateDisplayNow();
_v1SetBtnState('DH_V1_Streak_Btn',_C_RED,'STOP');
_v1SetProg('DH_V1_Streak',1);
const CH=["assist","characterIntro","characterMatch","characterPuzzle","characterSelect","characterTrace","characterWrite","completeReverseTranslation","definition","dialogue","extendedMatch","extendedListenMatch","form","freeResponse","gapFill","judge","listen","listenComplete","listenMatch","match","name","listenComprehension","listenIsolation","listenSpeak","listenTap","orderTapComplete","partialListen","partialReverseTranslate","patternTapComplete","radioBinary","radioImageSelect","radioListenMatch","radioListenRecognize","radioSelect","readComprehension","reverseAssist","sameDifferent","select","selectPronunciation","selectTranscription","svgPuzzle","syllableTap","syllableListenTap","speak","tapCloze","tapClozeTable","tapComplete","tapCompleteTable","tapDescribe","translate","transliterate","transliterationAssist","typeCloze","typeClozeTable","typeComplete","typeCompleteTable","writeComprehension"];
let farmStart;
try{
const s=new Date(_user.streakData?.currentStreak?.startDate||Date.now());
s.setDate(s.getDate()-1); farmStart=s;
}catch{ farmStart=new Date(); farmStart.setDate(farmStart.getDate()-1); }
let dayIdx=0;
let loopPct=0;
while(_v1Running&&_v1Task==='streak'){
const simDay=new Date(farmStart);
simDay.setDate(simDay.getDate()-dayIdx);
const end=Math.floor(simDay.getTime()/1000);
try{
const sr=await _gm('POST','https://www.duolingo.com/2023-05-23/sessions',{
challengeTypes:CH,fromLanguage:_user.fromLanguage,isFinalLevel:false,isV2:true,
juicy:true,learningLanguage:_user.learningLanguage,smartTipsVersion:2,type:'GLOBAL_PRACTICE'
});
if(sr.status===200){
const sess=JSON.parse(sr.responseText);
await new Promise((res,rej)=>GM_xmlhttpRequest({
method:'PUT',url:`https://www.duolingo.com/2023-05-23/sessions/${sess.id}`,
headers:_hdrs,
data:JSON.stringify({
...sess,heartsLeft:5,startTime:end-1,endTime:end,
enableBonusPoints:false,failed:false,maxInLessonStreak:9,shouldLearnThings:true
}),
onload:r=>res(r),onerror:()=>rej(new Error('net')),
timeout:15000,ontimeout:()=>rej(new Error('timeout'))
}));
_v1Earned.streak++;
_v1UpdateDisplayNow();
loopPct=(loopPct+1)%99+1;
_v1SetProg('DH_V1_Streak',loopPct);
}
}catch{}
dayIdx++;
await _sleep(_delay);
}
_v1ClearProg('DH_V1_Streak');
_v1SetBtnState('DH_V1_Streak_Btn',_C_BLUE,'RUN');
document.getElementById('DH_V1_Streak_Btn').disabled=!_user;
_v1Running=false; _v1Task=null;
if(_v1Earned.streak>0){ _notif('🔥','Streak Farm Done!',`Farmed ${_v1Earned.streak} streak days.`); setTimeout(_connect,1500); }
}
function _v1RunToggle(task){
if(_v1Running&&_v1Task===task){
_v1Running=false; _v1Task=null; return;
}
if(_v1Running){_notif('⚠️','Busy','Stop current V1 farm first.');return;}
if(!_user){_notif('⚠️','Not connected','Please wait.');return;}
_v1Running=true; _v1Task=task;
if(task==='xp') _v1FarmXP();
if(task==='gems') _v1FarmGems();
if(task==='streak') _v1FarmStreak();
}
async function _run(type,val){
if(_running){_running=false;_notif('⏹️','Stopped','Farm stopped.');return;}
if(!_user){_notif('⚠️','Not connected','Please wait.');return;}
_running=true; _task=type;
try{
if(type==='xp') await _farmXP(val);
if(type==='gem') await _farmGems(val);
if(type==='streak') await _farmStreak(val);
if(type==='league') await _farmLeague();
}catch(e){_notif('❌','Error',e.message);}
_running=false; _task=null;
if(type==='xp') _resetBtn('DH_XP_Btn','GET');
if(type==='gem') _resetBtn('DH_Gem_Btn','GET');
if(type==='streak') _resetBtn('DH_Streak_Btn','RUN');
if(type==='league') _resetLeague();
}
function _accGetAll(){ try{return JSON.parse(localStorage.getItem('dh2_accounts')||'[]');}catch{return [];} }
function _accSetAll(arr){ localStorage.setItem('dh2_accounts',JSON.stringify(arr)); }
function _accSaveCurrent(){
if(!_user||!_jwt||!_sub){ _notif('⚠️','Not connected','Please wait for connection.'); return; }
const all=_accGetAll();
if(all.find(a=>a.id==_sub)){ _notif('ℹ️','Already saved','This account is already in the list.'); return; }
let pic='';
if(_user.picture){
pic=_user.picture.replace(/\/(medium|large|small)$/,'/xlarge');
if(!pic.endsWith('/xlarge')&&pic.includes('duolingo.com/ssr-avatars')) pic+='/xlarge';
}
all.push({ id:_sub, username:_user.username||'User', pic, token:_jwt });
_accSetAll(all);
_notif('✅','Account Saved',`Saved account: ${_user.username}`);
_renderAccounts();
}
function _accRemove(id){
const all=_accGetAll().filter(a=>a.id!=id);
_accSetAll(all);
_renderAccounts();
_notif('🗑️','Removed','Account removed from list.');
}
function _accLogin(id){
const acc=_accGetAll().find(a=>a.id==id);
if(!acc){ _notif('⚠️','Not found','Account not found.'); return; }
document.cookie=`jwt_token=${acc.token}; domain=.duolingo.com; path=/; max-age=31536000`;
window.location.reload();
}
function _renderAccounts(){
const wrap=document.getElementById('DH_AccList_Wrap'); if(!wrap) return;
const all=_accGetAll();
const saveBtn=document.getElementById('DH_AccSave_Btn');
if(saveBtn) saveBtn.disabled=!_user;
if(all.length===0){
wrap.innerHTML=`<p class="DH_T2 DH_NoSel" style="text-align:center;padding:8px 0;">No saved accounts.</p>`;
return;
}
wrap.innerHTML='';
all.forEach(acc=>{
const card=document.createElement('div');
card.className='DH_Acc_Card';
const isCurrentUser=_sub&&acc.id==_sub;
const picHtml=acc.pic
?`<img src="${acc.pic}" style="width:100%;height:100%;object-fit:cover;border-radius:50%;" onerror="this.parentNode.innerHTML='👤'">`
:'👤';
card.innerHTML=`
<div class="DH_Acc_Avatar">${picHtml}</div>
<div class="DH_Acc_Info">
<p class="DH_Acc_Name DH_NoSel">${acc.username}</p>
${isCurrentUser
? `<p class="DH_Acc_Sub active DH_NoSel"><svg width="8" height="8" viewBox="0 0 8 8"><circle cx="4" cy="4" r="4" fill="rgb(var(--DH-green))"><animate attributeName="opacity" values="1;0.4;1" dur="2s" repeatCount="indefinite"/></circle></svg>Active</p>`
: `<p class="DH_Acc_Sub DH_NoSel">ID: ${String(acc.id).slice(0,8)}…</p>`
}
</div>
<div class="DH_Acc_Action_Row">
${!isCurrentUser?`<button class="DH_Acc_Btn login" data-id="${acc.id}">LOG IN</button>`:''}
<button class="DH_Acc_Btn del" data-id="${acc.id}">✕</button>
</div>
`;
card.querySelector('.del').addEventListener('click',e=>{ e.stopPropagation(); _accRemove(acc.id); });
const loginBtn=card.querySelector('.login');
if(loginBtn) loginBtn.addEventListener('click',e=>{ e.stopPropagation(); _accLogin(acc.id); });
wrap.appendChild(card);
});
}
function _goalHdrsLocal(jwt){
return {'Content-Type':'application/json','x-requested-with':'XMLHttpRequest','accept':'application/json; charset=UTF-8','Authorization':'Bearer '+jwt};
}
function _mqGm(method,url,data,hdrs){
return new Promise((res,rej)=>GM_xmlhttpRequest({
method,url,headers:hdrs,
data:data?JSON.stringify(data):null,
onload:r=>res(r),onerror:()=>rej(new Error('Network')),
timeout:15000,ontimeout:()=>rej(new Error('Timeout'))
}));
}
function _mqGetTimestamp(goalId){
const m=goalId.match(/^(\d{4})_(\d{2})_monthly/);
if(m){const d=new Date(Date.UTC(parseInt(m[1]),parseInt(m[2])-1,15,12,0,0));return d.toISOString();}
return new Date().toISOString();
}
async function _loadMonthlyQuests(){
const cont=document.getElementById('DH_MQ_Container'); if(!cont) return;
const claimBtn=document.getElementById('DH_MQ_ClaimAll_Btn');
if(!_jwt||!_sub){ cont.innerHTML=`<p class="DH_T2 DH_NoSel" style="text-align:center;padding:8px 0;">Not connected.</p>`; return; }
cont.innerHTML=`<p class="DH_T2 DH_NoSel" style="text-align:center;padding:8px 0;">⟳ Loading…</p>`;
const gh=_goalHdrsLocal(_jwt);
const tz=Intl.DateTimeFormat().resolvedOptions().timeZone;
try{
const [schemaR,progR]=await Promise.all([
_mqGm('GET',`${GOALS_API}/schema?ui_language=en&_=${Date.now()}`,null,gh),
_mqGm('GET',`${GOALS_API}/users/${_sub}/progress?timezone=${tz}&ui_language=en`,null,gh)
]);
if(schemaR.status!==200||progR.status!==200) throw new Error('API error');
const schema=JSON.parse(schemaR.responseText);
const prog=JSON.parse(progR.responseText);
const progress=prog.goals?.progress||{};
const earned=new Set(prog.badges?.earned||[]);
_questState={schema,progress,earned};
_renderMonthlyQuests(schema,progress,earned,gh);
if(claimBtn){ claimBtn.disabled=false; }
}catch(e){
cont.innerHTML=`<p class="DH_T2 DH_NoSel" style="text-align:center;color:rgb(var(--DH-red));padding:8px 0;">Failed to load quests.</p>`;
}
}
function _renderMonthlyQuests(schema,progress,earned,gh){
const cont=document.getElementById('DH_MQ_Container'); if(!cont) return;
cont.innerHTML='';
const now=new Date();
const yr=now.getFullYear().toString();
const mo=(now.getMonth()+1).toString().padStart(2,'0');
const mReg=new RegExp(`^${yr}_${mo}_monthly`);
const map=new Map();
schema.goals.forEach(g=>{
const m2=g.goalId.match(/^(\d{4}_\d{2})_monthly/);
if(!m2) return;
const key=m2[1];
const existing=map.get(key);
if(!existing){map.set(key,g);}
else{
const existIsChallenge=existing.category?.includes('CHALLENGE');
const newIsChallenge=g.category?.includes('CHALLENGE');
if(!existIsChallenge&&newIsChallenge) map.set(key,g);
}
});
const goals=[...map.values()].filter(g=>g.goalId.match(mReg)||true).reverse();
const monthly=goals.filter(g=>g.category&&g.category.includes('MONTHLY'));
if(monthly.length===0){
cont.innerHTML=`<p class="DH_T2 DH_NoSel" style="text-align:center;padding:8px 0;">No monthly quests found.</p>`;
return;
}
monthly.forEach(g=>{
const isEarned=earned.has(g.badgeId)||earned.has(g.goalId);
let cur=0;
const raw=progress[g.goalId];
if(typeof raw==='number') cur=raw;
else if(raw&&typeof raw==='object') cur=raw.progress||0;
const tgt=g.threshold||10;
let pct=Math.min(100,(cur/tgt)*100);
if(isEarned){pct=100;cur=tgt;}
const remaining=Math.max(0,tgt-cur);
let icon='https://d35aaqx5ub95lt.cloudfront.net/images/achievement/aca5f82d97f5e67c1acb1ea05a0e6d1a.svg';
const badge=schema.badges?.find(x=>x.badgeId===g.badgeId);
if(badge&&badge.icon?.enabled?.lightMode){
icon=badge.icon.enabled.lightMode.svg||badge.icon.enabled.lightMode.url||icon;
}
const item=document.createElement('div');
item.className=`DH_Quest_Item${isEarned?' done':''}`;
item.innerHTML=`
<img src="${icon}" class="DH_Quest_Icon" onerror="this.src='https://d35aaqx5ub95lt.cloudfront.net/images/achievement/aca5f82d97f5e67c1acb1ea05a0e6d1a.svg'">
<div class="DH_Quest_Info">
<p class="DH_Quest_Title DH_NoSel">${g.title?.uiString||g.goalId}</p>
<p class="DH_Quest_Meta DH_NoSel">${isEarned?'COMPLETED':''+cur+' / '+tgt+' · '+g.metric}</p>
<div class="DH_Quest_Bar_Bg"><div class="DH_Quest_Bar_Fill" style="width:${pct}%"></div></div>
</div>
${!isEarned&&remaining>0?`<button class="DH_Quest_Get_Btn" data-metric="${g.metric}" data-amount="${remaining}" data-id="${g.goalId}">GET +${remaining}</button>`:''}
${isEarned?`<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="flex-shrink:0;"><circle cx="12" cy="12" r="10" fill="rgba(var(--DH-green),0.15)" stroke="rgb(var(--DH-green))" stroke-width="1.5"/><path d="M7.5 12.5l3 3 6-6" stroke="rgb(var(--DH-green))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`:''}
`;
const btn=item.querySelector('.DH_Quest_Get_Btn');
if(btn) btn.addEventListener('click',async()=>{
btn.disabled=true; btn.textContent='…';
try{
const payload={
metric_updates:[{metric:btn.dataset.metric,quantity:parseInt(btn.dataset.amount)}],
timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,
timestamp:_mqGetTimestamp(btn.dataset.id)
};
const r=await _mqGm('POST',`${GOALS_API}/users/${_sub}/progress/batch`,payload,gh);
if(r.status===200){
btn.textContent='✓'; btn.classList.add('done');
_notif('✅','Quest Done!','Progress injected successfully.');
setTimeout(()=>_loadMonthlyQuests(),900);
} else { btn.textContent='ERR'; btn.disabled=false; }
}catch{ btn.textContent='ERR'; btn.disabled=false; }
});
cont.appendChild(item);
});
}
async function _claimAllMonthly(){
if(!_questState||!_questState.schema){ _notif('⚠️','Not loaded','Open Monthly Quests first.'); return; }
const claimBtn=document.getElementById('DH_MQ_ClaimAll_Btn');
if(claimBtn){ claimBtn.disabled=true; const lbl=claimBtn.querySelector('.DH_Sm_Btn_Label'); if(lbl) lbl.textContent='…'; }
const gh=_goalHdrsLocal(_jwt);
const uniqueMetrics=new Set();
_questState.schema.goals.forEach(g=>{
if(g.category&&g.category.includes('MONTHLY')&&g.metric) uniqueMetrics.add(g.metric);
});
if(uniqueMetrics.size>0){
const updates=[...uniqueMetrics].map(m=>({metric:m,quantity:2000}));
updates.push({metric:'QUESTS',quantity:1});
const payload={metric_updates:updates,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,timestamp:new Date().toISOString()};
try{
await _mqGm('POST',`${GOALS_API}/users/${_sub}/progress/batch`,payload,gh);
_notif('✅','Monthly Quests','All monthly quests claimed!');
setTimeout(()=>_loadMonthlyQuests(),900);
}catch{ _notif('❌','Error','Failed to claim quests.'); }
} else { _notif('ℹ️','Nothing to do','No monthly metrics found.'); }
if(claimBtn){ claimBtn.disabled=false; const lbl=claimBtn.querySelector('.DH_Sm_Btn_Label'); if(lbl) lbl.textContent='CLAIM'; }
}
document.getElementById('DH_Hide_Btn').addEventListener('click',()=>_doHide(!_hidden));
document.getElementById('DH_SwitchV1_Btn').addEventListener('click',()=>{
_v1Mode=true;
document.getElementById('DH_SwitchV1_Btn').style.display='none';
document.getElementById('DH_SwitchV2_Btn').style.display='';
_v1SyncUser();
_goPage('V1');
});
document.getElementById('DH_SwitchV2_Btn').addEventListener('click',()=>{
_v1Mode=false;
if(_v1Running){_v1Running=false;_v1Task=null;}
document.getElementById('DH_SwitchV2_Btn').style.display='none';
document.getElementById('DH_SwitchV1_Btn').style.display='';
_goPage(1);
});
document.getElementById('DH_V1_XP_Btn').addEventListener('click',()=>_v1RunToggle('xp'));
document.getElementById('DH_V1_Gem_Btn').addEventListener('click',()=>_v1RunToggle('gems'));
document.getElementById('DH_V1_Streak_Btn').addEventListener('click',()=>_v1RunToggle('streak'));
document.getElementById('DH_V1_Settings_Btn').addEventListener('click',()=>{ _goPage(4); _initHideProfileToggle(); });
document.getElementById('DH_Discord_Btn').addEventListener('click',()=>window.open('https://discord.com/invite/Gvmd7deFtS','_blank'));
document.getElementById('DH_GitHub_Btn').addEventListener('click',()=>window.open('https://github.com/not2pixel/DuoHacker','_blank'));
async function _getPrivacy(){
if(!_sub||!_hdrs) return null;
try{
const r=await _gm('GET',`https://www.duolingo.com/2023-05-23/users/${_sub}/privacy-settings?fields=privacySettings`);
if(r.status!==200) return null;
const data=JSON.parse(r.responseText);
const social=data.privacySettings?.find(x=>x.id==='disable_social');
return social?social.enabled:null;
}catch(e){ return null; }
}
async function _setPrivacy(hide){
if(!_sub||!_hdrs) return false;
try{
const r=await _gm('PATCH',`https://www.duolingo.com/2023-05-23/users/${_sub}/privacy-settings?fields=privacySettings`,{DISABLE_SOCIAL:hide});
return r.status===200||r.status===204;
}catch(e){ return false; }
}
function _applyHideProfileToggle(){
const tog=document.getElementById('DH_HideProfile_Toggle');
const lbl=document.getElementById('DH_HideProfile_Status');
if(!tog||!lbl) return;
if(_privacy===null){ lbl.textContent='Unavailable'; tog.disabled=true; return; }
if(!tog.dataset.dhBound){
tog.dataset.dhBound='1';
tog.addEventListener('change',async function(){
tog.disabled=true;
lbl.textContent='Saving\u2026';
const ok=await _setPrivacy(tog.checked);
if(ok){
_privacy=tog.checked;
lbl.textContent=tog.checked?'Profile is private':'Profile is public';
}else{
tog.checked=!tog.checked;
lbl.textContent='Failed \u2014 try again';
}
tog.disabled=false;
});
}
tog.checked=_privacy;
tog.disabled=false;
lbl.textContent=_privacy?'Profile is private':'Profile is public';
}
function _initHideProfileToggle(){
const tog=document.getElementById('DH_HideProfile_Toggle');
const lbl=document.getElementById('DH_HideProfile_Status');
if(!tog||!lbl) return;
if(!_sub||!_hdrs){ lbl.textContent='Not connected'; tog.disabled=true; return; }
if(_privacy!==null){ _applyHideProfileToggle(); return; }
lbl.textContent='Loading\u2026'; tog.disabled=true;
_getPrivacy().then(v=>{ _privacy=v; _applyHideProfileToggle(); });
}
document.getElementById('DH_Settings_Btn').addEventListener('click',()=>{_goPage(2);_initHideProfileToggle();});
document.getElementById('DH_Back_Btn').addEventListener('click',()=>_goBack());
document.getElementById('DH_Shop_Btn').addEventListener('click',()=>{_goPage(3);_loadShop();});
document.getElementById('DH_Shop_Back_Btn').addEventListener('click',()=>_goBack());
document.getElementById('DH_Page4_Btn').addEventListener('click',()=>{ _goPage(4); _initHideProfileToggle(); });
document.getElementById('DH_Settings_Back_Btn').addEventListener('click',()=>{
_initHideProfileToggle();
_goBack();
});
document.getElementById('DH_AccSettings_Btn').addEventListener('click',()=>_goPage(5));
document.getElementById('DH_AccMgr_Back_Btn').addEventListener('click',()=>_goBack());
document.getElementById('DH_AccSave_Btn').addEventListener('click',()=>_accSaveCurrent());
document.getElementById('DH_MonthlyQuest_Nav_Btn').addEventListener('click',e=>{
if(!e.target.closest('#DH_MonthlyQuest_Claim_Btn')) _goPage(6);
});
document.getElementById('DH_MonthlyQuest_Claim_Btn').addEventListener('click',e=>{
e.stopPropagation();
_goPage(6);
});
document.getElementById('DH_MQ_Back_Btn').addEventListener('click',()=>_goBack());
document.getElementById('DH_Credits_Back_Btn').addEventListener('click',()=>_goBack());
document.getElementById('DH_Credits_Btn').addEventListener('click',()=>{
const container=document.getElementById('DH_Credits_Container');
container.innerHTML='';
CREDITS.forEach(c=>{
const card=document.createElement('div');
card.className='DH_Credit_Card';
const header=document.createElement('div');
header.className='DH_Credit_Card_Header';
const img=document.createElement('img');
img.className='DH_Credit_Thumb';
img.src=c.thumbnail;
img.alt='';
img.onerror=function(){this.style.display='none';};
const info=document.createElement('div');
info.style.cssText='display:flex;flex-direction:column;gap:1px;min-width:0;';
const name=document.createElement('p');
name.className='DH_Credit_Script DH_NoSel';
name.textContent=c.script;
const author=document.createElement('p');
author.className='DH_Credit_Author DH_NoSel';
author.textContent='by '+c.author;
info.appendChild(name);
info.appendChild(author);
header.appendChild(img);
header.appendChild(info);
const task=document.createElement('p');
task.className='DH_Credit_Task DH_NoSel';
task.textContent=c.task;
const link=document.createElement('a');
link.className='DH_Credit_Link';
link.href=c.url;
link.target='_blank';
link.rel='noopener';
link.textContent='View Script \u2197';
card.appendChild(header);
card.appendChild(task);
card.appendChild(link);
container.appendChild(card);
});
_goPage(7);
});
document.getElementById('DH_MQ_ClaimAll_Btn').addEventListener('click',()=>_claimAllMonthly());
const xpI=document.getElementById('DH_XP_Input'),xpB=document.getElementById('DH_XP_Btn');
xpI.addEventListener('input',()=>{xpB.disabled=!_user||!xpI.value||+xpI.value<30;});
xpB.addEventListener('click',()=>{
if(_running&&_task==='xp'){_run('xp',0);return;}
if(_running){_notif('⚠️','Busy','Stop current farm first.');return;}
const v=+xpI.value;if(v<30){_notif('⚠️','Min 30 XP','Enter at least 30 XP.');return;}_run('xp',v);
});
xpI.addEventListener('keydown',e=>{if(e.key==='Enter'&&!xpB.disabled)xpB.click();});
const gmI=document.getElementById('DH_Gem_Input'),gmB=document.getElementById('DH_Gem_Btn');
gmI.addEventListener('input',()=>{gmB.disabled=!_user||!gmI.value||+gmI.value<1;});
gmB.addEventListener('click',()=>{
if(_running&&_task==='gem'){_run('gem',0);return;}
if(_running){_notif('⚠️','Busy','Stop current farm first.');return;}
const v=+gmI.value;if(v<1)return;_run('gem',v);
});
gmI.addEventListener('keydown',e=>{if(e.key==='Enter'&&!gmB.disabled)gmB.click();});
const stI=document.getElementById('DH_Streak_Input'),stB=document.getElementById('DH_Streak_Btn');
stI.addEventListener('input',()=>{stB.disabled=!_user||!stI.value||+stI.value<1;});
stB.addEventListener('click',()=>{
if(_running&&_task==='streak'){_run('streak',0);return;}
if(_running){_notif('⚠️','Busy','Stop current farm first.');return;}
const v=+stI.value;if(v<1)return;_run('streak',v);
});
stI.addEventListener('keydown',e=>{if(e.key==='Enter'&&!stB.disabled)stB.click();});
const prI=document.getElementById('DH_Practice_Input'),prB=document.getElementById('DH_Practice_Btn');
prI.addEventListener('input',()=>{prB.disabled=!_user;});
prB.addEventListener('click',()=>{
if(_lessonSolving){
_stopPractice();
sessionStorage.removeItem('dh2_practice');
_notif('⏹️','Stopped','Practice farm stopped.');
return;
}
if(_running){_notif('⚠️','Busy','Stop current farm first.');return;}
if(!_user){_notif('⚠️','Not connected','Please wait.');return;}
const v=parseInt(prI.value)||0;
sessionStorage.setItem('dh2_practice',JSON.stringify({active:true,count:v,done:0}));
_farmPractice(v);
});
document.getElementById('DH_League_Btn').addEventListener('click',()=>{
if(_running&&_task==='league'){_run('league',0);return;}
if(_running){_notif('⚠️','Busy','Stop current farm first.');return;}
_run('league',0);
});
document.getElementById('DH_Quest_Btn').addEventListener('click',async()=>{
if(_running){_notif('⚠️','Busy','Stop current farm first.');return;}
await _farmDailyQuest();
});
const delI=document.getElementById('DH_Delay_Input'),delB=document.getElementById('DH_Delay_Btn');
delB.addEventListener('click',()=>{
const v=parseInt(delI.value);
if(!isNaN(v)&&v>=0){
_delay=v; localStorage.setItem('dh2_delay',v);
_setBtnState('DH_Delay_Btn',_C_GREEN,'SAVED ✓');
setTimeout(()=>_setBtnState('DH_Delay_Btn',_C_BLUE,'SAVE'),1500);
}
});
const supT=document.getElementById('DH_Super_Toggle');
supT.checked=localStorage.getItem('dh2_super')==='true';
supT.addEventListener('change',()=>{ localStorage.setItem('dh2_super',supT.checked?'true':'false');
_notif('ℹ️','Reload required','Refresh page to apply.',4);
});
const solverT=document.getElementById('DH_Solver_Toggle');
solverT.checked=localStorage.getItem('duohacker_inject_solver')==='true';
_INJECT_SOLVER_ENABLED=solverT.checked;
solverT.addEventListener('change',()=>{
_INJECT_SOLVER_ENABLED=solverT.checked;
localStorage.setItem('duohacker_inject_solver',solverT.checked?'true':'false');
if(!_INJECT_SOLVER_ENABLED){
_autoSolver.removeUI();
} else {
const inLesson=window.location.pathname.includes('/lesson')||window.location.pathname.includes('/practice');
if(inLesson) setTimeout(()=>_autoSolver.createUI(),300);
}
});
document.getElementById('DH_Shop_Search').addEventListener('input',e=>{
_renderShop(_allShopItems,e.target.value);
});
let _hideAnimObserver=null;
const _HIDE_ANIM_STYLE_ID='DH_HideAnim_Style';
const _HIDE_ANIM_KEY='duohacker_hide_animation';
const _HIDE_PROTECT=['#DH_Root','#DH_Root *'];
function _applyHideAnim(){
if(document.getElementById(_HIDE_ANIM_STYLE_ID)) return;
const s=document.createElement('style');
s.id=_HIDE_ANIM_STYLE_ID;
s.textContent=`
body img:not(#DH_Root img),
body svg:not(#DH_Root svg),
body [role="img"]:not(#DH_Root [role="img"]),
body canvas:not(#DH_Root canvas),
body video:not(#DH_Root video),
body lottie-player:not(#DH_Root lottie-player) {
visibility:hidden!important;
animation:none!important;
transition:none!important;
}
body * {
animation-play-state:paused!important;
transition:none!important;
}
#DH_Root {
visibility:visible!important;
}
#DH_Root * {
animation-play-state:running!important;
visibility:visible!important;
transition:unset!important;
}`;
document.head.appendChild(s);
}
function _removeHideAnim(){
document.getElementById(_HIDE_ANIM_STYLE_ID)?.remove();
}
const _hideAnimT=document.getElementById('DH_HideAnim_Toggle');
_hideAnimT.checked=localStorage.getItem(_HIDE_ANIM_KEY)==='true';
if(_hideAnimT.checked) _applyHideAnim();
_hideAnimT.addEventListener('change',()=>{
localStorage.setItem(_HIDE_ANIM_KEY,_hideAnimT.checked?'true':'false');
_hideAnimT.checked?_applyHideAnim():_removeHideAnim();
});
const CREDITS = [
{
script: 'DuoHacker V1',
url: 'https://github.com/not2pixel/DuoHacker',
thumbnail: 'https://raw.githubusercontent.com/not2pixel/DuoHacker/refs/heads/main/images/DuoHacker_Logo_NoBG_PNG.png',
author: 'not2pixel',
task: 'Original script - The main cores are being used in V2'
},
{
script: 'Duolingo PRO',
url: 'https://github.com/anonymoushackerIV/Duolingo-PRO',
thumbnail: 'https://www.duolingopro.net/static/favicons/duo/128/light/primary.png',
author: 'anonymoushackerIV',
task: 'The V2 UI was inspired by this script'
}
];
const main=document.getElementById('DH_Main');
const box=document.getElementById('DH_Main_Box');
main.style.bottom=`-${box.offsetHeight-8}px`;
box.style.opacity='0'; box.style.filter='blur(8px)';
_doHide(false);
setTimeout(()=>{
main.style.transition='0.8s cubic-bezier(0.16,1,0.32,1)';
box.style.transition='0.8s cubic-bezier(0.16,1,0.32,1)';
main.style.bottom='16px'; box.style.opacity=''; box.style.filter='';
setTimeout(()=>{main.style.transition='';box.style.transition='';},800);
},600);
let _licenseLoaded=false;
function _loadLicense(){
if(_licenseLoaded) return;
const txt=document.getElementById('DH_License_Text');
if(!txt) return;
GM_xmlhttpRequest({
method:'GET',
url:'https://raw.githubusercontent.com/not2pixel/DuoHacker/refs/heads/main/LICENSE',
onload:r=>{
if(!document.getElementById('DH_License_Text')) return;
document.getElementById('DH_License_Text').textContent=
r.status===200?r.responseText:'Could not load license. Please check your connection.';
_licenseLoaded=true;
},
onerror:()=>{
const t=document.getElementById('DH_License_Text');
if(t) t.textContent='Could not load license. Please check your connection.';
}
});
}
document.getElementById('DH_License_Open_Btn').addEventListener('click',()=>_goPage(9));
document.getElementById('DH_License_Back_Btn').addEventListener('click',()=>_goBack());
_connect();
setTimeout(()=>{
if(!_v1Mode){
const sb=document.getElementById('DH_SwitchV1_Btn');
if(sb&&!_hidden) sb.style.display='';
}
},700);
_resumePracticeIfNeeded();
})();