Steam Inventory Card to Badge Info

badge displaying when using the SteamDB extension in the steam inventory to display the badges, too tired to add more functionality tonight, there is a known bug where sometimes it wont fetch on first click, if that happens just click again

// ==UserScript==
// @name         Steam Inventory Card to Badge Info
// @namespace    github.com/encumber
// @version      1.15
// @description  badge displaying when using the SteamDB extension in the steam inventory to display the badges, too tired to add more functionality tonight, there is a known bug where sometimes it wont fetch on first click, if that happens just click again
// @match        https://steamcommunity.com/*/inventory*
// @grant        GM_xmlhttpRequest
// @connect      api.steamsets.com
// @run-at       document-idle
// ==/UserScript==

(function() {
  const API_SECRET = ''; //Steamsets API Key https://steamsets.com/settings/developer-apps

  // Inject CSS for badge styling and foil effect
  const style = document.createElement('style');
  style.textContent = `
  /* Common badge style for size and layout */
  .steam-badge-item {
      margin: 4px;
      flex: 0 0 auto;
      text-align: center;
      font-family: Arial, sans-serif;
      font-size: 10px; /* Adjust size here globally */
      width: 70px;     /* Adjust width here globally */
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
  }

  /* Foil style with animated shine overlay */
  .steam-badge-item.foil {
      position: relative;
      overflow: hidden;
      background-color: #222;
      border: 1px solid rgba(255, 255, 255, 0.1);
      box-shadow: 0 0 10px rgba(255, 255, 245, 0.1);
  }

  .steam-badge-item.foil::before {
      content: '';
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: linear-gradient(
          45deg,
          rgba(255, 255, 255, 0) 70%,
          rgba(255, 255, 255, 0.3) 50%,
          rgba(255, 255, 255, 0) 60%
      );
      background-size: 300% 150%;
      animation: shine 60s linear infinite;
      pointer-events: none;
  }

  @keyframes shine {
      0% { background-position: -100% 0; }
      100% { background-position: 200% 0; }
  }
  `;
  document.head.appendChild(style);

  console.log("[BadgeScript] Script initialized.");

  function displayBadges(div, badges, appId) {
    // Remove all badge info blocks *not* for this appId
    document.querySelectorAll('.steamdb_badge_info_extended').forEach(node => {
      if (node.getAttribute('data-appid') !== String(appId)) {
        node.remove();
      }
    });

    // Check if info for this appId already exists; if yes, do nothing
    const existingForApp = document.querySelector('.steamdb_badge_info_extended[data-appid="' + appId + '"]');
    if (existingForApp) {
      console.log('[BadgeScript] Badge info for appId', appId, 'already exists. Skipping creation.');
      return;
    }

    // Create new info block
    const infoDiv = document.createElement('div');
    infoDiv.className = 'steamdb_badge_info_extended';
    infoDiv.setAttribute('data-appid', appId);

    Object.assign(infoDiv.style, {
      width: '316px',
      marginTop: '8px',
      padding: '8px',
      border: '1px solid #ccc',
      display: 'flex',
      flexWrap: 'wrap',
      justifyContent: 'center',
      boxSizing: 'border-box'
    });

    // Sort badges: non-foil first, then foil
    badges = badges.filter(b => typeof b.baseLevel === 'number' && typeof b.isFoil === 'boolean');
    badges.sort((a, b) => {
      if (a.isFoil === b.isFoil) {
        return a.baseLevel - b.baseLevel;
      }
      return a.isFoil ? 1 : -1;
    });

    badges.forEach(badge => {
      const badgeEl = document.createElement('div');
      badgeEl.className = 'steam-badge-item';
      if (badge.isFoil) {
        badgeEl.classList.add('foil');
      }

      const nameEl = document.createElement('div');
      nameEl.textContent = badge.name || 'Badge';
      Object.assign(nameEl.style, {
        fontWeight: 'bold',
        fontSize: '12px',
        width: '60px',
        whiteSpace: 'nowrap',
        overflow: 'hidden',
        textOverflow: 'ellipsis'
      });

      const badgeImage = badge.badgeImage.replace(/\?.*$/, '');
      const imgUrl = `https://cdn.steamstatic.com/steamcommunity/public/images/items/${badge.appId}/${badgeImage}`;
      const imgEl = document.createElement('img');
      imgEl.src = imgUrl;
      imgEl.style.maxWidth = '60px';
      imgEl.style.maxHeight = '60px';

      const scarcityEl = document.createElement('div');
      scarcityEl.textContent = badge.scarcity || 'Scarcity';
      scarcityEl.style.fontSize = '14px';

      badgeEl.appendChild(nameEl);
      badgeEl.appendChild(imgEl);
      badgeEl.appendChild(scarcityEl);
      infoDiv.appendChild(badgeEl);
    });

    div.parentNode.insertBefore(infoDiv, div.nextSibling);
  }

  function fetchBadgeData(div) {
    if (div.dataset.processed) {
      console.log("[BadgeScript] Already processed, skipping:", div);
      return;
    }
    div.dataset.processed = 'true';

    console.log("[BadgeScript] Badge detected, will process after delay.");
    setTimeout(() => {
      const a = div.querySelector('a');
      if (!a) {
        console.log("[BadgeScript] No link inside badge div after delay, skipping:", div);
        return;
      }
      const href = a.getAttribute('href');
      const match = href.match(/\/(\d+)(?:\?.*)?$/);
      if (!match) {
        console.log("[BadgeScript] No appId found in href:", href);
        return;
      }
      const appId = parseInt(match[1], 10);
      console.log("[BadgeScript] AppId detected:", appId);

      // Skip if info for this appId already exists
      if (document.querySelector('.steamdb_badge_info_extended[data-appid="' + appId + '"]')) {
        console.log('[BadgeScript] Badge info for appId', appId, 'already exists. Skipping fetch.');
        return;
      }

      // Fetch badge data
      GM_xmlhttpRequest({
        method: 'POST',
        url: 'https://api.steamsets.com/v1/app.listBadges',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': 'Bearer ' + API_SECRET
        },
        data: JSON.stringify({ appId: appId }),
        onload: function(res) {
          console.log('GM_xmlhttpRequest status:', res.status);
          console.log('GM_xmlhttpRequest response:', res.responseText);
          if (res.status >= 200 && res.status < 300) {
            try {
              const data = JSON.parse(res.responseText);
              console.log('Parsed response:', data);
              if (data && data.badges) {
                displayBadges(div, data.badges, appId);
              } else {
                console.log("[BadgeScript] No badges in response for appId:", appId);
              }
            } catch (e) {
              console.error("[BadgeScript] JSON parse error:", e);
            }
          } else {
            console.error("[BadgeScript] Request failed with status:", res.status);
          }
        },
        onerror: function(err) {
          console.error("[BadgeScript] GM_xmlhttpRequest error:", err);
        }
      });
    }, 500);
  }

  // Setup DOM mutation observer
  const observer = new MutationObserver(mutations => {
    for (const m of mutations) {
      for (const node of m.addedNodes) {
        if (node.nodeType !== 1) continue;

        if (node.matches('div.steamdb_badge_info')) {
          console.log("[BadgeScript] Found badge div:", node);
          const hrefA = node.querySelector('a');
          if (hrefA) {
            const href = hrefA.getAttribute('href');
            const match = href.match(/\/(\d+)(?:\?.*)?$/);
            if (match) {
              const appId = parseInt(match[1], 10);
              // Remove info blocks for other appIds
              document.querySelectorAll('.steamdb_badge_info_extended').forEach(n => {
                if (n.getAttribute('data-appid') !== String(appId)) {
                  n.remove();
                }
              });
            }
          }
          fetchBadgeData(node);
        } else {
          node.querySelectorAll('div.steamdb_badge_info').forEach(n => {
            console.log("[BadgeScript] Found nested badge div:", n);
            const hrefA = n.querySelector('a');
            if (hrefA) {
              const href = hrefA.getAttribute('href');
              const match = href.match(/\/(\d+)(?:\?.*)?$/);
              if (match) {
                const appId = parseInt(match[1], 10);
                // Remove info blocks for other appIds
                document.querySelectorAll('.steamdb_badge_info_extended').forEach(n2 => {
                  if (n2.getAttribute('data-appid') !== String(appId)) {
                    n2.remove();
                  }
                });
              }
            }
            fetchBadgeData(n);
          });
        }
      }
    }
  });
  console.log("[BadgeScript] Starting MutationObserver");
  observer.observe(document.body, { childList: true, subtree: true });

  // Process existing badges at page load
  document.querySelectorAll('div.steamdb_badge_info').forEach(div => {
    console.log("[BadgeScript] Existing badge div:", div);
    const hrefA = div.querySelector('a');
    if (hrefA) {
      const href = hrefA.getAttribute('href');
      const match = href.match(/\/(\d+)(?:\?.*)?$/);
      if (match) {
        const appId = parseInt(match[1], 10);
        // Remove info blocks for other appIds
        document.querySelectorAll('.steamdb_badge_info_extended').forEach(n => {
          if (n.getAttribute('data-appid') !== String(appId)) {
            n.remove();
          }
        });
      }
    }
    fetchBadgeData(div);
  });
})();