Greasy Fork is available in English.

显示Steam游戏在线人数

在Steam商店页右侧信息面板增加在线人数, 便于观察游戏的实际热度

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         显示Steam游戏在线人数
// @description  在Steam商店页右侧信息面板增加在线人数, 便于观察游戏的实际热度
// @icon         https://store.steampowered.com/favicon.ico
// @version      1.7
// @author       cweijan
// @namespace    cweijan/steam_online_players
// @license      MIT
// @run-at       document-start
// @grant        GM_xmlhttpRequest
// @include      *store.steampowered.com/app/*
// ==/UserScript==

function makeElement(htmlString) {
    var div = document.createElement('div');
    div.innerHTML = htmlString.trim();
    return div.firstChild;
}


let onlineTextNode, onedayPeakTextNode;
let initText = GM_xmlhttpRequest ? '数据加载中' : '无网络权限!', onedayInitText = initText
const appId = location.href.match(/app\/(\d+)/)?.[1];

function colorization(msg, node) {
    let color = '#848683';
    if (Number.isNaN(parseInt(msg))) return color;
    if (msg > 100000) {
        color = '#e72424'
    } else if (msg > 50000) {
        color = '#ce3a3a'
    } else if (msg > 10000) {
        color = '#7cc53f'
    } else if (msg > 3000) {
        color = '#579227'
    }
    if (node) {
        node.setAttribute('style', `color: ${color}`)
    }
    return color;
}

function fillText(msg, type) {
    if (type == 'peak') {
        if (onedayPeakTextNode) {
            onedayPeakTextNode.textContent = msg
            colorization(msg, onedayPeakTextNode)
        }
        else onedayInitText = msg
        return;
    }
    if (onlineTextNode) {
        onlineTextNode.textContent = msg
        colorization(msg, onlineTextNode)
    }
    else initText = msg
}

if (GM_xmlhttpRequest) {
    // 接口参考: https://partner.steamgames.com/doc/webapi/ISteamUserStats#GetNumberOfCurrentPlayers
    GM_xmlhttpRequest({
        method: "GET",
        url: `https://api.steampowered.com/ISteamUserStats/GetNumberOfCurrentPlayers/v1/?appid=${appId}`,
        onload(response) {
            try {
                const playerCount = JSON.parse(response.responseText).response.player_count;
                fillText(playerCount || 0)
            } catch (error) {
                fillText(`解析API返回值失败:${response.responseText}`)
            }
        },
        onerror(response) {
            console.log(response)
            fillText("请求失败")
        }
    })
    GM_xmlhttpRequest({
        method: "GET",
        url: `https://steamcharts.com/app/${appId}`,
        onload(response) {
            try {
                const playerCount = response.responseText.match(/(?<="num">)(\d+)/g)[1];
                fillText(playerCount, 'peak')
            } catch (error) {
                fillText(`解析返回值失败`, 'peak')
            }
        },
        onerror(response) {
            console.log(response)
            fillText("请求失败", 'peak')
        }
    })
}

let addedHistory = false;
const observer = new MutationObserver(mutationList => {
    for (var mutation of mutationList) {
        for (var node of mutation.addedNodes) {
            if (!node.querySelectorAll) continue;
            if (node.getAttribute("class") == 'btn_addtocart' && !addedHistory) {
                addedHistory = true;
                // 增加历史价格记录
                const historyBtnHtml = `<div class="btn_addtocart"><a class="btn_blue_steamui btn_medium" href="https://steamdb.info/app/${appId}/#pricehistory" target="_blank"> <span>查看历史价格</span> </a></div>`
                node.parentNode.insertBefore(makeElement(historyBtnHtml), node)
            } else if (node.getAttribute("id") == 'userReviews') {
                // 增加查看所有评价的链接
                node.appendChild(makeElement(`<a style="padding-left: 104px;" target="_blank" href="https://steamcommunity.com/app/${appId}/reviews?browsefilter=toprated">浏览所有评测</a>`))
                // 当前正在玩
                const html = `<div class="user_reviews">
                                <div class="user_reviews_summary_row">
                                    <div class="subtitle column">在玩人数:</div>
                                    <div class="summary column">
                                        <span id="onlinePlayers" style="color: ${colorization(initText)};">${initText}</span>
                                    </div>
                                </div>
                                <div class="user_reviews_summary_row">
                                    <div class="subtitle column">24小时峰值:</div>
                                        <div class="summary column">
                                        <span id="onedayPeakPlayers" style="color: ${colorization(onedayInitText)};">${onedayInitText}</span>
                                    </div>
                                </div>
                            </div>`
                // node.insertBefore(makeElement(html), node.firstChild)
                node.parentNode.insertBefore(makeElement(html), node)
                onlineTextNode = document.getElementById('onlinePlayers')
                onedayPeakTextNode = document.getElementById('onedayPeakPlayers')
            }
        }
    }
});

observer.observe(document, { childList: true, subtree: true });