您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds CNPrice column to SteamDB sales page with selectable currency conversions.
// ==UserScript== // @name SteamDB Sales CNPrice Injector // @namespace https://liangying.eu.org/ // @version 1.1.0 // @description Adds CNPrice column to SteamDB sales page with selectable currency conversions. // @author LiangYing // @match https://steamdb.info/sales/* // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @connect store.steampowered.com // @connect api.exchangerate-api.com // @icon https://store.steampowered.com/favicon.ico // @license MIT // @run-at document-end // ==/UserScript== (function() { 'use strict'; const COLUMN_CLASS = 'compare-price-column'; const PRICE_CACHE_DURATION = 24 * 60 * 60 * 1000; // 24小时的缓存时间 // 货币符号映射 const CURRENCY_SYMBOLS = { CNY: '¥', JPY: '¥', HKD: 'HK$', USD: '$', RUB: '₽', PHP: '₱', INR: '₹', KRW: '₩', CAD: 'C$' }; // 汇率对象 - 存储1 CNY兑换多少目标货币 let exchangeRates = { CNY: 1, JPY: 16.5, // 1 CNY = 16.5 JPY HKD: 1.09, // 1 CNY = 1.09 HKD USD: 0.14, // 1 CNY = 0.14 USD RUB: 12.7, // 1 CNY = 12.7 RUB PHP: 7.74, // 1 CNY = 7.74 PHP INR: 11.52, // 1 CNY = 11.52 INR KRW: 185.87, // 1 CNY = 185.87 KRW CAD: 0.19 // 1 CNY = 0.19 CAD }; // 当前选择的货币 let currentCurrency = null; // 价格缓存 const priceCache = { // 获取缓存的价格 get: function (appId) { const cached = GM_getValue(`price_${appId}`); if (!cached) return null; const { timestamp, data } = JSON.parse(cached); if (Date.now() - timestamp > PRICE_CACHE_DURATION) { GM_setValue(`price_${appId}`, ''); return null; } return data; }, // 设置价格缓存 set: function (appId, priceData) { const cacheData = { timestamp: Date.now(), data: priceData }; GM_setValue(`price_${appId}`, JSON.stringify(cacheData)); } }; // 创建UI元素 function createUI() { // 尝试找到现有的容器 const existingContainer = document.querySelector('.dt-layout-end'); if (existingContainer && !existingContainer.querySelector('.currency-selector')) { const rateSelect = document.createElement('select'); rateSelect.className = 'currency-selector'; rateSelect.innerHTML = ` <option value="">-- LiangYing Exchange --</option> <option value="CNY">CNY (中国)</option> <option value="JPY">JPY (日本)</option> <option value="HKD">HKD (香港)</option> <option value="USD">USD (美国)</option> <option value="RUB">RUB (俄罗斯)</option> <option value="PHP">PHP (菲律宾)</option> <option value="INR">INR (印度)</option> <option value="KRW">KRW (韩国)</option> <option value="CAD">CAD (加拿大)</option> `; rateSelect.style.marginLeft = '10px'; rateSelect.style.padding = '5px'; rateSelect.style.backgroundColor = '#1b2838'; rateSelect.style.color = '#c6d4df'; rateSelect.style.border = '1px solid #2a475e'; existingContainer.appendChild(rateSelect); rateSelect.addEventListener('change', function () { currentCurrency = this.value; if (!currentCurrency) { removePriceColumn(); return; } updateExchangeRates(() => { ensurePriceColumn(); refreshPrices(); }); }); return; } // 创建新的容器 if (!document.querySelector('.currency-selector')) { const controlContainer = document.createElement('div'); controlContainer.className = 'currency-selector-container'; controlContainer.style.margin = '10px 0'; controlContainer.style.textAlign = 'right'; const rateSelect = document.createElement('select'); rateSelect.className = 'currency-selector'; rateSelect.innerHTML = ` <option value="">-- LiangYing Exchange --</option> <option value="CNY">CNY (中国)</option> <option value="JPY">JPY (日本)</option> <option value="HKD">HKD (香港)</option> <option value="USD">USD (美国)</option> <option value="RUB">RUB (俄罗斯)</option> <option value="PHP">PHP (菲律宾)</option> <option value="INR">INR (印度)</option> <option value="KRW">KRW (韩国)</option> <option value="CAD">CAD (加拿大)</option> `; rateSelect.style.marginLeft = '10px'; rateSelect.style.padding = '5px'; rateSelect.style.backgroundColor = '#1b2838'; rateSelect.style.color = '#c6d4df'; rateSelect.style.border = '1px solid #2a475e'; controlContainer.appendChild(rateSelect); // 插入UI元素 const tableElement = document.querySelector('.table-sales') || document.querySelector('.dataTable'); if (tableElement && tableElement.parentNode) { tableElement.parentNode.insertBefore(controlContainer, tableElement); } else { const tableContainer = document.querySelector('.table-container') || document.querySelector('.table-responsive'); if (tableContainer) { tableContainer.insertBefore(controlContainer, tableContainer.firstChild); } } rateSelect.addEventListener('change', function () { currentCurrency = this.value; if (!currentCurrency) { removePriceColumn(); return; } updateExchangeRates(() => { ensurePriceColumn(); refreshPrices(); }); }); } } // 更新汇率数据 function updateExchangeRates(callback) { GM_xmlhttpRequest({ method: 'GET', url: 'https://api.exchangerate-api.com/v4/latest/CNY', onload: function (response) { if (response.status === 200) { try { const data = JSON.parse(response.responseText); exchangeRates.JPY = data.rates.JPY; exchangeRates.HKD = data.rates.HKD; exchangeRates.USD = data.rates.USD; exchangeRates.RUB = data.rates.RUB; exchangeRates.PHP = data.rates.PHP; exchangeRates.INR = data.rates.INR; exchangeRates.KRW = data.rates.KRW; exchangeRates.CAD = data.rates.CAD; if (callback) callback(); } catch (error) { console.error('Failed to parse exchange rates, using defaults:', error); if (callback) callback(); } } else { console.error('Failed to fetch exchange rates, using defaults:', response.status); if (callback) callback(); } }, onerror: function() { console.error('Failed to fetch exchange rates, using defaults'); if (callback) callback(); } }); } // 解析价格 function parsePrice(priceStr) { return parseFloat(priceStr.replace(/[^0-9.]/g, '')) || 0; } // 获取商店价格 function fetchGamePrice(appId, callback, retryCount = 0) { const maxRetries = 3; // 先检查缓存 const cachedPrice = priceCache.get(appId); if (cachedPrice) { callback(cachedPrice); return; } GM_xmlhttpRequest({ method: 'GET', url: `https://store.steampowered.com/api/appdetails/?appids=${appId}&cc=cn`, timeout: 10000, onload: function (response) { try { const data = JSON.parse(response.responseText); if (data[appId]?.success) { const priceInfo = data[appId].data.price_overview; if (priceInfo) { priceCache.set(appId, priceInfo); } callback(priceInfo); } else { callback(null); } } catch (error) { if (retryCount < maxRetries) { setTimeout(() => { fetchGamePrice(appId, callback, retryCount + 1); }, 2000 * (retryCount + 1)); } else { callback(null); } } }, onerror: function () { if (retryCount < maxRetries) { setTimeout(() => { fetchGamePrice(appId, callback, retryCount + 1); }, 2000 * (retryCount + 1)); } else { callback(null); } } }); } // 确保价格列存在 function ensurePriceColumn() { if (!currentCurrency) return; const header = document.querySelector('.table-sales thead tr, .dataTable thead tr'); if (!header) return; let priceHeader = header.querySelector(`.${COLUMN_CLASS}`); if (!priceHeader) { priceHeader = document.createElement('th'); priceHeader.className = COLUMN_CLASS; header.appendChild(priceHeader); } // 更新列标题 const symbol = CURRENCY_SYMBOLS[currentCurrency] || currentCurrency; priceHeader.textContent = `${symbol} Price Comparison`; priceHeader.style.whiteSpace = 'nowrap'; const rows = document.querySelectorAll('.table-sales tbody tr, .dataTable tbody tr'); rows.forEach(row => { if (!row.querySelector(`.${COLUMN_CLASS}`)) { const priceCell = document.createElement('td'); priceCell.className = COLUMN_CLASS; row.appendChild(priceCell); } }); } // 移除价格列 function removePriceColumn() { const header = document.querySelector('.table-sales thead tr, .dataTable thead tr'); if (!header) return; const priceHeader = header.querySelector(`.${COLUMN_CLASS}`); if (priceHeader) { priceHeader.remove(); } const rows = document.querySelectorAll('.table-sales tbody tr, .dataTable tbody tr'); rows.forEach(row => { const priceCell = row.querySelector(`.${COLUMN_CLASS}`); if (priceCell) { priceCell.remove(); } }); } // 更新单个游戏的价格显示 function updateGamePrice(row) { if (!currentCurrency) return; // 获取游戏ID const appId = row.dataset.appid; if (!appId) return; // 找到价格单元格 let priceCell = row.querySelector(`.${COLUMN_CLASS}`); if (!priceCell) { priceCell = document.createElement('td'); priceCell.className = COLUMN_CLASS; row.appendChild(priceCell); } // 如果已经有价格数据,则跳过 if (priceCell.textContent && !priceCell.textContent.includes('Loading')) { return; } priceCell.textContent = 'Loading...'; // 从SteamDB表格中获取目标货币价格(第5列) const targetCurrencyPriceElement = row.querySelector('td:nth-child(5)'); const targetCurrencyPrice = targetCurrencyPriceElement ? parsePrice(targetCurrencyPriceElement.textContent) : 0; if (!targetCurrencyPrice) { priceCell.textContent = 'N/A'; return; } fetchGamePrice(appId, (priceInfo) => { if (priceInfo) { // 中国区价格(人民币) const cnPrice = priceInfo.final / 100; // 汇率:1 CNY = X 目标货币 const exchangeRate = exchangeRates[currentCurrency]; // 转换后的目标货币价格 const convertedPrice = cnPrice * exchangeRate; // 计算比例:转换后价格 / SteamDB显示的目标货币价格 const ratio = targetCurrencyPrice > 0 ? (convertedPrice / targetCurrencyPrice * 100).toFixed(2) : 'N/A'; // 设置颜色 const color = (ratio < 100) ? '#5cff47' : (ratio > 100) ? '#ff4747' : '#ccc'; // 获取货币符号 const targetSymbol = CURRENCY_SYMBOLS[currentCurrency] || currentCurrency; const cnySymbol = CURRENCY_SYMBOLS.CNY; // 更新单元格内容 priceCell.innerHTML = ` <div>${cnySymbol}${cnPrice.toFixed(2)}</div> <div>${targetSymbol}${convertedPrice.toFixed(2)}</div> <div style="color: ${color}; font-weight: bold">${ratio}%</div> `; } else { priceCell.textContent = 'N/A'; } }); } // 刷新所有价格 function refreshPrices() { if (!currentCurrency) return; ensurePriceColumn(); const rows = document.querySelectorAll('.table-sales tbody tr, .dataTable tbody tr'); rows.forEach((row, index) => { setTimeout(() => { try { updateGamePrice(row); } catch (error) { console.error(`Error updating price for row ${index}:`, error); } }, index * 300); }); } // 监听表格变化 function setupTableObserver() { const tableBody = document.querySelector('.table-sales tbody, .dataTable tbody'); if (tableBody) { const tableObserver = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.type === 'childList' && currentCurrency) { ensurePriceColumn(); const newRows = Array.from(mutation.addedNodes).filter(node => node.nodeType === 1 && node.matches('tr') ); newRows.forEach(updateGamePrice); } } }); tableObserver.observe(tableBody, { childList: true, subtree: true }); } // 监听分页变化 const paginationContainer = document.querySelector('.pagination, .dataTables_paginate'); if (paginationContainer) { const paginationObserver = new MutationObserver(() => { if (currentCurrency) { ensurePriceColumn(); refreshPrices(); } }); paginationObserver.observe(paginationContainer, { childList: true, subtree: true }); } // 筛选表单监听 const filterForm = document.getElementById('js-filters'); if (filterForm) { filterForm.addEventListener('submit', () => { setTimeout(() => { if (currentCurrency) { ensurePriceColumn(); refreshPrices(); } }, 500); }); } } // 初始化 function init() { createUI(); setupTableObserver(); } // 等待页面加载完成后初始化 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址