Duolingo Super Manager - Create referral links, activate Super, manage accounts
// ==UserScript==
// @name Duolingo Auto Create Link Super
// @namespace http://tampermonkey.net/
// @version 2.0
// @description Duolingo Super Manager - Create referral links, activate Super, manage accounts
// @author MeowWoof
// @match https://www.duolingo.com/*
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// @connect duolingo-super022.vercel.app
// @run-at document-idle
// @license MIT
// @icon https://d35aaqx5ub95lt.cloudfront.net/vendor/38dc6a042b0de3f6aeb44ff2aa70de73.svg
// ==/UserScript==
(function () {
'use strict';
const API_BASE = 'https://duolingo-super022.vercel.app';
const FREE_KEY = 'DS-FREE-PUBLIC-KEY-2025';
const DISCORD_URL = 'https://discord.gg/ufBrcGemBH';
const CD_STORE = 'duods_cd_expire';
let _sess = null;
let _keyData = null;
let _cdTimer = null;
let _hist = [];
let _hidden = false;
let _animLock = false;
let _pgLock = false;
let _curPg = 'auth';
let _ntTimer;
let _isFree = false;
function _detectDark() {
const html = document.documentElement;
if (html.dataset.theme === 'dark') return true;
if (html.dataset.theme === 'light') return false;
const htmlCl = html.className || '';
const bodyCl = document.body.className || '';
if (htmlCl.includes('dark') || bodyCl.includes('dark')) return true;
if (htmlCl.includes('light') || bodyCl.includes('light')) return false;
const bodyStyle = window.getComputedStyle(document.body);
const htmlStyle = window.getComputedStyle(html);
for (const s of [bodyStyle, htmlStyle]) {
const bg = s.backgroundColor;
if (bg) {
const m = bg.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
if (m) {
const lum = 0.299 * +m[1] + 0.587 * +m[2] + 0.114 * +m[3];
if (lum < 60) return true;
if (lum > 180) return false;
}
}
}
try {
const testEl = document.querySelector('[class*="duo"][class*="dark"], [data-color-mode="dark"], [color-scheme="dark"]');
if (testEl) return true;
} catch {}
return window.matchMedia('(prefers-color-scheme: dark)').matches;
}
function _applyTheme() {
const root = document.getElementById('DDS_Root');
if (!root) return;
if (_detectDark()) {
root.setAttribute('data-dds-dark', '1');
} else {
root.removeAttribute('data-dds-dark');
}
}
function _watchTheme() {
_applyTheme();
const obs = new MutationObserver(() => _applyTheme());
obs.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme', 'class', 'data-color-mode'], subtree: false });
obs.observe(document.body, { attributes: true, attributeFilter: ['data-theme', 'class', 'data-color-mode'], subtree: false });
const duoAppRoot = document.getElementById('root') || document.getElementById('app') || document.querySelector('[id^="__next"]');
if (duoAppRoot) {
obs.observe(duoAppRoot, { attributes: true, attributeFilter: ['class', 'data-theme'] });
}
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', _applyTheme);
setInterval(_applyTheme, 500);
}
function _req(path, method, body) {
return new Promise(resolve => {
const hdrs = _hdrs();
if (!hdrs && path !== '/api/key/info') return resolve({ error: 'Unauthorized' });
const cfg = {
method: method || 'POST',
url: API_BASE + path,
headers: hdrs || { 'Content-Type': 'application/json' },
timeout: 90000,
onload: r => {
try { resolve(JSON.parse(r.responseText)); }
catch { resolve({ error: 'Parse error: ' + r.responseText.slice(0, 120) }); }
},
onerror: () => resolve({ error: 'Network error' }),
ontimeout: () => resolve({ error: 'Timeout (90s)' })
};
if (body) cfg.data = JSON.stringify(body);
GM_xmlhttpRequest(cfg);
});
}
function _hdrs() {
if (!_sess) return null;
if (Date.now() > _sess.exp) { _sess = null; return null; }
return {
'Content-Type': 'application/json',
'x-ds-key': _sess.key,
'x-ds-token': _sess.tok
};
}
function _fetchKey(key) {
return new Promise(resolve => {
GM_xmlhttpRequest({
method: 'GET',
url: `${API_BASE}/api/key/info?key=${encodeURIComponent(key)}`,
headers: { 'Content-Type': 'application/json' },
timeout: 30000,
onload: r => {
try {
const d = JSON.parse(r.responseText);
if (d.ok) {
_sess = {
key,
tok: d.session_token || _fnv(key),
exp: Date.now() + (d.session_ttl_ms || 3600000)
};
}
resolve(d);
} catch { resolve({ error: 'Parse error' }); }
},
onerror: () => resolve({ error: 'Network error' }),
ontimeout: () => resolve({ error: 'Timeout' })
});
});
}
function _fnv(s) {
let h = 0x811c9dc5;
for (let i = 0; i < s.length; i++) { h ^= s.charCodeAt(i); h = (h * 0x01000193) >>> 0; }
return h.toString(16).padStart(8, '0') + Date.now().toString(36);
}
const _set = (k, v) => GM_setValue(k, v);
const _get = (k, d) => GM_getValue(k, d);
GM_addStyle(`
#DDS_Root {
--dds-blue: 0, 122, 255;
--dds-green: 52, 199, 89;
--dds-red: 255, 59, 48;
--dds-orange: 255, 149, 0;
--dds-purple: 88, 101, 242;
--dds-card: 255, 255, 255;
--dds-border: 200, 200, 210;
--dds-text1: 30, 30, 40;
--dds-text2: 100, 100, 115;
--dds-inbg: 0, 122, 255;
--dds-cd-bg: 255, 149, 0;
--dds-tab-bg: 180, 180, 195;
--dds-surface: 245, 245, 250;
--dds-shadow: 0, 0, 0;
--dds-spin-fg: 0, 122, 255;
--dds-spin-bg: 0, 122, 255;
}
#DDS_Root[data-dds-dark] {
--dds-blue: 10, 132, 255;
--dds-green: 48, 209, 88;
--dds-red: 255, 69, 58;
--dds-orange: 255, 159, 10;
--dds-purple: 100, 115, 255;
--dds-card: 28, 28, 32;
--dds-border: 60, 60, 70;
--dds-text1: 240, 240, 248;
--dds-text2: 160, 160, 175;
--dds-inbg: 10, 132, 255;
--dds-cd-bg: 255, 159, 10;
--dds-tab-bg: 70, 70, 80;
--dds-surface: 38, 38, 44;
--dds-shadow: 0, 0, 0;
--dds-spin-fg: 255, 255, 255;
--dds-spin-bg: 255, 255, 255;
}
#DDS_Root * { box-sizing: border-box; }
#DDS_Root p, #DDS_Root span, #DDS_Root button,
#DDS_Root input, #DDS_Root label, #DDS_Root div, #DDS_Root textarea {
font-family: 'din-round', -apple-system, sans-serif !important;
}
#DDS_Root p, #DDS_Root span { margin: 0; padding: 0; }
#DDS_Root svg { flex-shrink: 0; }
#DDS_Root a { text-decoration: none; }
.dds-shell {
display: inline-flex; flex-direction: column;
justify-content: flex-end; align-items: flex-end;
gap: 8px; position: fixed; right: 16px; bottom: 16px;
z-index: 2147483647;
}
@media (max-width: 699px) { .dds-shell { margin-bottom: 80px; } }
.dds-box {
display: flex; width: 320px; padding: 16px;
flex-direction: column; align-items: center; gap: 8px;
overflow: hidden; border-radius: 24px;
outline: 1.5px solid rgba(var(--dds-border), 0.55); outline-offset: -1px;
background: rgba(var(--dds-card), 0.97);
backdrop-filter: blur(28px); -webkit-backdrop-filter: blur(28px);
box-shadow: 0 12px 48px rgba(var(--dds-shadow),.22), 0 2px 8px rgba(var(--dds-shadow),.10);
transition: height .6s cubic-bezier(.34,1.56,.64,1),
width .6s cubic-bezier(.34,1.56,.64,1);
}
.dds-row { display: flex; align-items: center; justify-content: space-between; align-self: stretch; }
.dds-hstack { display: flex; align-items: center; gap: 8px; align-self: stretch; }
.dds-vstack { display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 8px; align-self: stretch; }
.dds-nosel { user-select: none; -webkit-user-select: none; }
.dds-hr { align-self: stretch; height: 1px; background: rgba(var(--dds-border), .35); flex-shrink: 0; }
.dds-t1 { font-size: 15px; font-weight: 700; line-height: normal; color: rgb(var(--dds-text1)); margin: 0; }
.dds-t2 { font-size: 13px; font-weight: 600; line-height: normal; color: rgba(var(--dds-text2), .80); margin: 0; }
.dds-input-wrap {
display: flex; height: 48px; padding: 0 14px;
align-items: center; flex: 1 0 0; gap: 8px; border-radius: 12px;
outline: 2px solid rgba(var(--dds-inbg), .30); outline-offset: -2px;
background: rgba(var(--dds-inbg), .10);
position: relative; overflow: hidden;
}
.dds-input-wrap.ta {
height: auto; min-height: 72px; padding: 12px 14px; align-items: flex-start;
}
.dds-input {
border: none !important; outline: none !important; background: none !important;
text-align: right; font-size: 14px !important; font-weight: 600 !important;
color: rgb(var(--dds-blue)) !important; font-family: inherit !important;
width: 100%; -moz-appearance: textfield; line-height: 1.4;
}
.dds-input::placeholder { color: rgba(var(--dds-blue), .40) !important; }
.dds-input::-webkit-outer-spin-button,
.dds-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
textarea.dds-input {
resize: none; text-align: left; overflow-y: auto;
font-size: 12px !important; min-height: 48px; padding-top: 2px;
}
.dds-main-btn {
display: flex; height: 48px; padding: 12px 18px;
justify-content: center; align-items: center; gap: 8px;
border-radius: 14px; border: none; cursor: pointer;
user-select: none; -webkit-user-select: none;
outline: 2px solid rgba(var(--dds-shadow),.18); outline-offset: -2px;
background: rgb(var(--dds-blue)); white-space: nowrap; flex-shrink: 0;
transition: width .7s cubic-bezier(.77,0,.18,1),
background .7s cubic-bezier(.16,1,.32,1),
outline .7s cubic-bezier(.16,1,.32,1),
filter .35s cubic-bezier(.16,1,.32,1),
transform .35s cubic-bezier(.16,1,.32,1);
}
.dds-main-btn:hover { filter: brightness(.88); transform: scale(1.03); }
.dds-main-btn:active { filter: brightness(.80); transform: scale(.97); }
.dds-main-btn:disabled { opacity: .38; pointer-events: none; }
.dds-lbl {
font-size: 15px; font-weight: 800; letter-spacing: .2px;
transition: opacity .35s, filter .35s, color .35s;
white-space: nowrap;
}
.dds-sm-btn {
display: flex; height: 40px; padding: 0 16px;
justify-content: center; align-items: center; gap: 8px;
border-radius: 12px; border: none; cursor: pointer;
user-select: none; flex-shrink: 0;
outline: 2px solid rgba(var(--dds-shadow),.18); outline-offset: -2px;
background: rgb(var(--dds-blue)); white-space: nowrap;
transition: background .7s cubic-bezier(.16,1,.32,1),
filter .35s cubic-bezier(.16,1,.32,1),
transform .35s cubic-bezier(.16,1,.32,1);
}
.dds-sm-btn:hover { filter: brightness(.88); transform: scale(1.04); }
.dds-sm-btn:active { filter: brightness(.80); transform: scale(.96); }
.dds-sm-btn:disabled { opacity: .38; pointer-events: none; }
.dds-sm-lbl { font-size: 13px; font-weight: 800; transition: opacity .35s, filter .35s, color .35s; white-space: nowrap; }
.dds-prog-wrap {
align-self: stretch; height: 0; border-radius: 4px;
background: rgba(var(--dds-blue), .12); overflow: hidden;
transition: height .4s cubic-bezier(.16,1,.32,1);
}
.dds-prog-wrap.on { height: 5px; }
.dds-prog-fill {
height: 100%; border-radius: 4px; background: rgb(var(--dds-blue));
width: 0%; transition: width .5s cubic-bezier(.16,1,.32,1);
box-shadow: 0 0 8px rgba(var(--dds-blue), .45);
}
.dds-prog-fill.warn { background: rgb(var(--dds-red)); }
.dds-log {
background: rgba(var(--dds-blue), .06);
border: 1.5px solid rgba(var(--dds-blue), .18);
border-radius: 12px; padding: 10px 14px;
font-size: 11px !important; font-weight: 600 !important;
color: rgba(var(--dds-text1), .75) !important;
max-height: 160px; overflow-y: auto;
line-height: 1.8; font-family: monospace !important;
white-space: pre-wrap; word-break: break-all; align-self: stretch;
}
.dds-log::-webkit-scrollbar { width: 3px; }
.dds-log::-webkit-scrollbar-thumb { background: rgba(var(--dds-blue), .28); border-radius: 3px; }
.lo { color: rgb(var(--dds-green)); }
.le { color: rgb(var(--dds-red)); }
.li { color: rgb(var(--dds-blue)); }
.lw { color: rgb(var(--dds-orange)); }
.dds-badge {
display: flex; align-items: center; justify-content: space-between;
align-self: stretch; padding: 10px 14px; border-radius: 16px;
outline: 1.5px solid rgba(var(--dds-blue), .25); outline-offset: -1px;
background: rgba(var(--dds-blue), .08);
}
.dds-badge-key { font-size: 12px; font-weight: 800; color: rgb(var(--dds-blue)); font-family: monospace !important; letter-spacing: .5px; }
.dds-badge-meta { font-size: 10px !important; font-weight: 600 !important; color: rgba(var(--dds-text2), .75) !important; margin-top: 2px; }
.dds-free-tag {
display: inline-flex; align-items: center; justify-content: center;
padding: 3px 10px; min-width: 44px;
border-radius: 7px; font-size: 10px !important; font-weight: 800 !important;
background: rgba(var(--dds-green), .16);
outline: 1.5px solid rgba(var(--dds-green), .32); outline-offset: -1px;
color: rgb(var(--dds-green)) !important; letter-spacing: .5px;
flex-shrink: 0; white-space: nowrap;
}
.dds-quota-wrap {
align-self: stretch; display: flex; flex-direction: column; gap: 6px;
padding: 10px 14px; border-radius: 16px;
background: rgba(var(--dds-surface), .90);
outline: 1.5px solid rgba(var(--dds-border), .35); outline-offset: -1px;
}
.dds-quota-row { display: flex; justify-content: space-between; align-items: center; }
.dds-quota-lbl { font-size: 10px !important; font-weight: 700 !important; text-transform: uppercase; letter-spacing: .8px; color: rgba(var(--dds-text2), .65) !important; }
.dds-quota-val { font-size: 11px !important; font-weight: 800 !important; color: rgb(var(--dds-blue)) !important; }
.dds-track { height: 5px; background: rgba(var(--dds-blue), .14); border-radius: 4px; overflow: hidden; }
.dds-tfill { height: 100%; background: rgb(var(--dds-blue)); border-radius: 4px; transition: width .6s cubic-bezier(.34,1.56,.64,1); }
.dds-tfill.warn { background: rgb(var(--dds-red)); }
.dds-cd {
display: none; align-items: center; justify-content: space-between;
align-self: stretch; padding: 8px 14px; border-radius: 14px;
background: rgba(var(--dds-cd-bg), .12);
outline: 1.5px solid rgba(var(--dds-cd-bg), .28); outline-offset: -1px;
}
.dds-cd.on { display: flex; animation: dds-slidein .35s cubic-bezier(.34,1.56,.64,1); }
.dds-cd-lbl { display: flex; align-items: center; gap: 5px; font-size: 11px !important; font-weight: 700 !important; color: rgb(var(--dds-orange)) !important; }
.dds-cd-time { font-size: 14px !important; font-weight: 800 !important; color: rgb(var(--dds-orange)) !important; font-family: monospace !important; }
.dds-tabs {
display: flex; gap: 2px; align-self: stretch;
background: rgba(var(--dds-tab-bg), .15);
border-radius: 14px; padding: 3px;
outline: 1.5px solid rgba(var(--dds-border), .25); outline-offset: -1px;
}
.dds-tab {
flex: 1; padding: 7px 4px; border: none; border-radius: 11px;
background: none; color: rgba(var(--dds-text2), .65);
font-size: 10px !important; font-weight: 700 !important; cursor: pointer;
transition: all .28s cubic-bezier(.34,1.56,.64,1);
}
.dds-tab.on {
background: rgb(var(--dds-blue)); color: #fff;
box-shadow: 0 2px 10px rgba(var(--dds-blue), .42);
transform: scale(1.05);
}
.dds-tab:hover:not(.on) { color: rgb(var(--dds-text1)); background: rgba(var(--dds-blue), .08); transform: scale(1.02); }
.dds-pane { display: none; flex-direction: column; gap: 8px; align-self: stretch; }
.dds-pane.active { display: flex; animation: dds-fadein .22s cubic-bezier(.16,1,.32,1); }
.dds-page { display: none; }
.dds-page.active { display: flex; flex-direction: column; gap: 8px; align-self: stretch; align-items: center; }
@keyframes dds-fadein {
from { opacity: 0; transform: translateY(6px) scale(.98); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes dds-slidein {
from { opacity: 0; transform: translateY(-4px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes dds-float {
0%, 100% { transform: translateY(0) rotate(-1deg); }
50% { transform: translateY(-6px) rotate(1deg); }
}
.dds-lock-ring {
width: 58px; height: 58px; border-radius: 18px;
background: rgba(var(--dds-blue), .12);
outline: 1.5px solid rgba(var(--dds-blue), .28); outline-offset: -1px;
display: flex; align-items: center; justify-content: center;
animation: dds-float 3.4s ease-in-out infinite;
}
.dds-copy-tag {
display: inline-flex; align-items: center;
padding: 2px 8px; border-radius: 6px;
background: rgba(var(--dds-blue), .12);
outline: 1px solid rgba(var(--dds-blue), .25); outline-offset: -1px;
font-size: 9px !important; font-weight: 700 !important;
cursor: pointer; color: rgb(var(--dds-blue)) !important;
margin-left: 6px; transition: background .15s, transform .15s; vertical-align: middle;
}
.dds-copy-tag:hover { background: rgba(var(--dds-blue), .24); transform: scale(1.08); }
.dds-copy-tag:active { transform: scale(.94); }
.dds-err {
font-size: 11px !important; font-weight: 600 !important;
color: rgb(var(--dds-red)) !important; text-align: center;
min-height: 14px; align-self: stretch;
}
.dds-ico-box {
width: 26px; height: 26px; border-radius: 8px; flex-shrink: 0;
display: flex; align-items: center; justify-content: center;
}
.dds-danger-btn {
display: flex; height: 30px; padding: 0 12px;
justify-content: center; align-items: center; border-radius: 10px; border: none;
cursor: pointer; font-size: 11px !important; font-weight: 800 !important;
background: rgba(var(--dds-red), .10);
outline: 1.5px solid rgba(var(--dds-red), .25); outline-offset: -1px;
color: rgb(var(--dds-red)) !important; white-space: nowrap;
transition: filter .3s, transform .3s, background .3s, color .3s;
}
.dds-danger-btn:hover { background: rgb(var(--dds-red)); color: #fff !important; transform: scale(1.04); }
.dds-danger-btn:active { transform: scale(.95); }
.dds-hide-btn {
display: flex; height: 40px; padding: 0 16px;
align-items: center; gap: 8px;
border-radius: 14px; border: none; cursor: pointer; user-select: none;
outline: 2px solid rgba(var(--dds-shadow),.18); outline-offset: -2px;
background: rgb(var(--dds-blue));
transition: filter .35s cubic-bezier(.16,1,.32,1),
transform .35s cubic-bezier(.16,1,.32,1),
background .4s, outline .4s;
}
.dds-hide-btn:hover { filter: brightness(.88); transform: scale(1.04); }
.dds-hide-btn:active { filter: brightness(.80); transform: scale(.96); }
.dds-ntf-shell {
display: flex; justify-content: center; align-items: center;
width: 300px; position: fixed; left: calc(50% - 150px);
z-index: 2147483647; bottom: 16px; border-radius: 18px;
transition: .8s cubic-bezier(.16,1,.32,1); pointer-events: none;
}
.dds-ntf-box {
display: flex; width: 300px; padding: 14px 16px;
flex-direction: column; gap: 3px; border-radius: 18px;
outline: 1.5px solid rgba(var(--dds-border), .40); outline-offset: -1px;
background: rgba(var(--dds-card), 0.97);
backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
box-shadow: 0 8px 32px rgba(var(--dds-shadow),.16);
transition: .8s cubic-bezier(.16,1,.32,1); filter: blur(16px); opacity: 0;
pointer-events: auto;
}
.dds-ntf-box.show { filter: blur(0); opacity: 1; }
.dds-discord-card {
align-self: stretch; padding: 10px 14px; border-radius: 14px;
background: rgba(var(--dds-purple),.10);
outline: 1.5px solid rgba(var(--dds-purple),.28); outline-offset: -1px;
display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.dds-discord-link {
font-size: 11px !important; font-weight: 800 !important;
color: rgb(var(--dds-purple)) !important;
padding: 5px 12px; border-radius: 9px;
background: rgba(var(--dds-purple),.14);
outline: 1px solid rgba(var(--dds-purple),.25); outline-offset: -1px;
white-space: nowrap; flex-shrink: 0;
transition: background .2s, transform .2s;
}
.dds-discord-link:hover { background: rgba(var(--dds-purple),.26); transform: scale(1.05); }
.dds-sec-lbl {
font-size: 10px !important; font-weight: 700 !important;
text-transform: uppercase; letter-spacing: .7px;
color: rgba(var(--dds-text2), .65) !important; align-self: flex-start;
}
.dds-info-card {
padding: 10px 14px; border-radius: 14px;
background: rgba(var(--dds-surface), .90);
outline: 1.5px solid rgba(var(--dds-border), .30); outline-offset: -1px;
align-self: stretch;
}
.dds-dot-ring {
display: inline-flex; gap: 4px; align-items: center;
}
.dds-dot-ring span {
width: 5px; height: 5px; border-radius: 50%; background: currentColor;
animation: dds-dot-pulse 1.2s ease-in-out infinite;
}
.dds-dot-ring span:nth-child(2) { animation-delay: .18s; }
.dds-dot-ring span:nth-child(3) { animation-delay: .36s; }
@keyframes dds-dot-pulse {
0%, 80%, 100% { transform: scale(.55); opacity: .35; }
40% { transform: scale(1.1); opacity: 1; }
}
.dds-spin-ring {
width: 16px; height: 16px; border-radius: 50%;
border: 2.5px solid rgba(var(--dds-spin-bg), .28);
border-top-color: rgb(var(--dds-spin-fg));
animation: dds-spin 0.75s linear infinite;
flex-shrink: 0;
}
@keyframes dds-spin { to { transform: rotate(360deg); } }
.dds-pulse-dot {
width: 7px; height: 7px; border-radius: 50%;
background: rgb(var(--dds-green));
animation: dds-pulse-glow 2s ease-in-out infinite;
flex-shrink: 0;
}
@keyframes dds-pulse-glow {
0%, 100% { box-shadow: 0 0 0 0 rgba(var(--dds-green), .60); transform: scale(1); }
50% { box-shadow: 0 0 0 5px rgba(var(--dds-green), 0); transform: scale(1.18); }
}
.dds-shimmer {
position: absolute; top: 0; left: -120px; width: 80px; height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,.22), transparent);
animation: dds-shimmer-move 2.8s ease-in-out infinite 1.4s;
pointer-events: none;
}
@keyframes dds-shimmer-move {
0% { left: -120px; }
38% { left: 360px; }
100% { left: 360px; }
}
.dds-ripple-btn { position: relative; overflow: hidden; }
.dds-ripple-btn::after {
content: ''; position: absolute; border-radius: 50%;
width: 4px; height: 4px;
background: rgba(255,255,255,.38);
transform: scale(0); opacity: 1;
pointer-events: none;
}
.dds-ripple-btn.rippling::after { animation: dds-ripple .55s linear; }
@keyframes dds-ripple { to { transform: scale(80); opacity: 0; } }
`);
const _root = document.createElement('div');
_root.id = 'DDS_Root';
_root.innerHTML = `
<div class="dds-ntf-shell" id="dds-ntf">
<div class="dds-ntf-box" id="dds-ntf-box">
<p class="dds-t1 dds-nosel" id="dds-ntf-title"></p>
<p class="dds-t2 dds-nosel" id="dds-ntf-body" style="align-self:stretch;overflow-wrap:break-word;"></p>
</div>
</div>
<div class="dds-shell" id="dds-shell">
<div class="dds-hstack" style="align-self:flex-end;">
<button class="dds-hide-btn dds-nosel" id="dds-toggle">
<div class="dds-ico-box" id="dds-ico-wrap" style="background:rgba(255,255,255,.20);">
<svg id="dds-ico-eye" width="14" height="14" viewBox="0 0 24 18" fill="none">
<path d="M12 3C7.5 3 3.73 5.5 2 9c1.73 3.5 5.5 6 10 6s8.27-2.5 10-6c-1.73-3.5-5.5-6-10-6zm0 10a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-6.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5z" fill="#fff"/>
<line x1="2" y1="2" x2="22" y2="16" stroke="#fff" stroke-width="2" stroke-linecap="round"/>
</svg>
<svg id="dds-ico-eyeoff" width="14" height="14" viewBox="0 0 24 18" fill="none" style="display:none;">
<path d="M12 3C7.5 3 3.73 5.5 2 9c1.73 3.5 5.5 6 10 6s8.27-2.5 10-6c-1.73-3.5-5.5-6-10-6zm0 10a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-6.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5z" fill="rgb(var(--dds-blue))"/>
</svg>
</div>
<p class="dds-t1 dds-nosel" id="dds-toggle-txt" style="color:#fff;font-size:14px;">Hide</p>
</button>
</div>
<div class="dds-box" id="dds-box">
<!-- PAGE: Auth -->
<div class="dds-page active" id="dds-page-auth">
<div class="dds-lock-ring">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none">
<rect x="3" y="11" width="18" height="11" rx="3" fill="none" stroke="rgb(var(--dds-blue))" stroke-width="2"/>
<path d="M7 11V7a5 5 0 0 1 10 0v4" stroke="rgb(var(--dds-blue))" stroke-width="2" stroke-linecap="round"/>
<circle cx="12" cy="16.5" r="1.5" fill="rgb(var(--dds-blue))"/>
</svg>
</div>
<div style="display:flex;flex-direction:column;gap:4px;align-items:center;align-self:stretch;">
<p class="dds-t1 dds-nosel" style="font-size:16px;text-align:center;">Authenticate to continue</p>
<p class="dds-t2 dds-nosel" style="text-align:center;font-size:12px;">Enter your key to unlock all features</p>
</div>
<div class="dds-hr"></div>
<div class="dds-vstack" style="align-self:stretch;">
<div class="dds-hstack">
<div class="dds-input-wrap">
<div class="dds-ico-box" style="background:rgba(var(--dds-blue),.16);">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none">
<path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4" stroke="rgba(var(--dds-blue),.75)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<input id="dds-key-in" class="dds-input dds-nosel" type="password" placeholder="DS-XXXXXXXXXXXXXXXX" style="text-align:left;" autocomplete="off">
</div>
<button class="dds-main-btn dds-nosel dds-ripple-btn" id="dds-unlock-btn" style="flex:none;height:48px;border-radius:14px;padding:0 18px;position:relative;overflow:hidden;">
<div class="dds-shimmer"></div>
<span class="dds-lbl" id="dds-unlock-lbl" style="color:#fff;">UNLOCK</span>
</button>
</div>
<p class="dds-err" id="dds-auth-err"></p>
<div id="dds-auth-spin" style="display:none;justify-content:center;">
<div class="dds-dot-ring" style="color:rgb(var(--dds-blue));"><span></span><span></span><span></span></div>
</div>
</div>
<button class="dds-main-btn dds-nosel" id="dds-free-btn" style="width:100%;background:rgba(var(--dds-green),.13);outline:2px solid rgba(var(--dds-green),.30);outline-offset:-2px;">
<div class="dds-pulse-dot"></div>
<span class="dds-lbl" style="color:rgb(var(--dds-green));">Use Free Key</span>
</button>
<div class="dds-discord-card">
<div style="display:flex;flex-direction:column;gap:2px;min-width:0;flex:1;">
<p class="dds-t1 dds-nosel" style="font-size:11px;color:rgb(var(--dds-purple));">Get your own key</p>
<p class="dds-t2 dds-nosel" style="font-size:10px;">Join our Discord server</p>
</div>
<a class="dds-discord-link" href="${DISCORD_URL}" target="_blank" rel="noopener">Join</a>
</div>
<div class="dds-row" style="align-self:stretch;padding-top:2px;">
<p class="dds-t2 dds-nosel" style="color:rgba(var(--dds-blue),.50);">discord.gg/ufBrcGemBH</p>
<p class="dds-t2 dds-nosel" style="color:rgba(var(--dds-blue),.50);">v1.0</p>
</div>
</div>
<!-- PAGE: Dashboard -->
<div class="dds-page" id="dds-page-dash">
<div class="dds-badge">
<div style="display:flex;flex-direction:column;gap:4px;min-width:0;flex:1;">
<div style="display:flex;align-items:center;gap:7px;flex-wrap:nowrap;">
<p class="dds-badge-key dds-nosel" id="dds-key-disp" style="min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">DS-XXXX...</p>
<span class="dds-free-tag dds-nosel" id="dds-free-tag" style="display:none;">FREE</span>
</div>
<p class="dds-badge-meta dds-nosel" id="dds-key-meta">Batch: ? · CD: ?s</p>
</div>
<button class="dds-danger-btn dds-nosel" id="dds-chkey-btn">Change key</button>
</div>
<div class="dds-quota-wrap">
<div class="dds-quota-row">
<span class="dds-quota-lbl dds-nosel">DAILY QUOTA</span>
<span class="dds-quota-val dds-nosel" id="dds-quota-val">0 / 0</span>
</div>
<div class="dds-track"><div class="dds-tfill" id="dds-quota-fill" style="width:0%"></div></div>
</div>
<div class="dds-cd" id="dds-cd">
<span class="dds-cd-lbl dds-nosel">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="9" stroke="rgb(var(--dds-orange))" stroke-width="2"/>
<path d="M12 7v5l3 3" stroke="rgb(var(--dds-orange))" stroke-width="2" stroke-linecap="round"/>
</svg>
Cooldown
</span>
<span class="dds-cd-time dds-nosel" id="dds-cd-time">00:00</span>
</div>
<div class="dds-tabs">
<button class="dds-tab on dds-nosel" data-tab="link">Links</button>
<button class="dds-tab dds-nosel" data-tab="active">Active</button>
<button class="dds-tab dds-nosel" data-tab="hist">History</button>
</div>
<!-- TAB: Links -->
<div class="dds-pane active" id="dds-pane-link">
<div class="dds-info-card">
<p class="dds-t2 dds-nosel" style="font-size:11px;line-height:1.65;">
Create <span id="dds-batch-n" style="color:rgb(var(--dds-blue));font-weight:800;">?</span> links per batch. System runs in parallel.
</p>
</div>
<button class="dds-main-btn dds-nosel dds-ripple-btn" id="dds-create-btn" style="width:100%;position:relative;overflow:hidden;">
<div class="dds-shimmer"></div>
<div class="dds-ico-box" id="dds-create-ico" style="background:rgba(255,255,255,.20);">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5">
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>
</svg>
</div>
<span class="dds-lbl" id="dds-create-lbl" style="color:#fff;">Create Link Batch</span>
</button>
<div class="dds-prog-wrap" id="dds-create-prog">
<div class="dds-prog-fill" id="dds-create-fill"></div>
</div>
<p class="dds-sec-lbl">Output</p>
<div class="dds-log" id="dds-log-link">Ready...</div>
</div>
<!-- TAB: Active -->
<div class="dds-pane" id="dds-pane-active">
<p class="dds-sec-lbl">Duolingo JWT</p>
<div class="dds-input-wrap ta">
<textarea id="dds-jwt-in" class="dds-input dds-nosel" placeholder="eyJhbGci..." rows="3"></textarea>
</div>
<button class="dds-main-btn dds-nosel dds-ripple-btn" id="dds-activate-btn" style="width:100%;position:relative;overflow:hidden;">
<div class="dds-shimmer"></div>
<div class="dds-ico-box" id="dds-activate-ico" style="background:rgba(255,255,255,.20);">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</div>
<span class="dds-lbl" id="dds-activate-lbl" style="color:#fff;">Activate Super</span>
</button>
<div class="dds-log" id="dds-log-active">Waiting for command...</div>
</div>
<!-- TAB: History -->
<div class="dds-pane" id="dds-pane-hist">
<div class="dds-hstack">
<button class="dds-main-btn dds-nosel dds-ripple-btn" id="dds-loadhist-btn" style="flex:1;height:40px;border-radius:12px;padding:0 14px;position:relative;overflow:hidden;">
<div class="dds-ico-box" id="dds-loadhist-ico" style="background:rgba(255,255,255,.20);">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5">
<polyline points="1 4 1 10 7 10"/>
<path d="M3.51 15a9 9 0 1 0 .49-3.68"/>
</svg>
</div>
<span class="dds-lbl" id="dds-loadhist-lbl" style="color:#fff;font-size:13px;">Load history</span>
</button>
<button class="dds-sm-btn dds-nosel" id="dds-copyall-btn">
<div class="dds-ico-box" style="background:rgba(255,255,255,.20);">
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5">
<rect x="9" y="9" width="13" height="13" rx="2" stroke-linejoin="round"/>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
</svg>
</div>
<span class="dds-sm-lbl" style="color:#fff;">Copy all</span>
</button>
</div>
<div class="dds-log" id="dds-hist-log" style="max-height:200px;">Press load to view...</div>
</div>
<div class="dds-row" style="align-self:stretch;padding-top:2px;">
<p class="dds-t2 dds-nosel" style="color:rgba(var(--dds-blue),.50);">discord.gg/ufBrcGemBH</p>
<p class="dds-t2 dds-nosel" style="color:rgba(var(--dds-blue),.50);">v2.0</p>
</div>
</div>
<!-- PAGE: Change Key -->
<div class="dds-page" id="dds-page-chkey">
<div style="display:flex;flex-direction:column;gap:4px;align-items:center;align-self:stretch;">
<p class="dds-t1 dds-nosel" style="font-size:15px;text-align:center;">Switch Key</p>
<p class="dds-t2 dds-nosel" style="text-align:center;font-size:12px;">Enter a new key or return to free tier</p>
</div>
<div class="dds-hr"></div>
<div class="dds-vstack" style="align-self:stretch;">
<div class="dds-hstack">
<div class="dds-input-wrap">
<div class="dds-ico-box" style="background:rgba(var(--dds-blue),.16);">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none">
<path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4" stroke="rgba(var(--dds-blue),.75)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<input id="dds-newkey-in" class="dds-input dds-nosel" type="password" placeholder="DS-XXXXXXXXXXXXXXXX" style="text-align:left;" autocomplete="off">
</div>
<button class="dds-main-btn dds-nosel dds-ripple-btn" id="dds-newkey-btn" style="flex:none;height:48px;border-radius:14px;padding:0 18px;position:relative;overflow:hidden;">
<div class="dds-shimmer"></div>
<span class="dds-lbl" id="dds-newkey-lbl" style="color:#fff;">Apply</span>
</button>
</div>
<p class="dds-err" id="dds-chkey-err"></p>
<div id="dds-chkey-spin" style="display:none;justify-content:center;">
<div class="dds-dot-ring" style="color:rgb(var(--dds-blue));"><span></span><span></span><span></span></div>
</div>
</div>
<button class="dds-main-btn dds-nosel" id="dds-activefree-btn" style="width:100%;background:rgba(var(--dds-green),.13);outline:2px solid rgba(var(--dds-green),.30);outline-offset:-2px;">
<div class="dds-pulse-dot"></div>
<span class="dds-lbl" style="color:rgb(var(--dds-green));">Activate Free Key</span>
</button>
<div class="dds-discord-card">
<div style="display:flex;flex-direction:column;gap:2px;min-width:0;flex:1;">
<p class="dds-t1 dds-nosel" style="font-size:11px;color:rgb(var(--dds-purple));">Get a premium key</p>
<p class="dds-t2 dds-nosel" style="font-size:10px;">More quota, faster cooldown</p>
</div>
<a class="dds-discord-link" href="${DISCORD_URL}" target="_blank" rel="noopener">Discord</a>
</div>
<button class="dds-sm-btn dds-nosel" id="dds-back-btn" style="width:100%;background:rgba(var(--dds-surface),.95);outline:2px solid rgba(var(--dds-border),.35);outline-offset:-2px;">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="rgba(var(--dds-text2),.75)" stroke-width="2.5">
<path d="M19 12H5M12 19l-7-7 7-7"/>
</svg>
<span class="dds-sm-lbl" style="color:rgba(var(--dds-text2),.75);">Back</span>
</button>
<div class="dds-row" style="align-self:stretch;padding-top:2px;">
<p class="dds-t2 dds-nosel" style="color:rgba(var(--dds-blue),.50);">duods.vercel.app</p>
<p class="dds-t2 dds-nosel" style="color:rgba(var(--dds-blue),.50);">v1.0</p>
</div>
</div>
</div>
</div>
`;
document.body.appendChild(_root);
_watchTheme();
function _ripple(btn) {
btn.classList.remove('rippling');
void btn.offsetWidth;
btn.classList.add('rippling');
setTimeout(() => btn.classList.remove('rippling'), 560);
}
function _notify(title, body, dur = 5) {
const box = document.getElementById('dds-ntf-box');
document.getElementById('dds-ntf-title').textContent = title;
document.getElementById('dds-ntf-body').textContent = body;
box.classList.add('show');
clearTimeout(_ntTimer);
_ntTimer = setTimeout(() => box.classList.remove('show'), dur * 1000);
}
function _toggleVis(hide) {
if (_animLock) return;
_animLock = true;
_hidden = hide;
const shell = document.getElementById('dds-shell');
const box = document.getElementById('dds-box');
const icoE = document.getElementById('dds-ico-eye');
const icoH = document.getElementById('dds-ico-eyeoff');
const icoW = document.getElementById('dds-ico-wrap');
const txt = document.getElementById('dds-toggle-txt');
const btn = document.getElementById('dds-toggle');
const h = box.offsetHeight;
shell.style.transition = box.style.transition = '.8s cubic-bezier(.16,1,.32,1)';
if (hide) {
btn.style.background = 'rgba(var(--dds-card),.95)';
btn.style.outline = '2px solid rgba(var(--dds-blue),.25)';
if (icoW) { icoW.style.background = 'rgba(var(--dds-blue),.16)'; }
if (icoE) icoE.style.display = 'none';
if (icoH) icoH.style.display = '';
txt.textContent = 'Show';
txt.style.color = 'rgb(var(--dds-blue))';
shell.style.bottom = `-${h - 8}px`;
box.style.filter = 'blur(8px)';
box.style.opacity = '0';
} else {
btn.style.background = 'rgb(var(--dds-blue))';
btn.style.outline = '2px solid rgba(0,0,0,.18)';
if (icoW) { icoW.style.background = 'rgba(255,255,255,.20)'; }
if (icoH) icoH.style.display = 'none';
if (icoE) icoE.style.display = '';
txt.textContent = 'Hide';
txt.style.color = '#fff';
shell.style.bottom = '16px';
box.style.filter = '';
box.style.opacity = '';
}
setTimeout(() => {
shell.style.transition = '';
box.style.transition = '';
_animLock = false;
}, 800);
}
document.getElementById('dds-toggle').addEventListener('click', () => _toggleVis(!_hidden));
function _goPage(to) {
if (_pgLock || _curPg === to) return;
_pgLock = true;
const box = document.getElementById('dds-box');
const fromEl = document.getElementById(`dds-page-${_curPg}`);
const toEl = document.getElementById(`dds-page-${to}`);
if (!fromEl || !toEl) { _pgLock = false; return; }
const oldH = box.offsetHeight;
fromEl.style.display = 'none';
toEl.classList.add('active');
toEl.style.opacity = '0';
const newH = box.offsetHeight;
toEl.classList.remove('active');
toEl.style.opacity = '';
fromEl.style.display = '';
box.style.height = oldH + 'px';
box.style.transition = 'height .6s cubic-bezier(.34,1.56,.64,1)';
fromEl.style.transition = 'opacity .35s, filter .35s, transform .35s';
fromEl.style.opacity = '0';
fromEl.style.filter = 'blur(6px)';
fromEl.style.transform = 'scale(.96)';
requestAnimationFrame(() => { box.style.height = newH + 'px'; });
setTimeout(() => {
fromEl.classList.remove('active');
fromEl.style.cssText = '';
toEl.classList.add('active');
toEl.style.opacity = '0';
toEl.style.filter = 'blur(6px)';
toEl.style.transform = 'scale(1.04)';
void toEl.offsetWidth;
toEl.style.transition = 'opacity .4s, filter .4s, transform .5s cubic-bezier(.34,1.56,.64,1)';
toEl.style.opacity = '1';
toEl.style.filter = 'blur(0)';
toEl.style.transform = 'scale(1)';
_curPg = to;
setTimeout(() => {
box.style.height = '';
box.style.transition = '';
toEl.style.cssText = '';
_pgLock = false;
}, 400);
}, 380);
}
document.querySelectorAll('#DDS_Root .dds-tab').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('#DDS_Root .dds-tab').forEach(b => b.classList.remove('on'));
btn.classList.add('on');
['link', 'active', 'hist'].forEach(t => {
const el = document.getElementById(`dds-pane-${t}`);
if (el) el.classList.toggle('active', btn.dataset.tab === t);
});
});
});
function _log(id, msg, type = 'i') {
const el = document.getElementById(id);
if (!el) return;
const ts = new Date().toLocaleTimeString('en-US', { hour12: false });
const cls = { o: 'lo', e: 'le', i: 'li', w: 'lw' }[type] || 'li';
el.innerHTML += `<span class="${cls}">[${ts}] ${msg}</span>\n`;
el.scrollTop = el.scrollHeight;
}
function _logClear(id) {
const el = document.getElementById(id);
if (el) el.innerHTML = '';
}
const C_BLUE = { bg: 'rgb(var(--dds-blue))', out: 'rgba(0,0,0,.18)', tc: '#fff' };
const C_GREEN = { bg: 'rgba(var(--dds-green),.12)', out: 'rgba(var(--dds-green),.28)', tc: 'rgb(var(--dds-green))' };
const C_GRAY = { bg: 'rgba(var(--dds-surface),.95)', out: 'rgba(var(--dds-border),.40)', tc: 'rgba(var(--dds-text2),.80)' };
function _setLoading(btnId, icoId, on) {
const ico = document.getElementById(icoId);
if (!ico) return;
if (on) {
ico.innerHTML = `<div class="dds-spin-ring"></div>`;
} else {
const svgMap = {
'dds-create-ico': `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>`,
'dds-activate-ico': `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5"><path d="M5 12h14M12 5l7 7-7 7"/></svg>`,
'dds-loadhist-ico': `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5"><polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 .49-3.68"/></svg>`
};
if (svgMap[icoId]) ico.innerHTML = svgMap[icoId];
}
}
function _btnState(id, clr, text, icoId) {
const btn = document.getElementById(id);
if (!btn) return;
const lbl = btn.querySelector('.dds-lbl') || btn.querySelector('.dds-sm-lbl');
if (!lbl) return;
const prev = lbl.textContent;
lbl.textContent = text;
const nw = btn.offsetWidth;
lbl.textContent = prev;
btn.style.width = btn.offsetWidth + 'px';
requestAnimationFrame(() => {
lbl.style.opacity = '0';
lbl.style.filter = 'blur(4px)';
btn.style.width = nw + 'px';
btn.style.background = clr.bg;
btn.style.outline = `solid 2px ${clr.out}`;
btn.style.outlineOffset = '-2px';
if (icoId) {
const icoEl = document.getElementById(icoId);
if (icoEl) icoEl.style.background = clr.tc === '#fff' ? 'rgba(255,255,255,.20)' : 'rgba(0,0,0,.06)';
}
});
setTimeout(() => {
lbl.style.transition = '0s';
lbl.style.color = clr.tc;
void lbl.offsetWidth;
lbl.style.transition = '.35s';
lbl.textContent = text;
requestAnimationFrame(() => {
lbl.style.opacity = '1';
lbl.style.filter = 'blur(0)';
});
setTimeout(() => { btn.style.width = ''; }, 400);
}, 380);
}
function _refreshQuota(used, limit) {
const pct = limit > 0 ? Math.min((used / limit) * 100, 100) : 0;
const fill = document.getElementById('dds-quota-fill');
if (fill) { fill.style.width = pct + '%'; fill.classList.toggle('warn', pct > 85); }
const val = document.getElementById('dds-quota-val');
if (val) val.textContent = `${used} / ${limit}`;
}
function _saveCD(sec) {
if (sec > 0) _set(CD_STORE, Date.now() + sec * 1000);
}
function _loadCD() {
const exp = _get(CD_STORE, 0);
if (!exp) return 0;
const rem = Math.ceil((exp - Date.now()) / 1000);
return rem > 0 ? rem : 0;
}
function _startCD(sec, save = true) {
if (_cdTimer) clearInterval(_cdTimer);
let rem = Math.ceil(sec);
if (save) _saveCD(rem);
const cdEl = document.getElementById('dds-cd');
const cdTx = document.getElementById('dds-cd-time');
const cBtn = document.getElementById('dds-create-btn');
if (cdEl) cdEl.classList.add('on');
if (cBtn) cBtn.disabled = true;
function tick() {
if (rem <= 0) {
clearInterval(_cdTimer);
if (cdEl) cdEl.classList.remove('on');
if (cBtn) cBtn.disabled = false;
return;
}
const mm = String(Math.floor(rem / 60)).padStart(2, '0');
const ss = String(rem % 60).padStart(2, '0');
if (cdTx) cdTx.textContent = `${mm}:${ss}`;
rem--;
}
tick();
_cdTimer = setInterval(tick, 1000);
}
function _applyKey(data, key) {
_keyData = data;
_isFree = key === FREE_KEY;
const disp = document.getElementById('dds-key-disp');
const meta = document.getElementById('dds-key-meta');
const batchN = document.getElementById('dds-batch-n');
const freeTag = document.getElementById('dds-free-tag');
if (disp) disp.textContent = key.slice(0, 16) + '...';
if (freeTag) freeTag.style.display = _isFree ? '' : 'none';
let expStr = '';
if (data.expires_in_seconds != null) {
const h = Math.floor(data.expires_in_seconds / 3600);
const m = Math.floor((data.expires_in_seconds % 3600) / 60);
expStr = ` · Exp: ${h}h${m}m`;
}
if (meta) meta.textContent =
`Batch: ${data.batch_size} · CD: ${data.cd_seconds}s · Uses: ${data.used_total}/${data.total_limit}${expStr}`;
if (batchN) batchN.textContent = data.batch_size;
_refreshQuota(data.used_today || 0, data.daily_limit);
const cdRemain = data.cooldown_remain && data.cooldown_remain > 0
? data.cooldown_remain : _loadCD();
if (cdRemain > 0) _startCD(cdRemain, false);
}
async function _unlock(key, errId, spinId, btnId, lblId) {
const errEl = document.getElementById(errId);
const spin = document.getElementById(spinId);
const btn = document.getElementById(btnId);
const lbl = lblId ? document.getElementById(lblId) : null;
if (!key) return;
if (btn) { btn.disabled = true; if (btn.classList.contains('dds-ripple-btn')) _ripple(btn); }
if (errEl) errEl.textContent = '';
if (spin) spin.style.display = 'flex';
if (lbl) { lbl.style.opacity = '0'; lbl.style.filter = 'blur(3px)'; }
const r = await _fetchKey(key);
if (btn) btn.disabled = false;
if (spin) spin.style.display = 'none';
if (lbl) {
setTimeout(() => {
lbl.style.transition = '.35s';
lbl.style.opacity = '1';
lbl.style.filter = 'blur(0)';
}, 80);
}
if (r.ok && _sess) {
_set('dds_key', key);
_applyKey(r, key);
_goPage('dash');
_notify('Unlocked 🎉', `Welcome, ${r.label || 'user'}!`);
} else {
_sess = null;
if (errEl) errEl.textContent = 'Invalid key: ' + (r.error || 'Unknown error');
}
}
document.getElementById('dds-unlock-btn').addEventListener('click', () => {
const key = document.getElementById('dds-key-in').value.trim();
_unlock(key, 'dds-auth-err', 'dds-auth-spin', 'dds-unlock-btn', 'dds-unlock-lbl');
});
document.getElementById('dds-key-in').addEventListener('keydown', e => {
if (e.key === 'Enter') {
const key = document.getElementById('dds-key-in').value.trim();
_unlock(key, 'dds-auth-err', 'dds-auth-spin', 'dds-unlock-btn', 'dds-unlock-lbl');
}
});
document.getElementById('dds-free-btn').addEventListener('click', () => {
_unlock(FREE_KEY, 'dds-auth-err', 'dds-auth-spin', 'dds-free-btn', null);
});
document.getElementById('dds-chkey-btn').addEventListener('click', () => {
document.getElementById('dds-newkey-in').value = '';
document.getElementById('dds-chkey-err').textContent = '';
_goPage('chkey');
});
document.getElementById('dds-back-btn').addEventListener('click', () => {
_goPage('dash');
});
document.getElementById('dds-newkey-btn').addEventListener('click', () => {
const key = document.getElementById('dds-newkey-in').value.trim();
_unlock(key, 'dds-chkey-err', 'dds-chkey-spin', 'dds-newkey-btn', 'dds-newkey-lbl');
});
document.getElementById('dds-newkey-in').addEventListener('keydown', e => {
if (e.key === 'Enter') {
const key = document.getElementById('dds-newkey-in').value.trim();
_unlock(key, 'dds-chkey-err', 'dds-chkey-spin', 'dds-newkey-btn', 'dds-newkey-lbl');
}
});
document.getElementById('dds-activefree-btn').addEventListener('click', async () => {
const cdSaved = _loadCD();
const r = await _fetchKey(FREE_KEY);
if (r.ok && _sess) {
_set('dds_key', FREE_KEY);
_applyKey(r, FREE_KEY);
if (cdSaved > 0) _startCD(cdSaved, false);
_goPage('dash');
_notify('Free Key Active ✅', 'Now using the free public key.');
} else {
_sess = null;
document.getElementById('dds-chkey-err').textContent = 'Could not activate free key. Try again.';
}
});
document.getElementById('dds-create-btn').addEventListener('click', async () => {
if (!_sess) return;
const key = _get('dds_key', '');
if (!key) return;
const btn = document.getElementById('dds-create-btn');
const prog = document.getElementById('dds-create-prog');
const fill = document.getElementById('dds-create-fill');
_ripple(btn);
btn.disabled = true;
_setLoading('dds-create-btn', 'dds-create-ico', true);
_btnState('dds-create-btn', C_GRAY, 'Working...', null);
prog.classList.add('on');
fill.style.width = '15%';
_logClear('dds-log-link');
_log('dds-log-link', `Creating ${_keyData?.batch_size || '?'} links in parallel...`, 'i');
fill.style.width = '50%';
const r = await _req('/api/link/create', 'POST', { key });
fill.style.width = '100%';
setTimeout(() => { prog.classList.remove('on'); }, 600);
btn.disabled = false;
_setLoading('dds-create-btn', 'dds-create-ico', false);
if (r.ok && r.urls && r.urls.length > 0) {
_btnState('dds-create-btn', C_GREEN, `Done (${r.count})`, 'dds-create-ico');
_log('dds-log-link', `Created ${r.count} link${r.failed > 0 ? ` (${r.failed} failed)` : ''}`, 'o');
r.urls.forEach((url, i) => {
_log('dds-log-link', `#${i + 1} ${url}`, 'o');
_hist.unshift(url);
});
if (_keyData) {
_keyData.used_today = (_keyData.used_today || 0) + r.count;
_refreshQuota(_keyData.used_today, _keyData.daily_limit);
}
if (_keyData?.cd_seconds) _startCD(_keyData.cd_seconds);
setTimeout(() => _btnState('dds-create-btn', C_BLUE, 'Create Link Batch', 'dds-create-ico'), 3000);
} else {
_btnState('dds-create-btn', C_BLUE, 'Create Link Batch', 'dds-create-ico');
const msg = r.error || 'Unknown error';
_log('dds-log-link', `Error: ${msg}`, 'e');
const cdM = msg.match(/Cooldown.*?(\d+):(\d+)/);
if (cdM) _startCD(parseInt(cdM[1]) * 60 + parseInt(cdM[2]));
else if (r.cooldown_remain > 0) _startCD(r.cooldown_remain);
}
});
document.getElementById('dds-activate-btn').addEventListener('click', async () => {
if (!_sess) return;
const jwt = document.getElementById('dds-jwt-in').value.trim();
if (!jwt) return _log('dds-log-active', 'Enter your JWT token!', 'e');
if (!_hist.length) return _log('dds-log-active', 'No links in history! Create or load links first.', 'e');
const code = _hist[0].split('/').pop();
let uid = '';
try {
const seg = jwt.split('.')[1];
const pad = seg + '='.repeat((4 - seg.length % 4) % 4);
uid = String(JSON.parse(atob(pad)).sub || '');
} catch { return _log('dds-log-active', 'Invalid JWT format!', 'e'); }
if (!uid) return _log('dds-log-active', 'Could not extract UID from JWT.', 'e');
const btn = document.getElementById('dds-activate-btn');
_ripple(btn);
btn.disabled = true;
_setLoading('dds-activate-btn', 'dds-activate-ico', true);
_btnState('dds-activate-btn', C_GRAY, 'Activating...', null);
_log('dds-log-active', `UID: ${uid} · Code: ${code}`, 'i');
const r = await _req('/api/active', 'POST', { jwt, duo_uid: uid, invite_code: code });
btn.disabled = false;
_setLoading('dds-activate-btn', 'dds-activate-ico', false);
_btnState('dds-activate-btn', C_BLUE, 'Activate Super', 'dds-activate-ico');
if (r.ok) {
_log('dds-log-active',
r.already_member
? 'Account already has Super Duolingo.'
: 'Activated successfully! Open the app to verify.',
'o');
_notify('Super Activated 🦉', 'Free Super Duolingo activated!');
} else {
_log('dds-log-active', `Error: ${r.error || 'Failed'}`, 'e');
}
});
document.getElementById('dds-loadhist-btn').addEventListener('click', async () => {
if (!_sess) return;
const key = _get('dds_key', '');
if (!key) return;
const btn = document.getElementById('dds-loadhist-btn');
_ripple(btn);
btn.disabled = true;
_setLoading('dds-loadhist-btn', 'dds-loadhist-ico', true);
_btnState('dds-loadhist-btn', C_GRAY, 'Loading...', null);
const r = await _req(`/api/link/list?key=${encodeURIComponent(key)}`, 'GET');
btn.disabled = false;
_setLoading('dds-loadhist-btn', 'dds-loadhist-ico', false);
_btnState('dds-loadhist-btn', C_BLUE, 'Load history', 'dds-loadhist-ico');
if (r.ok && r.links) {
_hist = r.links;
_renderHist();
_notify('Loaded ✓', `${r.links.length} links in history.`, 3);
} else {
document.getElementById('dds-hist-log').innerHTML =
`<span class="le">Error: ${r.error || 'Unknown'}</span>`;
}
});
function _renderHist() {
const el = document.getElementById('dds-hist-log');
el.innerHTML = '';
if (!_hist.length) { el.innerHTML = '<span class="lw">No links yet.</span>'; return; }
_hist.forEach((url, i) => {
const code = url.split('/').pop();
el.innerHTML +=
`<span class="lo">#${String(i + 1).padStart(3, '0')} ${url}</span>` +
`<span class="dds-copy-tag" data-url="${url}" data-code="${code}">copy</span>\n`;
});
el.querySelectorAll('.dds-copy-tag').forEach(tag => {
tag.addEventListener('click', e => {
e.stopPropagation();
navigator.clipboard?.writeText(tag.dataset.url).catch(() => {});
tag.textContent = 'ok ✓';
setTimeout(() => { tag.textContent = 'copy'; }, 1200);
});
});
}
document.getElementById('dds-copyall-btn').addEventListener('click', () => {
if (!_hist.length) return;
navigator.clipboard?.writeText(_hist.join('\n')).catch(() => {});
const lbl = document.getElementById('dds-copyall-btn').querySelector('.dds-sm-lbl');
lbl.textContent = 'Copied!';
setTimeout(() => { lbl.textContent = 'Copy all'; }, 1500);
});
(function _boot() {
const saved = _get('dds_key', '');
const key = saved || FREE_KEY;
const shell = document.getElementById('dds-shell');
const box = document.getElementById('dds-box');
shell.style.bottom = `-${box.offsetHeight - 8}px`;
box.style.opacity = '0';
box.style.filter = 'blur(8px)';
_fetchKey(key).then(r => {
if (r.ok && _sess) {
if (!saved) _set('dds_key', FREE_KEY);
_applyKey(r, key);
_goPage('dash');
} else {
_sess = null;
if (key !== FREE_KEY) _set('dds_key', '');
document.getElementById('dds-key-in').value = saved || '';
}
});
_toggleVis(false);
setTimeout(() => {
shell.style.transition = box.style.transition = '.8s cubic-bezier(.16,1,.32,1)';
shell.style.bottom = '16px';
box.style.opacity = '';
box.style.filter = '';
setTimeout(() => { shell.style.transition = ''; box.style.transition = ''; }, 800);
}, 600);
})();
})();