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.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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';
         // }

    }

})();