Show prices on Steam followedgames page

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

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

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