DUO_FARMEDGEMS

THIS TOOL AUTO FARMED GEMS FOR YOU

// ==UserScript==
// @name         DUO_FARMEDGEMS
// @namespace    http://tampermonkey.net/
// @version      v1.0BETA
// @description  THIS TOOL AUTO FARMED GEMS FOR YOU
// @author       ´꒳`ⓎⒶⓂⒾⓈⒸⓇⒾⓅⓉ×͜×
// @match        https://www.duolingo.com/learn
// @icon         https://www.google.com/s2/favicons?sz=64&domain=duolingo.com
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    const colors = {
        primary: '#58CC02',
        secondary: '#1DA462',
        danger: '#FF4757',
        background: '#003049',
        text: '#FFFFFF'
    };

    function createElement(tag, options = {}) {
        const el = document.createElement(tag);
        if (options.styles) Object.assign(el.style, options.styles);
        if (options.content) el.innerHTML = options.content;
        if (options.events) {
            for (const [event, handler] of Object.entries(options.events)) {
                el.addEventListener(event, handler);
            }
        }
        return el;
    }

    function createBtn(label, bg, shadow, onClick) {
        return createElement('button', {
            styles: {
                flex: '1 1 120px',
                padding: '12px 0',
                fontSize: '16px',
                fontWeight: '600',
                background: bg,
                color: colors.text,
                border: 'none',
                borderRadius: '15px',
                cursor: 'pointer',
                transition: 'transform 0.3s ease',
                boxShadow: `0 8px 25px ${shadow}66`,
                minWidth: '120px'
            },
            content: label,
            events: {
                click: onClick,
                mouseenter: e => e.currentTarget.style.transform = 'translateY(-2px)',
                mouseleave: e => e.currentTarget.style.transform = 'translateY(0)'
            }
        });
    }

    function createSmallBtn(label, bg, shadow, onClick) {
        return createElement('button', {
            styles: {
                padding: '8px 16px',
                fontSize: '13px',
                fontWeight: '500',
                background: bg,
                color: colors.text,
                border: 'none',
                borderRadius: '10px',
                cursor: 'pointer',
                transition: 'transform 0.2s ease',
                boxShadow: `0 5px 15px ${shadow}44`,
                margin: '5px'
            },
            content: label,
            events: {
                click: onClick,
                mouseenter: e => e.currentTarget.style.transform = 'translateY(-1px)',
                mouseleave: e => e.currentTarget.style.transform = 'translateY(0)'
            }
        });
    }

    const gemBtn = createElement('button', {
        styles: {
            position: 'fixed',
            bottom: '25px',
            right: '10px',
            width: '60px',
            height: '60px',
            borderRadius: '50%',
            background: `linear-gradient(135deg, ${colors.primary}, ${colors.secondary})`,
            color: colors.text,
            fontSize: '32px',
            cursor: 'pointer',
            zIndex: 10000,
            boxShadow: '0 8px 20px rgba(88, 204, 2, 0.3)',
            transition: 'transform 0.3s ease',
            border: 'none',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center'
        },
        content: '💎',
        events: { click: togglePanel }
    });

    const panel = createElement('div', {
        styles: {
            position: 'fixed',
            top: '50%',
            left: '50%',
            width: '460px',
            background: colors.background,
            borderRadius: '25px',
            zIndex: 10000,
            padding: '30px',
            fontFamily: "'Segoe UI', system-ui",
            color: colors.text,
            opacity: '0',
            transform: 'translate(-50%, -50%) scale(0.9)',
            pointerEvents: 'none',
            transition: 'opacity 0.4s ease, transform 0.4s ease',
            overflow: 'hidden',
            minHeight: '320px',
            display: 'flex',
            flexDirection: 'column'
        }
    });
    panel.id = 'duoPanel';

    const style = document.createElement('style');
    style.textContent = `
        @keyframes borderAnim {
            0% { background-position: 0% 50%; }
            50% { background-position: 100% 50%; }
            100% { background-position: 0% 50%; }
        }
        #duoPanel {
            border: 2px solid transparent;
            background-origin: border-box;
            background-clip: padding-box, border-box;
            background-image:
                linear-gradient(${colors.background}, ${colors.background}),
                linear-gradient(270deg, #58CC02, #1DA462, #FF4757, #58CC02);
            background-size: 400% 400%;
            background-repeat: no-repeat;
            animation: borderAnim 10s linear infinite;
        }
        #duoPanel.active {
            opacity: 1 !important;
            transform: translate(-50%, -50%) scale(1) !important;
            pointer-events: auto !important;
        }
    `;
    document.head.appendChild(style);

    function togglePanel() {
        panel.classList.toggle('active');
        gemBtn.style.transform = panel.classList.contains('active') ? 'rotate(360deg)' : 'rotate(0deg)';
    }

    const header = createElement('div', {
        styles: { textAlign: 'center', marginBottom: '10px' },
        content: `
            <h1 style="margin: 0 0 10px 0;
                       font-size: 32px;
                       background: linear-gradient(45deg, ${colors.primary}, ${colors.secondary}, ${colors.text}cc);
                       -webkit-background-clip: text;
                       background-clip: text;
                       color: transparent;
                       font-weight: 700;
                       letter-spacing: 1px;">
            DUO_FARMEDGEMS</h1>
            <p style="margin: 0; color: ${colors.text}; font-size: 14px;">MADE BY YAMISCRIPT_DEV</p>
            <p id="_loginStatus">LOADING...</p>
        `
    });

    const usernameGreeting = createElement('div', {
        styles: {
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            gap: '12px',
            marginBottom: '20px',
            fontSize: '18px',
            color: colors.text
        },
    });

    async function fetchUsername() {
        const userId = getCookie('logged_out_uuid');
        const jwt = getCookie('jwt_token');
        if (!userId || !jwt) return;
        try {
            const res = await fetch(`https://www.duolingo.com/2017-06-30/users/${userId}?fields=username`, {
                headers: { authorization: `Bearer ${jwt}` }
            });
            const data = await res.json();
            if (data.username) {
                document.getElementById('_loginStatus').innerHTML = ``;
                usernameGreeting.innerHTML = `<span>👋Welcome, <b style="color: ${colors.primary}">${data.username}</b>!</span>`;
            }
        } catch (e) {
            document.getElementById('_loginStatus').innerText = 'BRUH!';
        }
    }

    const gemCountDiv = createElement('div', {
        styles: { textAlign: 'center', margin: '20px 0' },
        content: `<div id="gemCount" style="font-size: 42px;
                                            font-weight: 700;
                                            color: ${colors.primary};
                                            text-shadow: 0 4px 12px rgba(88,204,2,0.2);
                                            margin-top: 10px;">0</div>`
    });

    const btnGroup = createElement('div', {
        styles: {
            display: 'flex',
            gap: '15px',
            justifyContent: 'center',
            margin: '25px 0',
            flexWrap: 'wrap'
        }
    });

    const statusText = createElement('div', {
        styles: {
            textAlign: 'center',
            color: colors.text + '99',
            fontSize: '14px',
            marginTop: '5px'
        },
        content: '<span id="statusText">NON-ACTIVE:❌</span>'
    });

    const startBtn = createBtn('START', `linear-gradient(135deg, ${colors.primary}, ${colors.secondary})`, colors.primary, startFarm);
    const stopBtn = createBtn('STOP', `linear-gradient(135deg, ${colors.danger}, #CC2E3D)`, colors.danger, stopFarm);
    stopBtn.style.display = 'none';

    const discordBtn = createBtn('DISCORD', `linear-gradient(135deg, ${colors.secondary}, ${colors.primary})`, colors.secondary, () => window.open('https://discord.gg/aDD9DMz6', '_blank'));
    const profileBtn = createBtn('PROFILE', `linear-gradient(135deg, ${colors.primary}, ${colors.secondary})`, colors.primary, () => window.open('https://guns.lol/yamiscript_dev'));
    const settingBtn = createBtn('SETTING', `linear-gradient(135deg, ${colors.primary}, ${colors.secondary})`, colors.primary, showTokenPanel);

    const tokenPanel = createElement('div', {
        styles: {
            display: 'none',
            flexDirection: 'column',
            justifyContent: 'center',
            alignItems: 'center',
            padding: '20px',
            borderRadius: '15px',
            backgroundColor: '#144d1d',
            marginTop: '15px',
            textAlign: 'center',
            fontSize: '14px',
            userSelect: 'text'
        }
    });

    const tokenTextarea = createElement('textarea', {
        styles: {
            width: '100%',
            height: '80px',
            borderRadius: '10px',
            border: 'none',
            resize: 'none',
            padding: '10px',
            fontSize: '14px',
            fontFamily: 'monospace',
            marginBottom: '12px',
            backgroundColor: '#0b2e0f',
            color: '#a6e22e'
        },
        events: {
            focus: (e) => e.target.select()
        }
    });

    const copyTokenBtn = createSmallBtn('Copy token', `linear-gradient(135deg, ${colors.primary}, ${colors.secondary})`, colors.primary, () => {
        tokenTextarea.select();
        document.execCommand('copy');
        copyTokenBtn.textContent = 'Copied!';
        setTimeout(() => copyTokenBtn.textContent = 'Copy token', 2000);
    });

    const closeTokenBtn = createSmallBtn('Close', colors.danger, colors.danger, showMainPanel);

    tokenPanel.append(
        createElement('div', { content: '<b>Token JWT of you:</b>', styles: { marginBottom: '10px', fontSize: '10px' } }),
        tokenTextarea,
        copyTokenBtn,
        closeTokenBtn
    );

    btnGroup.append(startBtn, stopBtn, discordBtn, profileBtn, settingBtn);
    panel.append(header, usernameGreeting, gemCountDiv, btnGroup, statusText, tokenPanel);
    document.body.append(gemBtn, panel);

    let farming = false;
    let gemCount = parseInt(localStorage.getItem('gemsCount')) || 0;
    let farmInterval;

    function updateGemCount() {
        localStorage.setItem('gemsCount', gemCount);
        document.getElementById('gemCount').textContent = gemCount;
    }

    function showTokenPanel() {
        const token = getCookie('jwt_token') || 'Không tìm thấy token';
        tokenTextarea.value = token;
        btnGroup.style.display = 'none';
        statusText.style.display = 'none';
        gemCountDiv.style.display = 'none';
        tokenPanel.style.display = 'flex';
    }

    function showMainPanel() {
        tokenPanel.style.display = 'none';
        btnGroup.style.display = 'flex';
        statusText.style.display = 'block';
        gemCountDiv.style.display = 'block';
    }

    function getCookie(name) {
        const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
        return match ? match[2] : null;
    }

    async function farmGems() {
        try {
            const userId = getCookie('logged_out_uuid');
            const jwt = getCookie('jwt_token');
            const res = 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' })
            });
            if (res.status === 200) {
                for (let i = 1; i <= 15; i++) {
                    setTimeout(() => {
                        gemCount++;
                        updateGemCount();
                    }, i * 250);
                }
            }
        } catch (err) {
            console.error('Lỗi farm:', err);
        }
    }

    function startFarm() {
        if (!farming) {
            farming = true;
            startBtn.style.display = 'none';
            stopBtn.style.display = 'block';
            document.getElementById('statusText').textContent = 'ACTIVE:✅';
            farmInterval = setInterval(farmGems, 100);
        }
    }

    function stopFarm() {
        farming = false;
        clearInterval(farmInterval);
        stopBtn.style.display = 'none';
        startBtn.style.display = 'block';
        document.getElementById('statusText').textContent = 'NON-ACTIVE:❌';
        updateGemCount();
    }

    window.addEventListener('load', () => {
        fetchUsername();
        updateGemCount();
    });

    window.onbeforeunload = () => farming ? 'Đang farm gems - Bạn có chắc chắn muốn thoát?' : null;
})();