Steam CNY价格转换(商品界面)

Display Steam Market item price in CNY with dynamic exchange rate update interval

// ==UserScript==
// @name         Steam CNY价格转换(商品界面)
// @namespace    https://greasyfork.org/zh-CN/users/963647-moase
// @version      0.5
// @description  Display Steam Market item price in CNY with dynamic exchange rate update interval
// @author       MaoShiSanKe
// @match        https://steamcommunity.com/market/listings/*
// @grant        GM_xmlhttpRequest
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    const exchangeRateAPI = 'https://api.exchangerate-api.com/v4/latest/USD';
    const CACHE_EXPIRY_TIME = 30 * 60 * 1000; // 缓存过期时间:30分钟
    let lastExchangeRate = null;
    let lastExchangeRateTime = 0;
    let lastDataUpdateTime = 0;
    let isProcessing = false;  // 防止多次执行
    let isRateUpdating = false;  // 防止频繁的汇率请求
    let officialRefreshInterval = 5000;  // 默认5秒

    // 获取商品价格
    const getItemPrices = () => {
        let prices = [];
        document.querySelectorAll('.market_commodity_orders_table td').forEach((cell, index) => {
            if (index % 2 === 0) {
                const priceText = cell.textContent.trim();
                if (priceText.startsWith('$')) {
                    prices.push(parseFloat(priceText.substring(1)));
                }
            }
        });
        return prices;
    };

    // 获取汇率并缓存
    const getExchangeRate = () => {
        const currentTime = Date.now();

        // 如果缓存未过期,直接返回缓存的汇率
        if (lastExchangeRate && currentTime - lastExchangeRateTime < CACHE_EXPIRY_TIME) {
            return Promise.resolve(lastExchangeRate);
        }

        // 如果缓存过期,则重新请求API
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: exchangeRateAPI,
                onload: (response) => {
                    try {
                        const data = JSON.parse(response.responseText);
                        if (data.rates && data.rates.CNY) {
                            // 缓存汇率并设置时间戳
                            lastExchangeRate = data.rates.CNY;
                            lastExchangeRateTime = currentTime;
                            resolve(lastExchangeRate);
                        } else {
                            reject('CNY汇率未找到');
                        }
                    } catch (e) {
                        reject('解析汇率数据失败');
                    }
                },
                onerror: (error) => {
                    reject('请求失败: ' + error);
                }
            });
        });
    };

    // 显示转换后的价格
    const displayPricesInCNY = async () => {
        if (isProcessing) return;  // 防止重复处理
        isProcessing = true;

        const prices = getItemPrices();
        if (prices.length > 0) {
            try {
                const exchangeRate = await getExchangeRate();
                prices.forEach(price => {
                    const priceInCNY = (price * exchangeRate).toFixed(2);
                    const priceElement = document.createElement('span');
                    priceElement.textContent = `(¥\ ${priceInCNY} CNY)`;
                    priceElement.classList.add('cny-price');

                    const priceNode = Array.from(document.querySelectorAll('.market_commodity_orders_table td')).find(node => node.textContent.includes(`$${price.toFixed(2)}`));
                    if (priceNode && !priceNode.querySelector('.cny-price')) {
                        priceNode.appendChild(priceElement);
                    }
                });
            } catch (error) {
                console.error('获取汇率失败:', error);
            }
        }
        isProcessing = false;
    };

    // 捕获官方刷新间隔(如果可以的话)
    const captureRefreshInterval = () => {
        const refreshTimeNode = document.querySelector('.market_commodity_orders_table');
        if (refreshTimeNode) {
            // 检测到页面内数据更新的时间间隔
            const refreshInterval = window.getComputedStyle(refreshTimeNode).getPropertyValue('animation-duration');
            if (refreshInterval) {
                const seconds = parseFloat(refreshInterval) * 1000;  // 将秒转为毫秒
                officialRefreshInterval = seconds;
                console.log(`检测到官方刷新间隔: ${officialRefreshInterval / 1000} 秒`);
            }
        }
    };

    // 检测数据是否刷新并更新汇率
    const checkForDataUpdate = () => {
        const currentTime = Date.now();

        // 如果数据已经刷新,并且距离上次汇率更新超过刷新间隔,则更新汇率
        if (currentTime - lastDataUpdateTime >= officialRefreshInterval && !isRateUpdating) {
            isRateUpdating = true;
            lastDataUpdateTime = currentTime;
            displayPricesInCNY().finally(() => {
                isRateUpdating = false;
            });
        }
    };

    // 使用 MutationObserver 监听价格更新
    const observePriceChanges = () => {
        const targetNode = document.querySelector('.market_commodity_orders_table');
        if (targetNode) {
            const observer = new MutationObserver(() => {
                checkForDataUpdate();  // 检查数据更新时间
            });
            observer.observe(targetNode, { childList: true, subtree: true });
        }
    };

    // 处理页面加载
    window.addEventListener('load', () => {
        captureRefreshInterval();  // 捕获官方刷新间隔
        displayPricesInCNY();
        observePriceChanges();
        // 定时检查数据更新时间并更新汇率
        setInterval(checkForDataUpdate, officialRefreshInterval);
    });
})();