// ==UserScript== // @name SteamPY & CICI 价格显示 // @version 2.0 // @description 显示 Steam 游戏在 SteamPY 和 STEAMCICI 的价格信息 // @author Li // @match https://store.steampowered.com/app/* // @grant GM_xmlhttpRequest // @connect steampy.com // @connect steamcici.com // @connect steampowered.com // @run-at document-end // @icon https://steampy.com/m_logo.ico // @license MIT // @supportURL https://greasyfork.org/zh-CN/scripts/518189/feedback // @homepageURL https://greasyfork.org/zh-CN/scripts/518189 // @namespace https://greasyfork.org/users/ // @downloadURL https://update.greasyfork.icu/scripts/518189/SteamPY%20%20CICI%20%E4%BB%B7%E6%A0%BC%E6%98%BE%E7%A4%BA.user.js // @updateURL https://update.greasyfork.icu/scripts/518189/SteamPY%20%20CICI%20%E4%BB%B7%E6%A0%BC%E6%98%BE%E7%A4%BA.meta.js // ==/UserScript== (function () { 'use strict'; const STEAMPY_BASE_URL = "https://steampy.com/"; const CICI_BASE_URL = "https://steamcici.com/"; const API_ENDPOINTS = { // SteamPY API gameInfo: (subId, appId, type) => `${STEAMPY_BASE_URL}xboot/common/plugIn/getGame?subId=${subId}&appId=${appId}&type=${type}`, cdkDetail: (id) => `${STEAMPY_BASE_URL}cdkDetail?name=cn&gameId=${id}`, balanceBuyDetail: (id) => `${STEAMPY_BASE_URL}balanceBuyDetail?data=cn&gameId=${id}`, hotGameDetail: (id) => `${STEAMPY_BASE_URL}hotGameDetail?gameId=${id}`, // CICI API ciciGameList: (parentId) => `${CICI_BASE_URL}prod-api/user/system/shopGame/list?parentId=${parentId}`, }; const getAppId = () => { try { const gamePageElement = document.querySelector('.game_page_background.game[data-miniprofile-appid]'); const appId = gamePageElement?.getAttribute('data-miniprofile-appid'); return appId || null; } catch (err) { console.error("获取 AppID 失败:", err); return null; } }; const getSubIdElements = () => [...document.querySelectorAll('.game_area_purchase_game_wrapper')]; const createPlaceholder = (parent) => { const placeholder = document.createElement('div'); placeholder.className = 'price-box'; placeholder.innerHTML = `
加载中...
`; parent.appendChild(placeholder); return placeholder; }; const updatePlaceholder = (placeholder, content, isError = false) => { placeholder.innerHTML = isError ? `
${content}
` : content; }; const calculateDaysAgo = (dateString) => { if (!dateString) return ''; try { const targetDate = new Date(dateString); const today = new Date(); const diffTime = today - targetDate; const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24)); if (diffDays === 0) return '今天'; if (diffDays === 1) return '1天前'; return `${diffDays}天前`; } catch (err) { console.error('日期计算错误:', err); return ''; } }; const displayPrices = (pyRes, ciciRes, placeholder, subId) => { let content = ''; // SteamPY 价格显示 if (pyRes && pyRes.success) { const { keyPrice, marketPrice, daiPrice, id } = pyRes.result; content += ` PY CDKey 价格:¥${keyPrice || "未知"} PY 余额购价格:¥${marketPrice || "未知"} PY 代购价格:¥${daiPrice || "未知"} `; } else { content += ``; content += ``; content += ``; } // CICI 价格显示 if (ciciRes && ciciRes.code === 200 && ciciRes.rows) { const gameData = ciciRes.rows.find(item => item.gameId === subId); if (gameData) { const { lastLowSellPrice, lastHangingTime } = gameData; if (lastLowSellPrice !== null) { const timeAgo = calculateDaysAgo(lastHangingTime); const timeDisplay = timeAgo ? `(${timeAgo})` : ''; content += ` CICI CDKey 价格:¥${lastLowSellPrice}${timeDisplay} `; } else { content += ` CICI CDKey 价格:暂无上架 `; } } else { content += `CICI CDKey 价格:未找到商品`; } } else { content += `CICI CDKey 价格:加载失败`; } updatePlaceholder(placeholder, content); }; const appId = getAppId(); if (!appId) { console.error("无法获取 AppID。"); return; } const subIdElements = getSubIdElements(); subIdElements.forEach((element) => { try { const input = element.querySelector('input[name="subid"], input[name="bundleid"]'); if (!input) return; const subId = input.value; const type = input.name; const pyApiUrl = API_ENDPOINTS.gameInfo(subId, appId, type); const ciciApiUrl = API_ENDPOINTS.ciciGameList(appId); const placeholder = createPlaceholder(element); let pyResult = null; let ciciResult = null; let completedRequests = 0; const checkCompletion = () => { completedRequests++; if (completedRequests === 2) { displayPrices(pyResult, ciciResult, placeholder, subId); } }; // 请求 SteamPY API GM_xmlhttpRequest({ method: "GET", url: pyApiUrl, onload: (response) => { try { if (response.status === 200) { const responseText = response.responseText; try { pyResult = JSON.parse(responseText); } catch (jsonErr) { console.error("PY JSON 解析失败:", jsonErr); pyResult = { success: false, message: "数据解析错误" }; } } else { pyResult = { success: false, message: `HTTP ${response.status}` }; } } catch (err) { console.error("PY 处理响应失败:", err); pyResult = { success: false, message: "内部错误" }; } checkCompletion(); }, onerror: (error) => { console.error("PY 请求失败:", error); pyResult = { success: false, message: "网络请求错误" }; checkCompletion(); } }); // 请求 CICI API GM_xmlhttpRequest({ method: "GET", url: ciciApiUrl, onload: (response) => { try { if (response.status === 200) { const responseText = response.responseText; try { ciciResult = JSON.parse(responseText); } catch (jsonErr) { console.error("CICI JSON 解析失败:", jsonErr); ciciResult = { code: 500, msg: "数据解析错误" }; } } else { ciciResult = { code: response.status, msg: `HTTP ${response.status}` }; } } catch (err) { console.error("CICI 处理响应失败:", err); ciciResult = { code: 500, msg: "内部错误" }; } checkCompletion(); }, onerror: (error) => { console.error("CICI 请求失败:", error); ciciResult = { code: 500, msg: "网络请求错误" }; checkCompletion(); } }); } catch (err) { console.error("处理 SubID 失败:", err); } }); const style = document.createElement('style'); style.innerHTML = ` .price-box { font-size: 14px; margin-top: 8px; padding: 8px; background-color: rgba(0, 0, 0, 0.3); border-radius: 4px; color: #f0f0f0; display: flex; flex-wrap: wrap; gap: 8px; box-sizing: border-box; } .price-link { color: #ffffff; text-decoration: none; font-size: 13px; white-space: nowrap; } .price-link:hover { color: #cccccc; } .error-text { color: #ff6666; font-size: 12px; } .loading-text { color: #cccccc; font-size: 12px; } @media screen and (max-width: 767px) { .price-box { flex-direction: column; gap: 4px; } } `; document.head.appendChild(style); })();