Greasy Fork is available in English.

Show prices on Steam followedgames page

在Steam社区"已关注的游戏"页面显示游戏的价格

// ==UserScript==
// @name         Show prices on Steam followedgames page
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  在Steam社区"已关注的游戏"页面显示游戏的价格
// @author       lyzlyslyc
// @match        http*://steamcommunity.com/*/followedgames*
// @icon         https://store.steampowered.com/favicon.ico
// @grant        GM_xmlhttpRequest
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // Your code here...

    let interval = 1000;
    let requestTimeout = 3e3;
    let cc="";

    //查询账号区域
    console.log("Checking country.");
    getCountryCode();
    function getCountryCode(){
        GM_xmlhttpRequest({
            method: "get",
            url: `https://store.steampowered.com/api/getfundwalletinfo/`,
            responseType: "json",
            timeout: requestTimeout,
            onload: (result)=>{
                result = result.response;
                if(result.success){
                    cc=result.country_code;
                    console.log(`Country code:${cc}`);
                }
                setInterval(requestPrice,interval);
            },
            ontimeout:()=>{
                getCountryCode();
                console.log("Checking country timeout. Retrying.");
            },
            onerror:(e)=>{
                console.log("Checking country error.");
                console.log(e);
            }
        })
    }

    function requestPrice(){
        //获取关注列表
        let apps = document.querySelector(".games_list_rows").children;
        let queryList = {};
        let appidString = "";
        for(let i=0;i<apps.length;i++){
            if(apps[i].hasPrice||apps[i].hasPrice=="loading")continue;
            //找到第一个没查询且在可视区域内的app
            if(isAppVisible(apps[i])){
                //前后查询50个
                let min = (i-25)<0?0:(i-25);
                let max = (i+25)>apps.length?apps.length:(i+25);
                for(let j = min;j<max&&j<apps.length;j++){
                    if(apps[j].hasPrice||apps[j].hasPrice=="loading"){
                        max++;
                        continue;
                    }
                    let appUrl = apps[j].querySelector("a").href;
                    if(appUrl){
                        //获取appid
                        let appId = appUrl.match(/app\/(\d+)/)[1];
                        apps[j].hasPrice = "loading";
                        queryList[appId] = apps[j];
                        appidString+=appId+",";
                    }
                }
                break;
            }
        }

        if(appidString=="")return;
        //发送查询请求
        GM_xmlhttpRequest({
            method: "get",
            url: `https://store.steampowered.com/api/appdetails?appids=${appidString}&cc=${cc}&filters=price_overview`,
            responseType: "json",
            timeout: requestTimeout,
            onload: handleResult,
            ontimeout:handleTimeout
        })
        console.log(`Request:${appidString}`);
        function handleResult(result){
            try{
                result = result.response;
                let appId;
                //对每一个app
                for(appId in result){
                    //获取app信息:success和data
                    let appInfo = result[appId];
                    let price = null;
                    //应用已下架,success===false
                    if(!appInfo.success){
                        price = {
                            discount_percent: 0,
                            final : 0,
                            final_formatted: "Not for Sale"
                        };
                    }
                    //如果data为空,无法判断是免费或者还是未发售,需要继续请求
                    else if(appInfo.data.length===0){
                        //发送查询请求
                        GM_xmlhttpRequest({
                            method: "get",
                            url: `https://store.steampowered.com/api/appdetails?appids=${appId}&cc=${cc}`,
                            responseType: "json",
                            timeout: requestTimeout,
                            onload: handleResult,
                            ontimeout:handleTimeout
                        })
                        console.log(`RequestDetail:${appId}`);
                        continue;
                    }
                    //此处处理上面分支发送的请求
                    else if(appInfo.data.name){
                        //即将发售
                        if(appInfo.data.release_date.coming_soon){
                            price = {
                                discount_percent: 0,
                                final : 0,
                                final_formatted: "Coming Soon"
                            };
                        }
                        //免费
                        else if(appInfo.data.is_free){
                            price = {
                                discount_percent: 0,
                                final : 0,
                                final_formatted: "Free to Play"
                            };
                        }
                    }
                    else price = appInfo.data.price_overview;

                    //添加结果
                    let content = generatePrice(price);
                    let span = queryList[appId].querySelector(".game_purchase_action_bg");
                    if(!span)span = document.createElement("span");
                    span.innerHTML = content;
                    span.className = "game_purchase_action_bg";
                    let a = queryList[appId].querySelector(".followed_game_actions a");
                    a.parentElement.prepend(span);
                    queryList[appId].hasPrice = true;
                }
            }
            catch(e){
                let content =
                    `<span class="discount_block  no_discount discount_block_inline" data-price-final="0">`+
                    '<div class="discount_prices">'+
                    `<div class="discount_final_price">Request Error</div>`+
                    '</div>'+
                    '</span>';

                let appId;
                for(appId in queryList){
                    if(queryList[appId].hasPrice==true)continue;
                    let span = queryList[appId].querySelector(".game_purchase_action_bg");
                    if(!span)span = document.createElement("span");
                    span.innerHTML = content;
                    span.className = "game_purchase_action_bg";
                    let a = queryList[appId].querySelector(".followed_game_actions a");
                    a.parentElement.prepend(span);
                    queryList[appId].hasPrice = false;
                    console.log(`Error:${appId}`);
                    console.log(e);
                }
            }
        }

        function handleTimeout(){
            let content =
                `<span class="discount_block  no_discount discount_block_inline" data-price-final="0">`+
                '<div class="discount_prices">'+
                `<div class="discount_final_price">Request Timeout</div>`+
                '</div>'+
                '</span>';

            let appId;
            for(appId in queryList){
                if(queryList[appId].hasPrice==true)continue;
                let span = queryList[appId].querySelector(".game_purchase_action_bg");
                if(!span)span = document.createElement("span");
                span.innerHTML = content;
                span.className = "game_purchase_action_bg";
                let a = queryList[appId].querySelector(".followed_game_actions a");
                a.parentElement.prepend(span);
                queryList[appId].hasPrice = false;
                console.log(`Timeout:${appId}`);
            }
        }
    }

    function generatePrice(price){
        let content = "";
        let template = "";
        if(price.discount_percent==0){
            template =
                `<span class="discount_block  no_discount discount_block_inline" data-price-final="${price.final}">`+
                    '<div class="discount_prices">'+
                        `<div class="discount_final_price">${price.final_formatted}</div>`+
                    '</div>'+
                '</span>';
        }
        else {
            template =
                `<span class="discount_block  discount_block_inline" data-price-final="${price.final}">`+
                    `<div class="discount_pct">-${price.discount_percent}%</div>`+
                    '<div class="discount_prices">'+
                        `<div class="discount_original_price">${price.initial_formatted}</div>`+
                        `<div class="discount_final_price">${price.final_formatted}</div>`+
                    '</div>'+
                '</span>'
        }
        content+=template;
        return content;
    }

    function isAppVisible(app){
        let s = app.offsetTop;    // 元素相对于页面顶部的距离
        let x = app.offsetHeight;    //元素自身高度
        let t = document.documentElement.scrollTop;  // 页面在垂直方向上滚动的距离
        let y = document.documentElement.clientHeight;   //窗口可视区域的高度
        let isHidden = (s+x) < t || (s > (t+y));
        return !isHidden
    }
})();