您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Removes free games from your Steam library with a single click.
// ==UserScript== // @name Steam Licenses Remover // @namespace https://store.steampowered.com/account/licenses/ // @version 1.0 // @description Removes free games from your Steam library with a single click. // @author hycosi // @match https://store.steampowered.com/account/licenses/ // @icon https://i.imgur.com/OhMAUre.png // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; let isRunning = false; let removedCount = 0; function extractIdFromLink(link) { const match = link.match(/RemoveFreeLicense\(\s*(\d+)\s*,/); return match ? match[1] : null; } function getSessionIdFromCookie() { const match = document.cookie.match(/sessionid=([0-9a-f]+)/); return match ? match[1] : null; } async function removeGame(id) { let sessionId = window.g_sessionID || (typeof g_sessionID !== 'undefined' ? g_sessionID : undefined) || getSessionIdFromCookie(); if (!sessionId) { alert('Failed to retrieve sessionID!'); return { success: false, limit: false }; } try { const response = await fetch('https://store.steampowered.com/account/removelicense', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `sessionid=${encodeURIComponent(sessionId)}&packageid=${encodeURIComponent(id)}` }); if (response.ok) { const data = await response.json(); if (data.success === 1) { removedCount++; return { success: true, limit: false }; } else if (data.success === 84) { // Limit return { success: false, limit: true }; } } } catch (e) { console.error(`Error removing ID ${id}:`, e); } return { success: false, limit: false }; } function updateButtonText(text) { const btn = document.getElementById('remove-free-games-btn'); if (btn) btn.textContent = text; } function setButtonDisabled(disabled) { const btn = document.getElementById('remove-free-games-btn'); if (btn) btn.disabled = disabled; } async function showCountdown(seconds) { for (let i = seconds; i > 0; i--) { const min = Math.floor(i / 60).toString().padStart(2, '0'); const sec = (i % 60).toString().padStart(2, '0'); updateButtonText(`Limit reached. Waiting ${min}:${sec}`); await new Promise(r => setTimeout(r, 1000)); } } async function removeFreeGamesBatch() { const removeLinks = Array.from(document.querySelectorAll('a[href^="javascript:RemoveFreeLicense("]')); if (removeLinks.length === 0) { alert('No free games found!'); return { batchLimit: false, globalLimit: false }; } const batch = removeLinks.slice(0, 12); let globalLimit = false; for (let i = 0; i < batch.length; i++) { const id = extractIdFromLink(batch[i].href); if (id) { const result = await removeGame(id); if (result.limit) { globalLimit = true; break; } updateButtonText(`Removed: ${removedCount}`); await new Promise(r => setTimeout(r, 2000)); } } return { batchLimit: batch.length === 12, globalLimit }; } async function removeAllFreeGamesLoop() { isRunning = true; setButtonDisabled(true); removedCount = 0; let hasMore = true; while (hasMore) { const { batchLimit, globalLimit } = await removeFreeGamesBatch(); if (globalLimit) { await showCountdown(30 * 60); // 30 minutes continue; } if (batchLimit) { await showCountdown(30 * 60); // 30 minutes } hasMore = batchLimit; } updateButtonText('Done!'); setButtonDisabled(false); isRunning = false; } function addRemoveButton() { if (document.getElementById('remove-free-games-btn')) return; const btn = document.createElement('button'); btn.id = 'remove-free-games-btn'; btn.textContent = 'Remove all free games'; btn.style = 'margin: 5px 0 5px auto; float: right; position: relative; left: -680px; top: -3px; background: #1b2838; color: #fff; border: 1px solid #fff; padding: 9px 16px; border-radius: 4px; cursor: pointer;'; btn.onmouseover = function() { btn.style.cursor = 'pointer'; }; btn.onclick = function() { if (!isRunning) removeAllFreeGamesLoop(); }; const container = document.querySelector('.page_content_ctn'); if (container) { container.insertBefore(btn, container.firstChild); } else { document.body.insertBefore(btn, document.body.firstChild); } } const observer = new MutationObserver(() => { addRemoveButton(); }); observer.observe(document.body, { childList: true, subtree: true }); window.addEventListener('DOMContentLoaded', addRemoveButton); })();