Steam Licenses Remover

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);
})();