Pixel sites real online

Real online, allows canvas online

// ==UserScript==
// @name         Pixel sites real online
// @namespace    https://pixels.fun
// @version      1.8
// @license      MIT
// @description  Real online, allows canvas online
// @author       small bee
// @match        *://*.fun/*
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    let lastSum = 0;
    let textNode = null;
    let observer = null;

    async function updateOnlineCount() {
        try {
            const res = await fetch('/api/shards', { credentials: 'same-origin' });
            if (!res.ok) return;
            const data = await res.json();
            let sum = 0;
            data.forEach(item => {
                if (Array.isArray(item) && item[1]) {
                    Object.entries(item[1]).forEach(([k, v]) => {
                        if (k !== 'total' && typeof v === 'number') sum += v;
                    });
                }
            });

            const span = document.querySelector('.onlinebox span[title="Total Online Users"]');
            if (!span) return;

            if (!textNode) {
                textNode = Array.from(span.childNodes).find(n => n.nodeType === Node.TEXT_NODE);
                if (!textNode) {
                    textNode = document.createTextNode('');
                    span.insertBefore(textNode, span.firstChild);
                }
            }

            lastSum = sum;
            textNode.textContent = lastSum;
        } catch (err) {
            console.error('updateOnlineCount error', err);
        }
    }

    function observeTextNode() {
        if (observer) observer.disconnect();
        if (!textNode) return;

        observer = new MutationObserver(mutations => {
            const span = textNode.parentNode;
            if (span.getAttribute('title') !== 'Total Online Users') return;

            for (const m of mutations) {
                if (m.type === 'characterData' && m.target === textNode) {
                    if (m.target.nodeValue !== String(lastSum)) {
                        console.warn('Blocked external change to online count:', m.target.nodeValue);
                        m.target.nodeValue = String(lastSum);
                    }
                }
            }
        });

        observer.observe(textNode, { characterData: true });
    }

    async function showCanvasOnline() {
        const div = document.querySelector('.onlinebox');
        if (!div) return;

        div.addEventListener('click', async () => {
            const span = div.querySelector('span[title="Total Online Users"]');
            if (!span || span.querySelectorAll('svg').length < 2) return;

            const hash = window.location.hash;
            const match = /^#([a-z0-9])/i.exec(hash);
            if (!match) return;
            const letter = match[1];

            try {
                const meRes = await fetch('/api/me', { credentials: 'same-origin' });
                if (!meRes.ok) return;
                const me = await meRes.json();
                const canvases = me.canvases || {};

                let canvasId = null;
                for (const [id, info] of Object.entries(canvases)) {
                    if (info.ident === letter) {
                        canvasId = id;
                        break;
                    }
                }
                if (canvasId === null) return;

                const shardsRes = await fetch('/api/shards', { credentials: 'same-origin' });
                if (!shardsRes.ok) return;
                const shards = await shardsRes.json();

                let onlineCount = null;
                for (const item of shards) {
                    if (Array.isArray(item) && item[1] && item[1][canvasId] != null) {
                        onlineCount = item[1][canvasId];
                        break;
                    }
                }
                if (onlineCount == null) return;

                span.setAttribute('title', 'Online Users on Canvas');

                if (!textNode) {
                    textNode = Array.from(span.childNodes).find(n => n.nodeType === Node.TEXT_NODE);
                    if (!textNode) {
                        textNode = document.createTextNode('');
                        span.insertBefore(textNode, span.firstChild);
                    }
                }

                lastSum = onlineCount;
                textNode.textContent = lastSum;

                observeTextNode();
            } catch (err) {
                console.error('showCanvasOnline error', err);
            }
        });
    }

    updateOnlineCount().then(observeTextNode);
    setInterval(updateOnlineCount, 15000);
    showCanvasOnline();

})();