Greasy Fork

DUOAUTO_KEEPSTREAK

IT AUTO KEEP STREAK AUTOMATICALLY AND VERY TO USE

目前为 2025-02-03 提交的版本。查看 最新版本

// ==UserScript==
// @name        DUOAUTO_KEEPSTREAK
// @match       https://*.duolingo.com/*
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_addStyle
// @version     1.01BETA
// @author      HACKER_DUOLINGO_666
// @icon        https://www.google.com/s2/favicons?sz=64&domain=duolingo.com
// @description IT AUTO KEEP STREAK AUTOMATICALLY AND VERY TO USE 
// @namespace https://greasyfork.org/users/1430241
// ==/UserScript==
const getToken = () => document.cookie.split(';').map(c => c.trim()).find(c => c.startsWith('jwt_token='))?.split('=')[1] || null;

const decodeJWT = token => JSON.parse(decodeURIComponent(atob(token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/')).split('').map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)).join('')));

const getHeaders = token => ({ "Content-Type": "application/json", "Authorization": `Bearer ${token}`, "User-Agent": navigator.userAgent });

const fetchUserData = async userId => (await fetch(`https://www.duolingo.com/2017-06-30/users/${userId}?fields=fromLanguage,learningLanguage,streakData`, { headers })).json();

const hasStreak = data => data?.streakData?.currentStreak?.endDate === new Date().toISOString().split('T')[0];

const startSession = async (fromLang, learningLang) => {
    const payload = { fromLanguage: fromLang, learningLanguage: learningLang, type: "GLOBAL_PRACTICE", isV2: true };
    return (await fetch("https://www.duolingo.com/2017-06-30/sessions", { method: 'POST', headers, body: JSON.stringify(payload) })).json();
};

const completeSession = async session => {
    const payload = { ...session, heartsLeft: 0, startTime: Date.now() / 1000, endTime: Date.now() / 1000 + 112, failed: false };
    return (await fetch(`https://www.duolingo.com/2017-06-30/sessions/${session.id}`, { method: 'PUT', headers, body: JSON.stringify(payload) })).json();
};

const streakKeeper = async () => {
    try {
        const token = getToken();
        if (!token) return alert("Not logged in");
        const userId = decodeJWT(token).sub;
        headers = getHeaders(token);

        const userData = await fetchUserData(userId);
        if (hasStreak(userData)) return alert("You already have a streak today. Enjoy! :)");

        const session = await startSession(userData.fromLanguage, userData.learningLanguage);
        await completeSession(session);
        alert("Streak claimed successfully! Come back tomorrow to claim again.");
    } catch (error) {
        console.error(error);
        alert("An error occurred, please try again later.");
    }
};

// UI Creation
const createUI = () => {
    const container = document.createElement('div');
    container.style = `
        position: fixed;
        top: 10px;
        right: 10px;
        background: rgba(255, 255, 255, 0.1);
        border-radius: 10px;
        padding: 10px;
        backdrop-filter: blur(5px);
        box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
        transition: right 0.3s ease;
    `;

    // Claim Streak Button
    const claimButton = document.createElement('button');
    claimButton.style = `
        background: #28a745;
        color: #fff;
        border: none;
        border-radius: 5px;
        padding: 8px 15px;
        cursor: pointer;
        font-size: 14px;
        display: block;
        width: 100%;
    `;
    claimButton.innerText = 'Claim Streak';
    claimButton.onclick = async () => {
        claimButton.disabled = true;
        claimButton.style.background = 'gray';
        claimButton.innerText = 'Processing...';
        await streakKeeper();
        claimButton.disabled = false;
        claimButton.style.background = '#28a745';
        claimButton.innerText = 'Claim Streak';
    };

    container.appendChild(claimButton);
    document.body.appendChild(container);
};

// Run UI
createUI();