Greasy Fork is available in English.

Wikigacha Auto Player

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

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

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

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

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

이 스크립트를 설치하려면 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://greatest.deepsurf.us/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);
  });

})();