// ==UserScript== // @name steam价格转换 // @namespace https://github.com/marioplus/steam-price-converter // @version 2.3.8 // @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/vue@3.3.4/dist/vue.global.prod.js // @require https://cdn.jsdelivr.net/npm/mdui@1.0.2/dist/js/mdui.min.js // @require data:application/javascript,%3Bvar%20Reflect%3Dwindow.Reflect%3B // @require https://cdn.jsdelivr.net/npm/reflect-metadata@0.1.13/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_info // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_xmlhttpRequest // @grant unsafeWindow // @downloadURL none // ==/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}.sssss{color:#fff;line-height:1.2;padding:0 3px;border-radius:2px} "); (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); }; } 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); if (!config) { return; } const prop = anyThis[propKey]; const jsonNode = json[config.alias]; if (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$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 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); __publicField(this, "oldVersion", "0.0.0"); __publicField(this, "currVersion", "0.0.0"); } } __decorateClass$1([ JsonProperty() ], Setting.prototype, "countyCode", 2); __decorateClass$1([ JsonProperty() ], Setting.prototype, "currencySymbol", 2); __decorateClass$1([ JsonProperty() ], Setting.prototype, "currencySymbolBeforeValue", 2); __decorateClass$1([ JsonProperty() ], Setting.prototype, "rateCacheExpired", 2); __decorateClass$1([ JsonProperty() ], Setting.prototype, "useCustomRate", 2); __decorateClass$1([ JsonProperty() ], Setting.prototype, "customRate", 2); __decorateClass$1([ JsonProperty() ], Setting.prototype, "oldVersion", 2); __decorateClass$1([ JsonProperty() ], Setting.prototype, "currVersion", 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_info = /* @__PURE__ */ (() => typeof GM_info != "undefined" ? GM_info : 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: "USD" }, { 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: "USD" }, { 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 hintStyle(color) { return ` background: ${color}; color: white; padding: 1px 3px; border-radius: 2px; `; } function hintContent() { return `%c${title}`; } const LogLevels = { debug: { index: 0, color: "#009688", name: "debug", bindSource: console.debug }, info: { index: 1, color: "#2196f3", name: "info", bindSource: console.info }, warn: { index: 2, color: "#ffc107", name: "warn", bindSource: console.warn }, error: { index: 3, color: "#e91e63", name: "error", bindSource: console.error }, off: { index: 4, color: "", name: "off", bindSource: empLog } }; const LogConfig = { level: LogLevels.info }; function empLog() { } function unBind(level) { if (level.index < LogConfig.level.index) { Logger[level.name] = empLog.bind(this); } } const Logger = { debug: console.log.bind(console, hintContent(), hintStyle(LogLevels.debug.color)), info: console.info.bind(console, hintContent(), hintStyle(LogLevels.info.color)), warn: console.warn.bind(console, hintContent(), hintStyle(LogLevels.warn.color)), error: console.error.bind(console, hintContent(), hintStyle(LogLevels.error.color)) }; unBind(LogLevels.debug); unBind(LogLevels.info); unBind(LogLevels.warn); unBind(LogLevels.error); class Strings { static format(format, ...args) { args = args || []; let message = format; for (let arg of args) { message = message.replace("%s", arg); } return 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); setting.oldVersion = setting.currVersion; setting.currVersion = _GM_info.script.version; if (setting.oldVersion !== setting.currVersion) { Logger.debug(Strings.format(`版本更新:%s -> %s`, setting.oldVersion, setting.currVersion)); } Logger.debug("读取设置", setting); this.saveSetting(setting, false); return setting; } /** * 保存设置 * @param setting 设置 * @param logger 是否打印日志 */ saveSetting(setting, logger = true) { if (logger) { Logger.info("保存设置", 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 priceStr = content.replaceAll(/pуб\./g, "").replace(/\s/g, "").replace(/^[^0-9]+/, "").replace(/[^0-9,.]+$/, ""); const numberStr = priceStr.replace(/\D/g, ""); let price = Number.parseInt(numberStr); if (priceStr.match(/\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})`; } const message = Strings.format( "转换前文本:%s; 提取到的价格:%s; 转换后的价格:%s; 转换后文本:%s", safeContent, price, convertedPrice, finalContent ); Logger.debug(message); return finalContent; } class ElementConverter extends AbstractConverter { getCssSelectors() { const category = [ // 原价 ".Wh0L8EnwsPV_8VAu8TOYr", // 折扣价 "._1EKGZBnKFWOr3RqVdnLMRN" ]; const account = [ "div.accountData.price a" ]; const selectors = [ // 商店 // 首页 ".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", // 捆绑包 ".package_totals_row > .price:not(.bundle_discount)", "#package_savings_bar > .savings.bundle_savings", // 低于xxx 分类标题 ".home_page_content_title a.btn_small_tall > span" ]; selectors.push(...category); selectors.push(...account); return selectors; } 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]], // dlc 中没有折扣 [".game_area_dlc_row > .game_area_dlc_price", [ (el) => el, 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 __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 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([ JsonProperty() ], RateCache.prototype, "from", 2); __decorateClass([ JsonProperty() ], RateCache.prototype, "to", 2); __decorateClass([ JsonProperty() ], RateCache.prototype, "createdAt", 2); __decorateClass([ 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([ 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); if (!respType) { return data; } 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); }); } static getAsJson(url, details) { if (!details) { details = { url }; } details.method = "GET"; return this.request(details); } } class AugmentedSteamRateApi { getName() { return "AugmentedSteamRateApi"; } async getRate(currCounty, targetCounty) { Logger.info(Strings.format( "通过 AugmentedSteam 获取汇率 %s(%s) -> %s(%s)...", currCounty.currencyCode, currCounty.name, targetCounty.currencyCode, targetCounty.name )); const url = `https://api.augmentedsteam.com/rates/v1?to=${currCounty.currencyCode}`; let rate = await Http.getAsJson(url).then((res) => { if (!res) { return null; } return res[targetCounty.currencyCode][currCounty.currencyCode]; }).catch((err) => Logger.debug(Strings.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) { Logger.info("远程获取汇率..."); let rate; for (let rateApi of this.rateApis) { try { rate = await rateApi.getRate(currCounty, targetCounty); } catch (e) { Logger.error(`使用实现(${rateApi.getName()})获取汇率失败`); } if (rate) { return rate; } } throw Error("所有汇率获取实现获取汇率均失败"); } async getRate(currCounty, targetCounty) { const setting = SettingManager.instance.setting; if (setting.useCustomRate) { Logger.info("使用自定义汇率"); 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) { Logger.info(`本地缓存已过期`); 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 caches = new RateCaches(); const setting = SettingManager.instance.setting; if (setting.oldVersion !== setting.currVersion) { Logger.info(`脚本版本发生变化需要刷新汇率缓存`); this.clear(); return caches.readJsonString("{}"); } const jsonString = _GM_getValue(STORAGE_KEY_RATE_CACHES, "{}"); Logger.info(`读取汇率缓存`); return caches.readJsonString(jsonString); } saveRateCache() { Logger.info("保存汇率缓存", 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() { Logger.info("通过 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) { Logger.info("通过 cookie 获取区域代码成功:" + code); return code; } throw Error("通过 cookie 获取区域代码失败。"); } } class RequestStorePageCountyCodeGetter { match() { return !window.location.href.includes("store.steampowered.com"); } async getCountyCode() { Logger.info("通过 请求商店页面 获取区域代码..."); 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]; Logger.info("通过 请求商店页面 获取区域代码成功:" + countyCode); } }); if (countyCode) { return countyCode; } throw Error("通过 请求商店页面 获取区域代码失败。"); } } class StorePageCountyCodeGetter { match() { return window.location.href.includes("store.steampowered.com"); } getCountyCode() { Logger.info("通过 商店页面 获取区域代码..."); return new Promise((resolve) => { let countyCode = GStoreItemData.rgNavParams.__page_default_obj.countrycode; if (countyCode) { Logger.info(`通过 商店页面 获取区域代码成功:${countyCode}`); resolve(countyCode); return; } 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 countyCode2 = match.toString(); Logger.info(`通过 商店页面 获取区域代码成功:${countyCode2}`); resolve(countyCode2); return; } } }); throw Error("通过 商店页面 获取区域代码失败。"); }); } } class MarketPageCountyCodeGetter { match() { return window.location.href.includes("steamcommunity.com"); } getCountyCode() { Logger.info("通过 市场页面 获取区域代码..."); const code = g_strCountryCode; if (code) { return new Promise((resolve) => resolve(code)); } Logger.error("通过 市场页面 获取区域代码失败。"); throw Error("通过 市场页面 获取区域代码失败。"); } } const _CountyCodeGetterManager = class { constructor() { __publicField(this, "getters"); this.getters = [ new StorePageCountyCodeGetter(), new MarketPageCountyCodeGetter(), new RequestStorePageCountyCodeGetter(), new CookieCountyInfoGetter() ]; } match() { return true; } async getCountyCode() { Logger.info("获取区域代码..."); 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; } } Logger.error("获取区域代码..."); throw new Error("获取区域代码失败。"); } }; 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) { Logger.info(`${targetCounty.name}无需转换`); return; } const currCounty = CountyCode2CountyInfo.get(countyCode); if (!currCounty) { throw Error("获取货币代码失败"); } Logger.info("获取区域对应信息:", currCounty); const rate = await RateManager.instance.getRate(currCounty, targetCounty); if (!rate) { throw Error("获取汇率失败"); } Logger.info(Strings.format(`汇率 %s -> %s:%s`, 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; let 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/mdui@1.0.2/dist/css/mdui.min.css", "https://cdn.jsdelivr.net/npm/ionicons@4.6.4-1/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);