// ==UserScript== // @name Duo_KeepStreak // @namespace HACKER_DUOLINGO_666 // @version 1.0.1 // @description Automatically maintains the daily streak on Duolingo. // @author HACKER_DUOLINGO_666 // @match https://*.duolingo.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=duolingo.com // @downloadURL none // ==/UserScript== const getToken = () => document.cookie.split('; ').find(row => row.startsWith('jwt_token='))?.split('=')[1] || null; const parseJwt = token => { try { return JSON.parse(atob(token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/'))); } catch (e) { console.error("JWT parsing error", e); return null; } }; const getHeaders = token => ({ "Content-Type": "application/json", "Authorization": `Bearer ${token}`, "User-Agent": navigator.userAgent }); const fetchUserData = async (userId, headers) => { const response = await fetch(`https://www.duolingo.com/2017-06-30/users/${userId}?fields=fromLanguage,learningLanguage,streakData`, { headers }); return response.json(); }; const hasStreakToday = data => data.streakData?.currentStreak?.endDate === new Date().toISOString().split('T')[0]; const startSession = async (fromLang, learningLang, headers) => { const payload = { challengeTypes: ["translate", "match", "tapComplete", "reverseAssist", "judge"], fromLanguage: fromLang, learningLanguage: learningLang, type: "GLOBAL_PRACTICE" }; const response = await fetch("https://www.duolingo.com/2017-06-30/sessions", { method: 'POST', headers, body: JSON.stringify(payload) }); return response.json(); }; const completeSession = async (session, headers) => { const payload = { ...session, heartsLeft: 0, failed: false, shouldLearnThings: true }; const response = await fetch(`https://www.duolingo.com/2017-06-30/sessions/${session.id}`, { method: 'PUT', headers, body: JSON.stringify(payload) }); return response.json(); }; const attemptStreak = async () => { const token = getToken(); if (!token) return alert("❌ You are not logged into Duolingo!"); const userId = parseJwt(token)?.sub; if (!userId) return alert("❌ Error retrieving user ID."); const headers = getHeaders(token); const userData = await fetchUserData(userId, headers); if (hasStreakToday(userData)) return alert("✅ You have already maintained your streak today!"); try { const session = await startSession(userData.fromLanguage, userData.learningLanguage, headers); await completeSession(session, headers); alert("🎉 Streak has been maintained! Reload the page to check."); } catch (error) { console.error("Streak maintenance error", error); alert("⚠️ Error maintaining streak, try again!"); } }; const addButton = () => { const existingButton = document.getElementById("get-streak-btn"); if (existingButton) return; // Tránh tạo nút trùng lặp const button = document.createElement("button"); button.id = "get-streak-btn"; button.innerText = "🔥 Get Streak 🔥"; button.style.position = "fixed"; button.style.bottom = "20px"; button.style.right = "20px"; button.style.padding = "10px 15px"; button.style.backgroundColor = "#58cc02"; button.style.color = "white"; button.style.fontSize = "16px"; button.style.border = "none"; button.style.borderRadius = "5px"; button.style.cursor = "pointer"; button.style.zIndex = "1000"; button.onclick = attemptStreak; document.body.appendChild(button); }; // Đợi trang tải xong rồi thêm nút window.onload = () => { setTimeout(addButton, 2000); };