DuoHacker PRO

Hacklingo — tool for farming XP, streaks, and gems with ultra-fast multi-thread support and API selection.

// ==UserScript==
// @name         DuoHacker PRO
// @name:vi      DuoHacker PRO- Công cụ farm XP
// @name:en      DuoHacker PRO- XP Farming Tool
// @name:es     DuoHacker PRO- Herramienta de farmeo de XP
// @name:fr      DuoHacker PRO- Outil de farm XP
// @name:de      DuoHacker PRO- XP Farming Tool
// @name:pt-BR   DuoHacker PRO- Ferramenta de farm XP
// @name:ru      DuoHacker PRO- Инструмент для фарма XP
// @name:zh-CN   DuoHacker PRO- XP刷分工具
// @name:ja      DuoHacker PRO- XPファーミングツール
// @name:ko      DuoHacker PRO- XP 파밍 도구
// @description  Hacklingo — tool for farming XP, streaks, and gems with ultra-fast multi-thread support and API selection.
// @description:vi  Hacklingo — công cụ farm XP, streaks và gems với tốc độ cao nhờ multi-thread và lựa chọn API.
// @description:en  Hacklingo — tool for farming XP, streaks, and gems with ultra-fast multi-thread support and API selection.
// @description:es  Hacklingo — herramienta para farmear XP, rachas y gemas con soporte multi-hilo de alta velocidad y selección de API.
// @description:fr  Hacklingo — outil pour farmer XP, séries et gemmes avec un support multi-thread ultra-rapide et sélection d'API.
// @description:de  Hacklingo — Tool zum Farmen von XP, Serien und Edelsteinen mit ultraschneller Multi-Thread-Unterstützung und API-Auswahl.
// @description:pt-BR Hacklingo — ferramenta para farmar XP, streaks e gemas com suporte multi-thread de alta velocidade e seleção de API.
// @description:ru  Hacklingo — инструмент для фарма XP, серий и самоцветов с ультра-быстрой многопоточностью и выбором API.
// @description:zh-CN Hacklingo — 支持高速多线程和API选择的XP、连胜和宝石刷分工具。
// @description:ja  Hacklingo — XP・連続日数・ジェムを超高速マルチスレッドで稼ぐツール、API選択対応。
// @description:ko  Hacklingo — 초고속 멀티스레드와 API 선택으로 XP, 연속 기록, 보석을 파밍하는 도구.
// @namespace    https://twisk.fun
// @version      1.1.4
// @author       airpl4ne
// @author       Airplane Mode
// @author       S
// @match        https://*.duolingo.com/*
// @icon         https://github.com/pillowslua/images/blob/main/Hacklingo.png?raw=true
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';

  const templateRaw = `
    <!-- SVG Icons -->
    <svg style="display: none;">
      <defs>
        <symbol id="icon-settings" viewBox="0 0 24 24">
          <path d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z"/>
        </symbol>
        <symbol id="icon-gem" viewBox="0 0 24 24">
          <path d="M12,2L2,7l10,5l10-5L12,2z M12,14.5l-8-4v6.5l8,4l8-4V10.5L12,14.5z"/>
        </symbol>
        <symbol id="icon-fire" viewBox="0 0 24 24">
          <path d="M13.5,0.67s0.74,2.65,0.74,4.8c0,2.06-1.35,3.73-3.41,3.73c-2.07,0-3.63-1.67-3.63-3.73l0.03-0.36C5.21,7.51,4,10.62,4,14 c0,4.42,3.58,8,8,8s8-3.58,8-8C20,8.61,17.41,3.8,13.5,0.67z M11.71,19c-1.78,0-3.22-1.4-3.22-3.14c0-1.62,1.05-2.76,2.81-3.12 c1.77-0.36,3.6-1.21,4.62-2.58c0.39,1.29,0.59,2.65,0.59,4.04c0,2.65-2.15,4.8-4.8,4.8V19z"/>
        </symbol>
        <symbol id="icon-star" viewBox="0 0 24 24">
          <path d="M12,17.27L18.18,21l-1.64-7.03L22,9.24l-7.19-0.61L12,2L9.19,8.63L2,9.24l5.46,4.73L5.82,21L12,17.27z"/>
        </symbol>
        <symbol id="icon-close" viewBox="0 0 24 24">
          <path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41z"/>
        </symbol>
        <symbol id="icon-play" viewBox="0 0 24 24">
          <path d="M8,5v14l11-7L8,5z"/>
        </symbol>
        <symbol id="icon-stop" viewBox="0 0 24 24">
          <path d="M6,6h12v12H6V6z"/>
        </symbol>
      </defs>
    </svg>

    <div id="overlay"></div>
    <div id="container">
      <div id="header">
        <div class="header-content">
          <svg class="logo-icon" width="32" height="32" viewBox="0 0 24 24">
            <path fill="currentColor" d="M12,2C6.48,2,2,6.48,2,12s4.48,10,10,10s10-4.48,10-10S17.52,2,12,2z M13,17h-2v-6h2V17z M13,9h-2V7h2V9z"/>
          </svg>
          <span class="label">DuoHacker</span>
        </div>
        <button id="settings-btn" class="icon-btn">
          <svg class="icon" width="20" height="20"><use href="#icon-settings"/></svg>
        </button>
      </div>

      <div id="body">
        <div class="user-card">
          <div class="user-avatar">
            <span class="avatar-text" id="avatar-initial">D</span>
          </div>
          <div class="user-info-grid">
            <div class="info-item">
              <span class="info-label">Username</span>
              <span class="info-value" id="username">Loading...</span>
            </div>
            <div class="info-item">
              <span class="info-label">From → Learning</span>
              <span class="info-value"><span id="from">-</span> → <span id="learn">-</span></span>
            </div>
          </div>
        </div>

        <div class="stats-grid">
          <div class="stat-card streak">
            <svg class="stat-icon" width="24" height="24"><use href="#icon-fire"/></svg>
            <div class="stat-content">
              <span class="stat-label">Streak</span>
              <span class="stat-value" id="streak">0</span>
            </div>
          </div>
          <div class="stat-card gem">
            <svg class="stat-icon" width="24" height="24"><use href="#icon-gem"/></svg>
            <div class="stat-content">
              <span class="stat-label">Gems</span>
              <span class="stat-value" id="gem">0</span>
            </div>
          </div>
          <div class="stat-card xp">
            <svg class="stat-icon" width="24" height="24"><use href="#icon-star"/></svg>
            <div class="stat-content">
              <span class="stat-label">Total XP</span>
              <span class="stat-value" id="xp">0</span>
            </div>
          </div>
        </div>

        <div id="action-row">
          <select id="select-option" class="modern-select"></select>
          <button id="start-btn" class="action-btn start-btn">
            <svg class="btn-icon" width="18" height="18"><use href="#icon-play"/></svg>
            Start
          </button>
          <button id="stop-btn" class="action-btn stop-btn" hidden>
            <svg class="btn-icon" width="18" height="18"><use href="#icon-stop"/></svg>
            Stop
          </button>
        </div>

        <div id="notify" class="notify-box">
          <div class="notify-icon">ℹ️</div>
          <div class="notify-text">Initializing Hacklingo...</div>
        </div>
      </div>

      <div id="footer">
        <span class="footer-text">Stay safe, farm responsibly 💙</span>
      </div>
    </div>

    <div id="settings-container">
      <div id="settings-menu" class="modal-content">
        <div class="modal-header">
          <span class="label">Settings</span>
          <button id="settings-close" class="icon-btn">
            <svg class="icon" width="20" height="20"><use href="#icon-close"/></svg>
          </button>
        </div>
        <div class="modal-body">
          <div class="settings-group">
            <h3>General</h3>
            <div class="setting-item">
              <span>Auto open UI on load</span>
              <input type="checkbox" id="auto-open-ui" class="toggle-switch">
            </div>
            <div class="setting-item">
              <span>Auto start farming on load</span>
              <input type="checkbox" id="auto-start" class="toggle-switch">
            </div>
            <div class="setting-item">
              <span>Default farming option</span>
              <select id="default-option" class="modern-select"></select>
            </div>
            <div class="setting-item">
              <span>Hide username</span>
              <input type="checkbox" id="hide-username" class="toggle-switch">
            </div>
            <div class="setting-item">
              <span>Keep screen on</span>
              <input type="checkbox" id="keep-screen-on" class="toggle-switch">
            </div>
          </div>

          <div class="settings-group">
            <h3>Performance</h3>
            <div class="setting-item">
              <span>Delay time (100ms - 10000ms):<br><i class="muted">Lower = faster = higher ban risk</i></span>
              <input type="number" id="delay-time" min="100" max="10000" value="500" class="number-input">
            </div>
            <div class="setting-item">
              <span>Retry time (100ms - 10000ms):</span>
              <input type="number" id="retry-time" min="100" max="10000" value="1000" class="number-input">
            </div>
            <div class="setting-item">
              <span>Auto stop after (min) (0 = unlimited)</span>
              <input type="number" id="auto-stop-time" min="0" max="60" value="0" class="number-input">
            </div>
          </div>

          <div class="settings-group">
            <h3>Advanced</h3>
            <div class="setting-item">
              <span>Get JWT token</span>
              <button id="get-jwt-token" class="setting-btn">Get Token</button>
            </div>
            <div class="setting-item">
              <span>Set account to public</span>
              <button id="set-account-public" class="setting-btn">Set Public</button>
            </div>
            <div class="setting-item">
              <span>Set account to private</span>
              <button id="set-account-private" class="setting-btn">Set Private</button>
            </div>
            <div class="setting-item">
              <span>Quick logout</span>
              <button id="quick-logout" class="setting-btn danger">Logout</button>
            </div>
            <div class="setting-item">
              <span>Reset settings</span>
              <button id="reset-setting" class="setting-btn danger">Reset</button>
            </div>
          </div>

          <div class="settings-group">
            <h3>Links & Info</h3>
            <div class="setting-item">
              <span>Blank page (best performance)</span>
              <a href="https://www.duolingo.com/errors/0">Open</a>
            </div>
            <div class="setting-item">
              <span>Discord Server</span>
              <a href="https://discord.gg/Gvmd7deFtS">Open</a>
            </div>
            <div class="setting-item">
              <span>User info:</span>
            </div>
            <code id="user-info-display">Loading...</code>
          </div>
        </div>
        <div class="modal-footer">
          <button id="save-settings" class="save-btn">Save Settings</button>
        </div>
      </div>
    </div>

    <div id="floating-btn">
      <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
        <path d="M12,2C6.48,2,2,6.48,2,12s4.48,10,10,10s10-4.48,10-10S17.52,2,12,2z M13,17h-2v-6h2V17z M13,9h-2V7h2V9z"/>
      </svg>
    </div>
  `;

  const cssText = `
    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }

    #container {
      width: 90vw;
      max-width: 900px;
      min-height: 500px;
      max-height: 90vh;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      border-radius: 20px;
      box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
      display: flex;
      flex-direction: column;
      position: fixed;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      z-index: 9999;
      overflow: hidden;
    }

    #header {
      background: rgba(255, 255, 255, 0.95);
      backdrop-filter: blur(10px);
      padding: 20px 30px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      border-bottom: 1px solid rgba(102, 126, 234, 0.1);
    }

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

    .logo-icon {
      color: #667eea;
    }

    #header .label {
      font-size: 24px;
      font-weight: 700;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
      background-clip: text;
    }

    .icon-btn {
      background: rgba(102, 126, 234, 0.1);
      border: none;
      width: 40px;
      height: 40px;
      border-radius: 10px;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: all 0.3s ease;
    }

    .icon-btn:hover {
      background: rgba(102, 126, 234, 0.2);
      transform: scale(1.05);
    }

    .icon {
      fill: #667eea;
    }

    #body {
      flex: 1;
      background: #ffffff;
      padding: 30px;
      overflow-y: auto;
      display: flex;
      flex-direction: column;
      gap: 24px;
    }

    .user-card {
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      border-radius: 16px;
      padding: 24px;
      display: flex;
      gap: 20px;
      align-items: center;
      box-shadow: 0 8px 16px rgba(102, 126, 234, 0.2);
    }

    .user-avatar {
      width: 60px;
      height: 60px;
      border-radius: 50%;
      background: rgba(255, 255, 255, 0.2);
      display: flex;
      align-items: center;
      justify-content: center;
      border: 3px solid rgba(255, 255, 255, 0.3);
    }

    .avatar-text {
      font-size: 24px;
      font-weight: bold;
      color: white;
    }

    .user-info-grid {
      flex: 1;
      display: grid;
      gap: 12px;
    }

    .info-item {
      display: flex;
      flex-direction: column;
      gap: 4px;
    }

    .info-label {
      font-size: 12px;
      color: rgba(255, 255, 255, 0.8);
      text-transform: uppercase;
      font-weight: 600;
      letter-spacing: 0.5px;
    }

    .info-value {
      font-size: 16px;
      color: white;
      font-weight: 600;
    }

    .stats-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
      gap: 16px;
    }

    .stat-card {
      background: white;
      border-radius: 12px;
      padding: 20px;
      display: flex;
      align-items: center;
      gap: 16px;
      border: 2px solid #f0f0f0;
      transition: all 0.3s ease;
    }

    .stat-card:hover {
      transform: translateY(-2px);
      box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
    }

    .stat-card.streak { border-color: #ff9500; }
    .stat-card.gem { border-color: #667eea; }
    .stat-card.xp { border-color: #ffd700; }

    .stat-icon {
      width: 40px;
      height: 40px;
      padding: 8px;
      border-radius: 10px;
    }

    .stat-card.streak .stat-icon { background: #fff3e0; fill: #ff9500; }
    .stat-card.gem .stat-icon { background: #f0f4ff; fill: #667eea; }
    .stat-card.xp .stat-icon { background: #fffef0; fill: #ffd700; }

    .stat-content {
      display: flex;
      flex-direction: column;
      gap: 4px;
    }

    .stat-label {
      font-size: 12px;
      color: #666;
      font-weight: 600;
      text-transform: uppercase;
    }

    .stat-value {
      font-size: 24px;
      font-weight: 700;
      color: #333;
    }

    #action-row {
      display: flex;
      gap: 12px;
      align-items: stretch;
    }

    .modern-select {
      flex: 1;
      padding: 14px 16px;
      border: 2px solid #e0e0e0;
      border-radius: 12px;
      background: white;
      color: #333;
      font-size: 14px;
      font-weight: 500;
      cursor: pointer;
      transition: all 0.3s ease;
      outline: none;
    }

    .modern-select:hover {
      border-color: #667eea;
    }

    .modern-select:focus {
      border-color: #667eea;
      box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
    }

    .action-btn {
      padding: 14px 28px;
      border: none;
      border-radius: 12px;
      font-size: 14px;
      font-weight: 700;
      cursor: pointer;
      display: flex;
      align-items: center;
      gap: 8px;
      transition: all 0.3s ease;
      text-transform: uppercase;
      letter-spacing: 0.5px;
    }

    .start-btn {
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white;
      box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
    }

    .start-btn:hover:not(:disabled) {
      transform: translateY(-2px);
      box-shadow: 0 6px 16px rgba(102, 126, 234, 0.4);
    }

    .stop-btn {
      background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
      color: white;
      box-shadow: 0 4px 12px rgba(255, 107, 107, 0.3);
    }

    .stop-btn:hover:not(:disabled) {
      transform: translateY(-2px);
      box-shadow: 0 6px 16px rgba(255, 107, 107, 0.4);
    }

    .action-btn:disabled {
      opacity: 0.5;
      cursor: not-allowed;
      transform: none !important;
    }

    .btn-icon {
      fill: currentColor;
    }

    .notify-box {
      background: linear-gradient(135deg, #f0f4ff 0%, #f8f9ff 100%);
      border: 2px solid #667eea;
      border-radius: 12px;
      padding: 16px 20px;
      display: flex;
      gap: 12px;
      align-items: flex-start;
      min-height: 80px;
    }

    .notify-icon {
      font-size: 20px;
      flex-shrink: 0;
    }

    .notify-text {
      flex: 1;
      color: #333;
      font-size: 14px;
      line-height: 1.6;
    }

    #footer {
      background: rgba(255, 255, 255, 0.95);
      padding: 16px;
      text-align: center;
      border-top: 1px solid rgba(102, 126, 234, 0.1);
    }

    .footer-text {
      color: #667eea;
      font-size: 14px;
      font-weight: 600;
    }

    #overlay {
      position: fixed;
      top: 0;
      left: 0;
      width: 100vw;
      height: 100vh;
      background: rgba(0, 0, 0, 0.5);
      backdrop-filter: blur(4px);
      z-index: 9998;
    }

    #floating-btn {
      position: fixed;
      bottom: 30px;
      right: 30px;
      width: 60px;
      height: 60px;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      border-radius: 50%;
      box-shadow: 0 8px 24px rgba(102, 126, 234, 0.4);
      z-index: 10000;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: all 0.3s ease;
      color: white;
    }

    #floating-btn:hover {
      transform: scale(1.1);
      box-shadow: 0 12px 32px rgba(102, 126, 234, 0.5);
    }

    #settings-container {
      position: fixed;
      top: 0;
      left: 0;
      width: 100vw;
      height: 100vh;
      z-index: 10000;
      display: none;
      align-items: center;
      justify-content: center;
      background: rgba(0, 0, 0, 0.6);
      backdrop-filter: blur(8px);
    }

    .modal-content {
      width: 90vw;
      max-width: 700px;
      max-height: 80vh;
      background: white;
      border-radius: 20px;
      box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
      display: flex;
      flex-direction: column;
      overflow: hidden;
    }

    .modal-header {
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      padding: 24px 30px;
      display: flex;
      align-items: center;
      justify-content: space-between;
    }

    .modal-header .label {
      font-size: 20px;
      font-weight: 700;
      color: white;
    }

    .modal-header .icon-btn {
      background: rgba(255, 255, 255, 0.2);
    }

    .modal-header .icon-btn .icon {
      fill: white;
    }

    .modal-body {
      flex: 1;
      padding: 30px;
      overflow-y: auto;
      display: flex;
      flex-direction: column;
      gap: 24px;
    }

    .settings-group {
      display: flex;
      flex-direction: column;
      gap: 16px;
    }

    .settings-group h3 {
      font-size: 16px;
      color: #667eea;
      font-weight: 700;
      padding-bottom: 8px;
      border-bottom: 2px solid #f0f0f0;
      text-transform: uppercase;
      letter-spacing: 0.5px;
    }

    .setting-item {
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 16px;
      background: #f8f9ff;
      border-radius: 12px;
      gap: 16px;
    }

    .setting-item span {
      flex: 1;
      color: #333;
      font-size: 14px;
      line-height: 1.6;
    }

    .toggle-switch {
      width: 48px;
      height: 24px;
      cursor: pointer;
      accent-color: #667eea;
    }

    .number-input {
      width: 100px;
      padding: 8px 12px;
      border: 2px solid #e0e0e0;
      border-radius: 8px;
      text-align: center;
      font-size: 14px;
      font-weight: 600;
      color: #333;
      outline: none;
      transition: all 0.3s ease;
    }

    .number-input:focus {
      border-color: #667eea;
      box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
    }

    .setting-btn {
      padding: 8px 16px;
      border: 2px solid #667eea;
      border-radius: 8px;
      background: white;
      color: #667eea;
      font-size: 13px;
      font-weight: 700;
      cursor: pointer;
      transition: all 0.3s ease;
      text-transform: uppercase;
      letter-spacing: 0.5px;
    }

    .setting-btn:hover {
      background: #667eea;
      color: white;
    }

    .setting-btn.danger {
      border-color: #ff6b6b;
      color: #ff6b6b;
    }

    .setting-btn.danger:hover {
      background: #ff6b6b;
      color: white;
    }

    .setting-item a {
      color: #667eea;
      font-weight: 600;
      text-decoration: none;
      font-size: 14px;
      padding: 4px 0;
      border-bottom: 2px solid transparent;
      transition: all 0.3s ease;
    }

    .setting-item a:hover {
      border-bottom-color: #667eea;
    }

    .modal-footer {
      padding: 20px 30px;
      background: #f8f9ff;
      display: flex;
      justify-content: flex-end;
      border-top: 1px solid #e0e0e0;
    }

    .save-btn {
      padding: 12px 32px;
      border: none;
      border-radius: 12px;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white;
      font-size: 14px;
      font-weight: 700;
      cursor: pointer;
      transition: all 0.3s ease;
      text-transform: uppercase;
      letter-spacing: 0.5px;
      box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
    }

    .save-btn:hover {
      transform: translateY(-2px);
      box-shadow: 0 6px 16px rgba(102, 126, 234, 0.4);
    }

    .muted {
      color: #999;
      font-size: 12px;
      font-style: italic;
    }

    .blur {
      filter: blur(8px);
      user-select: none;
    }

    .hidden {
      display: none !important;
    }

    .disabled {
      opacity: 0.5;
      pointer-events: none;
      cursor: not-allowed;
    }

    code {
      display: block;
      background: #f5f5f5;
      border-left: 4px solid #667eea;
      padding: 16px;
      border-radius: 8px;
      font-family: 'Monaco', 'Courier New', monospace;
      font-size: 12px;
      line-height: 1.6;
      color: #333;
      overflow-x: auto;
      white-space: pre-wrap;
      word-wrap: break-word;
    }

    /* Scrollbar styling */
    #body::-webkit-scrollbar,
    .modal-body::-webkit-scrollbar {
      width: 8px;
    }

    #body::-webkit-scrollbar-track,
    .modal-body::-webkit-scrollbar-track {
      background: #f0f0f0;
      border-radius: 4px;
    }

    #body::-webkit-scrollbar-thumb,
    .modal-body::-webkit-scrollbar-thumb {
      background: #667eea;
      border-radius: 4px;
    }

    #body::-webkit-scrollbar-thumb:hover,
    .modal-body::-webkit-scrollbar-thumb:hover {
      background: #764ba2;
    }

    /* Responsive design */
    @media (max-width: 768px) {
      #container {
        width: 95vw;
        max-height: 95vh;
      }

      .stats-grid {
        grid-template-columns: 1fr;
      }

      #action-row {
        flex-direction: column;
      }

      .user-card {
        flex-direction: column;
        text-align: center;
      }
    }
  `;

  const log = (message) => {
    if (typeof GM_log !== "undefined") {
      GM_log(message);
    } else {
      console.log("[DuoFarmer]", message);
    }
  };

  const logError = (error, context = "") => {
    const message = error?.message || error?.toString() || "Unknown error";
    const fullMessage = context ? `[${context}] ${message}` : message;
    log(fullMessage);
  };

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

  const toTimestamp = (dateStr) => {
    return Math.floor(new Date(dateStr).getTime() / 1000);
  };

  const getCurrentUnixTimestamp = () => {
    return Math.floor(Date.now() / 1000);
  };

  const getJwtToken = () => {
    const cookies = document.cookie.split(";");
    for (let i = 0; i < cookies.length; i++) {
      const cookie = cookies[i].trim();
      if (cookie.startsWith("jwt_token=")) {
        return cookie.substring("jwt_token=".length);
      }
    }
    return null;
  };

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

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

  const extractSkillId = (currentCourse) => {
    const sections = currentCourse?.pathSectioned || [];
    for (const section of sections) {
      const units = section.units || [];
      for (const unit of units) {
        const levels = unit.levels || [];
        for (const level of levels) {
          const skillId = level.pathLevelMetadata?.skillId || level.pathLevelClientData?.skillId;
          if (skillId) return skillId;
        }
      }
    }
    return null;
  };

  class ApiService {
    constructor(jwt, defaultHeaders, userInfo, sub) {
      this.jwt = jwt;
      this.defaultHeaders = defaultHeaders;
      this.userInfo = userInfo;
      this.sub = sub;
    }

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

    async sendRequest({ url, payload, headers, method = "PUT" }) {
      try {
        const res = await fetch(url, {
          method,
          headers,
          body: payload ? JSON.stringify(payload) : void 0
        });
        return res;
      } catch (error) {
        return error;
      }
    }

    async setPrivacyStatus(privacyStatus) {
      const patchUrl = `https://www.duolingo.com/2017-06-30/users/${this.sub}/privacy-settings?fields=privacySettings`;
      const patchBody = {
        "DISABLE_SOCIAL": privacyStatus
      };
      return await this.sendRequest({ url: patchUrl, payload: patchBody, headers: this.defaultHeaders, method: "PATCH" });
    }

    async farmGemOnce() {
      const idReward = "SKILL_COMPLETION_BALANCED-dd2495f4_d44e_3fc3_8ac8_94e2191506f0-2-GEMS";
      const patchUrl = `https://www.duolingo.com/2017-06-30/users/${this.sub}/rewards/${idReward}`;
      const patchBody = {
        consumed: true,
        learningLanguage: this.userInfo.learningLanguage,
        fromLanguage: this.userInfo.fromLanguage
      };
      return await this.sendRequest({ url: patchUrl, payload: patchBody, headers: this.defaultHeaders, method: "PATCH" });
    }

    async farmStoryOnce(config = {}) {
      const startTime = getCurrentUnixTimestamp();
      const fromLanguage = this.userInfo.fromLanguage;
      const completeUrl = `https://stories.duolingo.com/api2/stories/en-${fromLanguage}-the-passport/complete`;
      const storyPayload = {
        awardXp: true,
        isFeaturedStoryInPracticeHub: false,
        completedBonusChallenge: true,
        mode: "READ",
        isV2Redo: false,
        isV2Story: false,
        isLegendaryMode: true,
        masterVersion: false,
        maxScore: 0,
        numHintsUsed: 0,
        score: 0,
        startTime,
        fromLanguage,
        learningLanguage: this.userInfo.learningLanguage,
        hasXpBoost: false,
        ...config.storyPayload || {}
      };
      return await this.sendRequest({ url: completeUrl, payload: storyPayload, headers: this.defaultHeaders, method: "POST" });
    }

    async farmSessionOnce(config = {}) {
      const startTime = config.startTime || getCurrentUnixTimestamp();
      const endTime = config.endTime || startTime + 60;
      const sessionPayload = {
        challengeTypes: [],
        fromLanguage: this.userInfo.fromLanguage,
        learningLanguage: this.userInfo.learningLanguage,
        type: "GLOBAL_PRACTICE",
        ...config.sessionPayload || {}
      };
      const sessionRes = await this.sendRequest({ url: "https://www.duolingo.com/2017-06-30/sessions", payload: sessionPayload, headers: this.defaultHeaders, method: "POST" });
      const sessionData = await sessionRes.json();
      const updateSessionPayload = {
        id: sessionData.id,
        metadata: sessionData.metadata,
        type: sessionData.type,
        fromLanguage: this.userInfo.fromLanguage,
        learningLanguage: this.userInfo.learningLanguage,
        challenges: [],
        adaptiveChallenges: [],
        sessionExperimentRecord: [],
        experiments_with_treatment_contexts: [],
        adaptiveInterleavedChallenges: [],
        sessionStartExperiments: [],
        trackingProperties: [],
        ttsAnnotations: [],
        heartsLeft: 0,
        startTime,
        enableBonusPoints: false,
        endTime,
        failed: false,
        maxInLessonStreak: 9,
        shouldLearnThings: true,
        ...config.updateSessionPayload || {}
      };
      const updateRes = await this.sendRequest({ url: `https://www.duolingo.com/2017-06-30/sessions/${sessionData.id}`, payload: updateSessionPayload, headers: this.defaultHeaders, method: "PUT" });
      return updateRes;
    }
  }

  class SettingsManager {
    constructor(shadowRoot, apiService = null) {
      this.shadowRoot = shadowRoot;
      this.apiService = apiService;
      this.DEFAULT_SETTINGS = {
        autoOpenUI: false,
        autoStart: false,
        defaultOption: 1,
        hideUsername: false,
        keepScreenOn: false,
        delayTime: 500,
        retryTime: 1000,
        autoStopTime: 0,
        darkMode: false,
        compactUI: false,
        showProgress: false,
        fontSize: "medium"
      };
      this.settings = this.loadSettings();
    }

    loadSettings() {
      try {
        const saved = localStorage.getItem("DuoFarmerSettings");
        if (saved) {
          return { ...this.DEFAULT_SETTINGS, ...JSON.parse(saved) };
        }
        return { ...this.DEFAULT_SETTINGS };
      } catch (error) {
        return { ...this.DEFAULT_SETTINGS };
      }
    }

    saveSettings(settings) {
      this.settings = settings;
      localStorage.setItem("DuoFarmerSettings", JSON.stringify(settings));
    }

    getSettings() {
      return { ...this.settings };
    }

    loadSettingsToUI() {
      const elements = this.getElements();
      if (elements.autoOpenUI) elements.autoOpenUI.checked = this.settings.autoOpenUI;
      if (elements.autoStart) elements.autoStart.checked = this.settings.autoStart;
      if (elements.defaultOption) elements.defaultOption.value = this.settings.defaultOption.toString();
      if (elements.hideUsername) elements.hideUsername.checked = this.settings.hideUsername;
      if (elements.keepScreenOn) elements.keepScreenOn.checked = this.settings.keepScreenOn;
      if (elements.delayTime) elements.delayTime.value = this.settings.delayTime;
      if (elements.retryTime) elements.retryTime.value = this.settings.retryTime;
      if (elements.autoStopTime) elements.autoStopTime.value = this.settings.autoStopTime;
      if (elements.darkMode) elements.darkMode.checked = this.settings.darkMode;
      if (elements.compactUI) elements.compactUI.checked = this.settings.compactUI;
      if (elements.showProgress) elements.showProgress.checked = this.settings.showProgress;
      if (elements.fontSize) elements.fontSize.value = this.settings.fontSize;
    }

    saveSettingsFromUI() {
      const elements = this.getElements();
      const settings = {
        autoOpenUI: elements.autoOpenUI?.checked || false,
        autoStart: elements.autoStart?.checked || false,
        defaultOption: parseInt(elements.defaultOption?.value) || 1,
        hideUsername: elements.hideUsername?.checked || false,
        keepScreenOn: elements.keepScreenOn?.checked || false,
        delayTime: Math.max(100, Math.min(10000, parseInt(elements.delayTime?.value) || 500)),
        retryTime: Math.max(100, Math.min(10000, parseInt(elements.retryTime?.value) || 1000)),
        autoStopTime: parseInt(elements.autoStopTime?.value) || 0,
        darkMode: elements.darkMode?.checked || false,
        compactUI: elements.compactUI?.checked || false,
        showProgress: elements.showProgress?.checked || false,
        fontSize: elements.fontSize?.value || "medium"
      };
      this.saveSettings(settings);
      return settings;
    }

    getElements() {
      return {
        autoOpenUI: this.shadowRoot.getElementById("auto-open-ui"),
        autoStart: this.shadowRoot.getElementById("auto-start"),
        defaultOption: this.shadowRoot.getElementById("default-option"),
        hideUsername: this.shadowRoot.getElementById("hide-username"),
        keepScreenOn: this.shadowRoot.getElementById("keep-screen-on"),
        delayTime: this.shadowRoot.getElementById("delay-time"),
        retryTime: this.shadowRoot.getElementById("retry-time"),
        autoStopTime: this.shadowRoot.getElementById("auto-stop-time"),
        darkMode: this.shadowRoot.getElementById("dark-mode"),
        compactUI: this.shadowRoot.getElementById("compact-ui"),
        showProgress: this.shadowRoot.getElementById("show-progress"),
        fontSize: this.shadowRoot.getElementById("font-size"),
        saveSettings: this.shadowRoot.getElementById("save-settings"),
        quickLogout: this.shadowRoot.getElementById("quick-logout"),
        resetTheme: this.shadowRoot.getElementById("reset-theme"),
        getJwtToken: this.shadowRoot.getElementById("get-jwt-token"),
        resetSetting: this.shadowRoot.getElementById("reset-setting"),
        settingsContainer: this.shadowRoot.getElementById("settings-container"),
        setAccountPublic: this.shadowRoot.getElementById("set-account-public"),
        setAccountPrivate: this.shadowRoot.getElementById("set-account-private")
      };
    }

    addEventListeners() {
      const elements = this.getElements();
      elements.saveSettings.addEventListener("click", () => {
        this.saveSettingsFromUI();
        alert("Settings saved successfully, reload the page to apply changes!");
        confirm("Reload now?") && location.reload();
      });
      elements.quickLogout.addEventListener("click", () => {
        if (confirm("Are you sure you want to logout?")) {
          window.location.href = "https://www.duolingo.com/logout";
        }
      });
      elements.resetTheme?.addEventListener("click", () => {
        // Not implemented in new UI
      });
      elements.getJwtToken.addEventListener("click", () => {
        const token = getJwtToken();
        if (token) {
          confirm(`Your JWT Token:\n\n${token}\n\nCopy to clipboard?`) && navigator.clipboard.writeText(token);
        }
      });
      elements.resetSetting.addEventListener("click", () => {
        if (confirm("Reset all settings to default? This cannot be undone.")) {
          localStorage.removeItem("DuoFarmerSettings");
          this.settings = { ...this.DEFAULT_SETTINGS };
          this.loadSettingsToUI();
          alert("All settings reset successfully! Reload to apply changes.");
        }
      });
      elements.setAccountPublic.addEventListener("click", async () => {
        if (confirm("Are you sure you want to set your account to public?")) {
          try {
            await this.apiService.setPrivacyStatus(false);
            alert("Account set to public successfully! Reload the page to see changes.");
          } catch (error) {
            alert("Failed to set account to public: " + error.message);
          }
        }
      });
      elements.setAccountPrivate.addEventListener("click", async () => {
        if (confirm("Are you sure you want to set your account to private?")) {
          try {
            await this.apiService.setPrivacyStatus(true);
            alert("Account set to private successfully! Reload the page to see changes.");
          } catch (error) {
            alert("Failed to set account to private: " + error.message);
          }
        }
      });
    }

    addEventSettings(container) {
      const elements = this.getElements();
      const settingsBtn = this.shadowRoot.getElementById("settings-btn");
      const settingsContainer = elements.settingsContainer;
      const settingsClose = this.shadowRoot.getElementById("settings-close");
      const toggleModal = (modalElement, mainElement) => ({
        show: () => {
          mainElement.style.display = "none";
          modalElement.style.display = "flex";
        },
        hide: () => {
          modalElement.style.display = "none";
          mainElement.style.display = "flex";
        }
      });
      const settingsModal = toggleModal(settingsContainer, container);
      settingsBtn.addEventListener("click", settingsModal.show);
      settingsClose.addEventListener("click", settingsModal.hide);
    }

    loadDefaultFarmingOption(optionsArray) {
      const select = this.shadowRoot.getElementById("select-option");
      const optionIndex = this.settings.defaultOption;
      select.selectedIndex = optionIndex;
    }

    populateDefaultOptionSelect(optionsArray) {
      const select = this.shadowRoot.getElementById("default-option");
      select.innerHTML = "";
      optionsArray.forEach((opt, index) => {
        const option = document.createElement("option");
        option.value = index.toString();
        option.textContent = opt.label;
        if (opt.disabled) option.disabled = true;
        select.appendChild(option);
      });
    }
  }

  let runtimeSettings = {
    delayTime: 500,
    retryTime: 1000,
    autoStopTime: 0
  };
  let jwt = null;
  let defaultHeaders = null;
  let userInfo = null;
  let sub = null;
  let skillId = null;
  let isRunning = false;
  let shadowRoot = null;
  let apiService = null;
  let settingsManager = null;
  let farmOptions = [];
  let autoStopTimerId = null;

  const getElements = () => {
    return {
      startBtn: shadowRoot.getElementById("start-btn"),
      stopBtn: shadowRoot.getElementById("stop-btn"),
      select: shadowRoot.getElementById("select-option"),
      floatingBtn: shadowRoot.getElementById("floating-btn"),
      container: shadowRoot.getElementById("container"),
      overlay: shadowRoot.getElementById("overlay"),
      notify: shadowRoot.getElementById("notify"),
      username: shadowRoot.getElementById("username"),
      from: shadowRoot.getElementById("from"),
      learn: shadowRoot.getElementById("learn"),
      streak: shadowRoot.getElementById("streak"),
      gem: shadowRoot.getElementById("gem"),
      xp: shadowRoot.getElementById("xp"),
      settingsBtn: shadowRoot.getElementById("settings-btn"),
      settingsContainer: shadowRoot.getElementById("settings-container"),
      settingsClose: shadowRoot.getElementById("settings-close"),
      userInfoDisplay: shadowRoot.getElementById("user-info-display"),
      setAccountPublic: shadowRoot.getElementById("set-account-public"),
      setAccountPrivate: shadowRoot.getElementById("set-account-private")
    };
  };

  const setRunningState = (running) => {
    isRunning = running;
    const { startBtn, stopBtn, select } = getElements();
    if (running) {
      startBtn.hidden = true;
      stopBtn.hidden = false;
      stopBtn.disabled = true;
      stopBtn.classList.add("disabled");
      select.disabled = true;
    } else {
      stopBtn.hidden = true;
      startBtn.hidden = false;
      startBtn.disabled = true;
      startBtn.classList.add("disabled");
      select.disabled = false;
      if (autoStopTimerId) {
        clearTimeout(autoStopTimerId);
        autoStopTimerId = null;
      }
    }
    setTimeout(() => {
      const { startBtn: btn, stopBtn: stop } = getElements();
      btn.classList.remove("disabled");
      btn.disabled = false;
      stop.classList.remove("disabled");
      stop.disabled = false;
    }, 3000);
  };

  const disableAllControls = (notifyMessage = null) => {
    const { startBtn, stopBtn, select } = getElements();
    startBtn.disabled = true;
    startBtn.classList.add("disabled");
    stopBtn.disabled = true;
    select.disabled = true;
    if (notifyMessage) {
      updateNotify(notifyMessage);
    }
  };

  const initInterface = () => {
    const container = document.createElement("div");
    shadowRoot = container.attachShadow({ mode: "open" });
    const style = document.createElement("style");
    style.textContent = cssText;
    shadowRoot.appendChild(style);
    const content = document.createElement("div");
    content.innerHTML = templateRaw;
    shadowRoot.appendChild(content);
    document.body.appendChild(container);

    const settingsContainer = shadowRoot.getElementById("settings-container");
    if (settingsContainer) {
      settingsContainer.style.display = "none";
    }

    const requiredElements = [
      "start-btn",
      "stop-btn",
      "select-option",
      "floating-btn",
      "container",
      "overlay",
      "notify"
    ];
    for (const id of requiredElements) {
      if (!shadowRoot.getElementById(id)) {
        throw new Error(`Required UI element '${id}' not found in template. Template may be corrupted.`);
      }
    }
  };

  const showElement = (element) => {
    if (element) element.style.display = "flex";
  };

  const hideElement = (element) => {
    if (element) element.style.display = "none";
  };

  const setInterfaceVisible = (visible) => {
    const { container, overlay } = getElements();
    if (visible) {
      showElement(container);
      showElement(overlay);
    } else {
      hideElement(container);
      hideElement(overlay);
    }
  };

  const addEventFloatingBtn = () => {
    const { floatingBtn } = getElements();
    floatingBtn.addEventListener("click", () => {
      if (isRunning) {
        if (confirm("Hacklingo is farming. Do you want to stop and hide UI?")) {
          setRunningState(false);
          setInterfaceVisible(false);
        }
        return;
      }
      toggleInterface();
    });
  };

  const addEventStartBtn = () => {
    const { startBtn, select } = getElements();
    startBtn.addEventListener("click", async () => {
      setRunningState(true);
      if (runtimeSettings.autoStopTime > 0) {
        autoStopTimerId = setTimeout(() => {
          alert(`Auto-stopped by setting (stop after ${runtimeSettings.autoStopTime} minutes).`);
          updateNotify(`Auto-stopped by setting (stop after ${runtimeSettings.autoStopTime} minutes).`);
          setRunningState(false);
        }, runtimeSettings.autoStopTime * 60 * 1000);
      }
      const selected = select.options[select.selectedIndex];
      const optionData = {
        type: selected.getAttribute("data-type"),
        amount: Number(selected.getAttribute("data-amount")),
        value: selected.value,
        label: selected.textContent,
        config: selected.getAttribute("data-config") ? JSON.parse(selected.getAttribute("data-config")) : {}
      };
      await farmSelectedOption(optionData);
    });
  };

  const addEventStopBtn = () => {
    const { stopBtn } = getElements();
    stopBtn.addEventListener("click", () => {
      setRunningState(false);
    });
  };

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

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

  const addEventListeners = () => {
    addEventStartBtn();
    addEventStopBtn();
    const { container } = getElements();
    settingsManager.addEventSettings(container);
    settingsManager.addEventListeners();
  };

  const populateOptions = () => {
    const select = shadowRoot.getElementById("select-option");
    select.innerHTML = "";
    farmOptions.forEach((opt) => {
      const option = document.createElement("option");
      option.value = opt.value;
      option.textContent = opt.label;
      option.setAttribute("data-type", opt.type);
      if (opt.amount != null) option.setAttribute("data-amount", String(opt.amount));
      if (opt.config) option.setAttribute("data-config", JSON.stringify(opt.config));
      if (opt.disabled) option.disabled = true;
      select.appendChild(option);
    });
  };

  const updateNotify = (message) => {
    const notifyText = shadowRoot.querySelector('.notify-text');
    if (notifyText) {
      const now = new Date().toLocaleTimeString();
      notifyText.innerText = `[${now}] ${message}`;
      log(`[${now}] ${message}`);
    }
  };

  const updateUserInfo = () => {
    const elements = getElements();
    if (userInfo) {
      elements.username.innerText = userInfo.username;
      elements.from.innerText = userInfo.fromLanguage;
      elements.learn.innerText = userInfo.learningLanguage;
      elements.streak.innerText = userInfo.streak;
      elements.gem.innerText = userInfo.gems;
      elements.xp.innerText = userInfo.totalXp;

      const avatarInitial = shadowRoot.getElementById('avatar-initial');
      if (avatarInitial && userInfo.username) {
        avatarInitial.innerText = userInfo.username.charAt(0).toUpperCase();
      }

      if (userInfo.privacySettings &&
          (userInfo.privacySettings.includes("DISABLE_FRIENDS_QUESTS") ||
           userInfo.privacySettings.includes("DISABLE_LEADERBOARDS"))) {
        hideElement(elements.setAccountPrivate);
      } else {
        hideElement(elements.setAccountPublic);
      }

      elements.userInfoDisplay.innerText = JSON.stringify({
        id: userInfo.id,
        username: userInfo.username,
        fromLanguage: userInfo.fromLanguage,
        learningLanguage: userInfo.learningLanguage,
        streak: userInfo.streak,
        gems: userInfo.gems,
        totalXp: userInfo.totalXp,
        creationDate: userInfo.creationDate,
        skillId,
        jwt: "hidden - use get jwt button to view",
        sub,
        privacySettings: userInfo.privacySettings,
        streakData: userInfo.streakData
      }, null, 2);
    }
  };

  const updateFarmResult = (type, farmedAmount) => {
    switch (type) {
      case "gem":
        userInfo = { ...userInfo, gems: userInfo.gems + farmedAmount };
        updateNotify(`You got ${farmedAmount} gem!!!`);
        break;
      case "xp":
        userInfo = { ...userInfo, totalXp: userInfo.totalXp + farmedAmount };
        updateNotify(`You got ${farmedAmount} XP!!!`);
        break;
      case "streak":
        userInfo = { ...userInfo, streak: userInfo.streak + farmedAmount };
        updateNotify(`You got ${farmedAmount} streak! (maybe some xp too, idk)`);
        break;
    }
    updateUserInfo();
  };

  const gemFarmingLoop = async () => {
    const gemFarmed = 30;
    while (isRunning) {
      try {
        await apiService.farmGemOnce(userInfo);
        updateFarmResult("gem", gemFarmed);
        await delay(runtimeSettings.delayTime);
      } catch (error) {
        updateNotify(`Error ${error.status}! Please report in telegram group!`);
        await delay(runtimeSettings.retryTime);
      }
    }
  };

  const xpFarmingLoop = async (value, amount, config = {}) => {
    while (isRunning) {
      try {
        let response;
        if (value === "session") {
          response = await apiService.farmSessionOnce(config);
        } else if (value === "story") {
          response = await apiService.farmStoryOnce(config);
        }
        if (response.status > 400) {
          updateNotify(`Something went wrong! Pls try other farming methods.
If you are using story method, u should try with English course!`);
          await delay(runtimeSettings.retryTime);
          continue;
        }
        const responseData = await response.json();
        const xpFarmed = (responseData?.awardedXp) || (responseData?.xpGain) || 0;
        updateFarmResult("xp", xpFarmed);
        await delay(runtimeSettings.delayTime);
      } catch (error) {
        updateNotify(`Error ${error.status}! Please report in telegram group!`);
        await delay(runtimeSettings.retryTime);
      }
    }
  };

  const streakFarmingLoop = async () => {
    const hasStreak = !!userInfo.streakData.currentStreak;
    const startStreakDate = hasStreak ? userInfo.streakData.currentStreak.startDate : new Date();
    const startFarmStreakTimestamp = toTimestamp(startStreakDate);
    let currentTimestamp = hasStreak ? startFarmStreakTimestamp - 86400 : startFarmStreakTimestamp;
    while (isRunning) {
      try {
        const sessionRes = await apiService.farmSessionOnce({ startTime: currentTimestamp, endTime: currentTimestamp + 60 });
        if (sessionRes) {
          currentTimestamp -= 86400;
          updateFarmResult("streak", 1);
          await delay(runtimeSettings.delayTime);
        } else {
          updateNotify("Failed to farm streak session, I'm trying again...");
          await delay(runtimeSettings.retryTime);
          continue;
        }
      } catch (error) {
        updateNotify(`Error in farmStreak: ${(error?.message) || error}`);
        await delay(runtimeSettings.retryTime);
        continue;
      }
    }
  };

  const farmSelectedOption = async (option) => {
    const { type, value, amount, config } = option;
    switch (type) {
      case "gem":
        gemFarmingLoop();
        break;
      case "xp":
        xpFarmingLoop(value, amount, config);
        break;
      case "streak":
        streakFarmingLoop();
        break;
    }
  };

  const loadSavedSettings = (settings) => {
    runtimeSettings = { ...runtimeSettings, ...settings };
    const elements = getElements();
    if (settings.autoOpenUI) {
      setInterfaceVisible(true);
    }
    if (settings.autoStart) {
      setInterfaceVisible(true);
      elements.startBtn.click();
    }
    if (settings.hideUsername) {
      elements.username.classList.add("blur");
    }
    if (settings.keepScreenOn && "wakeLock" in navigator) {
      navigator.wakeLock.request("screen").then((wakeLock) => {
        log("Screen wake lock active");
      });
    }
  };

  const initVariables = async () => {
    jwt = getJwtToken();
    if (!jwt) {
      disableAllControls("Please login to Duolingo and reload!");
      return;
    }
    defaultHeaders = formatHeaders(jwt);
    const decodedJwt = decodeJwtToken(jwt);
    sub = decodedJwt.sub;
    userInfo = await ApiService.getUserInfo(sub, defaultHeaders);
    apiService = new ApiService(jwt, defaultHeaders, userInfo, sub);
    settingsManager = new SettingsManager(shadowRoot, apiService);
    skillId = extractSkillId(userInfo.currentCourse || {});
    farmOptions = [
      { type: "separator", label: "⟡ GEM FARMING ⟡", value: "", disabled: true },
      { type: "gem", label: "Gem 30", value: "fixed", amount: 30 },
      { type: "separator", label: "⟡ XP SESSION FARMING ⟡", value: "", disabled: true },
      { type: "separator", label: "(slow, safe, any language)", value: "", disabled: true },
      { type: "xp", label: "XP 10", value: "session", amount: 10, config: {} },
      { type: "xp", label: "XP 20", value: "session", amount: 20, config: { updateSessionPayload: { hasBoost: true } } },
      { type: "xp", label: "XP 40", value: "session", amount: 40, config: { updateSessionPayload: { hasBoost: true, type: "TARGET_PRACTICE" } } },
      { type: "xp", label: "XP 50", value: "session", amount: 50, config: { updateSessionPayload: { enableBonusPoints: true, hasBoost: true, happyHourBonusXp: 10, type: "TARGET_PRACTICE" } } },
      { type: "xp", label: "XP 110", value: "session", amount: 110, config: { sessionPayload: { type: "UNIT_TEST", skillIds: skillId ? [skillId] : [] }, updateSessionPayload: { type: "UNIT_TEST", hasBoost: true, happyHourBonusXp: 10, pathLevelSpecifics: { unitIndex: 0 } } }, disabled: !skillId },
      { type: "separator", label: "⟡ XP STORY FARMING ⟡", value: "", disabled: true },
      { type: "separator", label: "(fast, unsafe, English only)", value: "", disabled: true },
      { type: "xp", label: "XP 50", value: "story", amount: 50, config: {} },
      { type: "xp", label: "XP 100", value: "story", amount: 100, config: { storyPayload: { happyHourBonusXp: 50 } } },
      { type: "xp", label: "XP 200", value: "story", amount: 200, config: { storyPayload: { happyHourBonusXp: 150 } } },
      { type: "xp", label: "XP 300", value: "story", amount: 300, config: { storyPayload: { happyHourBonusXp: 250 } } },
      { type: "xp", label: "XP 400", value: "story", amount: 400, config: { storyPayload: { happyHourBonusXp: 350 } } },
      { type: "xp", label: "XP 499", value: "story", amount: 499, config: { storyPayload: { happyHourBonusXp: 449 } } },
      { type: "separator", label: "⟡ STREAK FARMING ⟡", value: "", disabled: true },
      { type: "streak", label: "Streak farm (test)", value: "farm" }
    ];
  };

  const initSettings = () => {
    settingsManager.populateDefaultOptionSelect(farmOptions);
    settingsManager.loadDefaultFarmingOption(farmOptions);
    settingsManager.loadSettingsToUI();
  };

  (async () => {
    try {
      initInterface();
      setInterfaceVisible(false);
      addEventFloatingBtn();
      await initVariables();
      populateOptions();
      initSettings();
      updateUserInfo();
      addEventListeners();
      loadSavedSettings(settingsManager.getSettings());
      updateNotify('Hacklingo Pro ready! For safety, I suggest that you use 2nd accounts.\nLimited or no use of "Story Farming"!');
    } catch (err) {
      logError(err, "Hacklingo init error!");
    }
  })();
})();