Greasy Fork is available in English.

Humble Bundle Keys Backup

Displays a text area with game titles and keys so you can copy them out easily.

// ==UserScript==
// @name         Humble Bundle Keys Backup
// @namespace    Lex@GreasyFork
// @version      0.1.1
// @description  Displays a text area with game titles and keys so you can copy them out easily.
// @author       Lex
// @match        https://www.humblebundle.com/downloads*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    function formatGames(games) {
        // Ignore games which do not have keys revealed
        //games = games.filter(e => e.key);
        // Format the output as tab-separated
        games = games.map(e => (e.title+"\t"+e.key).trim());
        return games.join("\n");
    }

    function createNotify(bundle, message) {
        const areaName = "ktt-notify";
        let notify = bundle.querySelector("."+areaName);
        if (!notify) {
            notify = document.createElement("div");
            notify.className = areaName;
            bundle.append(notify);
        }
        notify.innerHTML = message;
        return notify;
    }

    function createArea() {
        const area = document.createElement("textarea");
        area.className = "key-text-area";
        area.style.width = "100%";
        area.setAttribute('readonly', true);
        return area;
    }

    // Updates an area if it needs updating, adjusting the height to fit the contents
    function updateArea(area, updateStr) {
        if (area.value != updateStr) {
            area.value = updateStr;
            // Adjust the height so all the contents are visible
            area.style.height = "";
            area.style.height = area.scrollHeight + 20 + "px";
        }
    }

    // Returns array of the games in the target bundle
    function getGames(bundle) {
        let games = [];
        bundle.querySelectorAll(".key-redeemer").forEach(div => {
            let game = {};
            game.title = div.querySelector(".heading-text h4").innerText;
            const keyfield = div.querySelector(".keyfield");
            if (!keyfield) return;
            game.key = keyfield.title;
            if (game.key.startsWith("Reveal your ")) {
                game.key = "";
                game.revealed = false;
            } else {
                game.revealed = true;
            }
            game.isGift = keyfield.classList.contains("redeemed-gift");
            game.isKey = keyfield.classList.contains("redeemed");
            games.push(game);
        });
        return games;
    }

    function handlePage() {
        document.querySelectorAll(".key-container.wrapper").forEach(bundle => {
            const gameCount = document.querySelectorAll(".keyfield").length;
            const revealedCount = document.querySelectorAll(".redeemed,.redeemed-gift").length;

            const color = gameCount == revealedCount ? "" : "tomato";
            let notifyHtml = `Found ${gameCount} keyfields. <span style="background:${color}">${revealedCount} are revealed.</span>`;
            if (gameCount != revealedCount) {
                notifyHtml += " Are some keys not revealed?";
            }
            createNotify(bundle, notifyHtml);

            const area = createArea();
            bundle.append(area);
            const games = getGames(bundle);
            updateArea(area, formatGames(games));
        });
    }

    function waitForLoad(query, callback) {
        if (document.querySelector(query)) {
            callback();
        } else {
            setTimeout(waitForLoad.bind(null, query, callback), 100);
        }
    }

    waitForLoad(".key-redeemer", handlePage);
})();