// ==UserScript== // @name Steam 家庭库已有游戏标记 // @namespace http://tampermonkey.net/ // @version 2024-04-07v1 // @description 能够自动扫描你的家庭库库存,并在Steam游戏页面标记,并支持一键安装游戏 // @author Cliencer Goge // @match https://store.steampowered.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=steampowered.com // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @license GPLv3 // @downloadURL none // ==/UserScript== var dialog,appid,observer var saves = readstorage() const url = window.location.pathname; (function() { 'use strict'; init() if(url=='/account/familymanagement' && saves.isStartDump){ observer_1(); }else{ if(g_ServerTime-saves.lastupDateTime>2592000){ let innerText if(saves.familyGameList.length == 0){ innerText="您似乎没有家庭库的游戏记录,是否现在扫描家庭库游戏并记录呢?" }else{ innerText="您已经超过1个月没有更新家庭库的游戏记录了,是否现在去扫描?" } ShowConfirmDialog('脚本提示',innerText,'扫描家庭库','取消').done(scan).fail(()=>{ ShowAlertDialog('脚本提示','如果需要手动扫描,可以在Steam主页右上角进入进行扫描','好的') }) } var search_suggestion = document.getElementById('search_suggestion_contents') var observer_search = new MutationObserver((mutations, obs) => { mutations.forEach(function(mutation) { if (mutation.addedNodes && mutation.addedNodes.length > 0) { mutation.addedNodes.forEach(function(node) { // 确保是元素节点 if (node.nodeType === Node.ELEMENT_NODE) { // 检查新节点是否有指定的类 if (node.classList.contains('match_app')) { addflag(node) } } }); } }) }); observer_search.observe(search_suggestion, {childList: true, subtree: true}); } if(url == "/"){ observer_3() observer = new MutationObserver((mutations, obs) => { mutations.forEach(function(mutation) { if (mutation.addedNodes && mutation.addedNodes.length > 0) { mutation.addedNodes.forEach(function(node) { // 确保是元素节点 if (node.nodeType === Node.ELEMENT_NODE) { if(node.classList.contains('live_streams_ctn')){return;} node.querySelectorAll("div").forEach((node)=>{ addflag(node) }) node.querySelectorAll("a").forEach((node)=>{ if(node.classList.contains('screenshot')){return;} if(node.querySelector('div.broadcast_live_stream_icon')){return;} addflag(node) }) } }) } }) }) observer.observe(document, {childList: true, subtree: true}); } if(url.startsWith('/app/')&&g_AccountID != 0){ //addBanner(document.querySelector('div.block.game_media_and_summary_ctn')) observer_2(); } if(url.startsWith('/search/')&&g_AccountID != 0){ observer_4() var search_results = document.getElementById('search_results') observer = new MutationObserver((mutations, obs) => { mutations.forEach(function(mutation) { if (mutation.addedNodes && mutation.addedNodes.length > 0) { mutation.addedNodes.forEach(function(node) { // 确保是元素节点 if (node.nodeType === Node.ELEMENT_NODE) { console.log(node) if (node.classList.contains('search_result_row') && node.classList.contains('ds_collapse_flag') && !node.classList.contains('ds_owned')) { addflag(node,"clear: left;") }else{ let lists = node.querySelectorAll("a.search_result_row.ds_collapse_flag") lists.forEach(function(bar){ addflag(bar,"clear: left;") }) } } }); } }) }); observer.observe(search_results, {childList: true, subtree: true}); } function init(){ let setting_btn = document.createElement('span'); setting_btn.id = "setting_btn" setting_btn.style = "position:relative;background:linear-gradient(to right, rgb(6 207 199 / 60%) 0%, rgb(33 105 106 / 60%) 100%)" setting_btn.innerHTML = `家庭游戏标记 脚本设置` setting_btn.onclick = btnonclick plug(); function plug(){ let headding = document.getElementById('global_action_menu') if(headding){ headding.insertBefore(setting_btn, headding.firstChild); }else{ setTimeout(plug,200) } } function btnonclick(){ ShowConfirmDialog('脚本设置',`目前你的家庭库一共记录了${saves.familyGameList.length}个游戏。\n上次扫描时间:${timestampToTime(saves.lastupDateTime)}`,'扫描家庭库',null,'清空库记录').done(function(arg){ if(arg == 'SECONDARY'){ ShowConfirmDialog('再次确认','你即将清空当前保存的家庭库列表,该行为无法撤销!','好的','算了').done(() =>{ saves = { isStartDump:false, familyGameList:[], lastupDateTime:0 } savestorage() ShowAlertDialog('完成','已经清除所有的缓存','好的') }) }else{ scan() } }) } } function scan(){ ShowAlertDialog('提示','即将打开家庭库页面进行扫描,请确认已加入一个有效的家庭组,否则脚本可能会出错,扫描期间不要关闭浏览器,耐心等待进度条结束!','好的,开始扫描').done(()=>{ var windowFeatures = "width=1000,height=700,resizable=no,scrollbars=yes,status=yes"; var newWindow = window.open('https://store.steampowered.com/account/familymanagement?tab=library', '家庭库扫描窗口---不要关闭!', windowFeatures); saves.isStartDump=true; savestorage() // 检查窗口是否成功打开 if (newWindow) { newWindow.focus(); } else { // 弹窗被阻止的情况 alert("打开新窗口被阻止,请在浏览器设置中允许打开新窗口!"); } }) } function observer_4(){ let block = document.getElementById('search_result_container') if(block){ let lists = block.querySelectorAll("a.search_result_row.ds_collapse_flag") lists.forEach(function(bar){ addflag(bar,"clear: left;") }) }else{ setTimeout(observer_4,200) } } function observer_3(){ let block = document.querySelector('div.home_tabs_content') if(block){ let lists = block.querySelectorAll("a.tab_item") lists.forEach(function(bar){ addflag(bar,"clear: both;") }) block = document.querySelector('div.carousel_container.maincap') lists = block.querySelectorAll("a.store_main_capsule") lists.forEach(function(bar){ addflag(bar) }) block = document.querySelector('div.carousel_container.spotlight') lists = block.querySelectorAll("div.home_area_spotlight") lists.forEach(function(bar){ addflag(bar) }) lists = block.querySelectorAll("a.store_capsule") lists.forEach(function(bar){ addflag(bar) }) block = document.getElementById('module_deep_dive') lists = block.querySelectorAll("a.store_capsule") lists.forEach(function(bar){ addflag(bar) }) block = document.getElementById('module_recommender') lists = block.querySelectorAll("a.store_capsule") lists.forEach(function(bar){ addflag(bar) }) block = document.getElementById('recommended_creators_carousel') lists = block.querySelectorAll("a.store_capsule") lists.forEach(function(bar){ addflag(bar) }) block = document.querySelector('div.specials_under10_content') lists = block.querySelectorAll("a.store_capsule") lists.forEach(function(bar){ addflag(bar) }) block = document.querySelector('div.marketingmessage_area') lists = block.querySelectorAll("a.home_marketing_message") lists.forEach(function(bar){ addflag(bar) }) }else{ setTimeout(observer_3,200) } } function observer_2(){ let block = document.querySelector('div.block.game_media_and_summary_ctn') if(block){ appid = Number(url.split('/')[2]) if(saves.familyGameList.includes(appid)){ addBanner(block) } }else{ setTimeout(observer_2,200) } } function addflag(node,insertBeforeStyle){ if(node.querySelector("div.ds_owned_flag")) return; let thisappid = node.getAttribute('data-ds-appid') let thisurl = node.getAttribute('href') if(thisappid && (thisurl == null || thisurl.startsWith('https://store.steampowered.com/app/')) && saves.familyGameList.includes(Number(thisappid))){ if(url.startsWith('/app/')){ node.classList.add('ds_owned'); } node.classList.add('ds_flagged'); node.classList.remove('ds_wishlist') var flag = document.createElement('div'); flag.className = "ds_flag ds_owned_flag" flag.innerHTML = '在家庭库中  ' flag.style = "background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAKCAYAAABi8KSDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OUNDNzBFNTUyMUM0MTFFNDk1REVFODRBNUU5RjA2MUYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OUNDNzBFNTYyMUM0MTFFNDk1REVFODRBNUU5RjA2MUYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5Q0M3MEU1MzIxQzQxMUU0OTVERUU4NEE1RTlGMDYxRiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5Q0M3MEU1NDIxQzQxMUU0OTVERUU4NEE1RTlGMDYxRiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pv3vUKAAAAAlSURBVHjaYvz//z8DsYARpFhISAivjnfv3jGSp3jUGeQ4AyDAADZHNe2nyOBrAAAAAElFTkSuQmCC') no-repeat 4px 4px #06cfbe" if(insertBeforeStyle){ node.insertBefore(flag, node.querySelector(`[style*="${insertBeforeStyle}"]`).nextSibling); }else{ node.appendChild(flag); } node.querySelectorAll("div.ds_flag.ds_wishlist_flag").forEach((wishlist_flag)=>{wishlist_flag.remove()}) } } function addBanner(block){ let appname = appHubAppName.innerText let owned = false if(block.querySelector('div.game_area_already_owned.page_content')){ owned = true } if(owned == false){ var headplug = document.createElement('div'); var targetElement = block.querySelector('div.queue_overflow_ctn'); headplug.style = "background:linear-gradient(to right, rgb(6 207 199 / 60%) 0%, rgb(33 105 106 / 60%) 100%);color:#06cfb5" headplug.className = "game_area_already_owned page_content" headplug.innerHTML =`
在家庭库中  
您的 Steam 家庭库中已有《${appname}》
` targetElement.parentNode.insertBefore(headplug, targetElement.nextSibling); var endplug = document.createElement('div'); targetElement = block.querySelector('div.purchase_options_content'); endplug.className = "game_area_play_stats" endplug.innerHTML = `
安装 Steam
马上开玩
` targetElement.parentNode.insertBefore(endplug, targetElement); } } function observer_1(){ let gametable = document.querySelectorAll('div._1o7lKXffOJjZ_CpH1bHfY-') if(gametable.length > 0){ let button_list = gametable[0].querySelectorAll('button._2UOyb8dGbKlL6QDQiqYFoc.DialogButton._DialogLayout.Secondary.Focusable') if(button_list.length > 1){ button_list[1].click(); console.log('点击显示全部') } let loadinghtml=`
` dialog = ShowBlockingWaitDialog('正在扫描家庭组库存...',loadinghtml) waitfordialog() function waitfordialog(){ if(dialog.m_$Content.length==0){ setTimeout(waitfordialog,500) }else{ setTimeout(startDump,500) } } }else{ setTimeout(observer_1,1000) } } function startDump(){ dialog.m_$Content[0].querySelector('div.waiting_dialog_throbber').remove() let bar = dialog.m_$Content[0].querySelector('.progress-filled') let containGames_panel = document.querySelectorAll('div._1o7lKXffOJjZ_CpH1bHfY-')[0] let totalGames = getGameCounts(containGames_panel)//游戏总数 let gameList = [] let dumped_panel_index = -1 scroll_page(); function scroll_page(){ var i = 0 let panelList = containGames_panel.querySelectorAll('div._3yp3JSnseRSpM7H2FnoqG9.Panel.Focusable') for(i = 0;i 1){console.log(`有跳过行数在index=${panel_index}`)} for(var games_box of panelList[i].children){ gameList.push(getGameAppid(games_box)) } dumped_panel_index = panel_index } if(gameList.length < totalGames){ updateProgressBar(gameList.length/totalGames) panelList[i-1].scrollIntoView({ behavior: 'auto', block: 'start' }); setTimeout(scroll_page,100) }else{ dialog.Dismiss() saves.familyGameList = gameList saves.lastupDateTime = g_ServerTime saves.isStartDump = false savestorage() ShowAlertDialog('完成',`你家庭组中的${totalGames}个游戏已全部记录,现在可以关闭该页面,回到主页后刷新生效!`,'知道啦,屑屑你',{strSubTitle:null,bExplicitDismissalOnly:false}) } } function updateProgressBar(progress) { bar.style.width = progress*100 + '%'; } } function getGameAppid(element){ return Number(element.firstChild.firstChild.getAttribute('src').split('/')[5]) } function getGameCounts(containGames_panel){ return Number(containGames_panel.querySelector('div.LP9H7bBiPB8N8jFzCQumL').lastChild.innerText.match(/\d*/)[0]) } })(); function readstorage(){ var saves = GM_getValue('saves') if(saves) return saves saves = { isStartDump:false, familyGameList:[], lastupDateTime:0 } return saves } function savestorage(){ GM_setValue('saves',saves) } function timestampToTime(timestamp) { if(timestamp == 0){return '无记录'} timestamp = timestamp ? timestamp : null; timestamp *= 1000 let date = new Date(timestamp);//时间戳为10位需*1000,时间戳为13位的话不需乘1000 let Y = date.getFullYear() + '-'; let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'; let D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' '; let h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':'; let m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + ':'; let s = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds(); return Y + M + D + h + m + s; }