Greasy Fork

Greasy Fork is available in English.

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);
  });
})();