// ==UserScript== // @name SteampPY已拥有游戏标记 // @namespace https://space.bilibili.com/93654843 // @version 2024-08-09 // @description 能够以不同的颜色在Steampy平台Cdkey市场上添加标记,包括已拥有,家庭库中及愿望单中 // @author FiNNiER // @match *://steampy.com/* // @icon https://steampy.com/img/logo.63413a4f.png // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @connect api.steampowered.com // @connect store.steampowered.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: [], }; (function () { 'use strict'; load(); init(); Start(); 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) { try { let data = JSON.parse(response.responseText); wishlist = data.rgWishlist; ownedApps = data.rgOwnedApps; let previousSaves = GM_getValue('Saves', { wishlist: [], ownedApps: [], familygameList: [], }); let newSave = { wishlist: wishlist, ownedApps: ownedApps, familygameList: previousSaves.familygameList, }; console.log(data); GM_setValue('Saves', newSave); resolve(newSave); // 成功时调用 resolve } catch (error) { reject(error); // 发生错误时调用 reject } }, onerror: function (error) { reject(error); // 请求失败时调用 reject }, }); }); } //读取家庭库并储存 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) { try { 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) { try { 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) { try { 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', { wishlist: [], ownedApps: [], familygameList: [], }); let newSave = { wishlist: previousSaves.wishlist, ownedApps: previousSaves.ownedApps, familygameList: familygameList, }; GM_setValue('Saves', newSave); resolve(familygameList); } catch (error) { reject(error); } }, onerror: function (error) { reject(error); }, }); } catch (error) { reject(error); } }, onerror: function (error) { reject(error); }, }); } catch (error) { reject(error); } }, onerror: function (error) { reject(error); }, }); }); } //脚本配置菜单 function init() { const settings = document.createElement('div'); settings.innerHTML = `
脚本设定
`; const targetElementSelector = '.balanceTitle > div'; function tryAppend() { const targetElement = document.querySelector(targetElementSelector); if (targetElement) { targetElement.appendChild(settings); document .getElementById('settings') .addEventListener('click', function () { document.getElementById('popup-window').style.display = 'block'; }); document .getElementById('close-popup') .addEventListener('click', function () { document.getElementById('popup-window').style.display = 'none'; }); document .getElementById('refresh-saves') .addEventListener('click', async function () { document.getElementById('loading').style.display = 'block'; await getOwnAndWish(); if (document.getElementById('family-library').checked) { await getFamilyGame(); } // document.getElementById('loading').style.display = 'none'; updateCounts(); }); document .getElementById('clear-saves') .addEventListener('click', function () { let nullSaves = { wishlist: [], ownedApps: [], familygameList: [], }; GM_setValue('Saves', nullSaves); updateCounts(); }); dragElement(document.getElementById('popup-window')); } else { setTimeout(tryAppend, 100); } } function updateCounts() { const saves = GM_getValue('Saves'); document.getElementById( 'ownedAppsCount' ).innerHTML = `已加载${saves.ownedApps.length} 个库存游戏及DLC`; document.getElementById( 'wishListCount' ).innerHTML = `已加载${saves.wishlist.length} 个愿望单游戏`; document.getElementById( 'familyGameCount' ).innerHTML = `已加载${saves.familygameList.length} 个家庭库游戏`; } tryAppend(); } //CSS样式 const style = document.createElement('style'); style.innerHTML = ` .popup-window { display: none; position: fixed; z-index: 1000; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 400px; background-color: white; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); border-radius: 10px; cursor: default ; /* 设置鼠标指针样式 */ } .popup-content { padding: 20px; font-size: 18px; /* 设置字体大小 */ cursor: default ; /* 设置鼠标指针样式 */ } .close-btn { float: right; font-size: 20px; cursor: default ; } `; document.head.appendChild(style); //拖拽功能 function dragElement(element) { let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; element.onmousedown = dragMouseDown; function dragMouseDown(e) { e = e || window.event; e.preventDefault(); pos3 = e.clientX; pos4 = e.clientY; document.onmouseup = closeDragElement; document.onmousemove = elementDrag; } function elementDrag(e) { e = e || window.event; e.preventDefault(); pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; element.style.top = element.offsetTop - pos2 + 'px'; element.style.left = element.offsetLeft - pos1 + 'px'; } function closeDragElement() { document.onmouseup = null; document.onmousemove = null; } } //等待页面加载 function Start() { const getterTimer = setInterval(() => { const dom = document.querySelector('.cdkGameIcon'); if (dom) { clearInterval(getterTimer); gameChecker(); } }, 200); } //游戏状态标记 function gameChecker() { const isAppOwned = (appId) => Saves.ownedApps.includes(appId); const isAppinwishlist = (appId) => Saves.wishlist.includes(appId); const isAppShared = (appId) => Saves.familygameList.includes(appId); const getAppId = (url) => (url.match(/\/apps\/(\d+)\//) || [])[1] || null; const game_layout = document.querySelector( '.flex-row.jc-space-flex-start.flex-wrap.w-auto' ); if (game_layout) { const gameIcons = game_layout.querySelectorAll('.cdkGameIcon'); gameIcons.forEach((icon) => { const appId = Number(getAppId(icon.getAttribute('data-src'))); if (isAppOwned(appId)) { const gameNameElement = icon .closest('.gameblock') .querySelector('.gameName'); if (gameNameElement) { gameNameElement.style.color = ownedColor; } } else if (isAppShared(appId)) { const gameNameElement = icon .closest('.gameblock') .querySelector('.gameName'); if (gameNameElement) { gameNameElement.style.color = familygameColor; } } else if (isAppinwishlist(appId)) { const gameNameElement = icon .closest('.gameblock') .querySelector('.gameName'); if (gameNameElement) { gameNameElement.style.color = wishlistColor; } } else { const gameNameElement = icon .closest('.gameblock') .querySelector('.gameName'); if (gameNameElement) { gameNameElement.style.color = unownedColor; } } }); } } //加载存档 function load() { var previousSave = GM_getValue('Saves'); if (previousSave !== undefined) { Saves = GM_getValue('Saves', { wishlist: [], ownedApps: [], familygameList: [], }); } else { GM_setValue('Saves', Saves); } } // 监听页面变化 function observePageChanges() { const targetNode = document.body; const config = { childList: true, subtree: true }; const callback = function (mutationsList, observer) { for (let mutation of mutationsList) { if (mutation.type === 'childList') { gameChecker(); break; } } }; const observer = new MutationObserver(callback); observer.observe(targetNode, config); }