Steam CNY价格转换(首页)

精确匹配DOM结构的价格转换

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Steam CNY价格转换(首页)
// @namespace    https://greasyfork.org/zh-CN/users/963647-moase
// @version      2.0
// @description  精确匹配DOM结构的价格转换
// @author       MaoShiSanKe
// @match        https://steamcommunity.com/market/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @connect      api.exchangerate-api.com
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    const CACHE_TIME = 3600000; // 1小时缓存
    const DEFAULT_RATE = 7.23; // 默认汇率
    let exchangeRate = DEFAULT_RATE;

    async function initExchangeRate() {
        const cached = GM_getValue('cny_rate_v2');
        if (cached && Date.now() - cached.timestamp < CACHE_TIME) {
            exchangeRate = cached.rate;
            return;
        }

        try {
            const rate = await fetchExchangeRate();
            GM_setValue('cny_rate_v2', {
                rate: rate,
                timestamp: Date.now()
            });
            exchangeRate = rate;
        } catch (e) {
            console.warn('汇率更新失败,使用缓存值', e);
        }
    }

    function fetchExchangeRate() {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: "https://api.exchangerate-api.com/v4/latest/USD",
                onload: function(response) {
                    try {
                        const data = JSON.parse(response.responseText);
                        resolve(Number(data.rates.CNY?.toFixed(2)) || DEFAULT_RATE);
                    } catch (e) {
                        reject(e);
                    }
                },
                onerror: reject
            });
        });
    }

    function convertPrice(priceText) {
        const amountMatch = priceText.match(/([\d.,]+)/);
        if (!amountMatch) return null;

        const amount = parseFloat(amountMatch[1].replace(',', ''));
        return `¥${(amount * exchangeRate).toFixed(2)} CNY`; // 格式调整为¥符号+两位小数
    }

    function processPriceElement(el) {
        if (el.dataset.processedV2) return;

        // 精确查找目标位置
        const priceContainer = el.querySelector('.market_table_value.normal_price');
        if (!priceContainer) return;

        const usdPrice = priceContainer.querySelector('.normal_price:not([data-cny])');
        const salePrice = priceContainer.querySelector('.sale_price');

        if (!usdPrice) return;

        const converted = convertPrice(usdPrice.textContent);
        if (!converted) return;

        // 创建精确的CNY元素
        const cnySpan = document.createElement('span');
        cnySpan.className = 'normal_price';
        cnySpan.dataset.price = usdPrice.dataset.price; // 继承原价数据
        cnySpan.dataset.currency = usdPrice.dataset.currency;
        cnySpan.textContent = converted;

        // 精确插入到USD价格和sale价格之间
        const refNode = salePrice ? salePrice : usdPrice.nextSibling;
        priceContainer.insertBefore(document.createElement('br'), usdPrice.nextSibling);
        priceContainer.insertBefore(cnySpan, refNode);

        el.dataset.processedV2 = 'true';
    }

    // 优化后的Observer处理
    const observer = new MutationObserver(mutations => {
        if (!document.querySelector('.market_listing_their_price')) return;

        requestIdleCallback(() => {
            document.querySelectorAll('.market_listing_their_price').forEach(processPriceElement);
        }, { timeout: 500 });
    });

    (async function main() {
        await initExchangeRate();
        observer.observe(document.body, {
            childList: true,
            subtree: true,
            attributes: false
        });
        // 初始处理
        requestIdleCallback(() => {
            document.querySelectorAll('.market_listing_their_price').forEach(processPriceElement);
        }, { timeout: 1000 });
    })();
})();