Greasy Fork

Greasy Fork is available in English.

DuoHacker — 新安全模式 Duolingo 农场工具

具有新安全模式的最佳免费 Duolingo 农场工具!

当前为 2025-10-27 提交的版本,查看 最新版本

// ==UserScript==
// @name         DuoHacker
// @name:zh-CN   DuoHacker — 新安全模式 Duolingo 农场工具
// @name:ja      DuoHacker — 新しい安全モード Duolingo ファーミングツール
// @name:es      DuoHacker — Nueva Modo Seguro Herramienta para farmear en Duolingo
// @name:ru      DuoHacker — Новый безопасный режим для фарминга Duolingo
// @name:pt-BR   DuoHacker — Novo Modo Seguro Ferramenta para farmar no Duolingo
// @name:de      DuoHacker — Neuer Sicherer Modus Duolingo Farming-Tool
// @name:it      DuoHacker — Nuova Modalità Sicura Strumento di farming Duolingo
// @name:ko      DuoHacker — 새로운 안전 모드 Duolingo 팜 도구
// @name:hi      DuoHacker — नया सुरक्षित मोड Duolingo फार्मिंग टूल
// @name:ar      DuoHacker — الوضع الآمن الجديد أداة زراعة Duolingo
// @name:tr      DuoHacker — Yeni Güvenli Mod Duolingo Farming Aracı
// @name:pl      DuoHacker — Nowy Tryb Bezpieczny Narzędzie do farmienia Duolingo
// @description  Best free-to-use Duolingo farming tool with new safe mode!
// @description:zh-CN  具有新安全模式的最佳免费 Duolingo 农场工具!
// @description:ja     新しい安全モードを搭載した最高の無料 Duolingo ファーミングツール!
// @description:es     ¡La mejor herramienta gratuita para farmear en Duolingo con nuevo modo seguro!
// @description:ru     Лучший бесплатный инструмент для фарминга Duolingo с новым безопасным режимом!
// @description:pt-BR  A melhor ferramenta gratuita para farmar no Duolingo com novo modo seguro!
// @description:de     Bestes kostenloses Duolingo Farming-Tool mit neuem Sicherem Modus!
// @description:it     Migliore strumento di farming Duolingo gratuito con nuova modalità sicura!
// @description:ko     새로운 안전 모드가 탑재된 최고의 무료 Duolingo 팜 도구!
// @description:hi     नए सुरक्षित मोड के साथ सर्वश्रेष्ठ मुफ्त Duolingo फार्मिंग टूल!
// @description:ar     أفضل أداة زراعة Duolingo مجانية مع الوضع الآمن الجديد!
// @description:tr     Yeni güvenli modlu en iyi ücretsiz Duolingo farming aracı!
// @description:pl     Najlepsze darmowe narzędzie do farmienia Duolingo z nowym trybem bezpiecznym!
// @namespace    https://irylisvps.vercel.app
// @version      2.0.0
// @author       DuoHacker Community
// @match        https://*.duolingo.com/*
// @icon         https://github.com/pillowslua/images/blob/main/logoo.png?raw=true
// @grant        none
// @license      MIT
// ==/UserScript==

const VERSION = "2.0.0";
const SAFE_DELAY = 2000;
const FAST_DELAY = 300;

var jwt, defaultHeaders, userInfo, sub;
let isRunning = false;
let currentMode = 'safe';
let currentTheme = localStorage.getItem('duofarmer_theme') || 'dark';
let hasJoined = localStorage.getItem('duofarmer_joined') === 'true';
let totalEarned = { xp: 0, gems: 0, streak: 0 };
let farmingStats = { sessions: 0, errors: 0, startTime: null };
let farmingInterval = null;

const initInterface = () => {
  const containerHTML = `
  <div id="_backdrop"></div>
  <div id="_container" class="theme-${currentTheme}">
    <div id="_header">
      <div class="_header_top">
        <div class="_brand">
          <div class="_logo_container">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120" class="_logo">
              <defs>
                <linearGradient id="logoGrad" x1="0%" y1="0%" x2="100%" y2="100%">
                  <stop offset="0%" style="stop-color:#00D4FF;stop-opacity:1" />
                  <stop offset="50%" style="stop-color:#7B2FF7;stop-opacity:1" />
                  <stop offset="100%" style="stop-color:#FF107F;stop-opacity:1" />
                </linearGradient>
                <filter id="logoShadow">
                  <feDropShadow dx="0" dy="4" stdDeviation="8" flood-opacity="0.3"/>
                </filter>
              </defs>
              <circle cx="60" cy="60" r="50" fill="url(#logoGrad)" filter="url(#logoShadow)"/>
              <path d="M60 30 Q75 45 60 70 Q45 45 60 30" fill="white" opacity="0.9"/>
              <circle cx="45" cy="50" r="4" fill="white"/>
              <circle cx="75" cy="50" r="4" fill="white"/>
              <path d="M45 75 Q60 85 75 75" stroke="white" stroke-width="3" fill="none" stroke-linecap="round"/>
            </svg>
          </div>
          <div class="_brand_text">
            <h1>DuoHacker</h1>
            <span class="_version_badge">v2.0</span>
          </div>
        </div>
        <div class="_header_controls">
          <button id="_settings_btn" class="_control_btn _settings">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <circle cx="12" cy="12" r="3"/>
              <path d="M12 1v6m0 6v6m4.22-13.22l4.24 4.24M1.54 1.54l4.24 4.24M20.46 20.46l-4.24-4.24M1.54 20.46l4.24-4.24"/>
            </svg>
          </button>
          <button id="_theme_toggle" class="_control_btn">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <circle cx="12" cy="12" r="5"/>
              <line x1="12" y1="1" x2="12" y2="3"/>
              <line x1="12" y1="21" x2="12" y2="23"/>
              <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>
              <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
              <line x1="1" y1="12" x2="3" y2="12"/>
              <line x1="21" y1="12" x2="23" y2="12"/>
              <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/>
              <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
            </svg>
          </button>
          <button id="_minimize_btn" class="_control_btn">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <line x1="5" y1="12" x2="19" y2="12"/>
            </svg>
          </button>
          <button id="_close_btn" class="_control_btn _close">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <line x1="18" y1="6" x2="6" y2="18"/>
              <line x1="6" y1="6" x2="18" y2="18"/>
            </svg>
          </button>
        </div>
      </div>
    </div>

    <div id="_main_content" style="display:none">

      <div class="_profile_card">
        <div class="_profile_header">
          <div class="_avatar">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
              <circle cx="12" cy="7" r="4"/>
            </svg>
          </div>
          <div class="_profile_info">
            <h2 id="_username">Loading...</h2>
            <p id="_user_details">Fetching data...</p>
          </div>
          <button id="_refresh_profile" class="_refresh_btn">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <polyline points="23 4 23 10 17 10"/>
              <path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
            </svg>
          </button>
        </div>
        <div class="_stats_row">
          <div class="_stat_item">
            <div class="_stat_icon">⚡</div>
            <div class="_stat_info">
              <span class="_stat_value" id="_current_xp">0</span>
              <span class="_stat_label">Total XP</span>
            </div>
          </div>
          <div class="_stat_item">
            <div class="_stat_icon">🔥</div>
            <div class="_stat_info">
              <span class="_stat_value" id="_current_streak">0</span>
              <span class="_stat_label">Streak</span>
            </div>
          </div>
          <div class="_stat_item">
            <div class="_stat_icon">💎</div>
            <div class="_stat_info">
              <span class="_stat_value" id="_current_gems">0</span>
              <span class="_stat_label">Gems</span>
            </div>
          </div>
        </div>
      </div>

      <div class="_mode_section">
        <h3>Select Farming Mode</h3>
        <div class="_mode_cards">
          <div class="_mode_card ${currentMode === 'safe' ? '_active' : ''}" data-mode="safe">
            <div class="_mode_icon">🛡️</div>
            <h4>Safe Mode</h4>
            <p>Slow but undetectable farming</p>
            <div class="_mode_specs">
              <span class="_spec">2s delay</span>
              <span class="_spec">100% safe</span>
            </div>
          </div>
          <div class="_mode_card ${currentMode === 'fast' ? '_active' : ''}" data-mode="fast">
            <div class="_mode_icon">⚡</div>
            <h4>Fast Mode</h4>
            <p>Quick farming with moderate risk</p>
            <div class="_mode_specs">
              <span class="_spec">0.3s delay</span>
              <span class="_spec">Use carefully</span>
            </div>
          </div>
        </div>
      </div>

      <div class="_options_section">
        <h3>Farming Options</h3>
        <div class="_option_grid">
          <button class="_option_btn" data-type="xp">
            <div class="_option_icon">⚡</div>
            <span>Farm XP</span>
          </button>
          <button class="_option_btn" data-type="gems">
            <div class="_option_icon">💎</div>
            <span>Farm Gems</span>
          </button>
          <button class="_option_btn" data-type="streak_repair">
            <div class="_option_icon">🔧</div>
            <span>Repair Streak</span>
          </button>
          <button class="_option_btn" data-type="streak_farm">
            <div class="_option_icon">🔥</div>
            <span>Farm Streak</span>
          </button>
        </div>
      </div>

      <div class="_control_panel">
        <button id="_start_farming" class="_start_btn">
          <span class="_btn_text">Start Farming</span>
          <div class="_btn_glow"></div>
        </button>
        <button id="_stop_farming" class="_stop_btn" style="display:none">
          <span class="_btn_text">Stop Farming</span>
        </button>
      </div>

      <div class="_live_stats">
        <h3>Live Statistics</h3>
        <div class="_stats_grid">
          <div class="_live_stat">
            <div class="_live_icon">⚡</div>
            <div class="_live_data">
              <span id="_earned_xp">0</span>
              <small>XP Earned</small>
            </div>
          </div>
          <div class="_live_stat">
            <div class="_live_icon">💎</div>
            <div class="_live_data">
              <span id="_earned_gems">0</span>
              <small>Gems Earned</small>
            </div>
          </div>
          <div class="_live_stat">
            <div class="_live_icon">🔥</div>
            <div class="_live_data">
              <span id="_earned_streak">0</span>
              <small>Streak Gained</small>
            </div>
          </div>
          <div class="_live_stat">
            <div class="_live_icon">⏱️</div>
            <div class="_live_data">
              <span id="_farming_time">00:00</span>
              <small>Time Elapsed</small>
            </div>
          </div>
        </div>
      </div>

      <div class="_console_section">
        <div class="_console_header">
          <h3>Activity Log</h3>
          <button id="_clear_console" class="_clear_btn">Clear</button>
        </div>
        <div id="_console_output" class="_console">
          <div class="_log_entry _info">
            <span class="_log_time">${new Date().toLocaleTimeString()}</span>
            <span class="_log_msg">DuoHacker v2.0 initialized</span>
          </div>
        </div>
      </div>

    </div>

    <div id="_join_section" class="_join_section">
      <div class="_join_content">
        <div class="_join_icon">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2.5-1-3.5a2.5 2.5 0 0 0-1.5 3.5z"/>
            <path d="M15.5 14.5A2.5 2.5 0 0 1 13 12c0-1.38.5-2.5 1-3.5a2.5 2.5 0 0 1 1.5 3.5z"/>
            <path d="M9 16c-1.5 1.5-2 3.5-2 5.5h10c0-2-.5-4-2-5.5"/>
            <path d="M9 16c0-1 1-2 1-3h4c0 1 1 2 1 3"/>
          </svg>
        </div>
        <h2>Join Our Community</h2>
        <p>Get access to updates, support, and exclusive features</p>
        <button id="_join_btn" class="_join_btn">
          <span>Join Discord Server</span>
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <path d="M18 8L22 12L18 16"/>
            <path d="M2 12H22"/>
          </svg>
        </button>
      </div>
    </div>

    <div class="_footer">
      <span>© 2025 DuoHacker by tw1sk</span>
      <div class="_footer_links">
        <button id="_website_btn" class="_footer_link">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <circle cx="12" cy="12" r="10"/>
            <path d="M2 12h20"/>
            <path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>
          </svg>
          Website
        </button>
        <button id="_discord_btn" class="_footer_link">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2.5-1-3.5a2.5 2.5 0 0 0-1.5 3.5z"/>
            <path d="M15.5 14.5A2.5 2.5 0 0 1 13 12c0-1.38.5-2.5 1-3.5a2.5 2.5 0 0 1 1.5 3.5z"/>
            <path d="M9 16c-1.5 1.5-2 3.5-2 5.5h10c0-2-.5-4-2-5.5"/>
            <path d="M9 16c0-1 1-2 1-3h4c0 1 1 2 1 3"/>
          </svg>
          Discord
        </button>
      </div>
      <span class="_footer_version">v2.0.0</span>
    </div>
  </div>

  <div id="_settings_modal" class="_modal" style="display:none">
    <div class="_modal_overlay"></div>
    <div class="_modal_container">
      <div class="_modal_header">
        <h2>Settings</h2>
        <button id="_close_settings" class="_close_modal_btn">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <line x1="18" y1="6" x2="6" y2="18"/>
            <line x1="6" y1="6" x2="18" y2="18"/>
          </svg>
        </button>
      </div>
      <div class="_modal_content">
        <div class="_settings_section">
          <h3>Account Management</h3>
          <div class="_setting_item">
            <button id="_get_jwt_btn" class="_setting_btn _primary">
              <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                <rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>
                <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
              </svg>
              Get JWT Token
            </button>
          </div>
          <div class="_setting_item">
            <button id="_logout_btn" class="_setting_btn _danger">
              <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/>
                <polyline points="16 17 21 12 16 7"/>
                <line x1="21" y1="12" x2="9" y2="12"/>
              </svg>
              Log Out
            </button>
          </div>
          <div class="_setting_item">
            <div class="_jwt_input_group">
              <input type="text" id="_jwt_input" placeholder="Enter JWT Token">
              <button id="_login_jwt_btn" class="_setting_btn _success">
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                  <path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"/>
                  <polyline points="10 17 15 12 10 7"/>
                  <line x1="15" y1="12" x2="3" y2="12"/>
                </svg>
                Login
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>

  <div id="_fab">
    <div class="_fab_ring"></div>
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
      <circle cx="12" cy="12" r="10"/>
      <path d="M12 6v6l4 2"/>
    </svg>
  </div>
`;

  const style = document.createElement("style");
  style.innerHTML = `
    :root {
      --primary-gradient: linear-gradient(135deg, #00D4FF 0%, #7B2FF7 50%, #FF107F 100%);
      --success-gradient: linear-gradient(135deg, #00F260 0%, #0575E6 100%);
      --danger-gradient: linear-gradient(135deg, #FF512F 0%, #DD2476 100%);
      --glass-bg: rgba(255, 255, 255, 0.1);
      --glass-border: rgba(255, 255, 255, 0.2);
      --shadow-glow: 0 0 20px rgba(123, 47, 247, 0.3);
      --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    }

    .theme-dark {
      --bg-primary: #0a0a0f;
      --bg-secondary: #151520;
      --bg-card: rgba(30, 30, 45, 0.8);
      --text-primary: #ffffff;
      --text-secondary: #a0a0b0;
      --text-muted: #707080;
      --border-color: rgba(255, 255, 255, 0.1);
      --accent-color: #7B2FF7;
      --success-color: #00F260;
      --error-color: #FF512F;
    }

    .theme-light {
      --bg-primary: #f8f9fa;
      --bg-secondary: #ffffff;
      --bg-card: rgba(255, 255, 255, 0.9);
      --text-primary: #212529;
      --text-secondary: #6c757d;
      --text-muted: #adb5bd;
      --border-color: rgba(0, 0, 0, 0.1);
      --accent-color: #7B2FF7;
      --success-color: #00F260;
      --error-color: #FF512F;
    }

    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
    }

    #_container {
      position: fixed;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      width: min(90vw, 900px);
      max-height: 90vh;
      background: var(--bg-primary);
      border-radius: 24px;
      box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
      border: 1px solid var(--border-color);
      overflow: hidden;
      z-index: 10000;
      display: flex;
      flex-direction: column;
      animation: containerAppear 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
    }

    @keyframes containerAppear {
      0% {
        opacity: 0;
        transform: translate(-50%, -50%) scale(0.8) rotateX(10deg);
      }
      100% {
        opacity: 1;
        transform: translate(-50%, -50%) scale(1) rotateX(0);
      }
    }

    #_backdrop {
      position: fixed;
      top: 0;
      left: 0;
      width: 100vw;
      height: 100vh;
      background: rgba(0, 0, 0, 0.7);
      backdrop-filter: blur(10px);
      z-index: 9999;
      animation: fadeIn 0.3s ease-out;
    }

    @keyframes fadeIn {
      from { opacity: 0; }
      to { opacity: 1; }
    }

    #_header {
      background: var(--bg-secondary);
      padding: 20px 24px;
      border-bottom: 1px solid var(--border-color);
    }

    ._header_top {
      display: flex;
      justify-content: space-between;
      align-items: center;
    }

    ._brand {
      display: flex;
      align-items: center;
      gap: 16px;
    }

    ._logo_container {
      width: 48px;
      height: 48px;
      animation: logoFloat 3s ease-in-out infinite;
    }

    @keyframes logoFloat {
      0%, 100% { transform: translateY(0); }
      50% { transform: translateY(-5px); }
    }

    ._logo {
      width: 100%;
      height: 100%;
    }

    ._brand_text {
      display: flex;
      align-items: center;
      gap: 12px;
    }

    ._brand_text h1 {
      font-size: 24px;
      font-weight: 700;
      background: var(--primary-gradient);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
      background-clip: text;
    }

    ._version_badge {
      background: var(--success-gradient);
      color: white;
      padding: 4px 12px;
      border-radius: 12px;
      font-size: 12px;
      font-weight: 600;
    }

    ._header_controls {
      display: flex;
      gap: 8px;
    }

    ._control_btn {
      width: 40px;
      height: 40px;
      border: none;
      background: var(--glass-bg);
      border: 1px solid var(--glass-border);
      border-radius: 12px;
      color: var(--text-secondary);
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: var(--transition);
      backdrop-filter: blur(10px);
    }

    ._control_btn:hover {
      background: var(--accent-color);
      color: white;
      transform: translateY(-2px);
      box-shadow: var(--shadow-glow);
    }

    ._control_btn._close:hover {
      background: var(--error-color);
    }

    ._control_btn._settings {
      background: var(--accent-color);
      color: white;
      box-shadow: var(--shadow-glow);
    }

    ._control_btn._settings:hover {
      background: var(--success-color);
      transform: translateY(-2px) scale(1.1);
    }

    #_main_content {
      flex: 1;
      overflow-y: auto;
      padding: 24px;
      display: flex;
      flex-direction: column;
      gap: 24px;
    }

    ._profile_card {
      background: var(--bg-card);
      border: 1px solid var(--border-color);
      border-radius: 20px;
      padding: 24px;
      backdrop-filter: blur(10px);
      transition: var(--transition);
    }

    ._profile_card:hover {
      transform: translateY(-2px);
      box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
    }

    ._profile_header {
      display: flex;
      align-items: center;
      gap: 16px;
      margin-bottom: 20px;
    }

    ._avatar {
      width: 60px;
      height: 60px;
      background: var(--primary-gradient);
      border-radius: 16px;
      display: flex;
      align-items: center;
      justify-content: center;
      color: white;
    }

    ._avatar svg {
      width: 32px;
      height: 32px;
    }

    ._profile_info h2 {
      font-size: 20px;
      font-weight: 600;
      color: var(--text-primary);
      margin-bottom: 4px;
    }

    ._profile_info p {
      color: var(--text-secondary);
      font-size: 14px;
    }

    ._refresh_btn {
      margin-left: auto;
      width: 36px;
      height: 36px;
      border: none;
      background: var(--glass-bg);
      border: 1px solid var(--border-color);
      border-radius: 10px;
      color: var(--text-secondary);
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: var(--transition);
    }

    ._refresh_btn:hover {
      background: var(--accent-color);
      color: white;
      transform: rotate(180deg);
    }

    ._stats_row {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: 16px;
    }

    ._stat_item {
      display: flex;
      align-items: center;
      gap: 12px;
      padding: 16px;
      background: var(--glass-bg);
      border: 1px solid var(--border-color);
      border-radius: 12px;
      backdrop-filter: blur(10px);
    }

    ._stat_icon {
      font-size: 24px;
    }

    ._stat_info {
      display: flex;
      flex-direction: column;
    }

    ._stat_value {
      font-size: 18px;
      font-weight: 600;
      color: var(--text-primary);
    }

    ._stat_label {
      font-size: 12px;
      color: var(--text-secondary);
    }

    ._mode_section h3 {
      font-size: 18px;
      font-weight: 600;
      color: var(--text-primary);
      margin-bottom: 16px;
    }

    ._mode_cards {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 16px;
    }

    ._mode_card {
      background: var(--bg-card);
      border: 2px solid var(--border-color);
      border-radius: 16px;
      padding: 20px;
      cursor: pointer;
      transition: var(--transition);
      text-align: center;
      backdrop-filter: blur(10px);
    }

    ._mode_card:hover {
      transform: translateY(-4px);
      box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2);
    }

    ._mode_card._active {
      border-color: var(--accent-color);
      background: linear-gradient(135deg, rgba(123, 47, 247, 0.1), rgba(255, 16, 127, 0.1));
      box-shadow: var(--shadow-glow);
    }

    ._mode_icon {
      font-size: 48px;
      margin-bottom: 12px;
    }

    ._mode_card h4 {
      font-size: 18px;
      font-weight: 600;
      color: var(--text-primary);
      margin-bottom: 8px;
    }

    ._mode_card p {
      color: var(--text-secondary);
      font-size: 14px;
      margin-bottom: 12px;
    }

    ._mode_specs {
      display: flex;
      justify-content: center;
      gap: 8px;
    }

    ._spec {
      background: var(--glass-bg);
      padding: 4px 8px;
      border-radius: 6px;
      font-size: 12px;
      color: var(--text-muted);
    }

    ._options_section h3 {
      font-size: 18px;
      font-weight: 600;
      color: var(--text-primary);
      margin-bottom: 16px;
    }

    ._option_grid {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      gap: 12px;
    }

    ._option_btn {
      background: var(--bg-card);
      border: 1px solid var(--border-color);
      border-radius: 12px;
      padding: 16px;
      cursor: pointer;
      transition: var(--transition);
      display: flex;
      align-items: center;
      gap: 12px;
      backdrop-filter: blur(10px);
    }

    ._option_btn:hover {
      transform: translateY(-2px);
      box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
      border-color: var(--accent-color);
    }

    ._option_btn._selected {
      background: var(--accent-color);
      color: white;
    }

    ._option_icon {
      font-size: 24px;
    }

    ._option_btn span {
      font-weight: 500;
    }

    ._control_panel {
      display: flex;
      justify-content: center;
      gap: 16px;
    }

    ._start_btn, ._stop_btn {
      position: relative;
      padding: 16px 48px;
      border: none;
      border-radius: 16px;
      font-size: 18px;
      font-weight: 600;
      cursor: pointer;
      transition: var(--transition);
      overflow: hidden;
    }

    ._start_btn {
      background: var(--success-gradient);
      color: white;
    }

    ._stop_btn {
      background: var(--danger-gradient);
      color: white;
    }

    ._start_btn:hover {
      transform: translateY(-2px);
      box-shadow: 0 15px 35px rgba(0, 242, 96, 0.3);
    }

    ._stop_btn:hover {
      transform: translateY(-2px);
      box-shadow: 0 15px 35px rgba(255, 81, 47, 0.3);
    }

    ._btn_glow {
      position: absolute;
      top: 0;
      left: -100%;
      width: 100%;
      height: 100%;
      background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
      transition: left 0.5s;
    }

    ._start_btn:hover ._btn_glow {
      left: 100%;
    }

    ._live_stats h3 {
      font-size: 18px;
      font-weight: 600;
      color: var(--text-primary);
      margin-bottom: 16px;
    }

    ._stats_grid {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      gap: 12px;
    }

    ._live_stat {
      background: var(--bg-card);
      border: 1px solid var(--border-color);
      border-radius: 12px;
      padding: 16px;
      display: flex;
      align-items: center;
      gap: 12px;
      backdrop-filter: blur(10px);
    }

    ._live_icon {
      font-size: 24px;
    }

    ._live_data {
      display: flex;
      flex-direction: column;
    }

    ._live_data span {
      font-size: 20px;
      font-weight: 600;
      color: var(--text-primary);
    }

    ._live_data small {
      font-size: 12px;
      color: var(--text-secondary);
    }

    ._console_section {
      background: var(--bg-card);
      border: 1px solid var(--border-color);
      border-radius: 16px;
      backdrop-filter: blur(10px);
    }

    ._console_header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 16px 20px;
      border-bottom: 1px solid var(--border-color);
    }

    ._console_header h3 {
      font-size: 16px;
      font-weight: 600;
      color: var(--text-primary);
    }

    ._clear_btn {
      background: var(--glass-bg);
      border: 1px solid var(--border-color);
      border-radius: 8px;
      padding: 6px 12px;
      color: var(--text-secondary);
      font-size: 12px;
      cursor: pointer;
      transition: var(--transition);
    }

    ._clear_btn:hover {
      background: var(--error-color);
      color: white;
    }

    ._console {
      height: 150px;
      overflow-y: auto;
      padding: 16px 20px;
      font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
      font-size: 13px;
    }

    ._log_entry {
      display: flex;
      gap: 12px;
      margin-bottom: 8px;
      animation: slideIn 0.3s ease-out;
    }

    @keyframes slideIn {
      from {
        opacity: 0;
        transform: translateX(-20px);
      }
      to {
        opacity: 1;
        transform: translateX(0);
      }
    }

    ._log_time {
      color: var(--text-muted);
      flex-shrink: 0;
    }

    ._log_msg {
      color: var(--text-secondary);
    }

    ._log_entry._success ._log_msg {
      color: var(--success-color);
    }

    ._log_entry._error ._log_msg {
      color: var(--error-color);
    }

    ._log_entry._info ._log_msg {
      color: var(--accent-color);
    }

    ._join_section {
      flex: 1;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 40px;
    }

    ._join_content {
      text-align: center;
      max-width: 400px;
    }

    ._join_icon {
      width: 80px;
      height: 80px;
      background: var(--primary-gradient);
      border-radius: 20px;
      display: flex;
      align-items: center;
      justify-content: center;
      margin: 0 auto 24px;
      color: white;
      animation: pulse 2s infinite;
    }

    @keyframes pulse {
      0%, 100% { transform: scale(1); }
      50% { transform: scale(1.05); }
    }

    ._join_icon svg {
      width: 40px;
      height: 40px;
    }

    ._join_content h2 {
      font-size: 24px;
      font-weight: 600;
      color: var(--text-primary);
      margin-bottom: 12px;
    }

    ._join_content p {
      color: var(--text-secondary);
      margin-bottom: 24px;
    }

    ._join_btn {
      background: var(--primary-gradient);
      color: white;
      border: none;
      padding: 14px 28px;
      border-radius: 12px;
      font-size: 16px;
      font-weight: 600;
      cursor: pointer;
      display: inline-flex;
      align-items: center;
      gap: 8px;
      transition: var(--transition);
    }

    ._join_btn:hover {
      transform: translateY(-2px);
      box-shadow: 0 15px 35px rgba(123, 47, 247, 0.3);
    }

    ._footer {
      padding: 16px 24px;
      background: var(--bg-secondary);
      border-top: 1px solid var(--border-color);
      display: flex;
      justify-content: space-between;
      align-items: center;
      font-size: 12px;
      color: var(--text-muted);
    }

    ._footer_links {
      display: flex;
      gap: 12px;
    }

    ._footer_link {
      display: flex;
      align-items: center;
      gap: 6px;
      background: var(--glass-bg);
      border: 1px solid var(--border-color);
      border-radius: 8px;
      padding: 6px 12px;
      color: var(--text-secondary);
      font-size: 12px;
      cursor: pointer;
      transition: var(--transition);
    }

    ._footer_link:hover {
      background: var(--accent-color);
      color: white;
    }

    ._footer_link svg {
      width: 14px;
      height: 14px;
    }

    ._footer_version {
      background: var(--glass-bg);
      padding: 4px 8px;
      border-radius: 6px;
    }

    #_fab {
      position: fixed;
      bottom: 24px;
      right: 24px;
      width: 56px;
      height: 56px;
      background: var(--primary-gradient);
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      color: white;
      cursor: pointer;
      box-shadow: 0 10px 25px rgba(123, 47, 247, 0.3);
      transition: var(--transition);
      z-index: 9998;
    }

    #_fab:hover {
      transform: scale(1.1);
      box-shadow: 0 15px 35px rgba(123, 47, 247, 0.5);
    }

    ._fab_ring {
      position: absolute;
      width: 100%;
      height: 100%;
      border: 2px solid var(--accent-color);
      border-radius: 50%;
      animation: ringPulse 2s infinite;
    }

    @keyframes ringPulse {
      0% {
        transform: scale(1);
        opacity: 1;
      }
      100% {
        transform: scale(1.5);
        opacity: 0;
      }
    }

    #_fab svg {
      width: 24px;
      height: 24px;
      z-index: 1;
    }

    ._modal {
      position: fixed;
      top: 0;
      left: 0;
      width: 100vw;
      height: 100vh;
      z-index: 10001;
      display: flex;
      align-items: center;
      justify-content: center;
    }

    ._modal_overlay {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: rgba(0, 0, 0, 0.8);
      backdrop-filter: blur(10px);
    }

    ._modal_container {
      position: relative;
      width: 90%;
      max-width: 500px;
      background: var(--bg-primary);
      border: 2px solid var(--accent-color);
      border-radius: 20px;
      box-shadow: 0 25px 50px rgba(123, 47, 247, 0.4);
      overflow: hidden;
      animation: modalSlideIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
    }

    @keyframes modalSlideIn {
      from {
        opacity: 0;
        transform: scale(0.8) translateY(30px);
      }
      to {
        opacity: 1;
        transform: scale(1) translateY(0);
      }
    }

    ._modal_header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 24px;
      background: var(--bg-secondary);
      border-bottom: 2px solid var(--accent-color);
    }

    ._modal_header h2 {
      font-size: 20px;
      font-weight: 700;
      color: var(--text-primary);
      background: var(--primary-gradient);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
      background-clip: text;
    }

    ._close_modal_btn {
      width: 36px;
      height: 36px;
      border: none;
      background: var(--error-color);
      color: white;
      border-radius: 10px;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: var(--transition);
    }

    ._close_modal_btn:hover {
      background: var(--accent-color);
      transform: scale(1.1);
    }

    ._modal_content {
      padding: 24px;
    }

    ._settings_section {
      margin-bottom: 24px;
    }

    ._settings_section:last-child {
      margin-bottom: 0;
    }

    ._settings_section h3 {
      font-size: 18px;
      font-weight: 600;
      color: var(--text-primary);
      margin-bottom: 20px;
      display: flex;
      align-items: center;
      gap: 8px;
    }

    ._setting_item {
      margin-bottom: 16px;
    }

    ._setting_item:last-child {
      margin-bottom: 0;
    }

    ._setting_btn {
      width: 100%;
      display: flex;
      align-items: center;
      gap: 12px;
      padding: 16px 20px;
      background: var(--bg-card);
      border: 2px solid var(--border-color);
      border-radius: 16px;
      color: var(--text-primary);
      font-size: 16px;
      font-weight: 600;
      cursor: pointer;
      transition: var(--transition);
      backdrop-filter: blur(10px);
    }

    ._setting_btn:hover {
      transform: translateY(-3px);
      box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
    }

    ._setting_btn._primary {
      background: var(--primary-gradient);
      color: white;
      border-color: var(--accent-color);
    }

    ._setting_btn._primary:hover {
      box-shadow: 0 15px 35px rgba(123, 47, 247, 0.4);
    }

    ._setting_btn._success {
      background: var(--success-gradient);
      color: white;
      border-color: var(--success-color);
    }

    ._setting_btn._success:hover {
      box-shadow: 0 15px 35px rgba(0, 242, 96, 0.4);
    }

    ._setting_btn._danger {
      background: var(--danger-gradient);
      color: white;
      border-color: var(--error-color);
    }

    ._setting_btn._danger:hover {
      box-shadow: 0 15px 35px rgba(255, 81, 47, 0.4);
    }

    ._setting_btn svg {
      width: 20px;
      height: 20px;
    }

    ._jwt_input_group {
      display: flex;
      gap: 12px;
    }

    #_jwt_input {
      flex: 1;
      padding: 16px 20px;
      background: var(--bg-card);
      border: 2px solid var(--border-color);
      border-radius: 16px;
      color: var(--text-primary);
      font-size: 14px;
      font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
      transition: var(--transition);
    }

    #_jwt_input:focus {
      outline: none;
      border-color: var(--accent-color);
      box-shadow: 0 0 0 4px rgba(123, 47, 247, 0.2);
    }

    ::-webkit-scrollbar {
      width: 8px;
    }

    ::-webkit-scrollbar-track {
      background: var(--bg-secondary);
      border-radius: 4px;
    }

    ::-webkit-scrollbar-thumb {
      background: var(--accent-color);
      border-radius: 4px;
    }

    ::-webkit-scrollbar-thumb:hover {
      background: var(--text-secondary);
    }

    @media (max-width: 768px) {
      #_container {
        width: 95vw;
        max-height: 95vh;
      }

      ._stats_row, ._mode_cards, ._option_grid, ._stats_grid {
        grid-template-columns: 1fr;
      }

      ._control_panel {
        flex-direction: column;
      }

      ._start_btn, ._stop_btn {
        width: 100%;
      }

      ._footer {
        flex-direction: column;
        gap: 12px;
      }

      ._footer_links {
        width: 100%;
        justify-content: center;
      }

      ._jwt_input_group {
        flex-direction: column;
      }
    }
  `;

  document.head.appendChild(style);

  const container = document.createElement("div");
  container.innerHTML = containerHTML;
  document.body.appendChild(container);
};

const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));

const logToConsole = (message, type = 'info') => {
  const console = document.getElementById('_console_output');
  if (!console) return;

  const timestamp = new Date().toLocaleTimeString();
  const entry = document.createElement('div');
  entry.className = `_log_entry _${type}`;
  entry.innerHTML = `
    <span class="_log_time">${timestamp}</span>
    <span class="_log_msg">${message}</span>
  `;

  console.appendChild(entry);
  console.scrollTop = console.scrollHeight;

  while (console.children.length > 50) {
    console.removeChild(console.firstChild);
  }
};

const updateEarnedStats = () => {
  const elements = {
    xp: document.getElementById('_earned_xp'),
    gems: document.getElementById('_earned_gems'),
    streak: document.getElementById('_earned_streak')
  };

  if (elements.xp) elements.xp.textContent = totalEarned.xp.toLocaleString();
  if (elements.gems) elements.gems.textContent = totalEarned.gems.toLocaleString();
  if (elements.streak) elements.streak.textContent = totalEarned.streak;
};

const updateFarmingTime = () => {
  if (!farmingStats.startTime) return;

  const elapsed = Date.now() - farmingStats.startTime;
  const minutes = Math.floor(elapsed / 60000);
  const seconds = Math.floor((elapsed % 60000) / 1000);

  const timeElement = document.getElementById('_farming_time');
  if (timeElement) {
    timeElement.textContent = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
  }
};

const setInterfaceVisible = (visible) => {
  const container = document.getElementById("_container");
  const backdrop = document.getElementById("_backdrop");

  if (container && backdrop) {
    container.style.display = visible ? "flex" : "none";
    backdrop.style.display = visible ? "block" : "none";
  }
};

const isInterfaceVisible = () => {
  const container = document.getElementById("_container");
  return container && container.style.display !== "none";
};

const toggleInterface = () => {
  setInterfaceVisible(!isInterfaceVisible());
};

const applyTheme = (theme) => {
  currentTheme = theme;
  localStorage.setItem('duofarmer_theme', theme);

  const container = document.getElementById("_container");
  if (container) {
    container.className = container.className.replace(/theme-\w+/, `theme-${theme}`);
  }
};

const addEventListeners = () => {
  document.getElementById('_fab')?.addEventListener('click', toggleInterface);

  document.getElementById('_minimize_btn')?.addEventListener('click', () => {
    setInterfaceVisible(false);
  });

  document.getElementById('_close_btn')?.addEventListener('click', () => {
    if (isRunning) {
      if (confirm('Farming is active. Are you sure you want to close?')) {
        stopFarming();
        setInterfaceVisible(false);
      }
    } else {
      setInterfaceVisible(false);
    }
  });

  document.getElementById('_theme_toggle')?.addEventListener('click', () => {
    applyTheme(currentTheme === 'dark' ? 'light' : 'dark');
  });

  document.getElementById('_settings_btn')?.addEventListener('click', () => {
    document.getElementById('_settings_modal').style.display = 'flex';
  });

  document.getElementById('_close_settings')?.addEventListener('click', () => {
    document.getElementById('_settings_modal').style.display = 'none';
  });

  document.getElementById('_settings_modal')?.addEventListener('click', (e) => {
    if (e.target.classList.contains('_modal_overlay')) {
      document.getElementById('_settings_modal').style.display = 'none';
    }
  });

  document.getElementById('_get_jwt_btn')?.addEventListener('click', () => {
    const token = getJwtToken();
    if (token) {
      navigator.clipboard.writeText(token);
      logToConsole('JWT Token copied to clipboard', 'success');
      alert('JWT Token copied to clipboard!\n\nToken: ' + token.substring(0, 50) + '...');
    } else {
      logToConsole('JWT Token not found', 'error');
      alert('JWT Token not found! Please make sure you are logged in to Duolingo.');
    }
  });

  document.getElementById('_logout_btn')?.addEventListener('click', () => {
    if (confirm('Are you sure you want to log out?')) {
      window.location.href = 'https://www.duolingo.com/logout';
    }
  });

  document.getElementById('_login_jwt_btn')?.addEventListener('click', () => {
    const jwtInput = document.getElementById('_jwt_input');
    const token = jwtInput.value.trim();

    if (token) {
      document.cookie = `jwt_token=${token}; path=/; domain=.duolingo.com`;
      logToConsole('JWT Token updated, refreshing page...', 'success');
      setTimeout(() => {
        window.location.reload();
      }, 1000);
    } else {
      logToConsole('Please enter a valid JWT Token', 'error');
      alert('Please enter a valid JWT Token');
    }
  });

  document.getElementById('_website_btn')?.addEventListener('click', () => {
    window.open('https://irylisvps.vercel.app/', '_blank');
  });

  document.getElementById('_discord_btn')?.addEventListener('click', () => {
    window.open('https://discord.gg/Gvmd7deFtS', '_blank');
  });

  document.getElementById('_join_btn')?.addEventListener('click', () => {
    window.open('https://discord.gg/Gvmd7deFtS', '_blank');
    localStorage.setItem('duofarmer_joined', 'true');
    hasJoined = true;
    document.getElementById('_join_section').style.display = 'none';
    document.getElementById('_main_content').style.display = 'flex';
    initializeFarming();
  });

  document.querySelectorAll('._mode_card').forEach(card => {
    card.addEventListener('click', () => {
      document.querySelectorAll('._mode_card').forEach(c => c.classList.remove('_active'));
      card.classList.add('_active');
      currentMode = card.dataset.mode;
      logToConsole(`Switched to ${currentMode} mode`, 'info');
    });
  });

  document.querySelectorAll('._option_btn').forEach(btn => {
    btn.addEventListener('click', () => {
      document.querySelectorAll('._option_btn').forEach(b => b.classList.remove('_selected'));
      btn.classList.add('_selected');
    });
  });

  document.getElementById('_start_farming')?.addEventListener('click', startFarming);
  document.getElementById('_stop_farming')?.addEventListener('click', stopFarming);

  document.getElementById('_refresh_profile')?.addEventListener('click', async () => {
    const btn = document.getElementById('_refresh_profile');
    btn.style.animation = 'spin 1s linear';
    await refreshUserData();
    btn.style.animation = '';
  });

  document.getElementById('_clear_console')?.addEventListener('click', () => {
    const console = document.getElementById('_console_output');
    if (console) {
      console.innerHTML = '';
      logToConsole('Console cleared', 'info');
    }
  });
};

const startFarming = async () => {
  if (isRunning) return;

  const selectedOption = document.querySelector('._option_btn._selected');
  if (!selectedOption) {
    logToConsole('Please select a farming option', 'error');
    return;
  }

  const type = selectedOption.dataset.type;
  const delay = currentMode === 'safe' ? SAFE_DELAY : FAST_DELAY;

  isRunning = true;
  farmingStats.startTime = Date.now();

  document.getElementById('_start_farming').style.display = 'none';
  document.getElementById('_stop_farming').style.display = 'block';

  logToConsole(`Started ${type} farming in ${currentMode} mode`, 'success');

  const timer = setInterval(updateFarmingTime, 1000);

  try {
    switch (type) {
      case 'xp':
        await farmXP(delay);
        break;
      case 'gems':
        await farmGems(delay);
        break;
      case 'streak_repair':
        await repairStreak();
        break;
      case 'streak_farm':
        await farmStreak();
        break;
    }
  } catch (error) {
    logToConsole(`Farming error: ${error.message}`, 'error');
  } finally {
    clearInterval(timer);
  }
};

const stopFarming = () => {
  if (!isRunning) return;

  isRunning = false;
  if (farmingInterval) {
    clearInterval(farmingInterval);
    farmingInterval = null;
  }

  document.getElementById('_start_farming').style.display = 'block';
  document.getElementById('_stop_farming').style.display = 'none';

  logToConsole('Farming stopped', 'info');
};

const farmXP = async (delayMs) => {
  while (isRunning) {
    try {
      const response = await farmXpOnce();
      if (response.ok) {
        const data = await response.json();
        const earned = data?.awardedXp || 0;
        totalEarned.xp += earned;
        updateEarnedStats();
        logToConsole(`Earned ${earned} XP`, 'success');
      }
      await delay(delayMs);
    } catch (error) {
      logToConsole(`XP farming error: ${error.message}`, 'error');
      await delay(delayMs * 2);
    }
  }
};

const farmGems = async (delayMs) => {
  while (isRunning) {
    try {
      const response = await farmGemOnce();
      if (response.ok) {
        totalEarned.gems += 30;
        updateEarnedStats();
        logToConsole('Earned 30 gems', 'success');
      }
      await delay(delayMs);
    } catch (error) {
      logToConsole(`Gem farming error: ${error.message}`, 'error');
      await delay(delayMs * 2);
    }
  }
};

const repairStreak = async () => {
  logToConsole('Starting streak repair...', 'info');

  try {
    if (!userInfo.streakData?.currentStreak) {
      logToConsole('No streak to repair!', 'error');
      return;
    }

    const startStreakDate = userInfo.streakData.currentStreak.startDate;
    const endStreakDate = userInfo.streakData.currentStreak.endDate;
    const startStreakTimestamp = Math.floor(new Date(startStreakDate).getTime() / 1000);
    const endStreakTimestamp = Math.floor(new Date(endStreakDate).getTime() / 1000);
    const expectedStreak = Math.floor((endStreakTimestamp - startStreakTimestamp) / (60 * 60 * 24)) + 1;

    if (expectedStreak > userInfo.streak) {
      logToConsole(`Found ${expectedStreak - userInfo.streak} frozen days. Repairing...`, 'warning');

      let currentTimestamp = Math.floor(Date.now() / 1000);
      for (let i = 0; i < expectedStreak && isRunning; i++) {
        await farmSessionOnce(currentTimestamp, currentTimestamp + 60);
        currentTimestamp -= 86400;
        logToConsole(`Repaired day ${i + 1}/${expectedStreak}`, 'info');
        await delay(currentMode === 'safe' ? SAFE_DELAY : FAST_DELAY);
      }

      const updatedUser = await getUserInfo(sub);
      if (updatedUser.streak >= expectedStreak) {
        logToConsole(`Streak repair completed! New streak: ${updatedUser.streak}`, 'success');
        userInfo = updatedUser;
        totalEarned.streak += (updatedUser.streak - userInfo.streak);
        updateUserInfo();
        updateEarnedStats();
      }
    } else {
      logToConsole('No frozen streak detected', 'info');
    }
  } catch (error) {
    logToConsole(`Streak repair failed: ${error.message}`, 'error');
  } finally {
    stopFarming();
  }
};

const farmStreak = async () => {
  logToConsole('Starting streak farming...', 'info');

  const hasStreak = !!userInfo.streakData?.currentStreak;
  const startStreakDate = hasStreak ? userInfo.streakData.currentStreak.startDate : new Date();
  const startFarmStreakTimestamp = Math.floor(new Date(startStreakDate).getTime() / 1000);
  let currentTimestamp = hasStreak ? startFarmStreakTimestamp - 86400 : startFarmStreakTimestamp;

  while (isRunning) {
    try {
      await farmSessionOnce(currentTimestamp, currentTimestamp + 60);
      currentTimestamp -= 86400;
      totalEarned.streak++;
      userInfo.streak++;
      updateUserInfo();
      updateEarnedStats();
      logToConsole(`Streak increased to ${userInfo.streak}`, 'success');
      await delay(currentMode === 'safe' ? SAFE_DELAY : FAST_DELAY);
    } catch (error) {
      logToConsole(`Streak farming error: ${error.message}`, 'error');
      await delay((currentMode === 'safe' ? SAFE_DELAY : FAST_DELAY) * 2);
    }
  }
};

const getJwtToken = () => {
  let match = document.cookie.match(new RegExp('(^| )jwt_token=([^;]+)'));
  if (match) {
    return match[2];
  }
  return null;
};

const decodeJwtToken = (token) => {
  const base64Url = token.split(".")[1];
  const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
  const jsonPayload = decodeURIComponent(
    atob(base64)
      .split("")
      .map(c => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2))
      .join("")
  );
  return JSON.parse(jsonPayload);
};

const formatHeaders = (jwt) => ({
  "Content-Type": "application/json",
  Authorization: "Bearer " + jwt,
  "User-Agent": navigator.userAgent,
});

const getUserInfo = async (sub) => {
  const userInfoUrl = `https://www.duolingo.com/2017-06-30/users/${sub}?fields=id,username,fromLanguage,learningLanguage,streak,totalXp,level,numFollowers,numFollowing,gems,creationDate,streakData`;
  const response = await fetch(userInfoUrl, {
    method: "GET",
    headers: defaultHeaders,
  });
  return await response.json();
};

const sendRequestWithDefaultHeaders = async ({ url, payload, headers = {}, method = "GET" }) => {
  const mergedHeaders = { ...defaultHeaders, ...headers };
  return await fetch(url, {
    method,
    headers: mergedHeaders,
    body: payload ? JSON.stringify(payload) : undefined,
  });
};

const farmXpOnce = async () => {
  const startTime = Math.floor(Date.now() / 1000);
  const fromLanguage = userInfo.fromLanguage;
  const completeUrl = `https://stories.duolingo.com/api2/stories/en-${fromLanguage}-the-passport/complete`;

  const payload = {
    awardXp: true,
    isFeaturedStoryInPracticeHub: false,
    completedBonusChallenge: true,
    mode: "READ",
    isV2Redo: false,
    isV2Story: false,
    isLegendaryMode: true,
    masterVersion: false,
    maxScore: 0,
    numHintsUsed: 0,
    score: 0,
    startTime: startTime,
    fromLanguage: fromLanguage,
    learningLanguage: "en",
    hasXpBoost: false,
    happyHourBonusXp: 449,
  };

  return await sendRequestWithDefaultHeaders({
    url: completeUrl,
    payload: payload,
    method: "POST",
  });
};

const farmGemOnce = async () => {
  const idReward = "SKILL_COMPLETION_BALANCED-dd2495f4_d44e_3fc3_8ac8_94e2191506f0-2-GEMS";
  const patchUrl = `https://www.duolingo.com/2017-06-30/users/${sub}/rewards/${idReward}`;

  const patchData = {
    consumed: true,
    learningLanguage: userInfo.learningLanguage,
    fromLanguage: userInfo.fromLanguage,
  };

  return await sendRequestWithDefaultHeaders({
    url: patchUrl,
    payload: patchData,
    method: "PATCH",
  });
};

const farmSessionOnce = async (startTime, endTime) => {
  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: userInfo.fromLanguage,
    isFinalLevel: false,
    isV2: true,
    juicy: true,
    learningLanguage: userInfo.learningLanguage,
    smartTipsVersion: 2,
    type: "GLOBAL_PRACTICE",
  };

  const sessionRes = await sendRequestWithDefaultHeaders({
    url: "https://www.duolingo.com/2017-06-30/sessions",
    payload: sessionPayload,
    method: "POST",
  });

  const sessionData = await sessionRes.json();

  const updateSessionPayload = {
    ...sessionData,
    heartsLeft: 0,
    startTime: startTime,
    enableBonusPoints: false,
    endTime: endTime,
    failed: false,
    maxInLessonStreak: 9,
    shouldLearnThings: true,
  };

  const updateRes = await sendRequestWithDefaultHeaders({
    url: `https://www.duolingo.com/2017-06-30/sessions/${sessionData.id}`,
    payload: updateSessionPayload,
    method: "PUT",
  });

  return await updateRes.json();
};

const updateUserInfo = () => {
  if (!userInfo) return;

  const elements = {
    username: document.getElementById('_username'),
    user_details: document.getElementById('_user_details'),
    currentStreak: document.getElementById('_current_streak'),
    currentGems: document.getElementById('_current_gems'),
    currentXp: document.getElementById('_current_xp')
  };

  if (elements.username) elements.username.textContent = userInfo.username;
  if (elements.user_details) {
    elements.user_details.textContent = `${userInfo.fromLanguage} → ${userInfo.learningLanguage}`;
  }
  if (elements.currentStreak) elements.currentStreak.textContent = userInfo.streak?.toLocaleString() || '0';
  if (elements.currentGems) elements.currentGems.textContent = userInfo.gems?.toLocaleString() || '0';
  if (elements.currentXp) elements.currentXp.textContent = userInfo.totalXp?.toLocaleString() || '0';
};

const refreshUserData = async () => {
  if (!sub || !defaultHeaders) return;

  try {
    logToConsole('Refreshing user data...', 'info');
    userInfo = await getUserInfo(sub);
    updateUserInfo();
    logToConsole('User data refreshed', 'success');
  } catch (error) {
    logToConsole(`Failed to refresh: ${error.message}`, 'error');
  }
};

const initializeFarming = async () => {
  try {
    jwt = getJwtToken();
    if (!jwt) {
      logToConsole('Please login to Duolingo and reload', 'error');
      return false;
    }

    defaultHeaders = formatHeaders(jwt);
    const decodedJwt = decodeJwtToken(jwt);
    sub = decodedJwt.sub;

    logToConsole('Loading user data...', 'info');
    userInfo = await getUserInfo(sub);

    if (userInfo && userInfo.username) {
      updateUserInfo();
      logToConsole(`Welcome ${userInfo.username}!`, 'success');
      return true;
    } else {
      logToConsole('Failed to load user data', 'error');
      return false;
    }
  } catch (error) {
    logToConsole(`Init error: ${error.message}`, 'error');
    return false;
  }
};

(async () => {
  try {
    initInterface();
    setInterfaceVisible(false);
    applyTheme(currentTheme);
    addEventListeners();

    if (hasJoined) {
      document.getElementById('_join_section').style.display = 'none';
      document.getElementById('_main_content').style.display = 'flex';
      initializeFarming();
    }

    logToConsole('DuoHacker v2.0 ready', 'success');
  } catch (error) {
    console.error('Init failed:', error);
  }
})();