// ==UserScript== // @name Better SteamPY // @namespace https://space.bilibili.com/93654843 // @version 20240904 // @description 提供Steampy界面美化,功能增强,如库中已有游戏标记(支持家庭库及愿望单)、标记资料受限游戏等功能 // @author FiNNiER // @match *://steampy.com/* // @icon https://steampy.com/img/logo.63413a4f.png // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @connect api.steampowered.com // @connect store.steampowered.com // @connect bartervg.com // @run-at document-body // @downloadURL none // ==/UserScript== // ==颜色配置== const ownedColor = '#0c8918'; // 已拥有 const wishlistColor = '#177cb0'; // 愿望单中 const familygameColor = '#ff8936'; // 家庭库中 const unownedColor = '#ff2e63'; // 未拥有 // ==颜色配置== var Saves = { wishlist: [], ownedApps: [], familygameList: [], lastupdatetime: 0, }; var limitedApps = []; (function () { 'use strict'; load(); observePageChanges(); })(); //读取个人库存及愿望单并储存 function getOwnAndWish() { return new Promise((resolve, reject) => { var wishlist = []; var ownedApps = []; GM_xmlhttpRequest({ method: 'GET', url: 'https://store.steampowered.com/dynamicstore/userdata/?t=' + Math.trunc(Date.now() / 1000), responseType: 'json', onload: function (response) { let data = JSON.parse(response.responseText); wishlist = data.rgWishlist; ownedApps = data.rgOwnedApps; let previousSaves = GM_getValue('Saves'); let newSave = { wishlist: wishlist, ownedApps: ownedApps, familygameList: previousSaves.familygameList, lastupdatetime: new Date().getTime(), }; GM_setValue('Saves', newSave); iview.Notice.success({ title: `Better Steampy`, desc: `已加载 ${ownedApps.length} 个库存游戏及DLC,${wishlist.length} 个愿望单游戏`, }); resolve(newSave); }, }); }); } //读取家庭库并储存 function getFamilyGame() { return new Promise((resolve, reject) => { var access_token; var family_groupid; var familygameList = []; GM_xmlhttpRequest({ method: 'GET', url: 'https://store.steampowered.com/pointssummary/ajaxgetasyncconfig', responseType: 'json', onload: function (response) { let data = JSON.parse(response.responseText); access_token = data.data.webapi_token; // access_token GM_xmlhttpRequest({ method: 'GET', url: `https://api.steampowered.com/IFamilyGroupsService/GetFamilyGroupForUser/v1/?access_token=${access_token}`, responseType: 'json', onload: function (response) { let data = JSON.parse(response.responseText); family_groupid = data.response.family_groupid; // family_groupid GM_xmlhttpRequest({ method: 'GET', url: `https://api.steampowered.com/IFamilyGroupsService/GetSharedLibraryApps/v1/?access_token=${access_token}&family_groupid=${family_groupid}&include_own=true`, responseType: 'json', onload: function (response) { let data = JSON.parse(response.responseText); data.response.apps.forEach((app) => { if (app.exclude_reason == 0) { familygameList.push(app.appid); } }); let previousSaves = GM_getValue('Saves'); let newSave = { wishlist: previousSaves.wishlist, ownedApps: previousSaves.ownedApps, familygameList: familygameList, lastupdatetime: new Date().getTime(), }; GM_setValue('Saves', newSave); iview.Notice.success({ title: `Better Steampy`, desc: `已加载 ${familygameList.length} 个家庭库游戏`, }); resolve(familygameList); }, }); }, }); }, }); }); } //获取受限游戏列表 function getLimitedGamesList() { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: 'https://bartervg.com/browse/tag/481/json/', responseType: 'json', onload: function (response) { var data = JSON.parse(response.responseText); var limitedGames = Object.keys(data).map(Number); GM_setValue('limitedApps', limitedGames); iview.Notice.success({ title: `Better Steampy`, desc: `已加载 ${limitedGames.length} 个受限游戏`, }); resolve(limitedGames); }, }); }); } //初始化脚本配置菜单 function init() { const settings = document.createElement('div'); settings.innerHTML = `
脚本设置

暂时不支持捆绑包标记

上次更新于 (每24小时执行一次自动更新)

已加载 {{ownedApps}} 个库存游戏及DLC

已加载 {{wishlist}} 个愿望单游戏

已加载 {{familygameList}} 个家庭库游戏

是否加入了家庭组:

重载存档 清除存档
数据来源于https://bartervg.com/browse/tag/481/json/ 数据并非实时更新,尚有部分未及时标记
是否启用受限游戏标注:

目前共加载{{limitedApps}}个受限游戏(跟随拥有状态自动更新)

刷新
是否关闭网页右下方推广侧栏:
`; const targetElement = document.querySelector('.balanceTitle > div'); targetElement.appendChild(settings); new Vue({ el: '#settings', data() { return { reloadLimitedSaves_loading: false, refershSaves_loading: false, modal: false, lastUpdateTime: Saves.lastupdatetime, ownedApps: Saves.ownedApps.length, wishlist: Saves.wishlist.length, familygameList: Saves.familygameList.length, limitedApps: limitedApps.length, isInFamilyGroup: JSON.parse(localStorage.getItem('isInfamily')), checkIsProfileFeatureLimited: JSON.parse( localStorage.getItem('IsProfileFeatureLimited') ), isSuspensionOff: JSON.parse(localStorage.getItem('isSuspensionOff')), }; }, methods: { updateValues() { this.ownedApps = Saves.ownedApps.length; this.wishlist = Saves.wishlist.length; this.familygameList = Saves.familygameList.length; this.limitedApps = limitedApps.length; this.lastUpdateTime = Saves.lastupdatetime; }, isInFamilyGroup_change(status) { if (status) { localStorage.setItem('isInfamily', JSON.stringify(true)); } else { localStorage.removeItem('isInfamily'); } }, checkIsProfileFeatureLimited_change(status) { if (status) { localStorage.setItem('IsProfileFeatureLimited', JSON.stringify(true)); Saves = GM_getValue('Saves'); const elements = document.querySelectorAll('.cdkGameIcon'); elements.forEach((element) => { cdkeyGameChecker(element); }); } else { localStorage.removeItem('IsProfileFeatureLimited'); const elements = document.querySelectorAll('.ProfileFeaturesLimited'); elements.forEach((element) => { element.parentNode.removeChild(element); }); } }, isSuspensionOff_change(status) { if (status) { localStorage.setItem('isSuspensionOff', JSON.stringify(true)); GM_addStyle('.suspension{display:none}'); } else { GM_addStyle('.suspension{display:block}'); localStorage.removeItem('isSuspensionOff'); } }, async reloadSaves() { this.$Notice.info({ title: '正在重载存档', }); this.refershSaves_loading = true; await Promise.all([ getOwnAndWish(), this.isInFamilyGroup ? getFamilyGame() : Promise.resolve(), ]); Saves = GM_getValue('Saves'); const elements = document.querySelectorAll('.cdkGameIcon'); elements.forEach((element) => { cdkeyGameChecker(element); }); this.updateValues(); this.refershSaves_loading = false; this.$Notice.success({ title: '重载完毕', }); }, async reloadLimitedSaves() { this.$Notice.info({ title: '正在加载受限游戏列表', }); this.reloadLimitedSaves_loading = true; await getLimitedGamesList(); limitedApps = GM_getValue('limitedApps'); this.updateValues(); this.reloadLimitedSaves_loading = false; this.$Notice.success({ title: '加载完毕', }); }, clearSaves() { this.$Notice.info({ title: '存档已清除', }); let nullSaves = { wishlist: [], ownedApps: [], familygameList: [], lastupdatetime: 0, }; Saves = nullSaves; GM_setValue('Saves', nullSaves); this.updateValues(); }, }, }); if (localStorage.getItem('isSuspensionOff') === 'true') { GM_addStyle('.suspension{display:none}'); } } //游戏状态标记-CDKEY function cdkeyGameChecker(element) { const isAppOwned = (appId) => Saves.ownedApps.includes(appId); const isAppinwishlist = (appId) => Saves.wishlist.includes(appId); const isAppShared = (appId) => Saves.familygameList.includes(appId); const isLimited = (appId) => limitedApps.includes(appId); const getAppId = (url) => (url.match(/\/apps\/(\d+)\//) || [])[1] || null; const getBundleId = (url) => (url.match(/\/bundles\/(\d+)\//) || [])[1] || null; const appId = Number(getAppId(element.getAttribute('data-src'))); const gameNameElement = element .closest('.gameblock') .querySelector('.gameName'); if (appId != 0) { if (isAppOwned(appId)) { gameNameElement.style.color = ownedColor; } else if (isAppShared(appId)) { gameNameElement.style.color = familygameColor; } else if (isAppinwishlist(appId)) { gameNameElement.style.color = wishlistColor; } else { gameNameElement.style.color = unownedColor; } if (localStorage.getItem('IsProfileFeatureLimited')) { const existingDiscountDiv = element.parentElement.querySelector( '.ProfileFeaturesLimited' ); if (existingDiscountDiv) { existingDiscountDiv.remove(); } if (isLimited(appId)) { const discountDiv = document.createElement('div'); discountDiv.className = 'ProfileFeaturesLimited'; discountDiv.textContent = '资料受限'; element.parentElement.appendChild(discountDiv); } } } } //加载存档 function load() { var previousSave = GM_getValue('Saves'); if (previousSave !== undefined) { Saves = GM_getValue('Saves'); } else { GM_setValue('Saves', Saves); } var previousLimitedApps = GM_getValue('limitedApps'); if (previousLimitedApps !== undefined) { limitedApps = GM_getValue('limitedApps'); } else { getLimitedGamesList(); } //自动更新 if (new Date().getTime() - Saves.lastupdatetime > 86400000) { iview.Notice.info({ title: '存档自动更新中', }); getOwnAndWish(); if (JSON.parse(localStorage.getItem('isInfamily'))) { getFamilyGame(); } getLimitedGamesList(); } } //监听页面变化 function observePageChanges() { const config = { childList: true, subtree: true, attributes: true, attributeFilter: ['data-src'], }; let hasExecuted = false; const callback = function (mutationsList, observer) { for (let mutation of mutationsList) { if ( mutation.type === 'attributes' && mutation.attributeName === 'data-src' ) { const targetElement = mutation.target; if (targetElement.classList.contains('cdkGameIcon')) { cdkeyGameChecker(targetElement); } } if (!hasExecuted && mutation.type === 'childList') { const balanceTitleElement = document.querySelector( '.balanceTitle > div' ); if (balanceTitleElement) { init(); hasExecuted = true; } } } }; const observer = new MutationObserver(callback); observer.observe(document.body, config); } //CSS样式 const style = document.createElement('style'); style.innerHTML = ` .ProfileFeaturesLimited { width: .65rem; height: .3rem; background: #ed4014; position: absolute; top: 0; color: #fff; text-align: center; line-height: .3rem; font-size: .12rem; } `; document.head.appendChild(style); //Todo list: //夜间模式 //侧栏收放 //中键快捷控制标签页 //设置内调整色号 //左右翻页 //按条件筛选