您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
按 Ctrl+ 单击特定图片 URL 模式时,会在后台打开一个新标签页,其中包含相应的 Steam 应用程序页面,并阻止默认单击操作 ,可以知道浏览过的游戏,并支持导出
// ==UserScript== // @name Steampy 一键开冲! // @namespace http://tampermonkey.net/ // @version 1.8 // @description 按 Ctrl+ 单击特定图片 URL 模式时,会在后台打开一个新标签页,其中包含相应的 Steam 应用程序页面,并阻止默认单击操作 ,可以知道浏览过的游戏,并支持导出 // @match https://steampy.com/* // @author zhxilo // @grant none // ==/UserScript== (function() { 'use strict'; // Add CSS for the viewed indicator, buttons, instructions, and collapsible panel GM_addStyle(` .viewed-indicator { position: absolute; top: 5px; left: 5px; background-color: red; color: black; padding: 2px 5px; border-radius: 3px; font-size: 12px; z-index: 1000; font-weight: bold; } #scriptControls { position: fixed; top: 10px; right: 10px; z-index: 10000; background-color: white; padding: 10px; border: 1px solid #ccc; border-radius: 5px; } #scriptControls button { margin: 5px; padding: 5px 10px; } #instructions { display: none; margin-top: 10px; padding: 10px; background-color: #f0f0f0; border-radius: 5px; } #toggleInstructions { cursor: pointer; color: blue; text-decoration: underline; } #collapseToggle { cursor: pointer; user-select: none; } #controlPanel { margin-top: 10px; } .hidden { display: none; } `); // Load viewed games from GM_getValue let viewedGames = GM_getValue('viewedGames', {}); let misclickPrevention = GM_getValue('misclickPrevention', false); // Function to get appId from image URL function getAppIdFromUrl(url) { const match = url.match(/\/apps\/(\d+)\//); return match ? match[1] : null; } // Function to toggle viewed status function toggleViewedStatus(appId, imgContainer) { if (viewedGames[appId]) { delete viewedGames[appId]; imgContainer.querySelector('.viewed-indicator')?.remove(); } else { viewedGames[appId] = true; addViewedIndicator(imgContainer); } GM_setValue('viewedGames', viewedGames); } // Function to add viewed indicator function addViewedIndicator(container) { if (!container.querySelector('.viewed-indicator')) { const indicator = document.createElement('div'); indicator.className = 'viewed-indicator'; indicator.textContent = '已浏览'; container.style.position = 'relative'; container.appendChild(indicator); } } // Apply viewed indicators function applyViewedIndicators() { document.querySelectorAll('img').forEach(img => { const appId = getAppIdFromUrl(img.src); if (appId) { const container = img.closest('.item-container') || img.parentElement; if (viewedGames[appId]) { addViewedIndicator(container); } else { container.querySelector('.viewed-indicator')?.remove(); } } }); } // Function to export viewed games function exportViewedGames() { const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(viewedGames)); const downloadAnchorNode = document.createElement('a'); downloadAnchorNode.setAttribute("href", dataStr); downloadAnchorNode.setAttribute("download", "steampy_viewed_games.json"); document.body.appendChild(downloadAnchorNode); downloadAnchorNode.click(); downloadAnchorNode.remove(); } // Function to import viewed games function importViewedGames() { const input = document.createElement('input'); input.type = 'file'; input.onchange = e => { const file = e.target.files[0]; const reader = new FileReader(); reader.readAsText(file, 'UTF-8'); reader.onload = readerEvent => { const content = readerEvent.target.result; viewedGames = JSON.parse(content); GM_setValue('viewedGames', viewedGames); applyViewedIndicators(); } } input.click(); } // Function to open all unviewed games function openAllUnviewed() { document.querySelectorAll('img').forEach(img => { const appId = getAppIdFromUrl(img.src); if (appId && !viewedGames[appId]) { const steamAppUrl = `https://store.steampowered.com/app/${appId}`; window.open(steamAppUrl, '_blank', 'noopener,noreferrer'); toggleViewedStatus(appId, img.closest('.item-container') || img.parentElement); } }); } // Function to toggle misclick prevention function toggleMisclickPrevention() { misclickPrevention = !misclickPrevention; GM_setValue('misclickPrevention', misclickPrevention); document.getElementById('misclickPreventionStatus').textContent = misclickPrevention ? '开启' : '关闭'; } // Add script control buttons and instructions function addScriptControls() { const controlsContainer = document.createElement('div'); controlsContainer.id = 'scriptControls'; const collapseToggle = document.createElement('div'); collapseToggle.id = 'collapseToggle'; collapseToggle.textContent = '▼ 控制面板'; collapseToggle.onclick = () => { const controlPanel = document.getElementById('controlPanel'); controlPanel.classList.toggle('hidden'); collapseToggle.textContent = controlPanel.classList.contains('hidden') ? '▶ 控制面板' : '▼ 控制面板'; }; const controlPanel = document.createElement('div'); controlPanel.id = 'controlPanel'; const refreshButton = document.createElement('button'); refreshButton.textContent = '刷新标记'; refreshButton.onclick = applyViewedIndicators; const exportButton = document.createElement('button'); exportButton.textContent = '导出已浏览'; exportButton.onclick = exportViewedGames; const importButton = document.createElement('button'); importButton.textContent = '导入已浏览'; importButton.onclick = importViewedGames; const openAllButton = document.createElement('button'); openAllButton.textContent = '打开所有未浏览'; openAllButton.onclick = openAllUnviewed; const misclickPreventionToggle = document.createElement('button'); misclickPreventionToggle.textContent = '防误触模式:'; misclickPreventionToggle.onclick = toggleMisclickPrevention; const misclickPreventionStatus = document.createElement('span'); misclickPreventionStatus.id = 'misclickPreventionStatus'; misclickPreventionStatus.textContent = misclickPrevention ? '开启' : '关闭'; const toggleInstructions = document.createElement('span'); toggleInstructions.id = 'toggleInstructions'; toggleInstructions.textContent = '显示使用说明'; toggleInstructions.onclick = () => { const instructions = document.getElementById('instructions'); if (instructions.style.display === 'none') { instructions.style.display = 'block'; toggleInstructions.textContent = '隐藏使用说明'; } else { instructions.style.display = 'none'; toggleInstructions.textContent = '显示使用说明'; } }; const instructions = document.createElement('div'); instructions.id = 'instructions'; instructions.innerHTML = ` <p>使用说明:</p> <ul> <li>Ctrl+左键点击:打开Steam商店页面并标记为已浏览</li> <li>Alt+左键点击:切换已浏览/未浏览状态</li> <li>刷新标记:手动刷新已浏览标记</li> <li>导出已浏览:下载已浏览游戏的数据文件</li> <li>导入已浏览:上传之前导出的数据文件</li> <li>打开所有未浏览:在新标签页中打开所有未浏览的游戏</li> <li>防误触模式:开启时,只有脚本定义的操作生效</li> </ul> `; instructions.style.display = 'none'; controlPanel.appendChild(refreshButton); controlPanel.appendChild(exportButton); controlPanel.appendChild(importButton); controlPanel.appendChild(openAllButton); controlPanel.appendChild(document.createElement('br')); controlPanel.appendChild(misclickPreventionToggle); controlPanel.appendChild(misclickPreventionStatus); controlPanel.appendChild(document.createElement('br')); controlPanel.appendChild(toggleInstructions); controlPanel.appendChild(instructions); controlsContainer.appendChild(collapseToggle); controlsContainer.appendChild(controlPanel); document.body.appendChild(controlsContainer); } // Main click event listener document.addEventListener('click', function(event) { if (misclickPrevention && !event.ctrlKey && !event.altKey) { return; // If misclick prevention is on and neither Ctrl nor Alt is pressed, do nothing } const img = event.target.closest('img'); if (img) { const appId = getAppIdFromUrl(img.src); if (appId) { const container = img.closest('.item-container') || img.parentElement; if (event.ctrlKey) { event.preventDefault(); event.stopPropagation(); const steamAppUrl = `https://store.steampowered.com/app/${appId}`; window.open(steamAppUrl, '_blank', 'noopener,noreferrer'); toggleViewedStatus(appId, container); } else if (event.altKey) { event.preventDefault(); event.stopPropagation(); toggleViewedStatus(appId, container); } } } }, true); // Use MutationObserver to handle dynamically loaded content const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.type === 'childList') { applyViewedIndicators(); } }); }); observer.observe(document.body, { childList: true, subtree: true }); // Initialize applyViewedIndicators(); addScriptControls(); // Set up adaptive auto-refresh let startTime = Date.now(); let intervalId; function adaptiveRefresh() { const elapsedTime = Date.now() - startTime; if (elapsedTime < 60000) { // First minute applyViewedIndicators(); intervalId = setTimeout(adaptiveRefresh, 10000); // Every 10 seconds } else { applyViewedIndicators(); intervalId = setInterval(applyViewedIndicators, 30000); // Every 30 seconds } } adaptiveRefresh(); })();