Wikigacha Auto Player

Automatically opens packs, watches ads, and selects the best 10 cards for daily raid battles on wikigacha.com

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name        Wikigacha Auto Player
// @description Automatically opens packs, watches ads, and selects the best 10 cards for daily raid battles on wikigacha.com
// @match       https://wikigacha.com/*
// @grant       none
// @run-at      document-start
// @version 0.0.1.20260322204210
// @namespace https://greasyfork.org/users/1583423
// ==/UserScript==

(function () {
  'use strict';

  // Inject alert override directly into page context
  const script = document.createElement('script');
  script.textContent = `
    window.alert = function(msg) {
      console.log('[Wikigacha] Alert suppressed:', msg);
    };
    window.confirm = function() { return true; };
  `;
  (document.head || document.documentElement).appendChild(script);
  script.remove();

  const INTERVAL = 300;
  const SCAN_RARITIES = ['LR', 'UR'];

  function findButtonByText(text) {
    return [...document.querySelectorAll('button')].find(
      b => b.textContent.trim().toLowerCase() === text.toLowerCase()
    );
  }

  function tryOpenPack() {
    const pack = document.querySelector('img[alt="Wiki Pack"]');
    if (pack) {
      pack.closest('button, div[role="button"], div')?.click() || pack.click();
      return true;
    }
    const tapBtn = [...document.querySelectorAll('*')].find(
      el => el.textContent.trim() === '▲ TAP TO OPEN ▲'
    );
    if (tapBtn) { tapBtn.click(); return true; }
    return false;
  }

  function tryWatchAd() {
    const adBtn = [...document.querySelectorAll('span')].find(
      s => s.textContent.trim() === '📺 Watch Ad to Refill'
    );
    if (adBtn) { adBtn.closest('button')?.click() || adBtn.click(); return true; }
    return false;
  }

  function tryCloseAd() {
    const closeBtn = findButtonByText('Close Ad');
    if (closeBtn) { closeBtn.click(); return true; }
    return false;
  }

  function tryBackToPacks() {
    const backBtn = findButtonByText('BACK TO PACKS');
    if (backBtn) { backBtn.click(); return true; }
    return false;
  }

  function tryRecoverFromError() {
    const collectionBtn = [...document.querySelectorAll('button')].find(
      b => b.textContent.trim() === 'Collection'
    );
    if (collectionBtn) {
      collectionBtn.click();
      setTimeout(() => {
        const packsBtn = [...document.querySelectorAll('button')].find(
          b => b.textContent.trim() === 'Packs'
        );
        if (packsBtn) packsBtn.click();
      }, 1500);
      return true;
    }
    return false;
  }

  // ── RAID AUTO-SELECT ────────────────────────────────────────────────

  let raidRunning = false;

  async function sleep(ms) {
    return new Promise(r => setTimeout(r, ms));
  }

  function getRowsFromPage() {
    return [...document.querySelectorAll('tbody tr')].map(row => {
      const cells = row.querySelectorAll('td');
      if (cells.length < 6) return null;
      const rarity = cells[2]?.textContent.trim();
      const atk = parseInt(cells[4]?.textContent.trim().replace(/,/g, ''), 10) || 0;
      const def = parseInt(cells[5]?.textContent.trim().replace(/,/g, ''), 10) || 0;
      const plusBtn = cells[0]?.querySelector('button:last-child');
      return { rarity, atk, def, total: atk + def, plusBtn };
    }).filter(Boolean);
  }

  async function collectAllCards() {
    let allCards = [];

    // Reset to page 1
    while (true) {
      const prevBtn = [...document.querySelectorAll('button')].find(b => b.textContent.trim() === 'Prev');
      if (!prevBtn || prevBtn.disabled) break;
      prevBtn.click();
      await sleep(600);
    }

    while (true) {
      await sleep(500);
      const rows = getRowsFromPage();
      let hitLowerRarity = false;

      for (const row of rows) {
        if (!SCAN_RARITIES.includes(row.rarity)) {
          hitLowerRarity = true;
          break;
        }
        allCards.push(row);
      }

      if (hitLowerRarity) {
        console.log(`[Wikigacha] Hit lower rarity, stopping scan with ${allCards.length} cards`);
        break;
      }

      const nextBtn = [...document.querySelectorAll('button')].find(b => b.textContent.trim() === 'Next');
      if (!nextBtn || nextBtn.disabled) break;
      nextBtn.click();
    }

    return allCards;
  }

  async function autoSelectBestRaid() {
    if (raidRunning) return;
    raidRunning = true;
    console.log('[Wikigacha] Starting raid auto-select...');

    // Reset any existing selection first
    const resetBtn = findButtonByText('Reset');
    if (resetBtn) { resetBtn.click(); await sleep(400); }

    const allCards = await collectAllCards();
    console.log(`[Wikigacha] Scraped ${allCards.length} LR/UR cards`);

    // Sort by ATK+DEF descending, pick top 10 with available + button
    const top10 = allCards
      .filter(c => c.plusBtn && !c.plusBtn.disabled)
      .sort((a, b) => b.total - a.total)
      .slice(0, 10);

    console.log('[Wikigacha] Top 10 by ATK+DEF:', top10.map(c => `${c.rarity} ${c.total}`));

    // Reset to page 1 again to start clicking
    while (true) {
      const prevBtn = [...document.querySelectorAll('button')].find(b => b.textContent.trim() === 'Prev');
      if (!prevBtn || prevBtn.disabled) break;
      prevBtn.click();
      await sleep(600);
    }

    // Click + for each top 10 card by matching stats page by page
    let remaining = [...top10];

    while (remaining.length > 0) {
      await sleep(500);
      const rows = getRowsFromPage();

      for (const row of rows) {
        const match = remaining.findIndex(
          c => c.atk === row.atk && c.def === row.def && row.plusBtn && !row.plusBtn.disabled
        );
        if (match !== -1) {
          row.plusBtn.click();
          remaining.splice(match, 1);
          await sleep(150);
        }
      }

      if (remaining.length === 0) break;

      // Stop paginating once we're past LR/UR
      const rows2 = getRowsFromPage();
      const hitLower = rows2.some(r => !SCAN_RARITIES.includes(r.rarity));
      if (hitLower) break;

      const nextBtn = [...document.querySelectorAll('button')].find(b => b.textContent.trim() === 'Next');
      if (!nextBtn || nextBtn.disabled) break;
      nextBtn.click();
    }

    console.log('[Wikigacha] Raid selection done!');
    raidRunning = false;
  }

  function injectRaidButton() {
    if (document.getElementById('wg-raid-btn')) return;
    const target = [...document.querySelectorAll('button')].find(b => b.textContent.trim() === 'Back to Raid Top');
    if (!target) return;

    const btn = document.createElement('button');
    btn.id = 'wg-raid-btn';
    btn.textContent = '⚔️ Auto-Select Best 10';
    btn.style.cssText = `
      margin-left: 8px;
      padding: 4px 14px;
      border-radius: 9999px;
      border: 1px solid #22d3ee;
      background: rgba(8,145,178,0.2);
      color: #cffafe;
      font-size: 13px;
      cursor: pointer;
    `;
    btn.addEventListener('click', autoSelectBestRaid);
    target.parentElement.appendChild(btn);
  }

  // ── MAIN LOOP ────────────────────────────────────────────────────────

  let stuckCounter = 0;

  window.addEventListener('load', () => {
    setInterval(() => {
      injectRaidButton();

      if (raidRunning) return;

      if (tryCloseAd()) { stuckCounter = 0; return; }
      if (tryBackToPacks()) { stuckCounter = 0; return; }
      if (tryWatchAd()) { stuckCounter = 0; return; }
      if (tryOpenPack()) { stuckCounter = 0; return; }

      stuckCounter++;
      if (stuckCounter >= 5) {
        const onPackPage = !!document.querySelector('img[alt="Wiki Pack"]') ||
          [...document.querySelectorAll('span')].some(s => s.textContent.trim() === '📺 Watch Ad to Refill');
        if (onPackPage) {
          console.log('[Wikigacha] Stuck on pack page, attempting recovery...');
          tryRecoverFromError();
        }
        stuckCounter = 0;
      }
    }, INTERVAL);
  });

})();