Challengers Tower - Inventory Export

Adds an Export button to the Inventory page that copies a plain-text list of all items with tier, quantity, and equipped status

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

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

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         Challengers Tower - Inventory Export
// @description  Adds an Export button to the Inventory page that copies a plain-text list of all items with tier, quantity, and equipped status
// @namespace    https://challengerstower.com
// @version      1.7.0
// @author       Zach
// @license      MIT
// @match        *://challengerstower.com/*
// @run-at       document-end
// @grant        none
// ==/UserScript==

function ctCopy(str) {
  var ta = document.createElement("textarea");
  ta.value = str;
  ta.style.position = "fixed";
  ta.style.left = "-9999px";
  document.body.appendChild(ta);
  ta.select();
  var ok = false;
  try { ok = document.execCommand("copy"); } catch(e) {}
  document.body.removeChild(ta);
  return ok;
}

function ctGetAlpineData(el) {
  // Try different Alpine versions' internal property names
  if(el._x_dataStack && el._x_dataStack[0]) return el._x_dataStack[0];
  if(el.__x && el.__x.$data) return el.__x.$data;
  if(el._x_data) return el._x_data;
  try {
    if(typeof Alpine !== "undefined" && Alpine.$data) return Alpine.$data(el);
  } catch(e) {}
  return null;
}

function ctFindGroups() {
  var els = document.querySelectorAll("[x-data]");
  for(var i=0; i<els.length; i++) {
    var data = ctGetAlpineData(els[i]);
    if(data && data.groups && data.groups.length > 0) {
      return data.groups;
    }
  }
  return null;
}

function ctScrapeDOM() {
  // Fallback: scrape from rendered DOM, inventory section only
  var results = [];
  var seen = {};

  // Find the inventory section by looking for the "Inventory" heading
  var headings = document.querySelectorAll("h3");
  var inventorySection = null;
  for(var i=0; i<headings.length; i++) {
    if(headings[i].textContent.trim() === "Inventory") {
      inventorySection = headings[i].closest(".bg-white, .bg-gray-800");
      break;
    }
  }

  var container = inventorySection || document;
  var nameEls = container.querySelectorAll("span.font-medium");

  nameEls.forEach(function(el) {
    var name = el.textContent.trim();
    if(!name || seen[name]) return;

    var card = el.closest(".bg-gray-100, .rounded-lg");
    if(!card) return;
    var cardText = card.textContent || "";

    var tierMatch = cardText.match(/\bT([0-9]+)\b/);
    var tier = tierMatch ? "T" + tierMatch[1] : "";
    var isUlt = /\bULT\b/.test(cardText) ? " [ULT]" : "";
    var equipped = /Equipped/i.test(cardText) ? " [Equipped]" : "";
    var upgradeMatch = cardText.match(/T[0-9]+:\s*[0-9]+%/);
    var progress = upgradeMatch ? " (" + upgradeMatch[0] + ")" : "";

    seen[name] = true;
    results.push(name + (tier ? " [" + tier + "]" : "") + isUlt + progress + equipped);
  });

  return results.length > 0 ? results : null;
}

function ctScrape() {
  var groups = ctFindGroups();
  if(groups) {
    return groups.map(function(g) {
      var tier = "T" + g.highest_tier;
      var isUlt = g.item.ultimate ? " [ULT]" : "";
      var count = g.total_count > 1 ? " x" + g.total_count : "";
      var progress = g.maxed_out ? " [MAX]" : " (T" + g.next_tier + ": " + Math.round(g.progress) + "%)";
      var equipped = g.equipped_tiers.length > 0 ? " [Equipped]" : "";
      return g.item.name + " [" + tier + "]" + isUlt + count + progress + equipped;
    });
  }
  // Fall back to DOM scraping
  return ctScrapeDOM();
}

function ctAddButton() {
  if(document.getElementById("ct-inv-export")) return;
  if(!/\/items/.test(window.location.href)) return;

  var btn = document.createElement("button");
  btn.id = "ct-inv-export";
  btn.textContent = "Export Inventory";
  btn.style.cssText = "position:fixed;bottom:80px;right:16px;z-index:99999;padding:10px 16px;background:#4f8ef7;color:#fff;border:none;border-radius:8px;font-weight:bold;font-size:14px;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,0.35);";

  btn.onclick = function() {
    var lines = ctScrape();
    if(!lines) {
      btn.textContent = "Not ready yet";
      btn.style.background = "#e74c3c";
      setTimeout(function() {
        btn.textContent = "Export Inventory";
        btn.style.background = "#4f8ef7";
      }, 2000);
      return;
    }
    var out = ["=== CT Inventory (" + lines.length + " items) ==="].concat(lines).join("\n");
    console.log(out);
    var ok = ctCopy(out);
    btn.textContent = ok ? "Copied! (" + lines.length + ")" : "See console";
    btn.style.background = ok ? "#2ecc71" : "#e74c3c";
    setTimeout(function() {
      btn.textContent = "Export Inventory";
      btn.style.background = "#4f8ef7";
    }, 3000);
  };

  document.body.appendChild(btn);
}

var ctAttempts = 0;
var ctPoll = setInterval(function() {
  ctAttempts++;
  if(!/\/items/.test(window.location.href)) {
    clearInterval(ctPoll);
    return;
  }
  if(document.body && (ctFindGroups() || ctAttempts >= 40)) {
    ctAddButton();
    clearInterval(ctPoll);
  }
}, 500);