Greasy Fork

Greasy Fork is available in English.

Steam Tracker App Info

Adds app category info to the booster game selector and provides a category summary with modified art area styling.

目前为 2025-05-09 提交的版本,查看 最新版本

// ==UserScript==
// @name         Steam Tracker App Info
// @namespace    https://github.com/encumber
// @version      0.8
// @description  Adds app category info to the booster game selector and provides a category summary with modified art area styling.
// @author       Nitoned
// @match        https://steamcommunity.com/tradingcards/boostercreator/*
// @grant        GM_xmlhttpRequest
// @connect      steam-tracker.com
// ==/UserScript==

(function() {
    'use strict';

    const boosterGameSelector = document.getElementById('booster_game_selector');
    const boosterCreatorArt = document.querySelector('.booster_creator_art'); // Get the art element by class

    if (!boosterGameSelector) {
        console.log('Booster game selector not found.');
        return;
    }

    if (!boosterCreatorArt) {
        console.log('Booster creator art element (class .booster_creator_art) not found.');
        // We can still add info to the selector, but won't have the summary location
    } else {
        // Modify the styling of the .booster_creator_art element
        boosterCreatorArt.style.right = '-50px';
        boosterCreatorArt.style.background = 'transparent';
        // You might need to adjust position if 'right' is not taking effect.
        // If its default position is static, making it relative or absolute might be necessary.
        // boosterCreatorArt.style.position = 'relative'; // Or 'absolute' depending on context
    }

    // Fetch the app list from Steam Tracker API
    GM_xmlhttpRequest({
        method: "GET",
        url: "https://steam-tracker.com/api?action=GetAppListV3",
        onload: function(response) {
            if (response.status === 200) {
                try {
                    const data = JSON.parse(response.responseText);

                    // Check if the parsed data is an object and has the 'removed_apps' key
                    if (data && typeof data === 'object' && data.removed_apps && Array.isArray(data.removed_apps)) {
                        const apps = data.removed_apps; // Access the array within the 'removed_apps' key
                        addAppInfoToSelector(apps);
                        if (boosterCreatorArt) {
                           createCategorySummary(apps, boosterCreatorArt); // Pass the art element
                        }
                    } else {
                        // Log the actual structure if it doesn't match
                        console.error('Steam Tracker API data not in expected format (expected object with "removed_apps" array). Received:', data);
                    }
                } catch (e) {
                    console.error('Error parsing Steam Tracker API response:', e);
                }
            } else {
                console.error('Error fetching Steam Tracker API data:', response.status, response.statusText);
            }
        },
        onerror: function(error) {
            console.error('Network error fetching Steam Tracker API data:', error);
        }
    });

    function addAppInfoToSelector(apps) {
        const appMap = new Map();
        apps.forEach(app => {
            // Ensure appid and category exist before adding to map
            if (app.appid !== undefined && app.category !== undefined) {
                 appMap.set(app.appid, app.category);
            } else {
                 console.warn('Skipping app with missing appid or category:', app);
            }
        });

        const options = boosterGameSelector.options;

        for (let i = 0; i < options.length; i++) {
            const option = options[i];
            // Ensure option value is a valid number before converting
            if (option.value && !isNaN(option.value)) {
                const appid = parseInt(option.value, 10);

                if (appMap.has(appid)) {
                    const category = appMap.get(appid);
                    // Only append if category is not null or empty string
                    if (category) {
                        option.text += ` - ${category}`;
                    }
                }
            }
        }
    }

    // Pass the target element to this function
    function createCategorySummary(apps, targetElement) {
        const appMap = new Map();
        apps.forEach(app => {
            if (app.appid !== undefined && app.category !== undefined) {
                 appMap.set(app.appid, app.category);
            }
        });

        const options = boosterGameSelector.options;
        const categoryCounts = new Map();

        // Calculate total game count (excluding the first option which is "Select a game")
        const totalGameCount = options.length > 0 ? options.length - 1 : 0;


        for (let i = 0; i < options.length; i++) {
            const option = options[i];
            if (option.value && !isNaN(option.value)) {
                const appid = parseInt(option.value, 10);

                if (appMap.has(appid)) {
                    const category = appMap.get(appid);
                    if (category) {
                        // Increment the count for this category
                        categoryCounts.set(category, (categoryCounts.get(category) || 0) + 1);
                    }
                }
            }
        }

        // Clear the existing content of the target element
        targetElement.innerHTML = '';

        // Create the summary content
        const summaryContainer = document.createElement('div');
        summaryContainer.id = 'steam-tracker-category-summary'; // Still good to have an ID for potential future targeting
        // Apply some basic styling to match Steam
        summaryContainer.style.padding = '15px';
        summaryContainer.style.backgroundColor = 'transparent'; // Background set on the parent .booster_creator_art
        summaryContainer.style.color = '#c7d5e0'; // Light text
        summaryContainer.style.fontFamily = '"Motiva Sans", Arial, Helvetica, sans-serif';
        summaryContainer.style.fontSize = '14px';
        summaryContainer.style.height = '100%'; // Make it fill the height of the art container
        summaryContainer.style.boxSizing = 'border-box'; // Include padding in height calculation
        // Note: OverflowY is now on the summaryContainer itself since it fills the art area
        summaryContainer.style.overflowY = 'auto';


        const title = document.createElement('h3');
        title.textContent = 'Steam Tracker Category Summary';
        title.style.color = '#ffffff'; // White title
        title.style.fontSize = '16px';
        title.style.fontWeight = 'normal';
        title.style.marginBottom = '10px';
        summaryContainer.appendChild(title);

        // Add the total game count
        const totalCountElement = document.createElement('p');
        totalCountElement.textContent = `Total Games: ${totalGameCount}`;
        totalCountElement.style.marginBottom = '10px';
        summaryContainer.appendChild(totalCountElement);


        if (categoryCounts.size > 0) {
            const summaryList = document.createElement('ul');
            summaryList.style.listStyle = 'none';
            summaryList.style.padding = '0';
            summaryList.style.margin = '0';
             // maxHeight is no longer needed on the list itself if the container scrolls
             // summaryList.style.maxHeight = 'calc(100% - 30px - ' + totalCountElement.offsetHeight + 'px)';


            // Sort categories alphabetically for consistent display
            const sortedCategories = Array.from(categoryCounts.keys()).sort();

            sortedCategories.forEach(category => {
                const count = categoryCounts.get(category);
                const listItem = document.createElement('li');
                listItem.textContent = `${category}: ${count}`;
                listItem.style.marginBottom = '5px';
                listItem.style.whiteSpace = 'nowrap'; // Prevent wrapping
                listItem.style.overflow = 'hidden'; // Hide overflowing text
                listItem.style.textOverflow = 'ellipsis'; // Add ellipsis for overflowing text

                summaryList.appendChild(listItem);
            });

            summaryContainer.appendChild(summaryList);
        } else {
            const noDataMessage = document.createElement('p');
            noDataMessage.textContent = 'No categorized apps found in the booster selector.';
            summaryContainer.appendChild(noDataMessage);
        }

         // Append the summary content to the target element
         // The targetElement is now .booster_creator_art itself
         targetElement.appendChild(summaryContainer);

         // The post-append height adjustment for the list is no longer needed
         // since the summaryContainer is scrolling.
         // if (categoryCounts.size > 0) {
         //     const adjustedMaxHeight = summaryContainer.offsetHeight - title.offsetHeight - totalCountElement.offsetHeight - 30; // 30 for padding/margins
         //     summaryContainer.querySelector('ul').style.maxHeight = adjustedMaxHeight + 'px';
         // }

    }

})();