// ==UserScript== // @name Better SteamPY // @namespace https://space.bilibili.com/93654843 // @version 20241208 // @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 gitee.com // @connect api.steampowered.com // @connect store.steampowered.com // @run-at document-body // @downloadURL https://update.greasyfork.icu/scripts/503737/Better%20SteamPY.user.js // @updateURL https://update.greasyfork.icu/scripts/503737/Better%20SteamPY.meta.js // ==/UserScript== var Saves = { wishlist: [], ownedApps: [], familygameList: [], lastupdatetime: 0, }; var limitedApps = []; var noGameList = []; var noDlc = false; var noownedGames = false; var noRestrictedGames = false; (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); 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); 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://gitee.com/Finnier/getSteamRestrictedGameLIst/raw/main/data/normalist.json', responseType: 'json', onload: function (response) { var data = JSON.parse(response.responseText); var limitedGames = data; GM_setValue('limitedApps', limitedGames); iview.Notice.success({ title: `Better Steampy`, desc: `已加载 ${limitedGames.length} 个非受限游戏`, }); resolve(limitedGames); }, }); }); } //获取非游戏列表 function getNogameList() { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: 'https://gitee.com/Finnier/getSteamAppListWithType/raw/main/data/Listwithnogame.json', responseType: 'json', onload: function (response) { var data = JSON.parse(response.responseText); var nogamelistdata = Object.keys(data).map(Number); GM_setValue('NoGameList', nogamelistdata); noGameList = nogamelistdata; iview.Notice.success({ title: `Better Steampy`, desc: `已加载 ${nogamelistdata.length} 个DLC及原声带`, }); resolve(nogamelistdata); }, }); }); } //初始化脚本配置菜单 function init() { const settings = document.createElement('div'); settings.innerHTML = `
脚本设置

暂时不支持捆绑包标记

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

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

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

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

是否加入了家庭组:

重载存档 清除存档
数据来源于https://github.com/F1NN1ER/getSteamRestrictedGameLIst 数据每日更新,可能尚有部分未及时标记
是否启用受限游戏标注:

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

刷新
已拥有
在愿望单中
在家庭库中
未拥有
是否关闭网页右下方推广侧栏:
`; const filter = document.createElement('div'); filter.innerHTML = `
不显示已拥有游戏 不显示资料受限游戏 不显示DLC及原声带 `; const targetElement = document.querySelector('.balanceTitle > div'); targetElement.appendChild(settings); targetElement.appendChild(filter); 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')), ownedAppsColor: localStorage.getItem('ownedColor'), wishlistColor: localStorage.getItem('wishlistColor'), familygameColor: localStorage.getItem('familygameColor'), unownedColor: localStorage.getItem('unownedColor'), defaultcolors: ['#0c8918', '#177cb0', '#ff8936', '#ff2e63'], }; }, 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'); } }, ownedAppsColor_change(color) { ownedColor = color; localStorage.setItem('ownedColor', color); const elements = document.querySelectorAll('.cdkGameIcon'); elements.forEach((element) => { cdkeyGameChecker(element); }); }, wishlistColor_change(color) { wishlistColor = color; localStorage.setItem('wishlistColor', color); const elements = document.querySelectorAll('.cdkGameIcon'); elements.forEach((element) => { cdkeyGameChecker(element); }); }, familygameColor_change(color) { familygameColor = color; localStorage.setItem('familygameColor', color); const elements = document.querySelectorAll('.cdkGameIcon'); elements.forEach((element) => { cdkeyGameChecker(element); }); }, unownedColor_change(color) { unownedColor = color; localStorage.setItem('unownedColor', color); const elements = document.querySelectorAll('.cdkGameIcon'); elements.forEach((element) => { cdkeyGameChecker(element); }); }, 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(); await getNogameList(); 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(); }, }, }); new Vue({ el: '#filter', data() { return { filter: [], }; }, methods: { filterChange() { noownedGames = this.filter.includes('noOwnedGames'); noRestrictedGames = this.filter.includes('noRestrictedGames'); noDlc = this.filter.includes('noDlc'); const elements = document.querySelectorAll('.cdkGameIcon'); elements.forEach((element) => { cdkeyGameChecker(element); }); }, }, }); 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 isNotLimited = (appId) => !limitedApps.includes(appId); const isDLC = (appId) => noGameList.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) { element.parentElement.parentElement.style.display = 'block'; if (noDlc) { if (isDLC(appId)) { element.parentElement.parentElement.style.display = 'none'; } } if (isAppOwned(appId)) { if (noownedGames) { element.parentElement.parentElement.style.display = 'none'; } else { 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 (isNotLimited(appId)) { if (noRestrictedGames) { element.parentElement.parentElement.style.display = 'none'; } else { 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(); } var previousNoGameList = GM_getValue('NoGameList'); if (previousNoGameList !== undefined) { noGameList = GM_getValue('NoGameList'); } else { getNogameList(); } //自动更新 if (new Date().getTime() - Saves.lastupdatetime > 86400000) { iview.Notice.info({ title: '存档自动更新中', }); getOwnAndWish(); if (JSON.parse(localStorage.getItem('isInfamily'))) { getFamilyGame(); } getLimitedGamesList(); getNogameList(); } } //监听页面变化 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); //默认颜色 if (!localStorage.getItem('ownedColor')) { localStorage.setItem('ownedColor', '#0c8918'); localStorage.setItem('wishlistColor', '#177cb0'); localStorage.setItem('familygameColor', '#ff8936'); localStorage.setItem('unownedColor', '#ff2e63'); } var ownedColor = localStorage.getItem('ownedColor'); var wishlistColor = localStorage.getItem('wishlistColor'); var familygameColor = localStorage.getItem('familygameColor'); var unownedColor = localStorage.getItem('unownedColor'); //Todo list: //夜间模式 //侧栏收放 //中键快捷控制标签页 //左右翻页