Automatically maintains the daily streak on Duolingo.
当前为
// ==UserScript==
// @name Duo_KeepStreak
// @namespace HACKER_DUOLINGO_666
// @version 1.01BETA
// @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
// ==/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 streak today!Reload the page to enjoy :)))");
try {
const session = await startSession(userData.fromLanguage, userData.learningLanguage, headers);
await completeSession(session, headers);
alert("Streak successfully maintained!");
} catch (error) {
console.error("Error maintaining streak", error);
alert("Error while maintaining streak, please try again!");
}
};
const createUI = () => {
const button = document.createElement('button');
button.textContent = 'Get Streak';
button.style = "position:fixed; bottom:20px; right:20px; padding:10px; background:#28a745; color:white; border:none; border-radius:5px; cursor:pointer;";
document.body.appendChild(button);
button.onclick = attemptStreak;
};
createUI();