Greasy Fork is available in English.
提供Steampy界面美化,功能增强,如库中已有游戏标记(支持家庭库及愿望单)等功能
当前为
// ==UserScript==
// @name Better SteampPY
// @namespace https://space.bilibili.com/93654843
// @version 2024-08-16
// @description 提供Steampy界面美化,功能增强,如库中已有游戏标记(支持家庭库及愿望单)等功能
// @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
// ==/UserScript==
// ==颜色配置==
const ownedColor = '#0c8918'; // 已拥有
const wishlistColor = '#177cb0'; // 愿望单中
const familygameColor = '#ff8936'; // 家庭库中
const unownedColor = '#ff2e63'; // 未拥有
// ==颜色配置==
var Saves = {
wishlist: [],
ownedApps: [],
familygameList: [],
};
(function () {
'use strict';
load();
init();
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 = `
<div class="ml-20-rem">
<div class="withdraw" id="settings">脚本设定</div>
</div>
<div id="popup-window" class="popup-window">
<div class="popup-content">
<span class="close-btn" id="close-popup">×</span>
<h1>脚本设定</h1>
<div id="loading" style="display: none;">加载中...</div>
<p id="ownedAppsCount">已加载${
GM_getValue('Saves').ownedApps.length
} 个库存游戏及DLC</p>
<p id="wishListCount">已加载${
GM_getValue('Saves').wishlist.length
} 个愿望单游戏</p>
<p id="familyGameCount">已加载${
GM_getValue('Saves').familygameList.length
} 个家庭库游戏</p>
<label for="family-library">是否加入了家庭库:</label>
<input type="checkbox" id="family-library" name="family-library">
<div class="button-container">
<button id="refresh-saves">刷新存档</button>
<button id="clear-saves">清除存档</button>
</div>
</div>
</div>
`;
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;
}
}
//游戏状态标记-CDKEY
function cdkeyGameChecker() {
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,
attributes: true,
attributeFilter: ['data-src'],
};
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();
}
}
}
};
const observer = new MutationObserver(callback);
observer.observe(targetNode, config);
}