// ==UserScript==
// @name steam价格转换
// @namespace https://github.com/marioplus/steam-price-converter
// @version 2.3.1
// @author marioplus
// @description steam商店中的价格转换为人民币
// @license GPL-3.0-or-later
// @icon https://vitejs.dev/logo.svg
// @homepage https://github.com/marioplus
// @match https://store.steampowered.com/*
// @match https://steamcommunity.com/*
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.global.prod.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/js/mdui.min.js
// @require data:application/javascript,%3Bvar%20Reflect%3Dwindow.Reflect%3B
// @require https://cdn.jsdelivr.net/npm/[email protected]/Reflect.min.js
// @connect api.augmentedsteam.com
// @connect store.steampowered.com
// @grant GM_addValueChangeListener
// @grant GM_cookie
// @grant GM_deleteValue
// @grant GM_getValue
// @grant GM_registerMenuCommand
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// @grant unsafeWindow
// ==/UserScript==
(e=>{const t=document.createElement("style");t.dataset.source="vite-plugin-monkey",t.textContent=e,document.head.append(t)})(" #spc-menu{position:absolute;top:0px;width:100px;height:100px;z-index:99999;padding:0;margin:0}.tab_item_discount{min-width:113px!important;width:unset}.discount_final_price{display:inline-block!important}.search_result_row .col.search_price{width:175px}.search_result_row .col.search_name{width:200px}.market_listing_their_price{width:160px} ");
(function (reflectMetadata, vue, mdui) {
'use strict';
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
class AbstractConverter {
/**
* 匹配到的元素,是否匹配这个 exchanger
* @param elementSnap 选择器选择到的元素快照
*/
match(elementSnap) {
if (!elementSnap || !elementSnap.element) {
return false;
}
const content = elementSnap.textContext;
if (!content) {
return false;
}
if (content.match(/\d/) === null) {
return false;
}
if (/^[,.\d\s]+$/.test(content)) {
return false;
}
const parent = elementSnap.element.parentElement;
if (!parent) {
return false;
}
for (const selector of this.getCssSelectors()) {
const element = parent.querySelector(selector);
if (element && element === elementSnap.element) {
elementSnap.selector = selector;
return true;
}
}
return false;
}
/**
* 替换之后的操作
* @param elementSnap 选择器选择到的元素快照
*/
// @ts-ignore
afterConvert(elementSnap) {
}
}
const metadataKey = "Metadata:JsonProperty";
function JsonProperty(config = {}) {
return (target, property) => {
config.alias = config.alias || property;
Reflect.defineMetadata(metadataKey, config, target, property);
};
}
function JsonAlias(alias) {
return (target, property) => {
const config = Reflect.getMetadata(metadataKey, target, property) || {};
config.alias = alias || property;
Reflect.defineMetadata(metadataKey, config, target, property);
};
}
class Serializable {
toJson() {
const anyThis = this;
const json = {};
Object.keys(this).forEach((propKey) => {
const config = Reflect.getMetadata(metadataKey, this, propKey);
const prop = anyThis[propKey];
if (!config || prop === void 0) {
return;
}
if (config.typeAs || config.typeAs === Map) {
json[config.alias] = {};
prop.forEach((v, k) => {
json[config.alias][k] = v;
});
return;
}
if (prop instanceof Serializable) {
json[config.alias] = prop.toJson();
return;
}
json[config.alias] = prop;
});
return json;
}
toJsonString() {
return JSON.stringify(this.toJson());
}
readJson(json) {
const anyThis = this;
Object.keys(this).forEach((propKey) => {
const config = Reflect.getMetadata(metadataKey, this, propKey);
const prop = anyThis[propKey];
const jsonNode = json[config.alias];
if (!config || jsonNode === void 0) {
return;
}
if (config.typeAs || config.typeAs === Map) {
let entries = Object.entries(jsonNode);
if (config.mapValue) {
entries = entries.map(([k, v]) => {
if (v instanceof Object) {
return [k, new Map(Object.entries(v))];
}
return [k, v];
});
}
anyThis[propKey] = new Map(entries);
return;
}
if (prop instanceof Serializable) {
prop.readJson(jsonNode);
return;
}
anyThis[propKey] = jsonNode;
});
return this;
}
readJsonString(jsonString) {
return this.readJson(JSON.parse(jsonString));
}
}
var __defProp$2 = Object.defineProperty;
var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
var __decorateClass$2 = (decorators, target, key, kind) => {
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
for (var i = decorators.length - 1, decorator; i >= 0; i--)
if (decorator = decorators[i])
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
if (kind && result)
__defProp$2(target, key, result);
return result;
};
class Setting extends Serializable {
constructor() {
super(...arguments);
__publicField(this, "countyCode", "CN");
__publicField(this, "currencySymbol", "¥");
__publicField(this, "currencySymbolBeforeValue", true);
__publicField(this, "rateCacheExpired", 1e3 * 60 * 60);
__publicField(this, "useCustomRate", false);
__publicField(this, "customRate", 1);
}
}
__decorateClass$2([
JsonProperty()
], Setting.prototype, "countyCode", 2);
__decorateClass$2([
JsonProperty()
], Setting.prototype, "currencySymbol", 2);
__decorateClass$2([
JsonProperty()
], Setting.prototype, "currencySymbolBeforeValue", 2);
__decorateClass$2([
JsonProperty()
], Setting.prototype, "rateCacheExpired", 2);
__decorateClass$2([
JsonProperty()
], Setting.prototype, "useCustomRate", 2);
__decorateClass$2([
JsonProperty()
], Setting.prototype, "customRate", 2);
var _GM_addValueChangeListener = /* @__PURE__ */ (() => typeof GM_addValueChangeListener != "undefined" ? GM_addValueChangeListener : void 0)();
var _GM_cookie = /* @__PURE__ */ (() => typeof GM_cookie != "undefined" ? GM_cookie : void 0)();
var _GM_deleteValue = /* @__PURE__ */ (() => typeof GM_deleteValue != "undefined" ? GM_deleteValue : void 0)();
var _GM_getValue = /* @__PURE__ */ (() => typeof GM_getValue != "undefined" ? GM_getValue : void 0)();
var _GM_registerMenuCommand = /* @__PURE__ */ (() => typeof GM_registerMenuCommand != "undefined" ? GM_registerMenuCommand : void 0)();
var _GM_setValue = /* @__PURE__ */ (() => typeof GM_setValue != "undefined" ? GM_setValue : void 0)();
var _GM_xmlhttpRequest = /* @__PURE__ */ (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)();
var _unsafeWindow = /* @__PURE__ */ (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)();
const STORAGE_KEY_PREFIX = "Storage:";
const STORAGE_KEY_RATE_CACHES = STORAGE_KEY_PREFIX + "RateCache";
const STORAGE_KEY_SETTING = STORAGE_KEY_PREFIX + "Setting";
const IM_KEY_PREFIX = "Im:";
const IM_KEY_MENU_STATUS = IM_KEY_PREFIX + "MenuStatus";
const IM_KEY_OPEN_MENU = IM_KEY_PREFIX + "Open";
const IM_KEY_CLOSE_MENU = IM_KEY_PREFIX + "Close";
const countyCurrencyCodes = [
{
code: "AL",
name: "阿尔巴尼亚",
nameEn: "Albania",
currencyCode: "ALL"
},
{
code: "DZ",
name: "阿尔及利亚",
nameEn: "Algeria",
currencyCode: "DZD"
},
{
code: "AF",
name: "阿富汗",
nameEn: "Afghanistan",
currencyCode: "AFN"
},
{
code: "AR",
name: "阿根廷",
nameEn: "Argentina",
currencyCode: "ARS"
},
{
code: "AE",
name: "阿联酋",
nameEn: "United Arab Emirates",
currencyCode: "AED"
},
{
code: "AW",
name: "阿鲁巴",
nameEn: "Aruba",
currencyCode: "AWG"
},
{
code: "OM",
name: "阿曼",
nameEn: "Oman",
currencyCode: "OMR"
},
{
code: "AZ",
name: "阿塞拜疆",
nameEn: "Azerbaijan",
currencyCode: "USD"
},
{
code: "EG",
name: "埃及",
nameEn: "Egypt",
currencyCode: "EGP"
},
{
code: "ET",
name: "埃塞俄比亚",
nameEn: "Ethiopia",
currencyCode: "ETB"
},
{
code: "IE",
name: "爱尔兰",
nameEn: "Ireland",
currencyCode: "EUR"
},
{
code: "EE",
name: "爱沙尼亚",
nameEn: "Estonia",
currencyCode: "EUR"
},
{
code: "AD",
name: "安道尔",
nameEn: "Andorra",
currencyCode: "EUR"
},
{
code: "AO",
name: "安哥拉",
nameEn: "Angola",
currencyCode: "AOA"
},
{
code: "AI",
name: "安圭拉",
nameEn: "Anguilla",
currencyCode: "XCD"
},
{
code: "AG",
name: "安提瓜和巴布达",
nameEn: "Antigua and Barbuda",
currencyCode: "XCD"
},
{
code: "AT",
name: "奥地利",
nameEn: "Austria",
currencyCode: "EUR"
},
{
code: "AU",
name: "澳大利亚",
nameEn: "Australia",
currencyCode: "AUD"
},
{
code: "MO",
name: "澳门",
nameEn: "Macao",
currencyCode: "MOP"
},
{
code: "BB",
name: "巴巴多斯",
nameEn: "Barbados",
currencyCode: "BBD"
},
{
code: "PG",
name: "巴布亚新几内亚",
nameEn: "Papua New Guinea",
currencyCode: "PGK"
},
{
code: "BS",
name: "巴哈马",
nameEn: "Bahamas",
currencyCode: "BSD"
},
{
code: "PK",
name: "巴基斯坦",
nameEn: "Pakistan",
currencyCode: "PKR"
},
{
code: "PY",
name: "巴拉圭",
nameEn: "Paraguay",
currencyCode: "PYG"
},
{
code: "PS",
name: "巴勒斯坦",
nameEn: "Palestine, State of",
currencyCode: "ILS"
},
{
code: "BH",
name: "巴林",
nameEn: "Bahrain",
currencyCode: "BHD"
},
{
code: "PA",
name: "巴拿马",
nameEn: "Panama",
currencyCode: "PAB"
},
{
code: "BR",
name: "巴西",
nameEn: "Brazil",
currencyCode: "BRL"
},
{
code: "BY",
name: "白俄罗斯",
nameEn: "Belarus",
currencyCode: "BYN"
},
{
code: "BM",
name: "百慕大",
nameEn: "Bermuda",
currencyCode: "BMD"
},
{
code: "BG",
name: "保加利亚",
nameEn: "Bulgaria",
currencyCode: "BGN"
},
{
code: "MP",
name: "北马里亚纳群岛",
nameEn: "Northern Mariana Islands",
currencyCode: "USD"
},
{
code: "BJ",
name: "贝宁",
nameEn: "Benin",
currencyCode: "XOF"
},
{
code: "BE",
name: "比利时",
nameEn: "Belgium",
currencyCode: "EUR"
},
{
code: "IS",
name: "冰岛",
nameEn: "Iceland",
currencyCode: "ISK"
},
{
code: "PR",
name: "波多黎各",
nameEn: "Puerto Rico",
currencyCode: "USD"
},
{
code: "BA",
name: "波黑",
nameEn: "Bosnia and Herzegovina",
currencyCode: "BAM"
},
{
code: "PL",
name: "波兰",
nameEn: "Poland",
currencyCode: "PLN"
},
{
code: "BO",
name: "玻利维亚",
nameEn: "Bolivia, Plurinational State of",
currencyCode: "BOB"
},
{
code: "BZ",
name: "伯利兹",
nameEn: "Belize",
currencyCode: "BZD"
},
{
code: "BW",
name: "博茨瓦纳",
nameEn: "Botswana",
currencyCode: "BWP"
},
{
code: "BT",
name: "不丹",
nameEn: "Bhutan",
currencyCode: "BTN"
},
{
code: "BF",
name: "布基纳法索",
nameEn: "Burkina Faso",
currencyCode: "XOF"
},
{
code: "BI",
name: "布隆迪",
nameEn: "Burundi",
currencyCode: "BIF"
},
{
code: "BV",
name: "布韦岛",
nameEn: "Bouvet Island",
currencyCode: "NOK"
},
{
code: "KP",
name: "朝鲜",
nameEn: "Korea, Democratic People's Republic of",
currencyCode: "KPW"
},
{
code: "GQ",
name: "赤道几内亚",
nameEn: "Equatorial Guinea",
currencyCode: "XAF"
},
{
code: "DK",
name: "丹麦",
nameEn: "Denmark",
currencyCode: "DKK"
},
{
code: "DE",
name: "德国",
nameEn: "Germany",
currencyCode: "EUR"
},
{
code: "TL",
name: "东帝汶",
nameEn: "Timor-Leste",
currencyCode: "USD"
},
{
code: "TG",
name: "多哥",
nameEn: "Togo",
currencyCode: "XOF"
},
{
code: "DO",
name: "多米尼加",
nameEn: "Dominican Republic",
currencyCode: "DOP"
},
{
code: "DM",
name: "多米尼克",
nameEn: "Dominica",
currencyCode: "XCD"
},
{
code: "RU",
name: "俄罗斯",
nameEn: "Russian Federation",
currencyCode: "RUB"
},
{
code: "EC",
name: "厄瓜多尔",
nameEn: "Ecuador",
currencyCode: "USD"
},
{
code: "ER",
name: "厄立特里亚",
nameEn: "Eritrea",
currencyCode: "ERN"
},
{
code: "FR",
name: "法国",
nameEn: "France",
currencyCode: "EUR"
},
{
code: "FO",
name: "法罗群岛",
nameEn: "Faroe Islands",
currencyCode: "DKK"
},
{
code: "PF",
name: "法属波利尼西亚",
nameEn: "French Polynesia",
currencyCode: "XPF"
},
{
code: "VA",
name: "梵蒂冈",
nameEn: "Holy See",
currencyCode: "EUR"
},
{
code: "PH",
name: "菲律宾",
nameEn: "Philippines",
currencyCode: "PHP"
},
{
code: "FJ",
name: "斐济",
nameEn: "Fiji",
currencyCode: "FJD"
},
{
code: "FI",
name: "芬兰",
nameEn: "Finland",
currencyCode: "EUR"
},
{
code: "CV",
name: "佛得角",
nameEn: "Cabo Verde",
currencyCode: "CVE"
},
{
code: "FK",
name: "福克兰群岛",
nameEn: "Falkland Islands (Malvinas)",
currencyCode: "FKP"
},
{
code: "GM",
name: "冈比亚",
nameEn: "Gambia",
currencyCode: "GMD"
},
{
code: "CG",
name: "刚果共和国",
nameEn: "Congo",
currencyCode: "XAF"
},
{
code: "CD",
name: "刚果民主共和国",
nameEn: "Congo, the Democratic Republic of the",
currencyCode: "CDF"
},
{
code: "CO",
name: "哥伦比亚",
nameEn: "Colombia",
currencyCode: "COP"
},
{
code: "CR",
name: "哥斯达黎加",
nameEn: "Costa Rica",
currencyCode: "CRC"
},
{
code: "GD",
name: "格林纳达",
nameEn: "Grenada",
currencyCode: "XCD"
},
{
code: "GL",
name: "格陵兰",
nameEn: "Greenland",
currencyCode: "DKK"
},
{
code: "GE",
name: "格鲁吉亚",
nameEn: "Georgia",
currencyCode: "GEL"
},
{
code: "GG",
name: "根西",
nameEn: "Guernsey",
currencyCode: "GBP"
},
{
code: "CU",
name: "古巴",
nameEn: "Cuba",
currencyCode: "CUP"
},
{
code: "GP",
name: "瓜德罗普",
nameEn: "Guadeloupe",
currencyCode: "EUR"
},
{
code: "GU",
name: "关岛",
nameEn: "Guam",
currencyCode: "USD"
},
{
code: "GY",
name: "圭亚那",
nameEn: "Guyana",
currencyCode: "GYD"
},
{
code: "KZ",
name: "哈萨克斯坦",
nameEn: "Kazakhstan",
currencyCode: "KZT"
},
{
code: "HT",
name: "海地",
nameEn: "Haiti",
currencyCode: "HTG"
},
{
code: "KR",
name: "韩国",
nameEn: "Korea, Republic of",
currencyCode: "KRW"
},
{
code: "NL",
name: "荷兰",
nameEn: "Netherlands",
currencyCode: "EUR"
},
{
code: "SX",
name: "荷属圣马丁",
nameEn: "Sint Maarten (Dutch part)",
currencyCode: "ANG"
},
{
code: "HM",
name: "赫德岛和麦克唐纳群岛",
nameEn: "Heard Island and McDonald Islands",
currencyCode: "AUD"
},
{
code: "ME",
name: "黑山",
nameEn: "Montenegro",
currencyCode: "EUR"
},
{
code: "HN",
name: "洪都拉斯",
nameEn: "Honduras",
currencyCode: "HNL"
},
{
code: "KI",
name: "基里巴斯",
nameEn: "Kiribati",
currencyCode: "AUD"
},
{
code: "DJ",
name: "吉布提",
nameEn: "Djibouti",
currencyCode: "DJF"
},
{
code: "KG",
name: "吉尔吉斯斯坦",
nameEn: "Kyrgyzstan",
currencyCode: "KGS"
},
{
code: "GN",
name: "几内亚",
nameEn: "Guinea",
currencyCode: "GNF"
},
{
code: "GW",
name: "几内亚比绍",
nameEn: "Guinea-Bissau",
currencyCode: "XOF"
},
{
code: "CA",
name: "加拿大",
nameEn: "Canada",
currencyCode: "CAD"
},
{
code: "GH",
name: "加纳",
nameEn: "Ghana",
currencyCode: "GHS"
},
{
code: "GA",
name: "加蓬",
nameEn: "Gabon",
currencyCode: "XAF"
},
{
code: "KH",
name: "柬埔寨",
nameEn: "Cambodia",
currencyCode: "KHR"
},
{
code: "CZ",
name: "捷克",
nameEn: "Czechia",
currencyCode: "CZK"
},
{
code: "ZW",
name: "津巴布韦",
nameEn: "Zimbabwe",
currencyCode: "ZWL"
},
{
code: "CM",
name: "喀麦隆",
nameEn: "Cameroon",
currencyCode: "XAF"
},
{
code: "QA",
name: "卡塔尔",
nameEn: "Qatar",
currencyCode: "QAR"
},
{
code: "KY",
name: "开曼群岛",
nameEn: "Cayman Islands",
currencyCode: "KYD"
},
{
code: "CC",
name: "科科斯(基林)群岛",
nameEn: "Cocos (Keeling) Islands",
currencyCode: "AUD"
},
{
code: "KM",
name: "科摩罗",
nameEn: "Comoros",
currencyCode: "KMF"
},
{
code: "CI",
name: "科特迪瓦",
nameEn: "Côte d'Ivoire",
currencyCode: "XOF"
},
{
code: "KW",
name: "科威特",
nameEn: "Kuwait",
currencyCode: "KWD"
},
{
code: "HR",
name: "克罗地亚",
nameEn: "Croatia",
currencyCode: "HRK"
},
{
code: "KE",
name: "肯尼亚",
nameEn: "Kenya",
currencyCode: "KES"
},
{
code: "CK",
name: "库克群岛",
nameEn: "Cook Islands",
currencyCode: "NZD"
},
{
code: "CW",
name: "库拉索",
nameEn: "Curaçao",
currencyCode: "ANG"
},
{
code: "LV",
name: "拉脱维亚",
nameEn: "Latvia",
currencyCode: "EUR"
},
{
code: "LS",
name: "莱索托",
nameEn: "Lesotho",
currencyCode: "LSL"
},
{
code: "LA",
name: "老挝",
nameEn: "Lao People's Democratic Republic",
currencyCode: "LAK"
},
{
code: "LB",
name: "黎巴嫩",
nameEn: "Lebanon",
currencyCode: "LBP"
},
{
code: "LT",
name: "立陶宛",
nameEn: "Lithuania",
currencyCode: "EUR"
},
{
code: "LR",
name: "利比里亚",
nameEn: "Liberia",
currencyCode: "LRD"
},
{
code: "LY",
name: "利比亚",
nameEn: "Libya",
currencyCode: "LYD"
},
{
code: "LI",
name: "列支敦士登",
nameEn: "Liechtenstein",
currencyCode: "CHF"
},
{
code: "RE",
name: "留尼汪",
nameEn: "Réunion",
currencyCode: "EUR"
},
{
code: "LU",
name: "卢森堡",
nameEn: "Luxembourg",
currencyCode: "EUR"
},
{
code: "RW",
name: "卢旺达",
nameEn: "Rwanda",
currencyCode: "RWF"
},
{
code: "RO",
name: "罗马尼亚",
nameEn: "Romania",
currencyCode: "RON"
},
{
code: "MG",
name: "马达加斯加",
nameEn: "Madagascar",
currencyCode: "MGA"
},
{
code: "IM",
name: "马恩岛",
nameEn: "Isle of Man",
currencyCode: "GBP"
},
{
code: "MV",
name: "马尔代夫",
nameEn: "Maldives",
currencyCode: "MVR"
},
{
code: "MT",
name: "马耳他",
nameEn: "Malta",
currencyCode: "EUR"
},
{
code: "MW",
name: "马拉维",
nameEn: "Malawi",
currencyCode: "MWK"
},
{
code: "MY",
name: "马来西亚",
nameEn: "Malaysia",
currencyCode: "MYR"
},
{
code: "ML",
name: "马里",
nameEn: "Mali",
currencyCode: "XOF"
},
{
code: "MH",
name: "马绍尔群岛",
nameEn: "Marshall Islands",
currencyCode: "USD"
},
{
code: "MQ",
name: "马提尼克",
nameEn: "Martinique",
currencyCode: "EUR"
},
{
code: "YT",
name: "马约特",
nameEn: "Mayotte",
currencyCode: "EUR"
},
{
code: "MU",
name: "毛里求斯",
nameEn: "Mauritius",
currencyCode: "MUR"
},
{
code: "MR",
name: "毛里塔尼亚",
nameEn: "Mauritania",
currencyCode: "MRU"
},
{
code: "US",
name: "美国",
nameEn: "United States of America",
currencyCode: "USD"
},
{
code: "AS",
name: "美属萨摩亚",
nameEn: "American Samoa",
currencyCode: "USD"
},
{
code: "VI",
name: "美属维尔京群岛",
nameEn: "Virgin Islands, U.S.",
currencyCode: "USD"
},
{
code: "MN",
name: "蒙古",
nameEn: "Mongolia",
currencyCode: "MNT"
},
{
code: "MS",
name: "蒙特塞拉特",
nameEn: "Montserrat",
currencyCode: "XCD"
},
{
code: "BD",
name: "孟加拉国",
nameEn: "Bangladesh",
currencyCode: "BDT"
},
{
code: "PE",
name: "秘鲁",
nameEn: "Peru",
currencyCode: "PEN"
},
{
code: "FM",
name: "密克罗尼西亚联邦",
nameEn: "Micronesia, Federated States of",
currencyCode: "USD"
},
{
code: "MM",
name: "缅甸",
nameEn: "Myanmar",
currencyCode: "MMK"
},
{
code: "MD",
name: "摩尔多瓦",
nameEn: "Moldova, Republic of",
currencyCode: "MDL"
},
{
code: "MA",
name: "摩洛哥",
nameEn: "Morocco",
currencyCode: "MAD"
},
{
code: "MC",
name: "摩纳哥",
nameEn: "Monaco",
currencyCode: "EUR"
},
{
code: "MZ",
name: "莫桑比克",
nameEn: "Mozambique",
currencyCode: "MZN"
},
{
code: "MX",
name: "墨西哥",
nameEn: "Mexico",
currencyCode: "MXN"
},
{
code: "NA",
name: "纳米比亚",
nameEn: "Namibia",
currencyCode: "NAD"
},
{
code: "ZA",
name: "南非",
nameEn: "South Africa",
currencyCode: "ZAR"
},
{
code: "GS",
name: "南乔治亚和南桑威奇群岛",
nameEn: "South Georgia and the South Sandwich Islands",
currencyCode: "GBP"
},
{
code: "SS",
name: "南苏丹",
nameEn: "South Sudan",
currencyCode: "SSP"
},
{
code: "NR",
name: "瑙鲁",
nameEn: "Nauru",
currencyCode: "AUD"
},
{
code: "NI",
name: "尼加拉瓜",
nameEn: "Nicaragua",
currencyCode: "NIO"
},
{
code: "NP",
name: "尼泊尔",
nameEn: "Nepal",
currencyCode: "NPR"
},
{
code: "NG",
name: "尼日利亚",
nameEn: "Nigeria",
currencyCode: "NGN"
},
{
code: "NU",
name: "纽埃",
nameEn: "Niue",
currencyCode: "NZD"
},
{
code: "NO",
name: "挪威",
nameEn: "Norway",
currencyCode: "NOK"
},
{
code: "NF",
name: "诺福克岛",
nameEn: "Norfolk Island",
currencyCode: "AUD"
},
{
code: "PW",
name: "帕劳",
nameEn: "Palau",
currencyCode: "USD"
},
{
code: "PN",
name: "皮特凯恩群岛",
nameEn: "Pitcairn",
currencyCode: "NZD"
},
{
code: "PT",
name: "葡萄牙",
nameEn: "Portugal",
currencyCode: "EUR"
},
{
code: "JP",
name: "日本",
nameEn: "Japan",
currencyCode: "JPY"
},
{
code: "SE",
name: "瑞典",
nameEn: "Sweden",
currencyCode: "SEK"
},
{
code: "CH",
name: "瑞士",
nameEn: "Switzerland",
currencyCode: "CHF"
},
{
code: "SV",
name: "萨尔瓦多",
nameEn: "El Salvador",
currencyCode: "USD"
},
{
code: "WS",
name: "萨摩亚",
nameEn: "Samoa",
currencyCode: "WST"
},
{
code: "RS",
name: "塞尔维亚",
nameEn: "Serbia",
currencyCode: "RSD"
},
{
code: "SL",
name: "塞拉利昂",
nameEn: "Sierra Leone",
currencyCode: "SLL"
},
{
code: "SN",
name: "塞内加尔",
nameEn: "Senegal",
currencyCode: "XOF"
},
{
code: "CY",
name: "塞浦路斯",
nameEn: "Cyprus",
currencyCode: "EUR"
},
{
code: "SC",
name: "塞舌尔",
nameEn: "Seychelles",
currencyCode: "SCR"
},
{
code: "SA",
name: "沙特阿拉伯",
nameEn: "Saudi Arabia",
currencyCode: "SAR"
},
{
code: "BL",
name: "圣巴泰勒米",
nameEn: "Saint Barthélemy",
currencyCode: "EUR"
},
{
code: "CX",
name: "圣诞岛",
nameEn: "Christmas Island",
currencyCode: "AUD"
},
{
code: "ST",
name: "圣多美和普林西比",
nameEn: "Sao Tome and Principe",
currencyCode: "STN"
},
{
code: "SH",
name: "圣赫勒拿、阿森松和特里斯坦-达库尼亚",
nameEn: "Saint Helena, Ascension and Tristan da Cunha",
currencyCode: "SHP"
},
{
code: "KN",
name: "圣基茨和尼维斯",
nameEn: "Saint Kitts and Nevis",
currencyCode: "XCD"
},
{
code: "LC",
name: "圣卢西亚",
nameEn: "Saint Lucia",
currencyCode: "XCD"
},
{
code: "SM",
name: "圣马力诺",
nameEn: "San Marino",
currencyCode: "EUR"
},
{
code: "PM",
name: "圣皮埃尔和密克隆",
nameEn: "Saint Pierre and Miquelon",
currencyCode: "EUR"
},
{
code: "VC",
name: "圣文森特和格林纳丁斯",
nameEn: "Saint Vincent and the Grenadines",
currencyCode: "XCD"
},
{
code: "LK",
name: "斯里兰卡",
nameEn: "Sri Lanka",
currencyCode: "LKR"
},
{
code: "SK",
name: "斯洛伐克",
nameEn: "Slovakia",
currencyCode: "EUR"
},
{
code: "SI",
name: "斯洛文尼亚",
nameEn: "Slovenia",
currencyCode: "EUR"
},
{
code: "SJ",
name: "斯瓦尔巴和扬马延",
nameEn: "Svalbard and Jan Mayen",
currencyCode: "NOK"
},
{
code: "SZ",
name: "斯威士兰",
nameEn: "Eswatini",
currencyCode: "SZL"
},
{
code: "SD",
name: "苏丹",
nameEn: "Sudan",
currencyCode: "SDG"
},
{
code: "SR",
name: "苏里南",
nameEn: "Suriname",
currencyCode: "SRD"
},
{
code: "SB",
name: "所罗门群岛",
nameEn: "Solomon Islands",
currencyCode: "SBD"
},
{
code: "SO",
name: "索马里",
nameEn: "Somalia",
currencyCode: "SOS"
},
{
code: "TJ",
name: "塔吉克斯坦",
nameEn: "Tajikistan",
currencyCode: "TJS"
},
{
code: "TH",
name: "泰国",
nameEn: "Thailand",
currencyCode: "THB"
},
{
code: "TZ",
name: "坦桑尼亚",
nameEn: "Tanzania, United Republic of",
currencyCode: "TZS"
},
{
code: "TO",
name: "汤加",
nameEn: "Tonga",
currencyCode: "TOP"
},
{
code: "TC",
name: "特克斯和凯科斯群岛",
nameEn: "Turks and Caicos Islands",
currencyCode: "USD"
},
{
code: "TT",
name: "特立尼达和多巴哥",
nameEn: "Trinidad and Tobago",
currencyCode: "TTD"
},
{
code: "TN",
name: "突尼斯",
nameEn: "Tunisia",
currencyCode: "TND"
},
{
code: "TV",
name: "图瓦卢",
nameEn: "Tuvalu",
currencyCode: "AUD"
},
{
code: "TR",
name: "土耳其",
nameEn: "Turkey",
currencyCode: "TRY"
},
{
code: "TM",
name: "土库曼斯坦",
nameEn: "Turkmenistan",
currencyCode: "TMT"
},
{
code: "TK",
name: "托克劳",
nameEn: "Tokelau",
currencyCode: "NZD"
},
{
code: "WF",
name: "瓦利斯和富图纳",
nameEn: "Wallis and Futuna",
currencyCode: "XPF"
},
{
code: "VU",
name: "瓦努阿图",
nameEn: "Vanuatu",
currencyCode: "VUV"
},
{
code: "GT",
name: "危地马拉",
nameEn: "Guatemala",
currencyCode: "GTQ"
},
{
code: "VE",
name: "委内瑞拉",
nameEn: "Venezuela, Bolivarian Republic of",
currencyCode: "VES"
},
{
code: "BN",
name: "文莱",
nameEn: "Brunei Darussalam",
currencyCode: "BND"
},
{
code: "UG",
name: "乌干达",
nameEn: "Uganda",
currencyCode: "UGX"
},
{
code: "UA",
name: "乌克兰",
nameEn: "Ukraine",
currencyCode: "UAH"
},
{
code: "UY",
name: "乌拉圭",
nameEn: "Uruguay",
currencyCode: "UYU"
},
{
code: "UZ",
name: "乌兹别克斯坦",
nameEn: "Uzbekistan",
currencyCode: "UZS"
},
{
code: "ES",
name: "西班牙",
nameEn: "Spain",
currencyCode: "EUR"
},
{
code: "GR",
name: "希腊",
nameEn: "Greece",
currencyCode: "EUR"
},
{
code: "SG",
name: "新加坡",
nameEn: "Singapore",
currencyCode: "SGD"
},
{
code: "NC",
name: "新喀里多尼亚",
nameEn: "New Caledonia",
currencyCode: "XPF"
},
{
code: "NZ",
name: "新西兰",
nameEn: "New Zealand",
currencyCode: "NZD"
},
{
code: "HU",
name: "匈牙利",
nameEn: "Hungary",
currencyCode: "HUF"
},
{
code: "SY",
name: "叙利亚",
nameEn: "Syrian Arab Republic",
currencyCode: "SYP"
},
{
code: "JM",
name: "牙买加",
nameEn: "Jamaica",
currencyCode: "JMD"
},
{
code: "AM",
name: "亚美尼亚",
nameEn: "Armenia",
currencyCode: "AMD"
},
{
code: "YE",
name: "也门",
nameEn: "Yemen",
currencyCode: "YER"
},
{
code: "IQ",
name: "伊拉克",
nameEn: "Iraq",
currencyCode: "IQD"
},
{
code: "IR",
name: "伊朗",
nameEn: "Iran, Islamic Republic of",
currencyCode: "IRR"
},
{
code: "IL",
name: "以色列",
nameEn: "Israel",
currencyCode: "ILS"
},
{
code: "IT",
name: "意大利",
nameEn: "Italy",
currencyCode: "EUR"
},
{
code: "IN",
name: "印度",
nameEn: "India",
currencyCode: "INR"
},
{
code: "ID",
name: "印度尼西亚",
nameEn: "Indonesia",
currencyCode: "IDR"
},
{
code: "GB",
name: "英国",
nameEn: "United Kingdom of Great Britain and Northern Ireland",
currencyCode: "GBP"
},
{
code: "VG",
name: "英属维尔京群岛",
nameEn: "Virgin Islands, British",
currencyCode: "USD"
},
{
code: "IO",
name: "英属印度洋领地",
nameEn: "British Indian Ocean Territory",
currencyCode: "GBP"
},
{
code: "IO",
name: "英属印度洋领地",
nameEn: "British Indian Ocean Territory",
currencyCode: "USD"
},
{
code: "JO",
name: "约旦",
nameEn: "Jordan",
currencyCode: "JOD"
},
{
code: "VN",
name: "越南",
nameEn: "Viet Nam",
currencyCode: "VND"
},
{
code: "ZM",
name: "赞比亚",
nameEn: "Zambia",
currencyCode: "ZMW"
},
{
code: "JE",
name: "泽西",
nameEn: "Jersey",
currencyCode: "GBP"
},
{
code: "TD",
name: "乍得",
nameEn: "Chad",
currencyCode: "XAF"
},
{
code: "GI",
name: "直布罗陀",
nameEn: "Gibraltar",
currencyCode: "GIP"
},
{
code: "CL",
name: "智利",
nameEn: "Chile",
currencyCode: "CLF"
},
{
code: "CL",
name: "智利",
nameEn: "Chile",
currencyCode: "CLP"
},
{
code: "CF",
name: "中非",
nameEn: "Central African Republic",
currencyCode: "XAF"
},
{
code: "CN",
name: "中国",
nameEn: "China",
currencyCode: "CNY"
},
{
code: "TW",
name: "中国台湾",
nameEn: "Taiwan, Province of China",
currencyCode: "TWD"
},
{
code: "HK",
name: "中国香港",
nameEn: "Hong Kong",
currencyCode: "HKD"
}
];
const CountyCode2CountyInfo = new Map(countyCurrencyCodes.map((v) => [v.code, v]));
new Map(countyCurrencyCodes.map((v) => [v.currencyCode, v]));
const title = `[steam-price-convertor ${( new Date()).getMilliseconds()}] `;
function format(format2, ...args) {
args = args || [];
let message = format2;
for (let arg of args) {
message = message.replace("%s", arg);
}
return title + message;
}
const _SettingManager = class {
constructor() {
__publicField(this, "setting");
this.setting = this.loadSetting();
}
loadSetting() {
const json = _GM_getValue(STORAGE_KEY_SETTING, new Setting().toJsonString());
const setting = new Setting().readJsonString(json);
console.log(format("读取设置"), setting);
return setting;
}
/**
* 保存设置
* @param setting 设置
*/
saveSetting(setting) {
console.log(format("保存设置"), setting);
_GM_setValue(STORAGE_KEY_SETTING, setting.toJsonString());
}
setCountyCode(countyCode) {
const county = CountyCode2CountyInfo.get(countyCode);
if (!county) {
throw Error(`国家代码不存在:${countyCode}`);
}
this.setting.countyCode = countyCode;
this.saveSetting(this.setting);
}
setCurrencySymbol(currencySymbol) {
this.setting.currencySymbol = currencySymbol;
this.saveSetting(this.setting);
}
setCurrencySymbolBeforeValue(isCurrencySymbolBeforeValue) {
this.setting.currencySymbolBeforeValue = isCurrencySymbolBeforeValue;
this.saveSetting(this.setting);
}
reset() {
this.saveSetting(new Setting());
}
setRateCacheExpired(rateCacheExpired) {
this.setting.rateCacheExpired = rateCacheExpired;
this.saveSetting(this.setting);
}
setUseCustomRate(isUseCustomRate) {
this.setting.useCustomRate = isUseCustomRate;
this.saveSetting(this.setting);
}
setCustomRate(customRate) {
this.setting.customRate = customRate;
this.saveSetting(this.setting);
}
};
let SettingManager = _SettingManager;
__publicField(SettingManager, "instance", new _SettingManager());
function parsePrice(content) {
const matches = content.match(new RegExp("(?<=^\\D*)\\d+[\\d,.\\s]*?(?=\\D*$)"));
if (!matches) {
throw Error("提取价格失败:content:" + content);
}
let priceStr = matches[0].replaceAll(/\D/g, "");
let price = Number.parseInt(priceStr);
if (matches[0].match(/\D\d\d$/)) {
price = price / 100;
}
return price;
}
function convertPrice(price, rate) {
return Number.parseFloat((price / rate).toFixed(2));
}
function convertPriceContent(originalContent, rate) {
const safeContent = originalContent.trim().replaceAll(/\(.+$/g, "").trim();
const price = parsePrice(safeContent);
const convertedPrice = convertPrice(price, rate);
const setting = SettingManager.instance.setting;
let finalContent;
if (setting.currencySymbolBeforeValue) {
finalContent = `${safeContent}(${setting.currencySymbol}${convertedPrice})`;
} else {
finalContent = `${safeContent}(${convertedPrice}${setting.currencySymbol})`;
}
console.debug(format(
"转换前文本:%s; 提取到的价格:%s; 转换后的价格:%s; 转换后文本:%s",
safeContent,
price,
convertedPrice,
finalContent
));
return finalContent;
}
class ElementConverter extends AbstractConverter {
getCssSelectors() {
return [
// 商店
// 首页
".discount_original_price",
".discount_final_price",
".col.search_price.responsive_secondrow strike",
// 头像旁边
"#header_wallet_balance > span.tooltip",
// 愿望单总价值
".esi-wishlist-stat > .num",
// // 新版卡片
".salepreviewwidgets_StoreOriginalPrice_1EKGZ",
".salepreviewwidgets_StoreSalePriceBox_Wh0L8",
// 分类查看游戏
".contenthubshared_OriginalPrice_3hBh3",
".contenthubshared_FinalPrice_F_tGv",
".salepreviewwidgets_StoreSalePriceBox_Wh0L8:not(.salepreviewwidgets_StoreSalePrepurchaseLabel_Wxeyn)",
// 购物车
".cart_item_price.with_discount > .original_price",
".cart_item_price.with_discount > div.price:not(.original_price)",
"#cart_estimated_total",
// 购物车 复核
".checkout_review_item_price > .price",
"#review_subtotal_value.price",
"#review_total_value.price",
".cart_item_price > .price",
// 市场
// 总余额
"#marketWalletBalanceAmount",
// 列表
"span.normal_price[data-price]",
"span.sale_price",
// 求购、求售统计
".market_commodity_orders_header_promote:nth-child(even)",
// 求购、求售列表
".market_commodity_orders_table td:nth-child(odd)",
// 详情列表
".market_table_value > span",
".jqplot-highlighter-tooltip",
// 消费记录
"tr.wallet_table_row > td.wht_total",
"tr.wallet_table_row > td.wht_wallet_change.wallet_column",
"tr.wallet_table_row > td.wht_wallet_balance.wallet_column",
// dlc
".game_area_dlc_row > .game_area_dlc_price",
// 捆绑包
".package_totals_row > .price:not(.bundle_discount)",
"#package_savings_bar > .savings.bundle_savings",
// 低于xxx 分类标题
".home_page_content_title a.btn_small_tall > span"
];
}
convert(elementSnap, rate) {
elementSnap.element.textContent = convertPriceContent(elementSnap.textContext, rate);
return true;
}
}
class TextNodeConverter extends AbstractConverter {
constructor() {
super(...arguments);
// @ts-ignore
__publicField(this, "parseFirstChildTextNodeFn", (el) => el.firstChild);
// @ts-ignore
__publicField(this, "targets", /* @__PURE__ */ new Map([
[
".col.search_price.responsive_secondrow",
[
// @ts-ignore
(el) => el.firstChild.nextSibling.nextSibling.nextSibling,
this.parseFirstChildTextNodeFn
]
],
["#header_wallet_balance", [this.parseFirstChildTextNodeFn]],
// iframe
[".game_purchase_price.price", [this.parseFirstChildTextNodeFn]],
// 低于xxx 分类标题
[".home_page_content_title", [this.parseFirstChildTextNodeFn]]
]));
}
getCssSelectors() {
return [...this.targets.keys()];
}
convert(elementSnap, rate) {
const selector = elementSnap.selector;
this.targets.get(selector);
const parseNodeFns = this.targets.get(selector);
if (!parseNodeFns) {
return false;
}
const textNode = this.safeParseNode(selector, elementSnap.element, parseNodeFns);
if (!textNode) {
return false;
}
const content = textNode.nodeValue;
if (!content || content.trim().length === 0) {
return false;
}
textNode.nodeValue = convertPriceContent(content, rate);
return true;
}
safeParseNode(selector, el, fns) {
for (let fn of fns) {
try {
const node = fn(el);
if (node.nodeName === "#text" && node.nodeValue && node.nodeValue.length > 0) {
return node;
}
} catch (e) {
console.debug("获取文本节点失败,但不确定该节点是否一定会出现。selector:" + selector);
}
}
return null;
}
}
const _ConverterManager = class {
constructor() {
__publicField(this, "converters");
this.converters = [
new ElementConverter(),
new TextNodeConverter()
];
}
getSelector() {
return this.converters.map((exchanger) => exchanger.getCssSelectors()).flat(1).join(", ");
}
convert(elements, rate) {
if (!elements) {
return;
}
elements.forEach((element) => {
const elementSnap = {
element,
textContext: element.textContent,
classList: element.classList,
attributes: element.attributes
};
this.converters.filter((converter) => converter.match(elementSnap)).forEach((converter) => {
try {
const exchanged = converter.convert(elementSnap, rate);
if (exchanged) {
converter.afterConvert(elementSnap);
}
} catch (e) {
console.group("转换失败");
console.error(e);
console.error("转换失败请将下列内容反馈给开发者,右键 > 复制(copy) > 复制元素(copy element)");
console.error("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓");
console.error(element);
console.error("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑");
console.groupEnd();
}
});
});
}
};
let ConverterManager = _ConverterManager;
__publicField(ConverterManager, "instance", new _ConverterManager());
var __defProp$1 = Object.defineProperty;
var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
var __decorateClass$1 = (decorators, target, key, kind) => {
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
for (var i = decorators.length - 1, decorator; i >= 0; i--)
if (decorator = decorators[i])
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
if (kind && result)
__defProp$1(target, key, result);
return result;
};
class RateCache extends Serializable {
constructor(from, to, rate, createdAt) {
super();
__publicField(this, "from");
__publicField(this, "to");
__publicField(this, "createdAt");
__publicField(this, "rate");
this.from = from;
this.to = to;
this.createdAt = createdAt || 0;
this.rate = rate || 0;
}
}
__decorateClass$1([
JsonProperty()
], RateCache.prototype, "from", 2);
__decorateClass$1([
JsonProperty()
], RateCache.prototype, "to", 2);
__decorateClass$1([
JsonProperty()
], RateCache.prototype, "createdAt", 2);
__decorateClass$1([
JsonProperty()
], RateCache.prototype, "rate", 2);
class RateCaches extends Serializable {
constructor() {
super(...arguments);
__publicField(this, "caches", /* @__PURE__ */ new Map());
}
getCache(from, to) {
return this.caches.get(this.buildCacheKey(from, to));
}
setCache(cache) {
this.caches.set(this.buildCacheKey(cache.from, cache.to), cache);
}
buildCacheKey(from, to) {
return `${from}:${to}`;
}
}
__decorateClass$1([
JsonProperty({ typeAs: Map })
], RateCaches.prototype, "caches", 2);
class Http {
static get(url, respType, details) {
if (!details) {
details = { url };
}
details.method = "GET";
return this.request(details, respType);
}
static post(url, respType, details) {
if (!details) {
details = { url };
}
details.method = "POST";
return this.request(details, respType);
}
static parseResponse(response, respType) {
const data = JSON.parse(response.response);
const res = new respType();
return res.readJson(data);
}
static request(details, respType) {
return new Promise((resolve, reject) => {
details.onload = (response) => resolve(this.parseResponse(response, respType));
details.onerror = (error) => reject(error);
_GM_xmlhttpRequest(details);
});
}
}
var __defProp2 = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __decorateClass = (decorators, target, key, kind) => {
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
for (var i = decorators.length - 1, decorator; i >= 0; i--)
if (decorator = decorators[i])
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
if (kind && result)
__defProp2(target, key, result);
return result;
};
class AugmentedSteamRateResponse extends Serializable {
constructor() {
super(...arguments);
__publicField(this, "result");
__publicField(this, "data");
}
}
__decorateClass([
JsonAlias()
], AugmentedSteamRateResponse.prototype, "result", 2);
__decorateClass([
JsonProperty({
typeAs: Map,
mapValue: true
})
], AugmentedSteamRateResponse.prototype, "data", 2);
class AugmentedSteamRateApi {
getName() {
return "AugmentedSteamRateApi";
}
async getRate(currCounty, targetCounty) {
console.info(format(
"通过 AugmentedSteam 获取汇率 %s(%s) -> %s(%s)...",
currCounty.currencyCode,
currCounty.name,
targetCounty.currencyCode,
targetCounty.name
));
const url = `https://api.augmentedsteam.com/v01/rates/?to=${currCounty.currencyCode}`;
let rate = await Http.get(url, AugmentedSteamRateResponse).then((res) => {
var _a, _b;
if ("success" !== (res == null ? void 0 : res.result)) {
return null;
}
return (_b = (_a = res.data) == null ? void 0 : _a.get(targetCounty.currencyCode)) == null ? void 0 : _b.get(currCounty.currencyCode);
}).catch((err) => console.log(format("通过 AugmentedSteam 获取汇率失败:%s", err)));
if (rate) {
return rate;
}
throw new Error(`通过 ${this.getName()} 获取汇率失败。`);
}
}
const _RateManager = class {
constructor() {
__publicField(this, "rateApis");
__publicField(this, "rateCaches");
this.rateApis = [
new AugmentedSteamRateApi()
];
this.rateCaches = this.loadRateCache();
}
getName() {
return "RateManager";
}
async getRate4Remote(currCounty, targetCounty) {
console.log(format("远程获取汇率..."));
let rate;
for (let rateApi of this.rateApis) {
try {
rate = await rateApi.getRate(currCounty, targetCounty);
} catch (e) {
console.error(`使用实现(${rateApi.getName()})获取汇率失败`);
}
if (rate) {
return rate;
}
}
throw Error("所有汇率获取实现获取汇率均失败");
}
async getRate(currCounty, targetCounty) {
const setting = SettingManager.instance.setting;
if (setting.useCustomRate) {
console.log("使用自定义汇率");
return setting.customRate;
}
let cache = this.rateCaches.getCache(currCounty.code, targetCounty.code);
const now = (/* @__PURE__ */ new Date()).getTime();
const expired = setting.rateCacheExpired;
if (!cache || !cache.rate || now > cache.createdAt + expired) {
console.info(format(`本地缓存已过期`));
cache = new RateCache(currCounty.code, targetCounty.code);
cache.rate = await this.getRate4Remote(currCounty, targetCounty);
cache.createdAt = (/* @__PURE__ */ new Date()).getTime();
this.rateCaches.setCache(cache);
this.saveRateCache();
}
return cache.rate;
}
loadRateCache() {
const jsonString = _GM_getValue(STORAGE_KEY_RATE_CACHES, "{}");
const caches = new RateCaches();
console.info(format(`读取汇率缓存`));
return caches.readJsonString(jsonString);
}
saveRateCache() {
console.info(format("保存汇率缓存"), this.rateCaches);
_GM_setValue(STORAGE_KEY_RATE_CACHES, this.rateCaches.toJsonString());
}
clear() {
_GM_deleteValue(STORAGE_KEY_RATE_CACHES);
}
};
let RateManager = _RateManager;
__publicField(RateManager, "instance", new _RateManager());
class CookieCountyInfoGetter {
match() {
return true;
}
async getCountyCode() {
console.info(format("通过 cookie 获取区域代码..."));
let code;
await new Promise((resolve) => _GM_cookie.list({ name: "steamCountry" }, (cookies) => {
if (cookies && cookies.length > 0) {
const match = cookies[0].value.match(/^[a-zA-Z][a-zA-Z]/);
if (match) {
code = match[0];
resolve(code);
}
}
})).then((res) => code = res);
if (code) {
console.info(format("通过 cookie 获取区域代码成功:" + code));
return code;
}
throw Error("通过 cookie 获取区域代码失败。");
}
}
class RequestStorePageCountyCodeGetter {
match() {
return !window.location.href.includes("store.steampowered.com");
}
async getCountyCode() {
console.info(format("通过 请求商店页面 获取区域代码..."));
let countyCode = void 0;
await new Promise((resolve) => _GM_xmlhttpRequest({
url: "https://store.steampowered.com/",
onload: (response) => resolve(response.responseText)
})).then((res) => {
const match = res.match(new RegExp("(?<=GDynamicStore.Init\\(.+')[A-Z][A-Z](?=',)"));
if (match) {
countyCode = match[0];
console.info(format("通过 请求商店页面 获取区域代码成功:" + countyCode));
}
});
if (countyCode) {
return countyCode;
}
throw Error("通过 请求商店页面 获取区域代码失败。");
}
}
class StorePageCountyCodeGetter {
match() {
return window.location.href.includes("store.steampowered.com");
}
getCountyCode() {
console.info(format("通过 商店页面 获取区域代码..."));
return new Promise((resolve) => {
document.querySelectorAll("script").forEach((scriptEl) => {
const scriptInnerText = scriptEl.innerText;
if (scriptInnerText.includes("$J( InitMiniprofileHovers );") || scriptInnerText.includes(`$J( InitMiniprofileHovers( 'https%3A%2F%2Fstore.steampowered.com%2F' ) );`)) {
const matcher = new RegExp("(?<=')[A-Z]{2}(?!=')", "g");
const match = scriptInnerText.match(matcher);
if (match) {
const countyCode = match.toString();
console.info(format("通过 商店页面 获取区域代码成功:" + countyCode));
resolve(countyCode);
}
}
});
throw Error(format("通过 商店页面 获取区域代码失败。"));
});
}
}
class MarketPageCountyCodeGetter {
match() {
return window.location.href.includes("steamcommunity.com");
}
getCountyCode() {
console.info(format("通过 市场页面 获取区域代码..."));
const code = g_strCountryCode;
if (code) {
return new Promise((resolve) => resolve(code));
}
throw Error(format("通过 市场页面 获取区域代码失败。"));
}
}
const _CountyCodeGetterManager = class {
constructor() {
__publicField(this, "getters");
this.getters = [
new StorePageCountyCodeGetter(),
new MarketPageCountyCodeGetter(),
new RequestStorePageCountyCodeGetter(),
new CookieCountyInfoGetter()
];
}
match() {
return true;
}
async getCountyCode() {
console.info(format("获取区域代码..."));
let code;
for (let getter of this.getters) {
if (getter.match()) {
try {
code = await getter.getCountyCode();
} catch (e) {
console.error(e);
}
}
if (code) {
return code;
}
}
throw new Error(format("获取区域代码失败。"));
}
};
let CountyCodeGetterManager = _CountyCodeGetterManager;
__publicField(CountyCodeGetterManager, "instance", new _CountyCodeGetterManager());
const _SpcManager = class {
constructor() {
}
setCountyCode(code) {
SettingManager.instance.setCountyCode(code);
}
setCurrencySymbol(symbol) {
SettingManager.instance.setCurrencySymbol(symbol);
}
setCurrencySymbolBeforeValue(isCurrencySymbolBeforeValue) {
SettingManager.instance.setCurrencySymbolBeforeValue(isCurrencySymbolBeforeValue);
}
setRateCacheExpired(expired) {
SettingManager.instance.setRateCacheExpired(expired);
}
resetSetting() {
SettingManager.instance.reset();
}
clearCache() {
RateManager.instance.clear();
}
setUseCustomRate(isUseCustomRate) {
SettingManager.instance.setUseCustomRate(isUseCustomRate);
}
setCustomRate(customRate) {
SettingManager.instance.setCustomRate(customRate);
}
};
let SpcManager = _SpcManager;
__publicField(SpcManager, "instance", new _SpcManager());
async function main(targetCounty) {
_unsafeWindow.SpcManager = SpcManager.instance;
let countyCode = await CountyCodeGetterManager.instance.getCountyCode();
if (targetCounty.code === countyCode) {
console.info(format(`${targetCounty.name}无需转换`));
return;
}
const currCounty = CountyCode2CountyInfo.get(countyCode);
if (!currCounty) {
throw Error("获取货币代码失败");
}
console.info(format("获取区域对应信息:"), currCounty);
const rate = await RateManager.instance.getRate(currCounty, targetCounty);
if (!rate) {
throw Error("获取汇率失败");
}
console.info(format(`汇率 ${currCounty.currencyCode} -> ${targetCounty.currencyCode}:${rate}`));
await convert(rate);
}
async function convert(rate) {
const exchangerManager = ConverterManager.instance;
const elements = document.querySelectorAll(exchangerManager.getSelector());
exchangerManager.convert(elements, rate);
const selector = exchangerManager.getSelector();
const priceObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
const target = mutation.target;
const priceEls = target.querySelectorAll(selector);
if (!priceEls || priceEls.length === 0) {
return;
}
exchangerManager.convert(priceEls, rate);
});
});
priceObserver.observe(document.body, {
childList: true,
subtree: true
});
}
function addCdnCss(urls) {
urls.forEach((url) => {
const linkEl = document.createElement("link");
linkEl.href = url;
linkEl.type = "text/css";
linkEl.rel = "stylesheet";
document.head.append(linkEl);
});
}
const _hoisted_1 = { class: "mdui-theme-primary-indigo mdui-theme-accent-indigo" };
const _hoisted_2 = { class: "mdui-container" };
const _hoisted_3 = {
class: "mdui-dialog",
id: "spc-dialog-menu"
};
const _hoisted_4 = /* @__PURE__ */ vue.createElementVNode("div", { class: "mdui-dialog-title" }, "Setting", -1);
const _hoisted_5 = { class: "mdui-dialog-content" };
const _hoisted_6 = { class: "mdui-list" };
const _hoisted_7 = { class: "mdui-list-item mdui-ripple" };
const _hoisted_8 = /* @__PURE__ */ vue.createElementVNode("i", { class: "mdui-list-item-icon mdui-icon material-icons" }, "", -1);
const _hoisted_9 = /* @__PURE__ */ vue.createElementVNode("div", { class: "mdui-list-item-content" }, "目标区域", -1);
const _hoisted_10 = ["value", "selected"];
const _hoisted_11 = { class: "mdui-list-item mdui-ripple" };
const _hoisted_12 = /* @__PURE__ */ vue.createElementVNode("i", { class: "mdui-list-item-icon mdui-icon material-icons" }, "", -1);
const _hoisted_13 = /* @__PURE__ */ vue.createElementVNode("div", { class: "mdui-list-item-content" }, "货币符号", -1);
const _hoisted_14 = {
class: "mdui-textfield",
style: { "width": "40px" }
};
const _hoisted_15 = { class: "mdui-list-item mdui-ripple" };
const _hoisted_16 = /* @__PURE__ */ vue.createElementVNode("i", { class: "mdui-list-item-icon mdui-icon material-icons" }, "", -1);
const _hoisted_17 = /* @__PURE__ */ vue.createElementVNode("div", { class: "mdui-list-item-content" }, "货币符号是否在价格之前", -1);
const _hoisted_18 = { class: "mdui-switch" };
const _hoisted_19 = /* @__PURE__ */ vue.createElementVNode("i", { class: "mdui-switch-icon" }, null, -1);
const _hoisted_20 = { class: "mdui-list-item mdui-ripple" };
const _hoisted_21 = /* @__PURE__ */ vue.createElementVNode("i", { class: "mdui-list-item-icon mdui-icon material-icons" }, "", -1);
const _hoisted_22 = /* @__PURE__ */ vue.createElementVNode("div", { class: "mdui-list-item-content" }, "使用自定义汇率", -1);
const _hoisted_23 = { class: "mdui-switch" };
const _hoisted_24 = /* @__PURE__ */ vue.createElementVNode("i", { class: "mdui-switch-icon" }, null, -1);
const _hoisted_25 = {
key: 0,
class: "mdui-list-item mdui-ripple"
};
const _hoisted_26 = /* @__PURE__ */ vue.createElementVNode("i", { class: "mdui-list-item-icon mdui-icon material-icons" }, "", -1);
const _hoisted_27 = /* @__PURE__ */ vue.createElementVNode("div", { class: "mdui-list-item-content" }, "自定义汇率", -1);
const _hoisted_28 = {
class: "mdui-textfield",
style: { "width": "40px" }
};
const _hoisted_29 = {
key: 1,
class: "mdui-list-item mdui-ripple"
};
const _hoisted_30 = /* @__PURE__ */ vue.createElementVNode("i", { class: "mdui-list-item-icon mdui-icon material-icons" }, "", -1);
const _hoisted_31 = /* @__PURE__ */ vue.createElementVNode("div", { class: "mdui-list-item-content" }, "汇率缓存时间(小时)", -1);
const _hoisted_32 = {
class: "mdui-textfield",
style: { "width": "40px" }
};
const _hoisted_33 = /* @__PURE__ */ vue.createElementVNode("div", { class: "mdui-dialog-actions" }, [
/* @__PURE__ */ vue.createElementVNode("button", {
class: "mdui-btn mdui-ripple",
onclick: "location.reload()"
}, "关闭并刷新"),
/* @__PURE__ */ vue.createElementVNode("button", {
class: "mdui-btn mdui-ripple",
"mdui-dialog-close": ""
}, "关闭")
], -1);
const _sfc_main = /* @__PURE__ */ vue.defineComponent({
__name: "App",
setup(__props) {
vue.onMounted(() => {
const menu = new mdui.Dialog("#spc-dialog-menu");
_GM_addValueChangeListener(IM_KEY_MENU_STATUS, (name, oldValue, newValue) => {
if (IM_KEY_OPEN_MENU !== newValue) {
return;
}
_GM_setValue(IM_KEY_MENU_STATUS, IM_KEY_CLOSE_MENU);
menu.open();
});
});
const settingManager = SettingManager.instance;
const countyInfos = vue.reactive(countyCurrencyCodes);
const countySelectStyle = vue.reactive(initCountySelectStyle());
const setting = vue.reactive({
countyCode: settingManager.setting.countyCode,
currencySymbol: settingManager.setting.currencySymbol,
currencySymbolBeforeValue: settingManager.setting.currencySymbolBeforeValue,
rateCacheExpired: settingManager.setting.rateCacheExpired / (60 * 60 * 1e3),
customRate: settingManager.setting.customRate,
useCustomRate: settingManager.setting.useCustomRate
});
vue.watch(setting, (newSetting) => {
settingManager.setting.readJson(newSetting);
settingManager.setting.rateCacheExpired *= 60 * 60 * 1e3;
settingManager.saveSetting(settingManager.setting);
});
function initCountySelectStyle() {
const countyInfo = CountyCode2CountyInfo.get(settingManager.setting.countyCode);
return {
width: `${countyInfo.name.length * 16 + 25}px`
};
}
function changeCounty(code) {
const countyInfo = CountyCode2CountyInfo.get(code);
countySelectStyle.width = `${countyInfo.name.length * 16 + 25}px`;
setting.countyCode = countyInfo.code;
}
return (_ctx, _cache) => {
return vue.openBlock(), vue.createElementBlock("div", _hoisted_1, [
vue.createElementVNode("div", _hoisted_2, [
vue.createElementVNode("div", _hoisted_3, [
_hoisted_4,
vue.createElementVNode("div", _hoisted_5, [
vue.createElementVNode("div", _hoisted_6, [
vue.createElementVNode("li", _hoisted_7, [
_hoisted_8,
_hoisted_9,
vue.createElementVNode("select", {
class: "mdui-select",
style: vue.normalizeStyle(countySelectStyle),
onChange: _cache[0] || (_cache[0] = ($event) => (
// @ts-ignore
changeCounty($event.target.value)
))
}, [
(vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(countyInfos, (countyInfo) => {
return vue.openBlock(), vue.createElementBlock("option", {
value: countyInfo.code,
selected: countyInfo.code === setting.countyCode
}, vue.toDisplayString(countyInfo.name), 9, _hoisted_10);
}), 256))
], 36)
]),
vue.createElementVNode("li", _hoisted_11, [
_hoisted_12,
_hoisted_13,
vue.createElementVNode("label", _hoisted_14, [
vue.withDirectives(vue.createElementVNode("input", {
class: "mdui-textfield-input",
type: "text",
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => setting.currencySymbol = $event)
}, null, 512), [
[vue.vModelText, setting.currencySymbol]
])
])
]),
vue.createElementVNode("li", _hoisted_15, [
_hoisted_16,
_hoisted_17,
vue.createElementVNode("label", _hoisted_18, [
vue.withDirectives(vue.createElementVNode("input", {
type: "checkbox",
"onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => setting.currencySymbolBeforeValue = $event),
onChange: _cache[3] || (_cache[3] = ($event) => (
// @ts-ignore
setting.currencySymbolBeforeValue = $event.target.checked
))
}, null, 544), [
[vue.vModelCheckbox, setting.currencySymbolBeforeValue]
]),
_hoisted_19
])
]),
vue.createElementVNode("li", _hoisted_20, [
_hoisted_21,
_hoisted_22,
vue.createElementVNode("label", _hoisted_23, [
vue.withDirectives(vue.createElementVNode("input", {
type: "checkbox",
"onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => setting.useCustomRate = $event)
}, null, 512), [
[vue.vModelCheckbox, setting.useCustomRate]
]),
_hoisted_24
])
]),
setting.useCustomRate ? (vue.openBlock(), vue.createElementBlock("li", _hoisted_25, [
_hoisted_26,
_hoisted_27,
vue.createElementVNode("label", _hoisted_28, [
vue.withDirectives(vue.createElementVNode("input", {
class: "mdui-textfield-input",
type: "number",
"onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => setting.customRate = $event)
}, null, 512), [
[vue.vModelText, setting.customRate]
])
])
])) : vue.createCommentVNode("", true),
!setting.useCustomRate ? (vue.openBlock(), vue.createElementBlock("li", _hoisted_29, [
_hoisted_30,
_hoisted_31,
vue.createElementVNode("label", _hoisted_32, [
vue.withDirectives(vue.createElementVNode("input", {
class: "mdui-textfield-input",
type: "number",
"onUpdate:modelValue": _cache[6] || (_cache[6] = ($event) => setting.rateCacheExpired = $event)
}, null, 512), [
[vue.vModelText, setting.rateCacheExpired]
])
])
])) : vue.createCommentVNode("", true)
])
]),
_hoisted_33
])
])
]);
};
}
});
(async () => {
if (window.location.href.match("store.steampowered.com")) {
initCdn();
initApp();
initMenu();
}
await doConvert();
})();
function initCdn() {
addCdnCss([
"https://cdn.jsdelivr.net/npm/[email protected]/dist/css/mdui.min.css",
"https://cdn.jsdelivr.net/npm/[email protected]/dist/css/ionicons.min.css"
]);
}
function initApp() {
vue.createApp(_sfc_main).mount(
(() => {
const app = document.createElement("div");
app.setAttribute("id", "spc-menu");
document.body.append(app);
return app;
})()
);
mdui.mutation();
}
async function doConvert() {
const countyCode = SettingManager.instance.setting.countyCode;
const county = CountyCode2CountyInfo.get(countyCode);
if (!county) {
throw Error("获取转换后的国家信息失败,国家代码:" + countyCode);
}
await main(county);
}
function initMenu() {
_GM_setValue(IM_KEY_MENU_STATUS, IM_KEY_CLOSE_MENU);
_GM_registerMenuCommand("设置", () => {
_GM_setValue(IM_KEY_MENU_STATUS, IM_KEY_OPEN_MENU);
});
}
})(Reflect, Vue, mdui);