// ==UserScript== // @name steam价格转换 // @namespace https://github.com/marioplus/steam-price-converter // @version 2.5.0 // @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/reflect-metadata@0.1.13/Reflect.min.js // @require data:application/javascript,%3Bvar%20Reflect%3Dwindow.Reflect%3B // @require https://cdn.jsdelivr.net/npm/mdui@2.1.1/mdui.global.min.js // @require https://cdn.jsdelivr.net/npm/vue@3.3.4/dist/vue.global.prod.js // @resource mdui/mdui.css https://cdn.jsdelivr.net/npm/mdui@2.1.1/mdui.css // @connect api.augmentedsteam.com // @connect store.steampowered.com // @connect cdn.jsdelivr.net // @grant GM_addStyle // @grant GM_addValueChangeListener // @grant GM_cookie // @grant GM_deleteValue // @grant GM_getResourceText // @grant GM_getValue // @grant GM_info // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_xmlhttpRequest // @grant unsafeWindow // @downloadURL none // ==/UserScript== (e=>{if(typeof GM_addStyle=="function"){GM_addStyle(e);return}const t=document.createElement("style");t.textContent=e,document.head.append(t)})(" .setting-item[data-v-34f99f99]{color:rgb(var(--mdui-color-on-surface));padding:1em .75em;min-width:33em}.setting-item-title[data-v-34f99f99]{display:flex;align-items:center;padding:8px 0}.setting-item-title label[data-v-34f99f99]{display:inline-block;padding-right:.4em;font-size:var(--mdui-typescale-body-large-size);font-weight:var(--mdui-typescale-body-medium-weight);letter-spacing:var(--mdui-typescale-body-medium-tracking);line-height:var(--mdui-typescale-body-medium-line-height)}.setting-item-title .setting-region-title-icon[data-v-34f99f99]{font-size:var(--mdui-typescale-body-large-size)}mdui-select[data-v-34f99f99]::part(menu){max-height:256px;overflow:auto}mdui-text-field[data-v-34f99f99]::part(suffix){color:#00f} "); (async function (reflectMetadata, mdui, vue) { '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; }; const cssLoader = (e) => { const t = GM_getResourceText(e); return GM_addStyle(t), t; }; cssLoader("mdui/mdui.css"); 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) { } } var TransformationType; (function(TransformationType2) { TransformationType2[TransformationType2["PLAIN_TO_CLASS"] = 0] = "PLAIN_TO_CLASS"; TransformationType2[TransformationType2["CLASS_TO_PLAIN"] = 1] = "CLASS_TO_PLAIN"; TransformationType2[TransformationType2["CLASS_TO_CLASS"] = 2] = "CLASS_TO_CLASS"; })(TransformationType || (TransformationType = {})); var MetadataStorage = ( /** @class */ function() { function MetadataStorage2() { this._typeMetadatas = /* @__PURE__ */ new Map(); this._transformMetadatas = /* @__PURE__ */ new Map(); this._exposeMetadatas = /* @__PURE__ */ new Map(); this._excludeMetadatas = /* @__PURE__ */ new Map(); this._ancestorsMap = /* @__PURE__ */ new Map(); } MetadataStorage2.prototype.addTypeMetadata = function(metadata) { if (!this._typeMetadatas.has(metadata.target)) { this._typeMetadatas.set(metadata.target, /* @__PURE__ */ new Map()); } this._typeMetadatas.get(metadata.target).set(metadata.propertyName, metadata); }; MetadataStorage2.prototype.addTransformMetadata = function(metadata) { if (!this._transformMetadatas.has(metadata.target)) { this._transformMetadatas.set(metadata.target, /* @__PURE__ */ new Map()); } if (!this._transformMetadatas.get(metadata.target).has(metadata.propertyName)) { this._transformMetadatas.get(metadata.target).set(metadata.propertyName, []); } this._transformMetadatas.get(metadata.target).get(metadata.propertyName).push(metadata); }; MetadataStorage2.prototype.addExposeMetadata = function(metadata) { if (!this._exposeMetadatas.has(metadata.target)) { this._exposeMetadatas.set(metadata.target, /* @__PURE__ */ new Map()); } this._exposeMetadatas.get(metadata.target).set(metadata.propertyName, metadata); }; MetadataStorage2.prototype.addExcludeMetadata = function(metadata) { if (!this._excludeMetadatas.has(metadata.target)) { this._excludeMetadatas.set(metadata.target, /* @__PURE__ */ new Map()); } this._excludeMetadatas.get(metadata.target).set(metadata.propertyName, metadata); }; MetadataStorage2.prototype.findTransformMetadatas = function(target, propertyName, transformationType) { return this.findMetadatas(this._transformMetadatas, target, propertyName).filter(function(metadata) { if (!metadata.options) return true; if (metadata.options.toClassOnly === true && metadata.options.toPlainOnly === true) return true; if (metadata.options.toClassOnly === true) { return transformationType === TransformationType.CLASS_TO_CLASS || transformationType === TransformationType.PLAIN_TO_CLASS; } if (metadata.options.toPlainOnly === true) { return transformationType === TransformationType.CLASS_TO_PLAIN; } return true; }); }; MetadataStorage2.prototype.findExcludeMetadata = function(target, propertyName) { return this.findMetadata(this._excludeMetadatas, target, propertyName); }; MetadataStorage2.prototype.findExposeMetadata = function(target, propertyName) { return this.findMetadata(this._exposeMetadatas, target, propertyName); }; MetadataStorage2.prototype.findExposeMetadataByCustomName = function(target, name) { return this.getExposedMetadatas(target).find(function(metadata) { return metadata.options && metadata.options.name === name; }); }; MetadataStorage2.prototype.findTypeMetadata = function(target, propertyName) { return this.findMetadata(this._typeMetadatas, target, propertyName); }; MetadataStorage2.prototype.getStrategy = function(target) { var excludeMap = this._excludeMetadatas.get(target); var exclude = excludeMap && excludeMap.get(void 0); var exposeMap = this._exposeMetadatas.get(target); var expose = exposeMap && exposeMap.get(void 0); if (exclude && expose || !exclude && !expose) return "none"; return exclude ? "excludeAll" : "exposeAll"; }; MetadataStorage2.prototype.getExposedMetadatas = function(target) { return this.getMetadata(this._exposeMetadatas, target); }; MetadataStorage2.prototype.getExcludedMetadatas = function(target) { return this.getMetadata(this._excludeMetadatas, target); }; MetadataStorage2.prototype.getExposedProperties = function(target, transformationType) { return this.getExposedMetadatas(target).filter(function(metadata) { if (!metadata.options) return true; if (metadata.options.toClassOnly === true && metadata.options.toPlainOnly === true) return true; if (metadata.options.toClassOnly === true) { return transformationType === TransformationType.CLASS_TO_CLASS || transformationType === TransformationType.PLAIN_TO_CLASS; } if (metadata.options.toPlainOnly === true) { return transformationType === TransformationType.CLASS_TO_PLAIN; } return true; }).map(function(metadata) { return metadata.propertyName; }); }; MetadataStorage2.prototype.getExcludedProperties = function(target, transformationType) { return this.getExcludedMetadatas(target).filter(function(metadata) { if (!metadata.options) return true; if (metadata.options.toClassOnly === true && metadata.options.toPlainOnly === true) return true; if (metadata.options.toClassOnly === true) { return transformationType === TransformationType.CLASS_TO_CLASS || transformationType === TransformationType.PLAIN_TO_CLASS; } if (metadata.options.toPlainOnly === true) { return transformationType === TransformationType.CLASS_TO_PLAIN; } return true; }).map(function(metadata) { return metadata.propertyName; }); }; MetadataStorage2.prototype.clear = function() { this._typeMetadatas.clear(); this._exposeMetadatas.clear(); this._excludeMetadatas.clear(); this._ancestorsMap.clear(); }; MetadataStorage2.prototype.getMetadata = function(metadatas, target) { var metadataFromTargetMap = metadatas.get(target); var metadataFromTarget; if (metadataFromTargetMap) { metadataFromTarget = Array.from(metadataFromTargetMap.values()).filter(function(meta) { return meta.propertyName !== void 0; }); } var metadataFromAncestors = []; for (var _i = 0, _a = this.getAncestors(target); _i < _a.length; _i++) { var ancestor = _a[_i]; var ancestorMetadataMap = metadatas.get(ancestor); if (ancestorMetadataMap) { var metadataFromAncestor = Array.from(ancestorMetadataMap.values()).filter(function(meta) { return meta.propertyName !== void 0; }); metadataFromAncestors.push.apply(metadataFromAncestors, metadataFromAncestor); } } return metadataFromAncestors.concat(metadataFromTarget || []); }; MetadataStorage2.prototype.findMetadata = function(metadatas, target, propertyName) { var metadataFromTargetMap = metadatas.get(target); if (metadataFromTargetMap) { var metadataFromTarget = metadataFromTargetMap.get(propertyName); if (metadataFromTarget) { return metadataFromTarget; } } for (var _i = 0, _a = this.getAncestors(target); _i < _a.length; _i++) { var ancestor = _a[_i]; var ancestorMetadataMap = metadatas.get(ancestor); if (ancestorMetadataMap) { var ancestorResult = ancestorMetadataMap.get(propertyName); if (ancestorResult) { return ancestorResult; } } } return void 0; }; MetadataStorage2.prototype.findMetadatas = function(metadatas, target, propertyName) { var metadataFromTargetMap = metadatas.get(target); var metadataFromTarget; if (metadataFromTargetMap) { metadataFromTarget = metadataFromTargetMap.get(propertyName); } var metadataFromAncestorsTarget = []; for (var _i = 0, _a = this.getAncestors(target); _i < _a.length; _i++) { var ancestor = _a[_i]; var ancestorMetadataMap = metadatas.get(ancestor); if (ancestorMetadataMap) { if (ancestorMetadataMap.has(propertyName)) { metadataFromAncestorsTarget.push.apply(metadataFromAncestorsTarget, ancestorMetadataMap.get(propertyName)); } } } return metadataFromAncestorsTarget.slice().reverse().concat((metadataFromTarget || []).slice().reverse()); }; MetadataStorage2.prototype.getAncestors = function(target) { if (!target) return []; if (!this._ancestorsMap.has(target)) { var ancestors = []; for (var baseClass = Object.getPrototypeOf(target.prototype.constructor); typeof baseClass.prototype !== "undefined"; baseClass = Object.getPrototypeOf(baseClass.prototype.constructor)) { ancestors.push(baseClass); } this._ancestorsMap.set(target, ancestors); } return this._ancestorsMap.get(target); }; return MetadataStorage2; }() ); var defaultMetadataStorage = new MetadataStorage(); function getGlobal() { if (typeof globalThis !== "undefined") { return globalThis; } if (typeof global !== "undefined") { return global; } if (typeof window !== "undefined") { return window; } if (typeof self !== "undefined") { return self; } } function isPromise(p) { return p !== null && typeof p === "object" && typeof p.then === "function"; } var __spreadArray = function(to, from, pack) { for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; function instantiateArrayType(arrayType) { var array = new arrayType(); if (!(array instanceof Set) && !("push" in array)) { return []; } return array; } var TransformOperationExecutor = ( /** @class */ function() { function TransformOperationExecutor2(transformationType, options) { this.transformationType = transformationType; this.options = options; this.recursionStack = /* @__PURE__ */ new Set(); } TransformOperationExecutor2.prototype.transform = function(source, value, targetType, arrayType, isMap, level) { var _this = this; if (level === void 0) { level = 0; } if (Array.isArray(value) || value instanceof Set) { var newValue_1 = arrayType && this.transformationType === TransformationType.PLAIN_TO_CLASS ? instantiateArrayType(arrayType) : []; value.forEach(function(subValue, index) { var subSource = source ? source[index] : void 0; if (!_this.options.enableCircularCheck || !_this.isCircular(subValue)) { var realTargetType = void 0; if (typeof targetType !== "function" && targetType && targetType.options && targetType.options.discriminator && targetType.options.discriminator.property && targetType.options.discriminator.subTypes) { if (_this.transformationType === TransformationType.PLAIN_TO_CLASS) { realTargetType = targetType.options.discriminator.subTypes.find(function(subType) { return subType.name === subValue[targetType.options.discriminator.property]; }); var options = { newObject: newValue_1, object: subValue, property: void 0 }; var newType = targetType.typeFunction(options); realTargetType === void 0 ? realTargetType = newType : realTargetType = realTargetType.value; if (!targetType.options.keepDiscriminatorProperty) delete subValue[targetType.options.discriminator.property]; } if (_this.transformationType === TransformationType.CLASS_TO_CLASS) { realTargetType = subValue.constructor; } if (_this.transformationType === TransformationType.CLASS_TO_PLAIN) { subValue[targetType.options.discriminator.property] = targetType.options.discriminator.subTypes.find(function(subType) { return subType.value === subValue.constructor; }).name; } } else { realTargetType = targetType; } var value_1 = _this.transform(subSource, subValue, realTargetType, void 0, subValue instanceof Map, level + 1); if (newValue_1 instanceof Set) { newValue_1.add(value_1); } else { newValue_1.push(value_1); } } else if (_this.transformationType === TransformationType.CLASS_TO_CLASS) { if (newValue_1 instanceof Set) { newValue_1.add(subValue); } else { newValue_1.push(subValue); } } }); return newValue_1; } else if (targetType === String && !isMap) { if (value === null || value === void 0) return value; return String(value); } else if (targetType === Number && !isMap) { if (value === null || value === void 0) return value; return Number(value); } else if (targetType === Boolean && !isMap) { if (value === null || value === void 0) return value; return Boolean(value); } else if ((targetType === Date || value instanceof Date) && !isMap) { if (value instanceof Date) { return new Date(value.valueOf()); } if (value === null || value === void 0) return value; return new Date(value); } else if (!!getGlobal().Buffer && (targetType === Buffer || value instanceof Buffer) && !isMap) { if (value === null || value === void 0) return value; return Buffer.from(value); } else if (isPromise(value) && !isMap) { return new Promise(function(resolve, reject) { value.then(function(data) { return resolve(_this.transform(void 0, data, targetType, void 0, void 0, level + 1)); }, reject); }); } else if (!isMap && value !== null && typeof value === "object" && typeof value.then === "function") { return value; } else if (typeof value === "object" && value !== null) { if (!targetType && value.constructor !== Object) if (!Array.isArray(value) && value.constructor === Array) ; else { targetType = value.constructor; } if (!targetType && source) targetType = source.constructor; if (this.options.enableCircularCheck) { this.recursionStack.add(value); } var keys = this.getKeys(targetType, value, isMap); var newValue = source ? source : {}; if (!source && (this.transformationType === TransformationType.PLAIN_TO_CLASS || this.transformationType === TransformationType.CLASS_TO_CLASS)) { if (isMap) { newValue = /* @__PURE__ */ new Map(); } else if (targetType) { newValue = new targetType(); } else { newValue = {}; } } var _loop_1 = function(key2) { if (key2 === "__proto__" || key2 === "constructor") { return "continue"; } var valueKey = key2; var newValueKey = key2, propertyName = key2; if (!this_1.options.ignoreDecorators && targetType) { if (this_1.transformationType === TransformationType.PLAIN_TO_CLASS) { var exposeMetadata = defaultMetadataStorage.findExposeMetadataByCustomName(targetType, key2); if (exposeMetadata) { propertyName = exposeMetadata.propertyName; newValueKey = exposeMetadata.propertyName; } } else if (this_1.transformationType === TransformationType.CLASS_TO_PLAIN || this_1.transformationType === TransformationType.CLASS_TO_CLASS) { var exposeMetadata = defaultMetadataStorage.findExposeMetadata(targetType, key2); if (exposeMetadata && exposeMetadata.options && exposeMetadata.options.name) { newValueKey = exposeMetadata.options.name; } } } var subValue = void 0; if (this_1.transformationType === TransformationType.PLAIN_TO_CLASS) { subValue = value[valueKey]; } else { if (value instanceof Map) { subValue = value.get(valueKey); } else if (value[valueKey] instanceof Function) { subValue = value[valueKey](); } else { subValue = value[valueKey]; } } var type = void 0, isSubValueMap = subValue instanceof Map; if (targetType && isMap) { type = targetType; } else if (targetType) { var metadata_1 = defaultMetadataStorage.findTypeMetadata(targetType, propertyName); if (metadata_1) { var options = { newObject: newValue, object: value, property: propertyName }; var newType = metadata_1.typeFunction ? metadata_1.typeFunction(options) : metadata_1.reflectedType; if (metadata_1.options && metadata_1.options.discriminator && metadata_1.options.discriminator.property && metadata_1.options.discriminator.subTypes) { if (!(value[valueKey] instanceof Array)) { if (this_1.transformationType === TransformationType.PLAIN_TO_CLASS) { type = metadata_1.options.discriminator.subTypes.find(function(subType) { if (subValue && subValue instanceof Object && metadata_1.options.discriminator.property in subValue) { return subType.name === subValue[metadata_1.options.discriminator.property]; } }); type === void 0 ? type = newType : type = type.value; if (!metadata_1.options.keepDiscriminatorProperty) { if (subValue && subValue instanceof Object && metadata_1.options.discriminator.property in subValue) { delete subValue[metadata_1.options.discriminator.property]; } } } if (this_1.transformationType === TransformationType.CLASS_TO_CLASS) { type = subValue.constructor; } if (this_1.transformationType === TransformationType.CLASS_TO_PLAIN) { if (subValue) { subValue[metadata_1.options.discriminator.property] = metadata_1.options.discriminator.subTypes.find(function(subType) { return subType.value === subValue.constructor; }).name; } } } else { type = metadata_1; } } else { type = newType; } isSubValueMap = isSubValueMap || metadata_1.reflectedType === Map; } else if (this_1.options.targetMaps) { this_1.options.targetMaps.filter(function(map) { return map.target === targetType && !!map.properties[propertyName]; }).forEach(function(map) { return type = map.properties[propertyName]; }); } else if (this_1.options.enableImplicitConversion && this_1.transformationType === TransformationType.PLAIN_TO_CLASS) { var reflectedType = Reflect.getMetadata("design:type", targetType.prototype, propertyName); if (reflectedType) { type = reflectedType; } } } var arrayType_1 = Array.isArray(value[valueKey]) ? this_1.getReflectedType(targetType, propertyName) : void 0; var subSource = source ? source[valueKey] : void 0; if (newValue.constructor.prototype) { var descriptor = Object.getOwnPropertyDescriptor(newValue.constructor.prototype, newValueKey); if ((this_1.transformationType === TransformationType.PLAIN_TO_CLASS || this_1.transformationType === TransformationType.CLASS_TO_CLASS) && // eslint-disable-next-line @typescript-eslint/unbound-method (descriptor && !descriptor.set || newValue[newValueKey] instanceof Function)) return "continue"; } if (!this_1.options.enableCircularCheck || !this_1.isCircular(subValue)) { var transformKey = this_1.transformationType === TransformationType.PLAIN_TO_CLASS ? newValueKey : key2; var finalValue = void 0; if (this_1.transformationType === TransformationType.CLASS_TO_PLAIN) { finalValue = value[transformKey]; finalValue = this_1.applyCustomTransformations(finalValue, targetType, transformKey, value, this_1.transformationType); finalValue = value[transformKey] === finalValue ? subValue : finalValue; finalValue = this_1.transform(subSource, finalValue, type, arrayType_1, isSubValueMap, level + 1); } else { if (subValue === void 0 && this_1.options.exposeDefaultValues) { finalValue = newValue[newValueKey]; } else { finalValue = this_1.transform(subSource, subValue, type, arrayType_1, isSubValueMap, level + 1); finalValue = this_1.applyCustomTransformations(finalValue, targetType, transformKey, value, this_1.transformationType); } } if (finalValue !== void 0 || this_1.options.exposeUnsetFields) { if (newValue instanceof Map) { newValue.set(newValueKey, finalValue); } else { newValue[newValueKey] = finalValue; } } } else if (this_1.transformationType === TransformationType.CLASS_TO_CLASS) { var finalValue = subValue; finalValue = this_1.applyCustomTransformations(finalValue, targetType, key2, value, this_1.transformationType); if (finalValue !== void 0 || this_1.options.exposeUnsetFields) { if (newValue instanceof Map) { newValue.set(newValueKey, finalValue); } else { newValue[newValueKey] = finalValue; } } } }; var this_1 = this; for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) { var key = keys_1[_i]; _loop_1(key); } if (this.options.enableCircularCheck) { this.recursionStack.delete(value); } return newValue; } else { return value; } }; TransformOperationExecutor2.prototype.applyCustomTransformations = function(value, target, key, obj, transformationType) { var _this = this; var metadatas = defaultMetadataStorage.findTransformMetadatas(target, key, this.transformationType); if (this.options.version !== void 0) { metadatas = metadatas.filter(function(metadata) { if (!metadata.options) return true; return _this.checkVersion(metadata.options.since, metadata.options.until); }); } if (this.options.groups && this.options.groups.length) { metadatas = metadatas.filter(function(metadata) { if (!metadata.options) return true; return _this.checkGroups(metadata.options.groups); }); } else { metadatas = metadatas.filter(function(metadata) { return !metadata.options || !metadata.options.groups || !metadata.options.groups.length; }); } metadatas.forEach(function(metadata) { value = metadata.transformFn({ value, key, obj, type: transformationType, options: _this.options }); }); return value; }; TransformOperationExecutor2.prototype.isCircular = function(object) { return this.recursionStack.has(object); }; TransformOperationExecutor2.prototype.getReflectedType = function(target, propertyName) { if (!target) return void 0; var meta = defaultMetadataStorage.findTypeMetadata(target, propertyName); return meta ? meta.reflectedType : void 0; }; TransformOperationExecutor2.prototype.getKeys = function(target, object, isMap) { var _this = this; var strategy = defaultMetadataStorage.getStrategy(target); if (strategy === "none") strategy = this.options.strategy || "exposeAll"; var keys = []; if (strategy === "exposeAll" || isMap) { if (object instanceof Map) { keys = Array.from(object.keys()); } else { keys = Object.keys(object); } } if (isMap) { return keys; } if (this.options.ignoreDecorators && this.options.excludeExtraneousValues && target) { var exposedProperties = defaultMetadataStorage.getExposedProperties(target, this.transformationType); var excludedProperties = defaultMetadataStorage.getExcludedProperties(target, this.transformationType); keys = __spreadArray(__spreadArray([], exposedProperties, true), excludedProperties); } if (!this.options.ignoreDecorators && target) { var exposedProperties = defaultMetadataStorage.getExposedProperties(target, this.transformationType); if (this.transformationType === TransformationType.PLAIN_TO_CLASS) { exposedProperties = exposedProperties.map(function(key) { var exposeMetadata = defaultMetadataStorage.findExposeMetadata(target, key); if (exposeMetadata && exposeMetadata.options && exposeMetadata.options.name) { return exposeMetadata.options.name; } return key; }); } if (this.options.excludeExtraneousValues) { keys = exposedProperties; } else { keys = keys.concat(exposedProperties); } var excludedProperties_1 = defaultMetadataStorage.getExcludedProperties(target, this.transformationType); if (excludedProperties_1.length > 0) { keys = keys.filter(function(key) { return !excludedProperties_1.includes(key); }); } if (this.options.version !== void 0) { keys = keys.filter(function(key) { var exposeMetadata = defaultMetadataStorage.findExposeMetadata(target, key); if (!exposeMetadata || !exposeMetadata.options) return true; return _this.checkVersion(exposeMetadata.options.since, exposeMetadata.options.until); }); } if (this.options.groups && this.options.groups.length) { keys = keys.filter(function(key) { var exposeMetadata = defaultMetadataStorage.findExposeMetadata(target, key); if (!exposeMetadata || !exposeMetadata.options) return true; return _this.checkGroups(exposeMetadata.options.groups); }); } else { keys = keys.filter(function(key) { var exposeMetadata = defaultMetadataStorage.findExposeMetadata(target, key); return !exposeMetadata || !exposeMetadata.options || !exposeMetadata.options.groups || !exposeMetadata.options.groups.length; }); } } if (this.options.excludePrefixes && this.options.excludePrefixes.length) { keys = keys.filter(function(key) { return _this.options.excludePrefixes.every(function(prefix) { return key.substr(0, prefix.length) !== prefix; }); }); } keys = keys.filter(function(key, index, self2) { return self2.indexOf(key) === index; }); return keys; }; TransformOperationExecutor2.prototype.checkVersion = function(since, until) { var decision = true; if (decision && since) decision = this.options.version >= since; if (decision && until) decision = this.options.version < until; return decision; }; TransformOperationExecutor2.prototype.checkGroups = function(groups) { if (!groups) return true; return this.options.groups.some(function(optionGroup) { return groups.includes(optionGroup); }); }; return TransformOperationExecutor2; }() ); var defaultOptions = { enableCircularCheck: false, enableImplicitConversion: false, excludeExtraneousValues: false, excludePrefixes: void 0, exposeDefaultValues: false, exposeUnsetFields: true, groups: void 0, ignoreDecorators: false, strategy: void 0, targetMaps: void 0, version: void 0 }; var __assign = function() { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var ClassTransformer = ( /** @class */ function() { function ClassTransformer2() { } ClassTransformer2.prototype.instanceToPlain = function(object, options) { var executor = new TransformOperationExecutor(TransformationType.CLASS_TO_PLAIN, __assign(__assign({}, defaultOptions), options)); return executor.transform(void 0, object, void 0, void 0, void 0, void 0); }; ClassTransformer2.prototype.classToPlainFromExist = function(object, plainObject, options) { var executor = new TransformOperationExecutor(TransformationType.CLASS_TO_PLAIN, __assign(__assign({}, defaultOptions), options)); return executor.transform(plainObject, object, void 0, void 0, void 0, void 0); }; ClassTransformer2.prototype.plainToInstance = function(cls, plain, options) { var executor = new TransformOperationExecutor(TransformationType.PLAIN_TO_CLASS, __assign(__assign({}, defaultOptions), options)); return executor.transform(void 0, plain, cls, void 0, void 0, void 0); }; ClassTransformer2.prototype.plainToClassFromExist = function(clsObject, plain, options) { var executor = new TransformOperationExecutor(TransformationType.PLAIN_TO_CLASS, __assign(__assign({}, defaultOptions), options)); return executor.transform(clsObject, plain, void 0, void 0, void 0, void 0); }; ClassTransformer2.prototype.instanceToInstance = function(object, options) { var executor = new TransformOperationExecutor(TransformationType.CLASS_TO_CLASS, __assign(__assign({}, defaultOptions), options)); return executor.transform(void 0, object, void 0, void 0, void 0, void 0); }; ClassTransformer2.prototype.classToClassFromExist = function(object, fromObject, options) { var executor = new TransformOperationExecutor(TransformationType.CLASS_TO_CLASS, __assign(__assign({}, defaultOptions), options)); return executor.transform(fromObject, object, void 0, void 0, void 0, void 0); }; ClassTransformer2.prototype.serialize = function(object, options) { return JSON.stringify(this.instanceToPlain(object, options)); }; ClassTransformer2.prototype.deserialize = function(cls, json, options) { var jsonObject = JSON.parse(json); return this.plainToInstance(cls, jsonObject, options); }; ClassTransformer2.prototype.deserializeArray = function(cls, json, options) { var jsonObject = JSON.parse(json); return this.plainToInstance(cls, jsonObject, options); }; return ClassTransformer2; }() ); function Type(typeFunction, options) { if (options === void 0) { options = {}; } return function(target, propertyName) { var reflectedType = Reflect.getMetadata("design:type", target, propertyName); defaultMetadataStorage.addTypeMetadata({ target: target.constructor, propertyName, reflectedType, typeFunction, options }); }; } var classTransformer = new ClassTransformer(); function instanceToPlain(object, options) { return classTransformer.instanceToPlain(object, options); } function plainToInstance(cls, plain, options) { return classTransformer.plainToInstance(cls, plain, options); } var __defProp$1 = Object.defineProperty; var __decorateClass$1 = (decorators, target, key, kind) => { var result = void 0; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = decorator(target, key, result) || result; if (result) __defProp$1(target, key, result); return result; }; class Setting { constructor() { /** * 目标国家代码,默认CN */ __publicField(this, "countyCode", "CN"); /** * 目标货币符号,默认 ¥ */ __publicField(this, "currencySymbol", "¥"); /** * 符号位置在首 */ __publicField(this, "currencySymbolBeforeValue", true); /** * 汇率获取时间,默认1小时 */ __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"); __publicField(this, "logLevel", "info"); } } __decorateClass$1([ Type(() => String) ], Setting.prototype, "logLevel"); const STORAGE_KEY_PREFIX = "Storage:"; const STORAGE_KEY_RATE_CACHES = STORAGE_KEY_PREFIX + "RateCache"; const STORAGE_KEY_SETTING = STORAGE_KEY_PREFIX + "Setting"; const IM_MENU_SETTING = "设置"; 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)(); class Http { static get(cls, url2, details) { if (!details) { details = { url: url2 }; } details.method = "GET"; return this.request(cls, details); } static post(url2, cls, details) { if (!details) { details = { url: url2 }; } details.method = "POST"; return this.request(cls, details); } static request(cls, details) { return new Promise((resolve, reject) => { details.onload = (response) => { if (cls.name === String.name) { resolve(response.response); } else { const json = JSON.parse(response.response); resolve(plainToInstance(cls, json)); } }; details.onerror = (error) => reject(error); _GM_xmlhttpRequest(details); }); } } const url = `https://cdn.jsdelivr.net/gh/marioplus/steam-price-converter@v${_GM_info.script.version}/src/county/countyCurrencyCodes.json`; const countyInfos = await( Http.get(Array, url)); const countyCode2Info = new Map(countyInfos.map((v) => [v.code, v])); function initializeLogTitle() { const isInIFrame = window.parent !== window || window.frames.length > 0; return isInIFrame ? `steam-price-convertor iframe(${(/* @__PURE__ */ new Date()).getMilliseconds()})` : "steam-price-convertor"; } function createLogStyle(color) { return ` background: ${color}; color: white; padding: 1px 3px; border-radius: 2px; `; } function composeLogHint() { return `%c${initializeLogTitle()}`; } const LogDefinitions = { debug: { index: 0, color: "#009688", label: "debug", bindMethod: console.info }, info: { index: 1, color: "#2196f3", label: "info", bindMethod: console.info }, warn: { index: 2, color: "#ffc107", label: "warn", bindMethod: console.warn }, error: { index: 3, color: "#e91e63", label: "error", bindMethod: console.error }, off: { index: 4, color: "", label: "off", bindMethod: () => { } } }; const Logger = { debug: noopLog.bind(null), info: noopLog.bind(null), warn: noopLog.bind(null), error: noopLog.bind(null) }; function noopLog() { } let currLogLevel = LogDefinitions.info; function refreshBinding() { const hint = composeLogHint(); Object.entries(LogDefinitions).forEach(([label, def]) => { if (def.index >= currLogLevel.index) { const logStyle = createLogStyle(def.color); Logger[label.toLowerCase()] = def.bindMethod.bind(console, hint, logStyle); } else { Logger[label.toLowerCase()] = noopLog.bind(null); } }); } function setLogLevel(levelLabel) { const newLevel = LogDefinitions[levelLabel]; if (newLevel) { currLogLevel = newLevel; refreshBinding(); } else { console.error(`Invalid log level: ${levelLabel}`); } } refreshBinding(); class Strings { static format(format, ...args) { args = args || []; let message = format; for (let arg of args) { message = message.replace("%s", arg); } return message; } } class JsonUtils { static toJson(obj) { return instanceToPlain(obj); } static toJsonString(obj) { return JSON.stringify(this.toJson(obj)); } static readJson(json, cls) { return plainToInstance(cls, json); } static readJsonString(jsonString, cls) { return this.readJson(JSON.parse(jsonString), cls); } } class GmUtils { static getValue(cls, key, defaultValue) { const value = _GM_getValue(key); return value ? JsonUtils.readJsonString(value, cls) : defaultValue; } static setValue(key, value) { _GM_setValue(key, JsonUtils.toJsonString(value)); } static deleteValue(key) { _GM_deleteValue(key); } static registerMenuCommand(caption, onClick, accessKey) { const key = `GM_registerMenuCommand@${caption}`; _GM_registerMenuCommand( caption, (event) => { this.setValue(key, true); Logger.debug("点击菜单:" + caption); this.setValue(key, false); if (onClick) { onClick(event); } }, accessKey ); } static addMenuClickEventListener(caption, onClick) { const key = `GM_registerMenuCommand@${caption}`; _GM_addValueChangeListener(key, (name, oldValue, newValue) => { if (newValue) { onClick(); } }); } } const _SettingManager = class _SettingManager { constructor() { __publicField(this, "setting"); this.setting = this.loadSetting(); } loadSetting() { const setting = GmUtils.getValue(Setting, STORAGE_KEY_SETTING, new Setting()); setting.oldVersion = setting.currVersion; setting.currVersion = _GM_info.script.version; if (setting.oldVersion === setting.currVersion) { Logger.info("读取设置", setting); } else { Logger.debug(Strings.format(`版本更新重置设置:%s -> %s`, setting.oldVersion, setting.currVersion)); this.saveSetting(setting); } return setting; } /** * 保存设置 * @param setting 设置 */ saveSetting(setting) { Logger.info("保存设置", setting); this.setting = setting; GmUtils.setValue(STORAGE_KEY_SETTING, setting); } setCountyCode(countyCode) { const county = countyCode2Info.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); } }; __publicField(_SettingManager, "instance", new _SettingManager()); let SettingManager = _SettingManager; function parsePrice(content) { let priceStr = content.replace(/руб\./g, "").replace(/pуб\./g, "").replace(/\s/g, "").replace(/^[^0-9]+/, "").replace(/[^0-9,.]+$/, "").replace(/,(?=\d\d\d)/g, ""); const numberStr = priceStr.replace(/\D/g, ""); let price = Number.parseInt(numberStr) ?? 0; 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 = setting.currencySymbolBeforeValue ? `${safeContent}(${setting.currencySymbol}${convertedPrice})` : `${safeContent}(${convertedPrice}${setting.currencySymbol})`; const message = `转换前文本:${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 wishlist = [ // 右上角钱包 "div.Hxi-pnf9Xlw- > div._79DIT7RUQ5g-", // 当前价格 "div.ME2eMO7C1Tk- > div.DOnsaVcV0Is-", // 原价 "div.ME2eMO7C1Tk- > div.ywNldZ-YzEE-" ]; 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); selectors.push(...wishlist); 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 _ConverterManager { 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(); } }); }); } }; __publicField(_ConverterManager, "instance", new _ConverterManager()); let ConverterManager = _ConverterManager; var __defProp2 = Object.defineProperty; var __decorateClass = (decorators, target, key, kind) => { var result = void 0; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = decorator(target, key, result) || result; if (result) __defProp2(target, key, result); return result; }; class RateCache { constructor(from, to, rate, createdAt) { __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; } } class RateCaches { constructor() { __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([ Type(() => Map) ], RateCaches.prototype, "caches"); class SpcContext { constructor(setting, targetCountyInfo, targetCountyCode) { __publicField(this, "_setting"); __publicField(this, "_targetCountyInfo"); __publicField(this, "_currentCountyInfo"); this._setting = setting; this._targetCountyInfo = targetCountyInfo; this._currentCountyInfo = targetCountyCode; } static getContext() { return _unsafeWindow.spcContext; } get setting() { return this._setting; } get targetCountyInfo() { return this._targetCountyInfo; } get currentCountyInfo() { return this._currentCountyInfo; } } class AugmentedSteamRateApi { getName() { return "AugmentedSteamRateApi"; } async getRate() { const context = SpcContext.getContext(); Logger.info(Strings.format( "通过 AugmentedSteam 获取汇率 %s(%s) -> %s(%s)...", context.currentCountyInfo.currencyCode, context.currentCountyInfo.name, context.targetCountyInfo.currencyCode, context.targetCountyInfo.name )); const url2 = `https://api.augmentedsteam.com/rates/v1?to=${context.currentCountyInfo.currencyCode}`; let rate = await Http.get(Map, url2).then((res) => res.get(context.targetCountyInfo.currencyCode)[context.currentCountyInfo.currencyCode]).catch((err) => Logger.error("通过 AugmentedSteam 获取汇率失败", err)); if (rate) { return rate; } throw new Error(`通过 ${this.getName()} 获取汇率失败。`); } } const _RateManager = class _RateManager { constructor() { __publicField(this, "rateApis"); __publicField(this, "rateCaches", new RateCaches()); this.rateApis = [ new AugmentedSteamRateApi() ]; } getName() { return "RateManager"; } async getRate4Remote() { Logger.info("远程获取汇率..."); let rate; for (let rateApi of this.rateApis) { try { rate = await rateApi.getRate(); } catch (e) { Logger.error(`使用实现(${rateApi.getName()})获取汇率失败`); } if (rate) { return rate; } } throw Error("所有汇率获取实现获取汇率均失败"); } async getRate() { const context = SpcContext.getContext(); if (context.setting.useCustomRate) { Logger.info("使用自定义汇率"); return context.setting.customRate; } this.rateCaches = this.loadRateCache(); let cache = this.rateCaches.getCache(context.currentCountyInfo.code, context.targetCountyInfo.code); const now = (/* @__PURE__ */ new Date()).getTime(); const expired = context.setting.rateCacheExpired; if (!cache || !cache.rate || now > cache.createdAt + expired) { Logger.info(`本地缓存已过期`); cache = new RateCache(context.currentCountyInfo.code, context.targetCountyInfo.code); cache.rate = await this.getRate4Remote(); cache.createdAt = (/* @__PURE__ */ new Date()).getTime(); this.rateCaches.setCache(cache); this.saveRateCache(); } return cache.rate; } loadRateCache() { const setting = SpcContext.getContext().setting; if (setting.oldVersion !== setting.currVersion) { Logger.info(`脚本版本发生变化需要刷新汇率缓存`); this.clear(); return new RateCaches(); } Logger.info(`读取汇率缓存`); return GmUtils.getValue(RateCaches, STORAGE_KEY_RATE_CACHES, new RateCaches()); } saveRateCache() { Logger.info("保存汇率缓存", this.rateCaches); GmUtils.setValue(STORAGE_KEY_RATE_CACHES, this.rateCaches); } clear() { GmUtils.deleteValue(STORAGE_KEY_RATE_CACHES); } }; __publicField(_RateManager, "instance", new _RateManager()); let RateManager = _RateManager; async function main() { const context = SpcContext.getContext(); if (context.currentCountyInfo.code === context.targetCountyInfo.code) { Logger.info(`${context.currentCountyInfo.name}无需转换`); return; } const rate = await RateManager.instance.getRate(); if (!rate) { throw Error("获取汇率失败"); } Logger.info(Strings.format(`汇率 %s -> %s:%s`, context.currentCountyInfo.currencyCode, context.targetCountyInfo.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 }); } const _withScopeId = (n) => (vue.pushScopeId("data-v-34f99f99"), n = n(), vue.popScopeId(), n); const _hoisted_1 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("link", { href: "https://fonts.googleapis.com/icon?family=Material+Icons", rel: "stylesheet" }, null, -1)); const _hoisted_2 = { "close-on-overlay-click": "", class: "setting" }; const _hoisted_3 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("span", { slot: "headline" }, "设置", -1)); const _hoisted_4 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("span", { slot: "description" }, "随心所欲设置 steam-price-converter", -1)); const _hoisted_5 = { class: "setting-item" }; const _hoisted_6 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "setting-item-title" }, [ /* @__PURE__ */ vue.createElementVNode("label", null, "目标区域"), /* @__PURE__ */ vue.createElementVNode("mdui-tooltip", { content: "将价格转换为此区域的货币", placement: "right" }, [ /* @__PURE__ */ vue.createElementVNode("mdui-icon", { name: "error", class: "setting-region-title-icon" }) ]) ], -1)); const _hoisted_7 = { class: "setting-item-content" }; const _hoisted_8 = ["value"]; const _hoisted_9 = ["value"]; const _hoisted_10 = { class: "setting-item" }; const _hoisted_11 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "setting-item-title" }, [ /* @__PURE__ */ vue.createElementVNode("label", null, "货币符号"), /* @__PURE__ */ vue.createElementVNode("mdui-tooltip", { content: "转换后的价格的货币符号", placement: "right" }, [ /* @__PURE__ */ vue.createElementVNode("mdui-icon", { name: "error", class: "setting-region-title-icon" }) ]) ], -1)); const _hoisted_12 = { class: "setting-item-content" }; const _hoisted_13 = ["value"]; const _hoisted_14 = { class: "setting-item" }; const _hoisted_15 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "setting-item-title" }, [ /* @__PURE__ */ vue.createElementVNode("label", null, "货币符号展示位置"), /* @__PURE__ */ vue.createElementVNode("mdui-tooltip", { content: "控制转换后的价格货币符号的位置", placement: "right" }, [ /* @__PURE__ */ vue.createElementVNode("mdui-icon", { name: "error", class: "setting-region-title-icon" }) ]) ], -1)); const _hoisted_16 = { class: "setting-item-content" }; const _hoisted_17 = ["value"]; const _hoisted_18 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("mdui-menu-item", { value: "true" }, "价格之前", -1)); const _hoisted_19 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("mdui-menu-item", { value: "false" }, "价格之后", -1)); const _hoisted_20 = [ _hoisted_18, _hoisted_19 ]; const _hoisted_21 = { class: "setting-item" }; const _hoisted_22 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "setting-item-title" }, [ /* @__PURE__ */ vue.createElementVNode("label", null, "汇率缓存有效期"), /* @__PURE__ */ vue.createElementVNode("mdui-tooltip", { content: "获取汇率后进行缓存,在此时间内将使用缓存汇率", placement: "right" }, [ /* @__PURE__ */ vue.createElementVNode("mdui-icon", { name: "error", class: "setting-region-title-icon" }) ]) ], -1)); const _hoisted_23 = { class: "setting-item-content" }; const _hoisted_24 = ["value"]; const _hoisted_25 = { class: "setting-item" }; const _hoisted_26 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "setting-item-title" }, [ /* @__PURE__ */ vue.createElementVNode("label", null, "使用自定义汇率"), /* @__PURE__ */ vue.createElementVNode("mdui-tooltip", { content: "使用自定义汇率进行价格转换,不再获取区域,不再根据区域获取汇率", placement: "right" }, [ /* @__PURE__ */ vue.createElementVNode("mdui-icon", { name: "error", class: "setting-region-title-icon" }) ]) ], -1)); const _hoisted_27 = { class: "setting-item-content" }; const _hoisted_28 = ["value"]; const _hoisted_29 = { class: "setting-item" }; const _hoisted_30 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "setting-item-title" }, [ /* @__PURE__ */ vue.createElementVNode("label", null, "自定义汇率"), /* @__PURE__ */ vue.createElementVNode("mdui-tooltip", { content: "开启“使用自定义汇率”后使用此汇率进行转换", placement: "right" }, [ /* @__PURE__ */ vue.createElementVNode("mdui-icon", { name: "error", class: "setting-region-title-icon" }) ]) ], -1)); const _hoisted_31 = { class: "setting-item-content" }; const _hoisted_32 = ["value"]; const _hoisted_33 = { class: "setting-item" }; const _hoisted_34 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("div", { class: "setting-item-title" }, [ /* @__PURE__ */ vue.createElementVNode("label", null, "日志等级"), /* @__PURE__ */ vue.createElementVNode("mdui-tooltip", { content: "日志等级 debug > info > warn > error > off", placement: "right" }, [ /* @__PURE__ */ vue.createElementVNode("mdui-icon", { name: "error", class: "setting-region-title-icon" }) ]) ], -1)); const _hoisted_35 = { class: "setting-item-content" }; const _hoisted_36 = ["value"]; const _hoisted_37 = ["value"]; const _hoisted_38 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("mdui-button", { class: "setting-btn-canal", slot: "action", variant: "text" }, "取消", -1)); const _hoisted_39 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("mdui-button", { class: "setting-btn-save", slot: "action", variant: "tonal" }, "保存", -1)); const _sfc_main = /* @__PURE__ */ vue.defineComponent({ __name: "App", setup(__props) { const vueCountyInfos = countyInfos; const setting = Object.assign({}, SettingManager.instance.setting); vue.onMounted(() => { var _a, _b; const dialog = document.querySelector(".setting"); (_a = dialog.querySelector(".setting-btn-canal")) == null ? void 0 : _a.addEventListener("click", () => dialog.open = false); (_b = dialog.querySelector(".setting-btn-save")) == null ? void 0 : _b.addEventListener("click", () => { SettingManager.instance.saveSetting(setting); dialog.open = false; }); GmUtils.addMenuClickEventListener(IM_MENU_SETTING, () => dialog.open = true); }); function getSelected(target) { var _a; return (_a = target.querySelector("*[selected]")) == null ? void 0 : _a.value; } return (_ctx, _cache) => { return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [ _hoisted_1, vue.createElementVNode("mdui-dialog", _hoisted_2, [ _hoisted_3, _hoisted_4, vue.createElementVNode("div", _hoisted_5, [ _hoisted_6, vue.createElementVNode("div", _hoisted_7, [ vue.createElementVNode("mdui-select", { value: vue.unref(setting).countyCode, placement: "bottom", onChange: _cache[0] || (_cache[0] = ($event) => vue.unref(setting).countyCode = getSelected($event.target)) }, [ (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(vue.unref(vueCountyInfos), (countyInfo) => { return vue.openBlock(), vue.createElementBlock("mdui-menu-item", { value: countyInfo.code }, vue.toDisplayString(countyInfo.name) + " (" + vue.toDisplayString(countyInfo.code) + ") ", 9, _hoisted_9); }), 256)) ], 40, _hoisted_8) ]) ]), vue.createElementVNode("div", _hoisted_10, [ _hoisted_11, vue.createElementVNode("div", _hoisted_12, [ vue.createElementVNode("mdui-text-field", { value: vue.unref(setting).currencySymbol, onChange: _cache[1] || (_cache[1] = ($event) => vue.unref(setting).currencySymbol = $event.target.value) }, null, 40, _hoisted_13) ]) ]), vue.createElementVNode("div", _hoisted_14, [ _hoisted_15, vue.createElementVNode("div", _hoisted_16, [ vue.createElementVNode("mdui-select", { value: vue.unref(setting).currencySymbolBeforeValue.toString(), placement: "bottom", onChange: _cache[2] || (_cache[2] = ($event) => vue.unref(setting).currencySymbolBeforeValue = getSelected($event.target) === "true") }, _hoisted_20, 40, _hoisted_17) ]) ]), vue.createElementVNode("div", _hoisted_21, [ _hoisted_22, vue.createElementVNode("div", _hoisted_23, [ vue.createElementVNode("mdui-text-field", { type: "number", value: vue.unref(setting).rateCacheExpired / (60 * 60 * 1e3), suffix: "h", onChange: _cache[3] || (_cache[3] = ($event) => vue.unref(setting).rateCacheExpired = $event.target.value * (60 * 60 * 1e3)) }, null, 40, _hoisted_24) ]) ]), vue.createElementVNode("div", _hoisted_25, [ _hoisted_26, vue.createElementVNode("div", _hoisted_27, [ vue.createElementVNode("mdui-switch", { slot: "end-icon", value: vue.unref(setting).useCustomRate, onChange: _cache[4] || (_cache[4] = ($event) => vue.unref(setting).useCustomRate = $event.target.checked) }, null, 40, _hoisted_28) ]) ]), vue.createElementVNode("div", _hoisted_29, [ _hoisted_30, vue.createElementVNode("div", _hoisted_31, [ vue.createElementVNode("mdui-text-field", { type: "number", value: vue.unref(setting).customRate, onChange: _cache[5] || (_cache[5] = ($event) => vue.unref(setting).customRate = $event.target.value) }, null, 40, _hoisted_32) ]) ]), vue.createElementVNode("div", _hoisted_33, [ _hoisted_34, vue.createElementVNode("div", _hoisted_35, [ vue.createElementVNode("mdui-select", { value: vue.unref(setting).logLevel, onChange: _cache[6] || (_cache[6] = ($event) => vue.unref(setting).logLevel = getSelected($event.target)) }, [ (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(vue.unref(LogDefinitions), (def) => { return vue.openBlock(), vue.createElementBlock("mdui-menu-item", { value: def.label }, vue.toDisplayString(def.label), 9, _hoisted_37); }), 256)) ], 40, _hoisted_36) ]) ]), _hoisted_38, _hoisted_39 ]) ], 64); }; } }); const _export_sfc = (sfc, props) => { const target = sfc.__vccOpts || sfc; for (const [key, val] of props) { target[key] = val; } return target; }; const App = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-34f99f99"]]); class CookieCountyInfoGetter { name() { return "cookie"; } match() { return true; } async getCountyCode() { return new Promise(async (resolve, reject) => { const cookies = await _GM_cookie.list({ name: "steamCountry" }); if (cookies && cookies.length > 0) { const match = cookies[0].value.match(/^[a-zA-Z][a-zA-Z]/); if (match) { resolve(match[0]); } } reject(); }); } } class RequestStorePageCountyCodeGetter { name() { return "请求商店页面"; } match() { return !window.location.href.includes("store.steampowered.com"); } async getCountyCode() { return new Promise(async (resolve, reject) => { try { const storeHtml = await Http.get(String, "https://store.steampowered.com/"); const match = storeHtml.match(new RegExp("(?<=GDynamicStore.Init\\(.+')[A-Z][A-Z](?=',)")); if (match) { return resolve(match[0]); } } catch (err) { Logger.error(err); } reject(); }); } } class StorePageCountyCodeGetter { name() { return "商店页面"; } match() { return window.location.href.includes("store.steampowered.com"); } getCountyCode() { return new Promise((resolve, reject) => { var _a, _b; try { let countyCode = (_b = (_a = GStoreItemData == null ? void 0 : GStoreItemData.rgNavParams) == null ? void 0 : _a.__page_default_obj) == null ? void 0 : _b.countrycode; if (countyCode) { resolve(countyCode); } } catch (e) { Logger.warn("读取商店页面区域代码变量失败: " + e.message); } 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(); resolve(countyCode); } } }); reject(); }); } } class MarketPageCountyCodeGetter { name() { return "市场页面"; } match() { return window.location.href.includes("steamcommunity.com"); } getCountyCode() { return new Promise((resolve, reject) => { try { const code = g_strCountryCode; if (code) return resolve(code); } catch (err) { Logger.error(err); } reject(); }); } } class UserConfigCountyInfoGetter { async getCountyCode() { return new Promise(async (resolve, reject) => { var _a; const code = _unsafeWindow.userConfig ? (_a = _unsafeWindow.userConfig) == null ? void 0 : _a.country_code : await this.getCountyCodeForDev(); if (code) { resolve(code); } else { reject(); } }); } match() { return true; } name() { return "window.UserConfig"; } async getCountyCodeForDev() { const html = await Http.get(String, window.location.href); const match = html.match(/,"country_code":"([A-Z]{2})"/); if (match) { return match[1]; } return void 0; } } const _CountyCodeGetterManager = class _CountyCodeGetterManager { constructor() { __publicField(this, "getters"); this.getters = [ new UserConfigCountyInfoGetter(), new StorePageCountyCodeGetter(), new MarketPageCountyCodeGetter(), new RequestStorePageCountyCodeGetter(), new CookieCountyInfoGetter() ]; } match() { return true; } async getCountyCode() { Logger.info("尝试获取区域代码"); for (let getter of this.getters) { if (!getter.match()) { continue; } Logger.debug(`尝试通过[${getter.name()}]获取区域代码`); try { const countyCode = await getter.getCountyCode(); Logger.info(`通过[${getter.name()}]获取区域代码成功`); return countyCode; } catch (e) { Logger.error(`通过[${getter.name()}]获取区域代码失败`); } } throw new Error("所有获取区域代码策略都获取失败"); } }; __publicField(_CountyCodeGetterManager, "instance", new _CountyCodeGetterManager()); let CountyCodeGetterManager = _CountyCodeGetterManager; const _SpcManager = class _SpcManager { 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); } }; __publicField(_SpcManager, "instance", new _SpcManager()); let SpcManager = _SpcManager; class ReactUtils { static waitForReactInit(callback, checkInterval = 500, timeout = 1e4) { return new Promise((resolve, reject) => { const start = Date.now(); const interval = setInterval(() => { const root = document.documentElement; for (const prop in root) { if (prop.startsWith("__react")) { clearInterval(interval); console.log(`React initialized with property: ${prop}`); Promise.resolve(callback(root, prop)).then(() => resolve()).catch(reject); return; } } if (Date.now() - start > timeout) { clearInterval(interval); reject(new Error("React initialization timeout exceeded.")); } }, checkInterval); }); } static useReact() { return !!document.querySelector("div[data-react-nav-root]"); } } (async () => { if (ReactUtils.useReact()) { await ReactUtils.waitForReactInit(async (root, reactProp) => { console.log("React is ready!", { root, reactProp }); await initContext(); initApp(); registerMenu(); await main(); }); } else { console.log("React is not detected!"); await initContext(); initApp(); registerMenu(); await main(); } })(); function initApp() { vue.createApp(App).mount( (() => { const app = document.createElement("div"); app.setAttribute("id", "spc-menu"); document.body.append(app); return app; })() ); } function registerMenu() { GmUtils.registerMenuCommand(IM_MENU_SETTING); } async function initContext() { const setting = SettingManager.instance.setting; setLogLevel(setting.logLevel); const targetCountyInfo = countyCode2Info.get(setting.countyCode); if (!targetCountyInfo) { throw Error("获取转换后的国家信息失败,国家代码:" + setting.countyCode); } Logger.info("目标区域:", targetCountyInfo); const currCountyCode = await CountyCodeGetterManager.instance.getCountyCode(); const currCountInfo = countyCode2Info.get(currCountyCode); if (!currCountyCode) { throw Error("缺少当前国家的信息映射:county: " + currCountyCode); } Logger.info("当前区域:", currCountInfo); _unsafeWindow.SpcManager = SpcManager.instance; _unsafeWindow.spcContext = new SpcContext(setting, targetCountyInfo, currCountInfo); } })(Reflect, mdui, Vue);