// ==UserScript==
// @name Duolingo SM
// @version 2.3.0
// @author MeowWoof
// @namespace http://tampermonkey.net/
// @description [Demo] Auto Farm XP, streak, gem hack – so easy!
// @match https://*.duolingo.com/*
// @grant none
// @license MIT
// @icon https://d35aaqx5ub95lt.cloudfront.net/vendor/a0ee30fa22ca3d00e9e5db913b1965b5.svg
// @downloadURL none
// ==/UserScript==
(function() {
'use strict';
// đừng đọc mà con cạc
const sessionUrl = "https://www.duolingo.com/2017-06-30/sessions";
let isFarming = false, isVisible = true;
let currentLanguage = "en";
const getJwtToken = () => document.cookie.split(';').find(c => c.trim().startsWith('jwt_token='))?.split('=')[1] || null;
const decodeJwtToken = token => JSON.parse(atob(token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/')));
const formatHeaders = jwtToken => ({ "Content-Type": "application/json", "Authorization": `Bearer ${jwtToken}`, "User-Agent": navigator.userAgent });
const getUserInfo = async (sub, headers) => (await fetch(`https://www.duolingo.com/2017-06-30/users/${sub}?fields=username,fromLanguage,learningLanguage`, { headers })).json();
const farmXp = async (headers, sessionPayload, updateSessionPayload) => {
while (isFarming) {
try {
const session = await (await fetch(sessionUrl, { method: 'POST', headers, body: JSON.stringify(sessionPayload) })).json();
const updatedSession = await (await fetch(`${sessionUrl}/${session.id}`, { method: 'PUT', headers, body: JSON.stringify({ ...session, ...updateSessionPayload }) })).json();
document.getElementById("_xpAmount").innerText = parseInt(document.getElementById("_xpAmount").innerText) + updatedSession.xpGain;
} catch (error) {
alert(currentLanguage === "en" ? "An error occurred while farming XP. Please try again!" : "Đã xảy ra lỗi khi farm XP. Vui lòng thử lại!");
await new Promise(resolve => setTimeout(resolve, 5000));
}
}
};
const translations = {
en: {
title: "Duolingo SM",
welcome: "Welcome to Duolingo SM!",
notLoggedIn: "You are not logged in! (if you are logged in, please refresh the page)",
hello: "Hello",
farmXp: "FARM XP",
farmGems: "FARM GEMS",
upgrade: "UPGRADE",
start: "Start Farming",
stop: "Stop Farming",
xpGained: "XP Gained",
warning: "Please use this tool responsibly.",
loading: "Processing...",
switchLang: "Tiếng Việt",
status: "Status",
active: "Active",
inactive: "Inactive"
},
vi: {
title: "Super-Duo",
welcome: "Chào mừng đến với Duolingo SM!",
notLoggedIn: "Bạn chưa đăng nhập! (nếu bạn đã đăng nhập, vui lòng tải lại trang)",
hello: "Xin chào",
farmXp: "FARM XP",
farmGems: "FARM GEMS",
upgrade: "NÂNG CẤP",
start: "Bắt đầu Farm",
stop: "Dừng Farm",
xpGained: "XP Đã Nhận",
warning: "Vui lòng sử dụng công cụ này 1 cách tự nhiên",
loading: "Đang xử lý...",
switchLang: "English",
status: "Trạng thái",
active: "Hoạt động",
inactive: "Không hoạt động"
}
};
const initSuperDuolingo = async () => {
const style = document.createElement('style');
style.innerHTML = `
:root {
/* Light theme colors */
--primary-bg: #ffffff;
--secondary-bg: #f8fafc;
--accent-bg: #f1f5f9;
--surface: #e2e8f0;
--surface-light: #f8fafc;
--primary-color: #0ea5e9;
--secondary-color: #6366f1;
--accent-color: #8b5cf6;
--success-color: #10b981;
--warning-color: #f59e0b;
--error-color: #ef4444;
--text-primary: #1e293b;
--text-secondary: #475569;
--text-muted: #64748b;
--gradient-primary: linear-gradient(135deg, #0ea5e9 0%, #3b82f6 100%);
--gradient-success: linear-gradient(135deg, #10b981 0%, #059669 100%);
--gradient-secondary: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.1);
--border-radius: 8px;
--border-radius-lg: 12px;
--transition: all 0.2s ease;
}
._sd_container {
position: fixed;
right: 16px;
top: 50%;
transform: translateY(-50%);
width: 280px;
background: var(--primary-bg);
border-radius: var(--border-radius-lg);
box-shadow: var(--shadow-xl);
z-index: 9999;
overflow: hidden;
transition: var(--transition);
border: 1px solid var(--surface);
}
._sd_container.hidden {
transform: translateY(-50%) translateX(100%);
opacity: 0;
}
._sd_header {
background: var(--gradient-primary);
padding: 16px;
text-align: center;
}
._sd_title {
font-size: 1.1rem;
font-weight: 700;
color: white;
margin: 0 0 8px 0;
}
._sd_language_switcher {
background: rgba(255, 255, 255, 0.2);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: var(--border-radius);
padding: 4px 12px;
font-size: 0.75rem;
color: white;
cursor: pointer;
transition: var(--transition);
font-weight: 500;
}
._sd_language_switcher:hover {
background: rgba(255, 255, 255, 0.3);
}
._sd_body {
padding: 16px;
background: var(--primary-bg);
}
._sd_welcome {
font-size: 0.85rem;
color: var(--text-secondary);
margin-bottom: 16px;
padding: 12px;
background: var(--surface-light);
border-radius: var(--border-radius);
border-left: 3px solid var(--primary-color);
}
._sd_status_indicator {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 16px;
padding: 8px 12px;
background: var(--surface-light);
border-radius: var(--border-radius);
font-size: 0.8rem;
}
._sd_status_dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--error-color);
transition: var(--transition);
}
._sd_status_dot.active {
background: var(--success-color);
}
._sd_status_text {
color: var(--text-muted);
font-weight: 500;
}
._sd_xp_counter {
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
padding: 10px;
background: var(--surface-light);
border-radius: var(--border-radius);
margin-bottom: 16px;
border: 1px solid var(--surface);
}
._sd_xp_counter_label {
font-size: 0.8rem;
color: var(--text-muted);
font-weight: 500;
}
._sd_xp_counter_value {
font-size: 1rem;
color: var(--primary-color);
font-weight: 700;
}
._sd_tabs {
display: flex;
flex-direction: column;
gap: 6px;
margin-bottom: 16px;
}
._sd_tab {
padding: 10px 12px;
border-radius: var(--border-radius);
background: var(--surface-light);
color: var(--text-secondary);
cursor: pointer;
transition: var(--transition);
font-weight: 500;
font-size: 0.85rem;
display: flex;
align-items: center;
justify-content: space-between;
border: 1px solid var(--surface);
}
._sd_tab:hover {
background: var(--accent-bg);
color: var(--text-primary);
border-color: var(--primary-color);
}
._sd_tab.active {
background: var(--primary-color);
color: white;
border-color: var(--primary-color);
}
._sd_tab_icon {
font-size: 1rem;
}
._sd_start_btn {
padding: 12px 20px;
background: var(--gradient-success);
border: none;
border-radius: var(--border-radius);
color: white;
font-weight: 600;
cursor: pointer;
transition: var(--transition);
width: 100%;
font-size: 0.9rem;
box-shadow: var(--shadow-sm);
}
._sd_start_btn:hover {
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
._sd_start_btn:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
._sd_toggle_btn {
position: fixed;
left: 16px;
bottom: 20px;
width: 44px;
height: 44px;
border-radius: 50%;
background: var(--gradient-primary);
border: none;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
z-index: 10000;
transition: var(--transition);
color: white;
font-weight: bold;
font-size: 1.2rem;
box-shadow: var(--shadow-lg);
}
._sd_toggle_btn:hover {
transform: scale(1.05);
box-shadow: var(--shadow-xl);
}
._sd_overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: none;
align-items: center;
justify-content: center;
z-index: 9999;
}
._sd_modal {
background: var(--primary-bg);
border-radius: var(--border-radius-lg);
padding: 24px;
width: 90%;
max-width: 320px;
box-shadow: var(--shadow-xl);
border: 1px solid var(--surface);
text-align: center;
}
._sd_modal_title {
font-size: 1.2rem;
font-weight: 700;
margin-bottom: 16px;
color: var(--text-primary);
}
._sd_spinner {
display: inline-block;
width: 40px;
height: 40px;
margin: 16px auto;
}
._sd_spinner:after {
content: " ";
display: block;
width: 32px;
height: 32px;
margin: 4px;
border-radius: 50%;
border: 3px solid var(--surface);
border-top: 3px solid var(--primary-color);
animation: _sd_spin 1s linear infinite;
}
._sd_xp_info {
font-size: 1rem;
color: var(--primary-color);
margin: 16px 0;
font-weight: 600;
padding: 12px;
background: var(--surface-light);
border-radius: var(--border-radius);
border: 1px solid var(--surface);
}
._sd_warning {
font-size: 0.8rem;
color: var(--text-secondary);
margin-bottom: 16px;
line-height: 1.4;
padding: 12px;
background: #fef3c7;
border-radius: var(--border-radius);
border: 1px solid #fbbf24;
}
._sd_stop_btn {
padding: 10px 20px;
background: var(--gradient-secondary);
border: none;
border-radius: var(--border-radius);
color: white;
font-weight: 600;
cursor: pointer;
transition: var(--transition);
font-size: 0.85rem;
box-shadow: var(--shadow-sm);
}
._sd_stop_btn:hover {
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
@keyframes _sd_spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Responsive */
@media (max-width: 768px) {
._sd_container {
width: 260px;
right: 12px;
}
._sd_toggle_btn {
left: 12px;
bottom: 16px;
width: 40px;
height: 40px;
font-size: 1.1rem;
}
._sd_modal {
margin: 16px;
padding: 20px;
}
}
/* Focus states */
._sd_tab:focus,
._sd_start_btn:focus,
._sd_stop_btn:focus,
._sd_language_switcher:focus,
._sd_toggle_btn:focus {
outline: 2px solid var(--primary-color);
outline-offset: 2px;
}
`;
document.head.appendChild(style);
const containerHTML = `
${translations[currentLanguage].welcome}
${translations[currentLanguage].status}: ${translations[currentLanguage].inactive}
${translations[currentLanguage].xpGained}:
0
XP
${translations[currentLanguage].farmXp}
⚡
${translations[currentLanguage].farmGems}
💎
${translations[currentLanguage].upgrade}
👑
${translations[currentLanguage].title}
${translations[currentLanguage].loading}
${translations[currentLanguage].warning}
${translations[currentLanguage].xpGained}:
0 XP
`;
document.body.insertAdjacentHTML('beforeend', containerHTML);
const JWT = getJwtToken();
if (!JWT) {
document.getElementById("_sd_start_btn").disabled = true;
document.getElementById("_sd_welcome").innerText = translations[currentLanguage].notLoggedIn;
return;
}
const HEADERS = formatHeaders(JWT);
try {
const { username, fromLanguage, learningLanguage } = await getUserInfo(
decodeJwtToken(JWT).sub,
HEADERS
);
document.getElementById("_sd_welcome").innerHTML =
`${translations[currentLanguage].hello} ${username}! 🎉`;
const sessionPayload = {
challengeTypes: [
"assist", "characterIntro", "characterMatch", "characterPuzzle",
"characterSelect", "characterTrace", "characterWrite",
"completeReverseTranslation", "definition", "dialogue",
"extendedMatch", "extendedListenMatch", "form", "freeResponse",
"gapFill", "judge", "listen", "listenComplete", "listenMatch",
"match", "name", "listenComprehension", "listenIsolation",
"listenSpeak", "listenTap", "orderTapComplete", "partialListen",
"partialReverseTranslate", "patternTapComplete", "radioBinary",
"radioImageSelect", "radioListenMatch", "radioListenRecognize",
"radioSelect", "readComprehension", "reverseAssist",
"sameDifferent", "select", "selectPronunciation",
"selectTranscription", "svgPuzzle", "syllableTap",
"syllableListenTap", "speak", "tapCloze", "tapClozeTable",
"tapComplete", "tapCompleteTable", "tapDescribe", "translate",
"transliterate", "transliterationAssist", "typeCloze",
"typeClozeTable", "typeComplete", "typeCompleteTable",
"writeComprehension"
],
fromLanguage,
learningLanguage,
type: "GLOBAL_PRACTICE"
};
const updateSessionPayload = {
heartsLeft: 0,
startTime: Math.floor(Date.now() / 1000),
enableBonusPoints: false,
endTime: Math.floor(Date.now() / 1000) + 112,
failed: false,
maxInLessonStreak: 9,
shouldLearnThings: true
};
document.getElementById("_sd_toggle_btn").addEventListener("click", () => {
isVisible = !isVisible;
document.getElementById("_sd_container").classList.toggle("hidden", !isVisible);
document.getElementById("_sd_toggle_btn").innerHTML = isVisible ? "→" : "←";
});
document.getElementById("_sd_start_btn").addEventListener("click", () => {
document.getElementById("_sd_overlay").style.display = "flex";
document.getElementById("_sd_status_dot").classList.add("active");
document.getElementById("_sd_status_text").innerText =
`${translations[currentLanguage].status}: ${translations[currentLanguage].active}`;
isFarming = true;
const syncXpCounters = () => {
const mainXp = document.getElementById("_xpAmount").innerText;
document.getElementById("_xpAmount_modal").innerText = mainXp;
};
const observer = new MutationObserver(syncXpCounters);
observer.observe(document.getElementById("_xpAmount"), { childList: true, subtree: true });
farmXp(HEADERS, sessionPayload, updateSessionPayload);
});
document.getElementById("_sd_stop_btn").addEventListener("click", () => {
document.getElementById("_sd_overlay").style.display = "none";
document.getElementById("_sd_status_dot").classList.remove("active");
document.getElementById("_sd_status_text").innerText =
`${translations[currentLanguage].status}: ${translations[currentLanguage].inactive}`;
isFarming = false;
const startBtn = document.getElementById("_sd_start_btn");
startBtn.disabled = true;
startBtn.innerText = "...";
setTimeout(() => {
startBtn.disabled = false;
startBtn.innerText = translations[currentLanguage].start;
}, 2000);
});
document.getElementById("_sd_language_switcher").addEventListener("click", () => {
currentLanguage = currentLanguage === "en" ? "vi" : "en";
updateLanguage();
});
document.getElementById("_sd_tab_farm_gems").addEventListener("click", () => {
window.open("https://discord.gg/ufBrcGemBH", "_blank");
});
document.getElementById("_sd_tab_upgrade").addEventListener("click", () => {
window.open("https://duolingo.click", "_blank");
});
} catch (error) {
console.error("Error initializing SuperDuolingo:", error);
document.getElementById("_sd_start_btn").disabled = true;
document.getElementById("_sd_welcome").innerText = translations[currentLanguage].notLoggedIn;
}
};
const updateLanguage = () => {
const elements = {
"_sd_title": translations[currentLanguage].title,
"_sd_welcome": translations[currentLanguage].welcome,
"_sd_language_switcher": translations[currentLanguage].switchLang,
"_sd_start_btn": translations[currentLanguage].start,
"_sd_xp_label": translations[currentLanguage].xpGained,
"_sd_xp_label_modal": translations[currentLanguage].xpGained,
"_sd_modal_title": translations[currentLanguage].title,
"_sd_loading_message": translations[currentLanguage].loading,
"_sd_warning": translations[currentLanguage].warning,
"_sd_stop_btn": translations[currentLanguage].stop
};
Object.entries(elements).forEach(([id, text]) => {
const element = document.getElementById(id);
if (element) element.innerText = text;
});
document.querySelector("#_sd_tab_farm_xp span").innerText = translations[currentLanguage].farmXp;
document.querySelector("#_sd_tab_farm_gems span").innerText = translations[currentLanguage].farmGems;
document.querySelector("#_sd_tab_upgrade span").innerText = translations[currentLanguage].upgrade;
const statusDot = document.getElementById("_sd_status_dot");
const statusText = document.getElementById("_sd_status_text");
const isActive = statusDot.classList.contains("active");
statusText.innerText = `${translations[currentLanguage].status}: ${isActive ? translations[currentLanguage].active : translations[currentLanguage].inactive}`;
};
const addTabClickEvents = () => {
document.querySelectorAll("._sd_tab").forEach(tab => {
tab.addEventListener("click", () => {
document.querySelectorAll("._sd_tab").forEach(t => t.classList.remove("active"));
tab.classList.add("active");
});
});
};
const addKeyboardShortcuts = () => {
document.addEventListener("keydown", (e) => {
if (e.ctrlKey && e.shiftKey && e.key === 'D') {
e.preventDefault();
document.getElementById("_sd_toggle_btn").click();
}
if (e.key === 'Escape' && document.getElementById("_sd_overlay").style.display === "flex") {
document.getElementById("_sd_stop_btn").click();
}
});
};
const addSoundEffects = () => {
const playSound = (frequency, duration) => {
if (typeof AudioContext !== 'undefined' || typeof webkitAudioContext !== 'undefined') {
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.value = frequency;
oscillator.type = 'sine';
gainNode.gain.setValueAtTime(0.1, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + duration);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + duration);
}
};
const originalXpElement = document.getElementById("_xpAmount");
if (originalXpElement) {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'childList' || mutation.type === 'characterData') {
const newValue = parseInt(mutation.target.textContent || mutation.target.innerText);
if (newValue > 0 && isFarming) {
playSound(800, 0.2);
}
}
});
});
observer.observe(originalXpElement, { childList: true, subtree: true, characterData: true });
}
};
const addAnimations = () => {
const xpCounter = document.getElementById("_xpAmount");
if (xpCounter) {
const observer = new MutationObserver(() => {
xpCounter.style.transform = "scale(1.2)";
xpCounter.style.color = "var(--success-color)";
setTimeout(() => {
xpCounter.style.transform = "scale(1)";
xpCounter.style.color = "var(--primary-color)";
}, 300);
});
observer.observe(xpCounter, { childList: true, subtree: true });
}
};
const addTooltips = () => {
const tooltips = {
"_sd_tab_farm_xp": "Farm XP automatically to level up faster",
"_sd_tab_farm_gems": "Join our Discord community for gem farming",
"_sd_tab_upgrade": "Upgrade to Pro version for more features",
"_sd_toggle_btn": "Toggle SuperDuolingo panel (Ctrl+Shift+D)"
};
Object.entries(tooltips).forEach(([id, text]) => {
const element = document.getElementById(id);
if (element) {
element.title = text;
}
});
};
const init = () => {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initSuperDuolingo);
} else {
initSuperDuolingo();
}
setTimeout(() => {
addTabClickEvents();
addKeyboardShortcuts();
addSoundEffects();
addAnimations();
addTooltips();
const xpElement = document.getElementById("_xpAmount");
if (xpElement) {
const observer = new MutationObserver(() => {
const currentXp = parseInt(xpElement.innerText) || 0;
});
observer.observe(xpElement, { childList: true, subtree: true });
}
}, 1000);
};
let inactivityTimer;
const resetInactivityTimer = () => {
clearTimeout(inactivityTimer);
inactivityTimer = setTimeout(() => {
if (!isFarming && isVisible) {
document.getElementById("_sd_toggle_btn").click();
}
}, 300000);
};
['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart'].forEach(event => {
document.addEventListener(event, resetInactivityTimer, true);
});
const logError = (error, context) => {
console.error(`[SuperDuolingo] Error in ${context}:`, error);
};
if (typeof module !== 'undefined' && module.exports) {
module.exports = {
initSuperDuolingo,
updateLanguage,
farmXp,
getJwtToken,
formatHeaders
};
}
init();
})();