您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
XP + Gems farming tool for Duolingo, with multi-language UI and floating Discord chat
// ==UserScript== // @name YM DuoCheat_Hub // @namespace http://tampermonkey.net/ // @version v1.0BETA // @description XP + Gems farming tool for Duolingo, with multi-language UI and floating Discord chat // @author ´꒳`ⓎⒶⓂⒾⓈⒸⓇⒾⓅⓉ×͜× // @match https://www.duolingo.com/* // @grant none // @run-at document-idle // @icon https://www.google.com/s2/favicons?sz=64&domain=duolingo.com // ==/UserScript== (function () { 'use strict'; const SERVER_ID = '1377275722342858973'; const WIDGET_URL = `https://discord.com/widget?id=${SERVER_ID}&theme=dark`; const VERSION = 'v1.0BETA'; let lang = 'en'; const LANG = { en: { header: '🌈 DUO HUB TOOL', farmLabel: '🔧 What do you want to farm?', start: '🚀 Start farm', done: '🚜 Farm finished', copied: 'Token copied!', placeholder: 'Enter target amount', settings: 'Settings', support: 'Support', profile: 'Profile', discord: 'Discord', version: 'Version', madeby: 'Made by' } }; const t = k => LANG[lang][k]; const style = document.createElement('style'); style.textContent = ` @keyframes rainbowBorder { 0%,100%{border-color:red}14%{border-color:orange}28%{border-color:yellow} 42%{border-color:green}57%{border-color:blue}71%{border-color:indigo}85%{border-color:violet} } .duo-rainbow-panel { position:fixed;bottom:100px;left:20px;background:#f0f9ff;color:#222;padding:16px;border:3px solid; border-radius:16px;z-index:9999;font-size:14px;box-shadow:0 4px 10px rgba(0,0,0,.2);width:280px; display:flex;flex-direction:column;gap:12px;transition:opacity 0.4s ease, transform 0.4s ease; font-family:'Segoe UI',sans-serif;animation:rainbowBorder 5s infinite linear; } .duo-panel-hidden{opacity:0!important;transform:translateY(30px)!important;pointer-events:none!important;} .duo-toggle-btn { position:fixed;bottom:20px;right:20px;width:64px;height:64px;background:#58cc02;color:#fff; border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:30px;cursor:pointer; z-index:10000;border:5px solid;animation:rainbowBorder 3s infinite linear; box-shadow:0 6px 16px rgba(0,0,0,.35); } .duo-toggle-version { position:fixed;bottom:75px;right:22px;background:rgba(0,0,0,.5);color:#fff;font-size:10px; padding:2px 6px;border-radius:6px;z-index:10000;font-family:monospace; } .duo-discord-btn { position:fixed;bottom:100px;right:20px;width:60px;height:60px;background:#5865F2;color:#fff; border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:26px;cursor:pointer; z-index:10000;box-shadow:0 6px 16px rgba(0,0,0,.35); } .duo-notification-btn { position:fixed;bottom:170px;right:20px;width:60px;height:60px;background:#ffb703;color:#000; border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:26px;cursor:pointer; z-index:10000;box-shadow:0 6px 16px rgba(0,0,0,.35); } .duo-discord-chat { position:fixed;bottom:180px;right:20px;width:350px;height:500px;background:#fff;border:3px solid #5865F2; border-radius:10px;overflow:hidden;z-index:10000;display:none;box-shadow:0 6px 16px rgba(0,0,0,.4); } `; document.head.appendChild(style); const toggleBtn = Object.assign(document.createElement('div'), {className:'duo-toggle-btn', textContent:'⚙️'}); const versionTag = Object.assign(document.createElement('div'), {className:'duo-toggle-version',textContent:VERSION}); const panel = Object.assign(document.createElement('div'), {className:'duo-rainbow-panel'}); document.body.append(toggleBtn, versionTag, panel); const discordBtn = Object.assign(document.createElement('div'), { className: 'duo-discord-btn', innerHTML: '💬' }); const discordChat = document.createElement('div'); discordChat.className = 'duo-discord-chat'; const iframe = document.createElement('iframe'); iframe.src = WIDGET_URL; iframe.style.border = 'none'; iframe.width = '100%'; iframe.height = '100%'; iframe.setAttribute('allowtransparency', 'true'); iframe.setAttribute('frameborder', '0'); iframe.setAttribute('sandbox', 'allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts'); discordChat.appendChild(iframe); const notificationBtn = Object.assign(document.createElement('div'), { className: 'duo-notification-btn', innerHTML: '🔔' }); notificationBtn.onclick = () => { alert('📢 Notice from admin: Currently, XP and GEMS farming is working. STREAK farming is under development! Thank you! (ADMIN: YAMISCRIPT_DEV)'); navigator.vibrate?.(40); }; let chatVisible = false; discordBtn.onclick = () => { chatVisible = !chatVisible; discordChat.style.display = chatVisible ? 'block' : 'none'; navigator.vibrate?.(40); }; document.body.append(discordBtn, notificationBtn, discordChat); const header = Object.assign(document.createElement('div'), { textContent: t('header'), style: 'font-weight:bold;font-size:17px;text-align:center;color:#0a4d2f' }); const createBtn = (label, emoji, onClick) => { const b = document.createElement('button'); b.innerHTML = `${emoji} ${label}`; Object.assign(b.style,{ padding:'10px',background:'#fff',color:'#1c1c1c',border:'2px solid #58CC02',borderRadius:'10px', fontSize:'14px',cursor:'pointer',fontWeight:'600',width:'100%' }); b.onclick = onClick; return b; }; const supportBtn = createBtn(t('support'), '❤️', () => alert('[email protected]')); const profileBtn = createBtn(t('profile'), '👤', () => window.open('https://guns.lol/yamiscript_dev','_blank')); const discordBtn2 = createBtn(t('discord'), '💬', () => window.open('https://discord.gg/F3uWQNpt','_blank')); const settingsBtn = createBtn(t('settings'),'⚙️', () => alert('⚙️Feature under development...')); const btnGrid = document.createElement('div'); Object.assign(btnGrid.style,{display:'grid',gridTemplateColumns:'1fr 1fr',gap:'10px'}); btnGrid.append(supportBtn, profileBtn, discordBtn2, settingsBtn); const farmDiv = document.createElement('div'); farmDiv.innerHTML = ` <div id="farmLabel">${t('farmLabel')}</div> <select id="farmType" style="width:100%;padding:6px;border:1px solid #ccc;border-radius:6px;margin:6px 0;"> <option value="xp">XP</option><option value="gems">Gems</option><option value="streak">Streak</option> </select> <input id="targetValue" type="number" placeholder="${t('placeholder')}" style="width:100%;padding:6px;border:1px solid #ccc;border-radius:6px;margin-bottom:6px;"> <button id="startFarm" style="background:#58cc02;color:#fff;width:100%;padding:8px;border:none;border-radius:8px;cursor:pointer;"> ${t('start')} </button> <div id="progressText" style="margin-top:6px;font-size:13px;color:#333;">❌Chưa hoạt động</div> <div style="height:10px;background:#ccc;border-radius:6px;overflow:hidden;"> <div id="progressBar" style="height:100%;width:0%;background:#58cc02;transition:width .3s;"></div> </div> `; const footer = document.createElement('div'); footer.innerHTML = ` <div style="font-size:12px;color:#444;">${t('version')}: ${VERSION}</div> <div style="font-size:11px;color:#888;margin-top:2px;">${t('madeby')} <b>YAMISCRIPT_DEV</b></div> `; footer.style.textAlign = 'center'; panel.append(header, btnGrid, farmDiv, footer); let visible = true; toggleBtn.onclick = () => { visible = !visible; panel.classList.toggle('duo-panel-hidden', !visible); navigator.vibrate?.(50); }; const getCookie = n => document.cookie.match(new RegExp(`${n}=([^;]+)`))?.[1] || null; const getJwtToken = () => getCookie('jwt_token'); const decodeJwt = t => JSON.parse(atob(t.split('.')[1].replace(/-/g,'+').replace(/_/g,'/'))); const farmGems = async target => { try { const userId = getCookie('logged_out_uuid'); const jwt = getJwtToken(); if (!userId || !jwt) return alert('⚠️ Unable to get token!'); for (let i = 0; i < target; i++) { await fetch(`https://www.duolingo.com/2017-06-30/users/${userId}/rewards/CAPSTONE_COMPLETION-xxxx-2-GEMS`,{ method:'PATCH', headers:{accept:'application/json',authorization:`Bearer ${jwt}`,'content-type':'application/json'}, body:JSON.stringify({amount:0,consumed:true,skillId:'xxx',type:'mission'}) }); document.getElementById('progressText').textContent = `💎 Đã farm ${i+1}/${target} gems`; document.getElementById('progressBar').style.width = `${((i+1)/target)*100}%`; await new Promise(r=>setTimeout(r,0)); } document.getElementById('progressText').textContent = '✅Gems farming completed!'; } catch(e){ console.error('Lỗi farm gems:',e); } }; const farmXp = async (headers, sessionPayload, updatePayload, targetXP) => { let currentXP = 0; while (currentXP < targetXP) { const s = await fetch('https://www.duolingo.com/2017-06-30/sessions', {method:'POST',headers,body:JSON.stringify(sessionPayload)}).then(r=>r.json()); const u = await fetch(`https://www.duolingo.com/2017-06-30/sessions/${s.id}`, {method:'PUT',headers,body:JSON.stringify({...s,...updatePayload})}).then(r=>r.json()); currentXP += u.xpGain || 10; document.getElementById('progressText').textContent = `🧠 Đã farm ${currentXP}/${targetXP} XP`; document.getElementById('progressBar').style.width = `${(currentXP/targetXP)*100}%`; await new Promise(r=>setTimeout(r,0)); } document.getElementById('progressText').textContent = t('done'); }; const duoxpanel = async xp => { const jwt = getJwtToken(); if (!jwt) return alert('⚠️ Not logged in!'); const sub = decodeJwt(jwt).sub; const headers = {'Authorization':`Bearer ${jwt}`,'Content-Type':'application/json'}; const info = await fetch(`https://www.duolingo.com/2017-06-30/users/${sub}?fields=username,fromLanguage,learningLanguage`,{headers}).then(r=>r.json()); const sessionPayload = {challengeTypes:['definition','translate','assist'],fromLanguage:info.fromLanguage,learningLanguage:info.learningLanguage,type:'GLOBAL_PRACTICE'}; const updatePayload = {heartsLeft:0,startTime:Math.floor(Date.now()/1000),endTime:Math.floor(Date.now()/1000)+100,failed:false,maxInLessonStreak:10,shouldLearnThings:true}; await farmXp(headers, sessionPayload, updatePayload, xp); }; document.getElementById('startFarm').onclick = async () => { const type = document.getElementById('farmType').value; const amount = parseInt(document.getElementById('targetValue').value||'0'); if (!amount || amount<=0) return alert('❌ Invalid amount!'); document.getElementById('progressText').textContent = '⏳ Đang farm...'; document.getElementById('progressBar').style.width = '0%'; if (type === 'xp') await duoxpanel(amount); else if (type === 'gems') await farmGems(amount); else alert('🚧 This feature is not supported yet.'); }; })();