Itch.io Web Integration

Shows if an Itch.io link has been claimed or not

От 06.06.2020. Виж последната версия.

// ==UserScript==
// @name        Itch.io Web Integration
// @namespace   Lex@GreasyFork
// @match       https://www.steamgifts.com/discussion/kGstL/free-itchio-games-and-everything-on-100-sale
// @grant       GM_xmlhttpRequest
// @grant       GM_getValue
// @grant       GM_setValue
// @version     0.1.2
// @author      Lex
// @description Shows if an Itch.io link has been claimed or not
// @connect     itch.io
// ==/UserScript==

// TODO
// Add ability to claim the game with one click

(function(){
    'use strict';
    
    const INVALIDATION_TIME = 5*60*60*1000; // 5 hour cache time
    const ITCH_GAME_CACHE_KEY = 'ItchGameCache';
    var ItchGameCache;
    
    // Promise wrapper for GM_xmlhttpRequest
    const Request = details => new Promise((resolve, reject) => {
        details.onerror = details.ontimeout = reject;
        details.onload = resolve;
        GM_xmlhttpRequest(details);
    });
    
    function loadItchCache() {
        ItchGameCache = JSON.parse(GM_getValue(ITCH_GAME_CACHE_KEY) || '{}');
    }
    
    function _saveItchCache() {
        if (ItchGameCache === undefined) return;
        GM_setValue(ITCH_GAME_CACHE_KEY, JSON.stringify(ItchGameCache));
    }
    
    function setItchGameCache(key, game) {
        loadItchCache(); // refresh our cache in case another tab has edited it
        ItchGameCache[key] = game;
        _saveItchCache();
    }
    
    function deleteItchGameCache(key) {
        if (key === undefined) return;
        loadItchCache();
        delete ItchGameCache[key];
        _saveItchCache();
    }
    
    function getItchGameCache(link) {
        if (!ItchGameCache) loadItchCache();
        if (Object.prototype.hasOwnProperty.call(ItchGameCache, link)) {
            return ItchGameCache[link];
        }
        return null;
    }
    
    // Sends an XHR request and parses the results into a game object
    async function fetchItchGame(url) {
        const response = await Request({method: "GET",
                                 url: url});
        const parser = new DOMParser();
        const dom = parser.parseFromString(response.responseText, 'text/html');
        const h2 = dom.querySelector(".purchase_banner_inner .key_row .ownership_reason");
        const game = {};
        game.cachetime = (new Date()).getTime();
        game.url = url;
        game.isOwned = h2 !== null
        return game;
    }
    
    // Loads an itch game from cache or fetches the page if needed
    async function getItchGame(url) {
        let game = getItchGameCache(url);
        if (game !== null) {
            const isExpired = (new Date()).getTime() - game.cachetime > INVALIDATION_TIME;
            if (isExpired) {
                game = null;
            }
        }
        if (game === null) {
            game = await fetchItchGame(url);
            setItchGameCache(url, game);
        }
        return game;
    }
    
    // Appends the isOwned tag to an anchor link
    function appendTags(a, isOwned) {
        const div = document.createElement("div");
        a.after(div);
        const ownMark = isOwned ? `<span title="Game is already claimed on itch.io">✔️</span>` : `<span title="Game is not claimed">❌</span>`;
        div.outerHTML = `<div style="margin-left: 5px; background:rgb(200,200,200); border-radius: 5px; display: inline-block;">${ownMark}</div>`;
    }
    
    function addClickHandler(a) {
        a.addEventListener('mouseup', event => {
            deleteItchGameCache(event.target.href);
        });
    }

    // Handles an itch.io link on a page
    async function handleLink(a) {
        addClickHandler(a);
        const game = await getItchGame(a.href);
        appendTags(a, game.isOwned);
    }
    
    // Finds all the itch.io links on the current page
    function getItchLinks() {
        const links = [...document.querySelectorAll("a[href*='itch.io/']")];
        return links.filter(a => /^https:\/\/[^.]+\.itch\.io\/[^/]+$/.test(a.href));
    }
    
    function handlePage() {
        const as = getItchLinks();
        as.forEach(handleLink);
    }
    
    handlePage();
})();