Greasy Fork is available in English.
Wargaming商店外区货币价值转换为CNY并附代充折扣显示(目前支持ARS、SGD、HKD、TWD、USD、JPY)
当前为
// ==UserScript==
// @name Wargaming商店外区货币转换器
// @namespace http://tampermonkey.net/
// @version 2.0
// @description Wargaming商店外区货币价值转换为CNY并附代充折扣显示(目前支持ARS、SGD、HKD、TWD、USD、JPY)
// @author SundayRX
// @match https://wargaming.net/shop/*
// @grant GM_xmlhttpRequest
// @connect api.exchangerate-api.com
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// 配置
const CONFIG = {
discount: 0.87, // 代冲折扣
updateInterval: 600000, // 10分钟更新间隔
highlightStyle: `
.currency-conversion {
background-color: #ffffcc;
border-radius: 3px;
padding: 2px 4px;
margin-left: 5px;
font-weight: bold;
font-size: 0.9em;
color: #d32f2f;
display: inline-block;
}
.currency-processed {
display: inline-flex;
align-items: center;
}
`,
showOriginalValue: true
};
class Currency {
constructor(Type, ExchangeRateAPI, ExchangeRate, MatchRegex, Symbol = null) {
this.Type = Type;
this.ExchangeRateAPI = ExchangeRateAPI;
this.ExchangeRate = ExchangeRate;
this.MatchRegex = MatchRegex;
this.Symbol = Symbol;
}
}
const CurrencyDict = [
new Currency('ARS', 'https://api.exchangerate-api.com/v4/latest/ARS', 0.005, /([\d,]+(?:\.\d+)?)\s*(ARS)/i),
new Currency('SGD', 'https://api.exchangerate-api.com/v4/latest/SGD', 5.5, /([\d,]+(?:\.\d+)?)\s*(SGD)/i),
new Currency('HKD', 'https://api.exchangerate-api.com/v4/latest/HKD', 0.916, /([\d,]+(?:\.\d+)?)\s*(HKD)/i),
new Currency('TWD', 'https://api.exchangerate-api.com/v4/latest/TWD', 0.233, /([\d,]+(?:\.\d+)?)\s*(TWD)/i),
new Currency('USD', 'https://api.exchangerate-api.com/v4/latest/USD', 7.2, null, 'USD'),
new Currency('JPY', 'https://api.exchangerate-api.com/v4/latest/JPY', 0.048, null, 'JPY')
];
let isProcessing = false;
let observer = null;
let processedElements = new Set();
// 初始化脚本
function init() {
console.log('Wargaming商店货币转换器已加载 - 支持ARS, SGD, HKD, TWD, USD, JPY');
addStyles();
FetchExchangeRate();
setInterval(FetchExchangeRate, CONFIG.updateInterval);
convertPageCurrencyValues();
observeDOMChanges();
}
// 添加样式到页面
function addStyles() {
const style = document.createElement('style');
style.textContent = CONFIG.highlightStyle;
document.head.appendChild(style);
}
// 获取汇率
function FetchExchangeRate() {
CurrencyDict.forEach(currency => {
GM_xmlhttpRequest({
method: 'GET',
url: currency.ExchangeRateAPI,
onload: function(response) {
try {
const data = JSON.parse(response.responseText);
if (data?.rates?.CNY) {
currency.ExchangeRate = data.rates.CNY;
console.log(`汇率已更新: 1 ${currency.Type} = ${currency.ExchangeRate} CNY`);
updateConvertedValues();
} else {
console.warn(`无法从API获取${currency.Type}汇率,使用备用汇率`);
}
} catch (e) {
console.warn(`解析${currency.Type}汇率API响应失败:`, e);
}
},
onerror: function(error) {
console.warn(`获取${currency.Type}汇率失败:`, error, '使用备用汇率');
}
});
});
}
function ContainsSpecialValue(element) {
if (processedElements.has(element) || element.closest('.currency-processed')) {
return false;
}
const text = element.textContent;
const currencyElements = element.querySelectorAll('.currency-code');
for (let currencyElement of currencyElements) {
const title = currencyElement.getAttribute('title');
if (title && (title === 'USD' || title === 'JPY')) {
return true;
}
}
for (let currency of CurrencyDict) {
if (currency.MatchRegex && currency.MatchRegex.test(text)) {
return true;
}
}
return false;
}
function ExtractSpecialValue(element) {
const text = element.textContent;
const currencyElements = element.querySelectorAll('.currency-code');
for (let currencyElement of currencyElements) {
const title = currencyElement.getAttribute('title');
if (title === 'USD' || title === 'JPY') {
let priceContainer = element;
while (priceContainer && !priceContainer.textContent.match(/([\d,]+(?:\.\d+)?)/)) {
priceContainer = priceContainer.parentElement;
}
if (priceContainer) {
const priceText = priceContainer.textContent;
const priceMatch = priceText.match(/([\d,]+(?:\.\d+)?)/);
if (priceMatch) {
const numericValue = priceMatch[1].replace(/,/g, '');
return [parseFloat(numericValue), title];
}
}
}
}
for (let currency of CurrencyDict) {
if (currency.MatchRegex) {
const match = text.match(currency.MatchRegex);
if (match && match[1]) {
const numericValue = match[1].replace(/,/g, '');
return [parseFloat(numericValue), currency.Type];
}
}
}
return [null, null];
}
function FormatCurrency(value, currency) {
const targetCurrency = CurrencyDict.find(c => c.Type === currency);
if (targetCurrency) {
const originalValue = value * targetCurrency.ExchangeRate;
const discountedValue = originalValue * CONFIG.discount;
return `${originalValue.toFixed(2)} (${discountedValue.toFixed(2)}) CNY`;
}
return value.toFixed(2);
}
// 转换页面中的货币值
function convertPageCurrencyValues() {
if (isProcessing) return;
isProcessing = true;
if (observer) observer.disconnect();
const priceElements = findPriceElements();
priceElements.forEach(processPriceElement);
if (observer) {
observer.observe(document.body, {
childList: true,
subtree: true
});
}
isProcessing = false;
}
// 查找所有价格元素(仅目标product-price容器)
function findPriceElements() {
const elements = [];
const seenElements = new Set();
// 仅选择目标span容器:.product-price(排除外层p标签)
const selectors = ['.product-price:not(.currency-processed)'];
selectors.forEach(selector => {
document.querySelectorAll(selector).forEach(element => {
if (ContainsSpecialValue(element) && !seenElements.has(element) && !processedElements.has(element)) {
const hasProcessedChild = element.querySelector('.currency-processed');
if (!hasProcessedChild) {
elements.push(element);
seenElements.add(element);
}
}
});
});
return elements;
}
// 处理价格元素
function processPriceElement(element) {
if (element.classList.contains('currency-processed') || processedElements.has(element)) {
return;
}
const [PriceValue, PriceType] = ExtractSpecialValue(element);
if (PriceValue !== null && PriceType !== null) {
const formattedCNY = FormatCurrency(PriceValue, PriceType);
const existingConversion = findExistingConversion(element);
if (existingConversion) {
existingConversion.textContent = `≈${formattedCNY}`;
existingConversion.title = `${formatNumber(PriceValue)} ${PriceType} ≈ ${formattedCNY}`;
} else {
const conversionElement = document.createElement('span');
conversionElement.className = 'currency-conversion';
conversionElement.textContent = `≈${formattedCNY}`;
conversionElement.title = `${formatNumber(PriceValue)} ${PriceType} ≈ ${formattedCNY}`;
// 插入到.product-price_wrap后面(在目标容器内部)
const priceWrap = element.querySelector('.product-price_wrap');
if (priceWrap) {
priceWrap.parentNode.insertBefore(conversionElement, priceWrap.nextSibling);
} else {
element.appendChild(conversionElement);
}
}
element.classList.add('currency-processed');
processedElements.add(element);
// 标记子元素防止重复处理
element.querySelectorAll('*').forEach(child => {
child.classList.add('currency-processed');
processedElements.add(child);
});
}
}
// 查找已存在的转换元素(在目标容器内部)
function findExistingConversion(element) {
return element.querySelector('.currency-conversion');
}
// 格式化数字
function formatNumber(num) {
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
// 更新已转换的值
function updateConvertedValues() {
document.querySelectorAll('.currency-conversion').forEach(element => {
const priceElement = element.closest('.product-price');
if (priceElement) {
const [PriceValue, PriceType] = ExtractSpecialValue(priceElement);
if (PriceValue !== null && PriceType !== null) {
const formattedCNY = FormatCurrency(PriceValue, PriceType);
element.textContent = `≈${formattedCNY}`;
element.title = `${formatNumber(PriceValue)} ${PriceType} ≈ ${formattedCNY}`;
}
}
});
}
// 监听DOM变化
function observeDOMChanges() {
observer = new MutationObserver(mutations => {
let shouldConvert = false;
mutations.forEach(mutation => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
const priceSelectors = ['.product-price:not(.currency-processed)'];
const priceElements = node.querySelectorAll
? node.querySelectorAll(priceSelectors.join(', '))
: [];
if (priceElements.length > 0 || (node.matches && node.matches(priceSelectors.join(', ')))) {
shouldConvert = true;
}
}
});
}
});
if (shouldConvert && !isProcessing) {
clearTimeout(window.currencyConversionTimeout);
window.currencyConversionTimeout = setTimeout(convertPageCurrencyValues, 500);
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
// 页面加载完成后初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();