WOS Giftcode Helper

Adds multi-ID management and fast switching to the WOS Giftcode site

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         WOS Giftcode Helper
// @version      2.5.0
// @author       IlIl
// @match        *://wos-giftcode.centurygame.com/*
// @match        *://wos-giftcode.centurygame.com
// @icon         https://wos-giftcode.centurygame.com/favicon.ico
// @grant        none
// @namespace https://greasyfork.org/users/373220
// @description Adds multi-ID management and fast switching to the WOS Giftcode site
// ==/UserScript==

(function () {
  'use strict';

  const tag = document.createElement('div');
  tag.textContent = 'WOS ID Helper Loaded';
  tag.style.position = 'fixed';
  tag.style.right = '8px';
  tag.style.bottom = '8px';
  tag.style.zIndex = '99999';
  tag.style.padding = '4px 8px';
  tag.style.background = 'rgba(0,0,0,.6)';
  tag.style.color = '#fff';
  tag.style.fontSize = '12px';
  tag.style.borderRadius = '4px';
  document.documentElement.appendChild(tag);

  const IDS_KEY = 'wos_multi_ids';
  const INDEX_KEY = 'wos_current_idx';

  let sessionGiftCode = '';

  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

  function loadIds() {
    try {
      const raw = localStorage.getItem(IDS_KEY);
      if (!raw) return [];
      const arr = JSON.parse(raw);
      if (!Array.isArray(arr)) return [];
      return arr
        .map((x) => String(x).trim())
        .filter((x, i, a) => x && a.indexOf(x) === i);
    } catch (e) {
      return [];
    }
  }

  function saveIds(list) {
    const clean = list
      .map((x) => String(x).trim())
      .filter((x, i, a) => x && a.indexOf(x) === i);
    localStorage.setItem(IDS_KEY, JSON.stringify(clean));
    return clean;
  }

  function loadIndex(max) {
    const v = parseInt(localStorage.getItem(INDEX_KEY), 10);
    if (isNaN(v) || v < 0) return 0;
    if (typeof max === 'number' && max > 0 && v >= max) return 0;
    return v;
  }

  function saveIndex(idx) {
    localStorage.setItem(INDEX_KEY, String(idx || 0));
  }

  function isVisible(el) {
    if (!el) return false;
    const style = window.getComputedStyle(el);
    if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') {
      return false;
    }
    const rects = el.getClientRects();
    return rects && rects.length > 0;
  }

  function bindGiftCodePersistence() {
    const giftInput =
      document.querySelector('.code_con input[placeholder="请输入兑换码"]') ||
      document.querySelector('.code_con input[type="text"]');

    const exchangeBtn = document.querySelector('.btn.exchange_btn');

    if (!giftInput) return;

    if (sessionGiftCode) {
      giftInput.value = sessionGiftCode;
      giftInput.dispatchEvent(new Event('input', { bubbles: true }));
    }

    if (!giftInput._wosGiftBound) {
      giftInput._wosGiftBound = true;
      giftInput.addEventListener('input', () => {
        sessionGiftCode = giftInput.value;
      });
    }

    if (exchangeBtn && !exchangeBtn._wosGiftBound) {
      exchangeBtn._wosGiftBound = true;
      exchangeBtn.addEventListener('click', () => {
        sessionGiftCode = giftInput.value;
      });
    }
  }

  const I18N = {
    'zh-CN': {
      addIdButton: '添加ID',
      manageTitle: '管理ID',
      switchTitle: '切换ID',
      save: '保存',
      hint: '提示:第一个ID会在打开页面时自动登录。',
      onlyOneId: '当前只保存了一个ID,无法切换。',
      atLeastOne: '至少保留一个ID'
    },
    'zh-TW': {
      addIdButton: '新增ID',
      manageTitle: '管理ID',
      switchTitle: '切換ID',
      save: '儲存',
      hint: '提示:第一個ID會在打開頁面時自動登入。',
      onlyOneId: '目前只儲存了一個ID,無法切換。',
      atLeastOne: '至少保留一個ID'
    },
    'en': {
      addIdButton: 'Add ID',
      manageTitle: 'Manage IDs',
      switchTitle: 'Switch ID',
      save: 'Save',
      hint: 'Hint: the first ID will be auto-logged in when you open the page.',
      onlyOneId: 'Only one ID saved, cannot switch.',
      atLeastOne: 'Please keep at least one ID.'
    },
    'fr': {
      addIdButton: 'Ajouter ID',
      manageTitle: 'Gérer les ID',
      switchTitle: 'Changer d’ID',
      save: 'Enregistrer',
      hint: 'Astuce : le premier ID sera automatiquement connecté à l’ouverture de la page.',
      onlyOneId: 'Un seul ID est enregistré, impossible de changer.',
      atLeastOne: 'Veuillez garder au moins un ID.'
    },
    'ja': {
      addIdButton: 'IDを追加',
      manageTitle: 'ID管理',
      switchTitle: 'ID切り替え',
      save: '保存',
      hint: 'ヒント:一番上のIDはページを開いたとき自動的にログインされます。',
      onlyOneId: '保存されているIDは1つだけのため、切り替えできません。',
      atLeastOne: '少なくとも1つのIDを残してください。'
    },
    'ko': {
      addIdButton: 'ID 추가',
      manageTitle: 'ID 관리',
      switchTitle: 'ID 전환',
      save: '저장',
      hint: '팁: 첫 번째 ID는 페이지를 열면 자동으로 로그인됩니다.',
      onlyOneId: '저장된 ID가 하나뿐이어서 전환할 수 없습니다.',
      atLeastOne: '최소한 하나의 ID는 남겨 두세요.'
    },
    'de': {
      addIdButton: 'ID hinzufügen',
      manageTitle: 'IDs verwalten',
      switchTitle: 'ID wechseln',
      save: 'Speichern',
      hint: 'Hinweis: Die erste ID wird beim Öffnen der Seite automatisch eingeloggt.',
      onlyOneId: 'Es ist nur eine ID gespeichert, ein Wechsel ist nicht möglich.',
      atLeastOne: 'Bitte behalten Sie mindestens eine ID.'
    },
    'ar': {
      addIdButton: 'إضافة معرّف',
      manageTitle: 'إدارة المعرّفات',
      switchTitle: 'تبديل المعرّف',
      save: 'حفظ',
      hint: 'ملاحظة: سيتم تسجيل الدخول تلقائيًا باستخدام أوّل معرّف عند فتح الصفحة.',
      onlyOneId: 'يوجد معرّف واحد فقط، لا يمكن التبديل.',
      atLeastOne: 'يجب الاحتفاظ بمعرّف واحد على الأقل.'
    },
    'es': {
      addIdButton: 'Añadir ID',
      manageTitle: 'Gestionar ID',
      switchTitle: 'Cambiar ID',
      save: 'Guardar',
      hint: 'Consejo: el primer ID se iniciará sesión automáticamente al abrir la página.',
      onlyOneId: 'Solo hay un ID guardado, no se puede cambiar.',
      atLeastOne: 'Mantén al menos un ID.'
    },
    'pt': {
      addIdButton: 'Adicionar ID',
      manageTitle: 'Gerir IDs',
      switchTitle: 'Trocar ID',
      save: 'Salvar',
      hint: 'Dica: o primeiro ID será conectado automaticamente ao abrir a página.',
      onlyOneId: 'Há apenas um ID salvo, não é possível trocar.',
      atLeastOne: 'Mantenha pelo menos um ID.'
    }
  };

  let currentLang = null;

  function detectLang() {
    const cur = document.querySelector('.lang_switch .cur_lang');
    if (cur) {
      const txt = cur.textContent.trim();
      if (txt.includes('简体')) return 'zh-CN';
      if (txt.includes('繁體') || txt.includes('繁体')) return 'zh-TW';
      if (/English/i.test(txt)) return 'en';
      if (txt.includes('Français')) return 'fr';
      if (txt.includes('日本')) return 'ja';
      if (txt.includes('한국')) return 'ko';
      if (txt.includes('Deutsch')) return 'de';
      if (txt.includes('العربية') || txt.includes('عرب')) return 'ar';
      if (txt.includes('Español') || txt.includes('Espa')) return 'es';
      if (txt.includes('Português') || txt.includes('Portugu')) return 'pt';
    }

    const nav = (navigator.language || '').toLowerCase();
    if (nav.startsWith('zh-tw') || nav.startsWith('zh-hk')) return 'zh-TW';
    if (nav.startsWith('zh')) return 'zh-CN';
    if (nav.startsWith('fr')) return 'fr';
    if (nav.startsWith('ja')) return 'ja';
    if (nav.startsWith('ko')) return 'ko';
    if (nav.startsWith('de')) return 'de';
    if (nav.startsWith('ar')) return 'ar';
    if (nav.startsWith('es')) return 'es';
    if (nav.startsWith('pt')) return 'pt';
    if (nav.startsWith('en')) return 'en';

    return 'en';
  }

  function t(key) {
    let lang = currentLang || detectLang();
    if (!I18N[lang]) lang = 'en'; 
    const dict = I18N[lang] || I18N['en'];
    return dict[key] || I18N['en'][key] || key;
  }

  currentLang = detectLang();

  let loginInited = false;

  function setupLoginPage() {
    if (loginInited) return;

    const idInput =
      document.querySelector('.roleId_con input[type="text"]') ||
      document.querySelector('.roleId_con input[placeholder]') ||
      document.querySelector('input[placeholder="角色ID"]') ||
      document.querySelector('input[placeholder="请输入角色ID"]');

    const loginBtn = document.querySelector('.btn.login_btn');

    if (!idInput || !loginBtn) return;

    loginInited = true;

    let ids = loadIds();
    if (ids.length > 0) {
      const idx = loadIndex(ids.length);
      const id = ids[idx] || ids[0];

      if (id) {
        idInput.value = id;
        idInput.dispatchEvent(new Event('input', { bubbles: true }));
        if (loginBtn.classList.contains('disabled')) {
          loginBtn.classList.remove('disabled');
        }
        loginBtn.click();
      }
    }

    loginBtn.addEventListener('click', () => {
      const val = idInput.value.trim();
      if (!val) return;

      let list = loadIds();
      let idx = list.indexOf(val);
      if (idx === -1) {
        list.push(val);
        list = saveIds(list);
        idx = list.indexOf(val);
      }
      saveIndex(idx);
    });
  }

  let loggedInInited = false;
  let btnWrap = null;
  let managerContainer = null;
  let isManaging = false;

  const ADD_ICON = (() => {
    const svg =
      '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">' +
      '<rect x="10" y="4" width="4" height="16" fill="white"/>' +
      '<rect x="4" y="10" width="16" height="4" fill="white"/>' +
      '</svg>';
    return 'data:image/svg+xml;utf8,' + encodeURIComponent(svg);
  })();

  const SWITCH_ICON = (() => {
    const svg =
      '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">' +
      '<path d="M5 7h11l-3-3 1.4-1.4L20.8 7 14.4 11.4 13 10l3-3H5z" fill="white"/>' +
      '<path d="M19 17H8l3 3-1.4 1.4L3.2 17 9.6 12.6 11 14l-3 3h11z" fill="white"/>' +
      '</svg>';
    return 'data:image/svg+xml;utf8,' + encodeURIComponent(svg);
  })();

  function styleSmallSquareButton(btn, iconDataUrl) {
    btn.style.width = '0.64rem';
    btn.style.height = '0.6rem';
    btn.style.borderRadius = '0.16rem';
    btn.style.border = 'none';
    btn.style.backgroundColor = '#20609f';
    btn.style.display = 'flex';
    btn.style.alignItems = 'center';
    btn.style.justifyContent = 'center';
    btn.style.cursor = 'pointer';
    btn.style.padding = '0';

    if (iconDataUrl) {
      btn.style.backgroundImage = `url("${iconDataUrl}")`;
      btn.style.backgroundRepeat = 'no-repeat';
      btn.style.backgroundPosition = 'center';
      btn.style.backgroundSize = '.48rem auto';
    } else {
      btn.style.backgroundImage = 'none';
    }
  }

  function buildManagerUI(container) {
    container.innerHTML = '';

    const templateInput =
      document.querySelector('.code_con input[type="text"]') ||
      document.querySelector('.code_con .input_wrap input') ||
      document.querySelector('.code_con input');
    const inputStyle = templateInput ? window.getComputedStyle(templateInput) : null;

    const exchangeBtnTemplate = document.querySelector('.btn.exchange_btn');
    const exchangeStyle = exchangeBtnTemplate ? window.getComputedStyle(exchangeBtnTemplate) : null;

    const listWrapper = document.createElement('div');
    listWrapper.style.display = 'flex';
    listWrapper.style.flexDirection = 'column';
    listWrapper.style.gap = '0.1rem';
    listWrapper.style.maxHeight = '3.2rem';
    listWrapper.style.overflowY = 'auto';

    function refreshIndexesAndDeleteButton() {
      const rows = Array.from(listWrapper.children);
      rows.forEach((row, i) => {
        const label = row.querySelector('.wos-id-label');
        if (label) label.textContent = (i + 1) + '.';

        const delBtn = row.querySelector('button[data-del]');
        if (delBtn) {
          delBtn.style.visibility = rows.length > 1 ? 'visible' : 'hidden';
        }
      });
    }

    function createRow(value) {
      const row = document.createElement('div');
      row.style.display = 'flex';
      row.style.alignItems = 'center';
      row.style.gap = '0.08rem';

      const label = document.createElement('span');
      label.className = 'wos-id-label';
      label.style.width = '0.35rem';
      label.style.fontSize = '0.35rem';
      label.style.color = '#fff';

      const input = document.createElement('input');
      input.type = 'text';
      input.setAttribute('data-wos-id-input', '1');
      input.value = value || '';
      input.maxLength = 20;
      input.style.flex = '1';

      if (inputStyle) {
        input.style.height = inputStyle.height;
        input.style.fontSize = inputStyle.fontSize;
        input.style.padding = inputStyle.padding;
        input.style.borderRadius = inputStyle.borderRadius;
        input.style.border = inputStyle.border;
      } else {
        input.style.height = '0.44rem';
        input.style.fontSize = '0.18rem';
        input.style.borderRadius = '0.06rem';
        input.style.border = 'none';
        input.style.padding = '0 0.1rem';
      }

      const delBtn = document.createElement('button');
      delBtn.setAttribute('data-del', '1');

      const minusBar = document.createElement('span');
      minusBar.style.display = 'block';
      minusBar.style.width = '70%';           
      minusBar.style.height = '0.08rem';      
      minusBar.style.borderRadius = '500px';  
      minusBar.style.background = '#1b3b5d';  
      delBtn.appendChild(minusBar);

      const inputHeight = inputStyle ? inputStyle.height : '0.4rem';
      delBtn.style.height = inputHeight;
      delBtn.style.width = inputHeight; 
      delBtn.style.borderRadius = inputStyle ? inputStyle.borderRadius : '0.06rem';
      delBtn.style.border = 'none';
      delBtn.style.background = '#d0d7e6';
      delBtn.style.color = '#1b3b5d';
      delBtn.style.fontSize = '0.35rem';
      delBtn.style.fontWeight = 'bold';
      delBtn.style.display = 'flex';
      delBtn.style.alignItems = 'center';
      delBtn.style.justifyContent = 'center';
      delBtn.style.cursor = 'pointer';

      delBtn.addEventListener('click', () => {
        listWrapper.removeChild(row);
        refreshIndexesAndDeleteButton();
      });

      row.appendChild(label);
      row.appendChild(input);
      row.appendChild(delBtn);
      listWrapper.appendChild(row);
      refreshIndexesAndDeleteButton();
    }

    const ids = loadIds();
    if (ids.length > 0) {
      ids.forEach((id) => createRow(id));
    } else {
      createRow('');
    }

    const addRowBtn = document.createElement('button');
    addRowBtn.textContent = '+ ' + t('addIdButton');
    addRowBtn.style.marginTop = '0.16rem';
    addRowBtn.style.width = '100%';
    addRowBtn.style.height = inputStyle ? inputStyle.height : '0.44rem';
    addRowBtn.style.borderRadius = inputStyle ? inputStyle.borderRadius : '0.06rem';
    addRowBtn.style.border = 'none';
    addRowBtn.style.background = '#e5edf9';
    addRowBtn.style.color = '#1b3b5d';
    addRowBtn.style.fontSize = inputStyle ? inputStyle.fontSize : '0.18rem';
    addRowBtn.style.cursor = 'pointer';

    addRowBtn.addEventListener('click', () => {
      const inputs = listWrapper.querySelectorAll('input[data-wos-id-input]');
      const lastInput = inputs[inputs.length - 1];
      if (lastInput && !lastInput.value.trim()) {
        lastInput.focus();
        return;
      }
      createRow('');
      const rows = listWrapper.querySelectorAll('input[data-wos-id-input]');
      const newest = rows[rows.length - 1];
      if (newest) newest.focus();
    });

    const saveBtn = document.createElement('button');
    saveBtn.textContent = t('save');
    saveBtn.style.marginTop = '0.16rem';
    saveBtn.style.width = '100%';

    if (exchangeStyle) {
      saveBtn.style.height = exchangeStyle.height;
      saveBtn.style.borderRadius = exchangeStyle.borderRadius;
      saveBtn.style.fontSize = exchangeStyle.fontSize;
      saveBtn.style.fontWeight = exchangeStyle.fontWeight;
      saveBtn.style.lineHeight = exchangeStyle.lineHeight;
      saveBtn.style.backgroundColor = exchangeStyle.backgroundColor;
      saveBtn.style.backgroundImage = exchangeStyle.backgroundImage;
      saveBtn.style.backgroundSize = exchangeStyle.backgroundSize;
      saveBtn.style.backgroundRepeat = exchangeStyle.backgroundRepeat;
      saveBtn.style.color = exchangeStyle.color;
      saveBtn.style.border = exchangeStyle.border;
    } else {
      saveBtn.style.height = '0.44rem';
      saveBtn.style.borderRadius = '0.06rem';
      saveBtn.style.border = 'none';
      saveBtn.style.background = '#c2ccd8';
      saveBtn.style.color = '#1b3b5d';
      saveBtn.style.fontSize = '0.18rem';
    }
    saveBtn.style.cursor = 'pointer';

    const hint = document.createElement('div');
    hint.textContent = t('hint');
    hint.style.marginTop = '0.12rem';
    hint.style.fontSize = '0.16rem';
    hint.style.color = '#c5d3ea';

    container.appendChild(listWrapper);
    container.appendChild(addRowBtn);
    container.appendChild(saveBtn);
    container.appendChild(hint);

    saveBtn.addEventListener('click', () => {
      const inputs = listWrapper.querySelectorAll('input[data-wos-id-input]');
      const list = [];
      inputs.forEach((inp) => {
        const v = inp.value.trim();
        if (v && !list.includes(v)) list.push(v);
      });

      if (list.length === 0) {
        alert(t('atLeastOne'));
        return;
      }

      saveIds(list);
      saveIndex(0);
      leaveManageMode();
    });
  }

  let originalTitle = '';
  let originalButtonText = '';

  function enterManageMode() {
    if (isManaging) return;
    isManaging = true;

    const mainContent = document.querySelector('.content_wrapper .main_content');
    const titleEl = document.querySelector('.content_wrapper .title');
    const exchangeBtn = document.querySelector('.btn.exchange_btn');

    if (!mainContent || !titleEl || !exchangeBtn || !managerContainer) return;

    originalTitle = titleEl.textContent.trim();
    originalButtonText = exchangeBtn.textContent.trim();

    Array.from(mainContent.children).forEach((child) => {
      if (child !== managerContainer) {
        child.style.display = 'none';
      }
    });

    exchangeBtn.style.display = 'none';

    titleEl.textContent = t('manageTitle');

    buildManagerUI(managerContainer);
    managerContainer.style.display = 'block';

    updateButtonsVisibility();
  }

  function leaveManageMode() {
    if (!isManaging) return;
    isManaging = false;

    const mainContent = document.querySelector('.content_wrapper .main_content');
    const titleEl = document.querySelector('.content_wrapper .title');
    const exchangeBtn = document.querySelector('.btn.exchange_btn');

    if (!mainContent || !titleEl || !exchangeBtn || !managerContainer) return;

    Array.from(mainContent.children).forEach((child) => {
      if (child !== managerContainer) {
        child.style.display = '';
      }
    });

    managerContainer.style.display = 'none';

    if (originalTitle) titleEl.textContent = originalTitle;
    exchangeBtn.textContent = originalButtonText || exchangeBtn.textContent;
    exchangeBtn.style.display = '';

    bindGiftCodePersistence();
    updateButtonsVisibility();
  }

  function updateButtonsVisibility() {
    if (!btnWrap) return;
    const exitCon = document.querySelector('.roleInfo_con .exit_con');
    const roleIdCon = document.querySelector('.roleId_con');
    const shouldShow = exitCon && isVisible(exitCon) && !roleIdCon && !isManaging;
    btnWrap.style.display = shouldShow ? 'flex' : 'none';
  }

  function setupLoggedInPage() {
    if (loggedInInited) {
      bindGiftCodePersistence();
      updateButtonsVisibility();
      return;
    }

    const roleBar = document.querySelector('.roleInfo_con');
    const exitCon = roleBar && roleBar.querySelector('.exit_con');
    const mainContent = document.querySelector('.content_wrapper .main_content');

    if (!roleBar || !exitCon || !mainContent) return;

    loggedInInited = true;

    const rbStyle = window.getComputedStyle(roleBar);
    if (rbStyle.position === 'static' || !rbStyle.position) {
      roleBar.style.position = 'relative';
    }

    btnWrap = document.createElement('div');
    btnWrap.style.display = 'flex';
    btnWrap.style.flexDirection = 'row';
    btnWrap.style.alignItems = 'center';
    btnWrap.style.gap = '0.2rem';
    btnWrap.style.zIndex = '5';

    const addBtn = document.createElement('button');
    addBtn.textContent = '';
    addBtn.title = t('manageTitle');
    styleSmallSquareButton(addBtn, ADD_ICON);

    const switchBtn = document.createElement('button');
    switchBtn.textContent = '';
    switchBtn.title = t('switchTitle');
    styleSmallSquareButton(switchBtn, SWITCH_ICON);

    btnWrap.appendChild(addBtn);
    btnWrap.appendChild(switchBtn);
    roleBar.appendChild(btnWrap);

    function adjustButtonsPosition() {
      const roleRect = roleBar.getBoundingClientRect();
      const exitRect = exitCon.getBoundingClientRect();
      const top = exitRect.top - roleRect.top;
      const gapPx = 12;
      const rightDist = roleRect.right - exitRect.left + gapPx;
      btnWrap.style.position = 'absolute';
      btnWrap.style.top = top + 'px';
      btnWrap.style.right = rightDist + 'px';
    }

    adjustButtonsPosition();
    window.addEventListener('resize', adjustButtonsPosition);

    managerContainer = document.createElement('div');
    managerContainer.id = 'wos-id-manager';
    managerContainer.style.display = 'none';
    managerContainer.style.marginTop = '0.3rem';
    mainContent.appendChild(managerContainer);

    addBtn.addEventListener('click', () => {
      enterManageMode();
    });

    switchBtn.addEventListener('click', async () => {
      const ids = loadIds();
      if (ids.length <= 1) {
        alert(t('onlyOneId'));
        return;
      }

      const idx = loadIndex(ids.length);
      const nextIdx = (idx + 1) % ids.length;
      saveIndex(nextIdx);
      const targetId = ids[nextIdx];

      exitCon.click();

      let idInput, loginBtn;
      const timeout = 8000;
      const start = Date.now();

      while (Date.now() - start < timeout) {
        idInput =
          document.querySelector('.roleId_con input[type="text"]') ||
          document.querySelector('.roleId_con input[placeholder]') ||
          document.querySelector('input[placeholder="角色ID"]') ||
          document.querySelector('input[placeholder="请输入角色ID"]');

        loginBtn = document.querySelector('.btn.login_btn');

        if (idInput && loginBtn && isVisible(idInput) && isVisible(loginBtn)) {
          break;
        }
        await sleep(100);
      }

      if (!idInput || !loginBtn) {
        console.warn('没有在预期时间内找到登录页面元素,切换失败。');
        return;
      }

      idInput.value = targetId;
      idInput.dispatchEvent(new Event('input', { bubbles: true }));
      if (loginBtn.classList.contains('disabled')) {
        loginBtn.classList.remove('disabled');
      }

      await sleep(150);
      loginBtn.click();
    });

    bindGiftCodePersistence();
    updateButtonsVisibility();
  }


  function onLanguageChanged() {
    if (btnWrap) {
      const buttons = btnWrap.querySelectorAll('button');
      if (buttons[0]) buttons[0].title = t('manageTitle');
      if (buttons[1]) buttons[1].title = t('switchTitle');
    }

    if (isManaging && managerContainer) {
      const titleEl = document.querySelector('.content_wrapper .title');
      if (titleEl) {
        titleEl.textContent = t('manageTitle');
      }
      managerContainer.innerHTML = '';
      buildManagerUI(managerContainer);
    }
  }


  function detectAndInit() {
    const roleIdInput =
      document.querySelector('.roleId_con input[type="text"]') ||
      document.querySelector('.roleId_con input[placeholder]') ||
      document.querySelector('input[placeholder="角色ID"]') ||
      document.querySelector('input[placeholder="请输入角色ID"]');

    const loggedRoleBar = document.querySelector('.roleInfo_con');

    if (roleIdInput && !isVisible(loggedRoleBar)) {
      setupLoginPage();
      updateButtonsVisibility();
    } else if (loggedRoleBar && isVisible(loggedRoleBar)) {
      setupLoggedInPage();
    } else {
      updateButtonsVisibility();
    }
  }

  const observer = new MutationObserver(() => {
    const newLang = detectLang();
    if (newLang !== currentLang) {
      currentLang = newLang;
      onLanguageChanged();
    }
    detectAndInit();
  });

  observer.observe(document.documentElement, {
    childList: true,
    subtree: true
  });

  detectAndInit();
})();