Greasy Fork is available in English.

SteamPY 价格显示

显示 Steam 游戏在 SteamPY 的 CDKey 价格、余额购价格和代购价格

// ==UserScript==
// @name         SteamPY 价格显示
// @version      1.0
// @description  显示 Steam 游戏在 SteamPY 的 CDKey 价格、余额购价格和代购价格
// @author       Li
// @match        https://store.steampowered.com/app/*
// @grant        GM_xmlhttpRequest
// @run-at       document-end
// @icon         https://steampy.com/m_logo.ico
// @license      MIT
// @supportURL   https://greasyfork.org/zh-CN/scripts/518189-steampy-%E4%BB%B7%E6%A0%BC%E6%98%BE%E7%A4%BA/feedback
// @homepageURL  https://greasyfork.org/zh-CN/scripts/518189-steampy-%E4%BB%B7%E6%A0%BC%E6%98%BE%E7%A4%BA
// @namespace https://greasyfork.org/users/
// ==/UserScript==

(function () {
    'use strict';

    const BASE_URL = "https://steampy.com/";

    // SteamPY的API
    const API_ENDPOINTS = {
        gameInfo: (subId, appId, type) => `${BASE_URL}xboot/common/plugIn/getGame?subId=${subId}&appId=${appId}&type=${type}`,
        cdkDetail: (id) => `${BASE_URL}cdkDetail?name=cn&gameId=${id}`,
        balanceBuyDetail: (id) => `${BASE_URL}balanceBuyDetail?data=cn&gameId=${id}`,
        hotGameDetail: (id) => `${BASE_URL}hotGameDetail?gameId=${id}`,
    };

    // 获取 AppID
    const getAppId = () => {
        const link = document.querySelector('.apphub_OtherSiteInfo a');
        const appIdMatch = link?.href?.match(/\d+$/);
        return appIdMatch ? appIdMatch[0] : null;
    };

    // 获取 SubID
    const getSubIdElements = () => [...document.querySelectorAll('.game_area_purchase_game_wrapper')];

    // 占位符
    const createPlaceholder = (parent) => {
        const placeholder = document.createElement('div');
        placeholder.className = 'price-box';
        placeholder.innerHTML = `<div class="loading-text">加载中...</div>`;
        parent.appendChild(placeholder);
        return placeholder;
    };

    const updatePlaceholder = (placeholder, content, isError = false) => {
        placeholder.innerHTML = isError
            ? `<div class="error-text">${content}</div>`
            : content;
    };

    // 显示价格
    const displayPrices = (res, placeholder) => {
        if (!res.success) {
            updatePlaceholder(placeholder, res.message || "加载失败:未知错误", true);
            return;
        }
        const { keyPrice, marketPrice, daiPrice, id } = res.result;
        updatePlaceholder(
            placeholder,
            `
                <a href="${API_ENDPOINTS.cdkDetail(id)}" target="_blank" class="price-link">
                    CDKey 价格:¥${keyPrice || "未知"}
                </a>
                <a href="${API_ENDPOINTS.balanceBuyDetail(id)}" target="_blank" class="price-link">
                    余额购价格:¥${marketPrice || "未知"}
                </a>
                <a href="${API_ENDPOINTS.hotGameDetail(id)}" target="_blank" class="price-link">
                    代购价格:¥${daiPrice || "未知"}
                </a>
            `
        );
    };

    const appId = getAppId();
    if (!appId) {
        console.error("无法获取 AppID");
        return;
    }

    const subIdElements = getSubIdElements();
    subIdElements.forEach((element, index) => {
        const input = element.querySelector('input[name="subid"], input[name="bundleid"]');
        if (!input) return;

        const subId = input.value;
        const type = input.name;
        const apiUrl = API_ENDPOINTS.gameInfo(subId, appId, type);

        const placeholder = createPlaceholder(element);

        GM_xmlhttpRequest({
            method: "GET",
            url: apiUrl,
            onload: (response) => {
                try {
                    const result = JSON.parse(response.responseText);
                    displayPrices(result, placeholder);
                } catch (err) {
                    console.error("解析响应失败:", err);
                    updatePlaceholder(placeholder, "加载失败:解析错误", true);
                }
            },
            onerror: () => updatePlaceholder(placeholder, "加载失败:网络错误", true),
        });
    });

    // 基本样式
    const style = document.createElement('style');
    style.innerHTML = `
        .price-box {
            font-size: 14px;
            margin-top: 8px;
            padding: 8px;
            background-color: rgba(0, 0, 0, 0.3);
            border-radius: 4px;
            color: #f0f0f0;
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
            box-sizing: border-box;
        }
        .price-link {
            color: #ffffff;
            text-decoration: none;
            font-size: 13px;
            white-space: nowrap;
        }
        .price-link:hover {
            color: #cccccc;
        }
        @media screen and (max-width: 767px) {
            .price-box {
                flex-direction: column;
                gap: 4px;
            }
        }
    `;
    document.head.appendChild(style);
})();