// ==UserScript== // @name Steam赛博父子鉴定 (游戏库蓝绿) // @license MIT // @namespace http://tampermonkey.net/ // @version 0.1.2 // @description 帮助大家找到心仪的赛博义父 // @author Rawwiin // @match https://steamcommunity.com/id/*/games/* // @match https://steamcommunity.com/profiles/*/games/* // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== // @grant GM_xmlhttpRequest // @downloadURL none // ==/UserScript== const url_my_wishlist = "https://steamcommunity.com/my/wishlist/"; // const wishlistUrl = "https://store.steampowered.com/wishlist" const url_my_games = "https://steamcommunity.com/my/games?tab=all"; const color_own = "#54662f"; const color_wish = "#4c90b6"; var isMarkOwn = true; var isMarkWish = true; var isHideOwn = false; var shownCount = 0; var myAppidList; var myWishAppidList; var hisAppidList; var hisGameMap = new Map(); var gameListObserver; let curUrl; (function () { "use strict"; console.log("开始鉴定..."); init(); })(); function init() { clear(); const myGamesPromise = new Promise((resolve, reject) => { if (myAppidList) { resolve(); return; } load(url_my_games, (res) => { myAppidList = getAppids(res); console.log("加载我的游戏", myAppidList.length); resolve(); }); }); const myWishPromise = new Promise((resolve, reject) => { if (myWishAppidList) { resolve(); return; } load(url_my_wishlist, (res) => { myWishAppidList = getAppids(res); console.log("加载我的愿望单", myWishAppidList.length); resolve(); }); }); const hisGameListPromise = loadHisGameList(1000); Promise.all([myGamesPromise, myWishPromise, hisGameListPromise]) .then(() => { initHisAppidMap(); markGameList(); if (!curUrl) { addSectionTabListener(); } initialStatusBar(); }) .catch((error) => { // console.error("至少一个请求失败:", error); }); } function clear() { hisAppidList = null; hisGameMap.clear(); removeStatusBar(); if (gameListObserver) { gameListObserver.disconnect(); gameListObserver = null; } } function loadHisGameList(interval) { return new Promise((resolve) => { let count = 0; const intervalId = setInterval(() => { if (++count > 10 || hisAppidList) { // 结束定时器 clearInterval(intervalId); resolve(); return; } let gameslist_config = document.getElementById("gameslist_config"); if (gameslist_config) { addGameListObserver(interval); let data_profile_gameslist = gameslist_config.getAttribute( "data-profile-gameslist" ); hisAppidList = getAppids(data_profile_gameslist); console.log("加载TA的游戏", hisAppidList.length); clearInterval(intervalId); resolve(); } }, interval); }); } function initHisAppidMap() { var gameListElement = document.getElementsByClassName( "_29H3o3m-GUmx6UfXhQaDAm" ); if (gameListElement) { for (var i = 0; i < gameListElement.length; ++i) { let appid = getAppidFromElement(gameListElement[i]); if (!hisGameMap.has(appid)) { hisGameMap.set(appid, gameListElement[i]); } } hideGameList(); } } function initialStatusBar() { // let element = document.getElementsByClassName("_2_BHLBIwWKIyKmoabfDFH-"); let element = document.getElementsByClassName("_3tY9vKLCmyG2H2Q4rUJpkr "); if (!element || !element.length) { return; } removeStatusBar(); let notHave = 0; let inWish = 0; hisAppidList.forEach(function (appid) { if (!myAppidList.includes(appid)) { notHave++; } if (myWishAppidList.includes(appid)) { inWish++; } }); let html = "
" + "" + "" + "" + "" + "
"; // element[0].innerHTML +=html; element[0].insertAdjacentHTML("beforebegin", html); var checkbox = document.getElementById("checkbox_hideMine"); if (checkbox) { checkbox.addEventListener("change", function () { isHideOwn = checkbox.checked; hideGameList(); }); } // let span_notHave = document.getElementById("notHave"); // if (span_notHave) span_notHave.textContent = notHave; // let span_inWish = document.getElementById("inWish"); // if (span_inWish) span_inWish.textContent = inWish; } function removeStatusBar() { let statusBars = document.getElementsByClassName("cyberFatherStatusBar"); if (statusBars && statusBars.length) { for (let i = 0; i < statusBars.length; i++) { statusBars[i].remove(); } } } function identify() { let identity = ""; let my = myAppidList.length; let his = hisAppidList.length; if (my == 0 || his == 0) { return "无法鉴定"; } let diff = his - my; let multi = his / my; if (diff == 0) { identity = "亲兄弟"; } else if (my >= 1000) { if (multi >= 3) { identity = "义父"; } else if (multi >= 1) { identity = "义兄"; } else if (multi >= 0.5) { identity = "义弟"; } else { identity = "义子"; } } else if (my > 100) { if (diff >= 400) { identity = "义父"; } else if (diff > 0) { identity = "义兄"; } else if (diff >= -100 && multi > 0.6) { identity = "义弟"; } else { identity = "义子"; } } else { if (diff > 0) { identity = "义兄"; } else { identity = "义弟"; } } let describe; // if (myAppidList.length >= 10 && hisAppidList.length >= 10) { // let myTop100 = myAppidList.slice(0, 100); // let hisTop100 = hisAppidList.slice(0, 100); // let hisTop10 = hisTop100.slice(0, 10); // // let intersection = new Set( // // [...myTop100].filter((x) => hisTop100.has(x)) // // ); // let intersection = 0; // for (let i = 0; i < myTop100.length; i++) { // if (hisTop100.includes(myTop100[i])) { // if (i < 10 && hisTop10.includes(myTop100[i])) { // intersection += myTop100.length / 10 - i + 1; // } else { // intersection += 1; // } // } // } // let fact = intersection / myTop100.length; // if (intersection >= 90 || fact >= 0.9) { // describe = "臭味相投"; // } else if (intersection >= 80 || fact >= 0.8) { // describe = "心照神交"; // } else if (intersection >= 70 || fact >= 0.7) { // describe = "相知恨晚"; // } else if (intersection >= 60 || fact >= 0.6) { // describe = "志同道合"; // } else if (intersection >= 50 || fact >= 0.5) { // describe = "同声相应"; // } else if (intersection >= 40 || fact >= 0.4) { // describe = "不谋而合"; // } else if (intersection >= 30 || fact >= 0.3) { // describe = "所见略同"; // } else if (intersection >= 20 || fact >= 0.2) { // describe = "萍水相逢"; // } else if (intersection >= 10 || fact >= 0.1) { // describe = "聊胜于无"; // } else if (intersection >= 1 || fact >= 0.01) { // describe = "南辕北辙"; // } else { // describe = "格格不入"; //断长续短 // } // } return describe ? describe + "的" + identity : identity; } function hideGameList() { shownCount = 0; hisGameMap.forEach(function (gameDiv, appid) { hideGame(appid, gameDiv); }); } function hideGame(appid, gameDiv) { if (isHideOwn && myAppidList && myAppidList.includes(appid)) { gameDiv.style.display = "none"; gameDiv.style.top = "-150px"; } else { gameDiv.style.display = "block"; gameDiv.style.top = shownCount++ * 150 + "px"; } } function addSectionTabListener() { let sectionTabs = document.getElementsByClassName( "_1sHACvEQL-LRtUYan0JxdB" ); curUrl = window.location.href; let regex = /games\/\?(\w|=|&)*?tab=(all|perfect|recent)/g; sectionTabs[0].addEventListener("click", function (event) { let targetUrl = event.target.baseURI ? event.target.baseURI : ""; if (curUrl == targetUrl) { return; } curUrl = targetUrl; // console.log("点击了:", targetUrl); if (regex.match(targetUrl)) { init(); } else { clear(); } }); } function addGameListObserver(interval) { let count = 0; const intervalId = setInterval(() => { if (++count > 10 || hisAppidList) { // 结束定时器 clearInterval(intervalId); return; } var targetNode = document.getElementsByClassName( "_3tY9vKLCmyG2H2Q4rUJpkr" // "gameslist-root" )[0]; if (targetNode) { let down = true; // 创建一个观察者对象 gameListObserver = new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { if (mutation.type === "childList") { if (down) { down = false; setTimeout(() => { down = true; console.log("处理变化"); initHisAppidMap(); markGameList(); }, 1500); } } }); }); // 传入目标节点和观察选项 gameListObserver.observe(targetNode, { // attributes: true, childList: true, // subtree: true, }); // gameListObserver.disconnect(); clearInterval(intervalId); } }, interval); } function markGameList() { hisGameMap.forEach(function (gameDiv, appid) { markGame(appid, gameDiv); }); } function markGame(appid, gameDiv) { let color = ""; if (isMarkOwn && myAppidList && myAppidList.includes(appid)) { color = color_own; } else if ( isMarkWish && myWishAppidList && myWishAppidList.includes(appid) ) { color = color_wish; } gameDiv.style.backgroundColor = color; } function load(url, resolve) { try { return GM_xmlhttpRequest({ method: "GET", url: url, onload: function (xhr) { // console.log(xhr); resolve(xhr.responseText); }, }); } catch (e) { // location.href = 'https://keylol.com'; } } function getAppids(res, sort) { let appid; if (sort) { let appidAndplaytimeRegex = /appid("|\\"|"):(\d+).*?playtime_forever("|\\"|"):(\d+)/g; let obj = {}; while (appid = appidAndplaytimeRegex.exec(res)) { obj[appid[2]] = appid[4]; } let sortedKeys = Object.keys(obj).sort((a, b) => obj[b] - obj[a]); return sortedKeys; } else { let appidRegex = /appid("|\\"|"):(\d+)/g; let appidSet = new Set(); // let appidList = []; while (appid = appidRegex.exec(res)) { // appidList.push(appid[2]); appidSet.add(appid[2]); } return Array.from(appidSet); } } function getAppidFromElement(element) { let appidRegex = /app\/(\d+)/; let href = element .getElementsByClassName("_22awlPiAoaZjQMqxJhp-KP")[0] .getAttribute("href"); let appid = appidRegex.exec(href); return appid[1]; }