Rumble Unique Chatters and Number of Messages for Live Streams

Displays the number of unique chatters and messages in Rumble live streams.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Rumble Unique Chatters and Number of Messages for Live Streams
// @namespace    http://tampermonkey.net/
// @version      1.6
// @description  Displays the number of unique chatters and messages in Rumble live streams.
// @match        https://rumble.com/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    /**
     * Utility function to wait for a specific element to appear in the DOM.
     * @param {string} selector - CSS selector of the target element.
     * @param {number} timeout - Maximum time to wait in milliseconds.
     * @returns {Promise<Element>} - Resolves with the found element or rejects on timeout.
     */
    function waitForElement(selector, timeout = 10000) {
        return new Promise((resolve, reject) => {
            const element = document.querySelector(selector);
            if (element) {
                return resolve(element);
            }

            const observer = new MutationObserver(() => {
                const elem = document.querySelector(selector);
                if (elem) {
                    observer.disconnect();
                    resolve(elem);
                }
            });

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

            // Timeout after specified milliseconds
            setTimeout(() => {
                observer.disconnect();
                reject(new Error(`Element ${selector} not found within ${timeout}ms`));
            }, timeout);
        });
    }

    async function init() {
        try {
            // Check if the live info element exists
            const liveInfoSelector = "div.video-header-live-info";
            try {
                const liveInfoElement = await waitForElement(liveInfoSelector, 5000);
            } catch (error) {
                console.warn("[Chat User Monitor] Live info element not found within 5 seconds. Exiting script.");
                return; // Exit the script if the live info element is not found
            }

            // Wait for the chat box element to be available
            const chatBoxSelector = "#chat-history-list";
            const chatBox = await waitForElement(chatBoxSelector);

            // Wait for the chat controls container to append the user count display
            const chatControlsSelector = "form#chat-message-form > div.chat-message-form-section.chat-message-form-section-justify-between > div.chat--rant-row:nth-of-type(1)";
            const chatControlsContainer = await waitForElement(chatControlsSelector);

            // Initialize Sets to track unique users and messages
            const seenUsers = new Set();
            const seenMessages = new Set();

            // Create a container for displaying the user count with SVG
            const userCountDisplay = document.createElement("div");
            userCountDisplay.id = "user-count-container";
            userCountDisplay.classList.add("video-category-tag");
            userCountDisplay.title = "Number of unique users in the chat";

            // Insert the user SVG icon
            const userIconContainer = document.createElement("div");
            userIconContainer.innerHTML = `
                <svg width="20px" height="20px" viewBox="0 0 16 16" fill="currentColor">
                    <path d="M8 7C9.65685 7 11 5.65685 11 4C11 2.34315 9.65685 1 8 1C6.34315 1 5 2.34315 5 4C5 5.65685 6.34315 7 8 7Z"/>
                    <path d="M14 12C14 10.3431 12.6569 9 11 9H5C3.34315 9 2 10.3431 2 12V15H14V12Z"/>
                </svg>
            `;
            userIconContainer.style.marginRight = "8px";

            // Create the user count element
            const userCountElement = document.createElement("span");
            userCountElement.id = "seen-users-count";
            userCountElement.textContent = "0";
            userCountElement.style.fontSize = "12px";

            // Append SVG and user count to the container
            userCountDisplay.appendChild(userIconContainer);
            userCountDisplay.appendChild(userCountElement);

            // Append the container to the chat controls container
            chatControlsContainer.appendChild(userCountDisplay);

            // Create a container for displaying the message count with SVG
            const messageCountDisplay = document.createElement("div");
            messageCountDisplay.id = "message-count-container";
            messageCountDisplay.style.display = "flex";
            messageCountDisplay.style.alignItems = "center";
            messageCountDisplay.style.marginLeft = "16px";
            messageCountDisplay.title = "Number of messages in the chat";

            // Insert the message SVG icon
            const messageIconContainer = document.createElement("div");
            messageIconContainer.innerHTML = `
                <svg width="20px" height="20px" viewBox="0 0 24 24" fill="currentColor">
                    <path d="M21.0039 12C21.0039 16.9706 16.9745 21 12.0039 21C9.9675 21 3.00463 21 3.00463 21C3.00463 21 4.56382 17.2561 3.93982 16.0008C3.34076 14.7956 3.00391 13.4372 3.00391 12C3.00391 7.02944 7.03334 3 12.0039 3C16.9745 3 21.0039 7.02944 21.0039 12Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                </svg>
            `;
            messageIconContainer.style.marginRight = "8px";

            // Create the message count element
            const messageCountElement = document.createElement("span");
            messageCountElement.id = "seen-messages-count";
            messageCountElement.textContent = "0";
            messageCountElement.style.fontSize = "12px";

            // Append SVG and message count to the message container
            messageCountDisplay.appendChild(messageIconContainer);
            messageCountDisplay.appendChild(messageCountElement);

            // Append the message count container to the user count container
            userCountDisplay.appendChild(messageCountDisplay);

            /**
             * Updates the displayed counts of users and messages.
             */
            const updateCounts = () => {
                userCountElement.textContent = seenUsers.size;
                messageCountElement.textContent = seenMessages.size;
            };

            /**
             * Observes the chat box for new messages and users.
             */
            const observeChat = () => {
                const observer = new MutationObserver(mutations => {
                    mutations.forEach(mutation => {
                        mutation.addedNodes.forEach(node => {
                            if (node.nodeType === Node.ELEMENT_NODE && node.classList.contains('chat-history--row')) {
                                try {
                                    const messageId = node.getAttribute("data-message-id");
                                    const userId = node.getAttribute("data-message-user-id");

                                    // Track unique messages
                                    if (messageId && !seenMessages.has(messageId)) {
                                        seenMessages.add(messageId);
                                    }

                                    // Track unique users
                                    if (userId && !seenUsers.has(userId)) {
                                        seenUsers.add(userId);
                                    }

                                    // Update the displayed counts
                                    updateCounts();
                                } catch (error) {
                                    console.error("[Chat User Monitor] Error processing message:", error);
                                }
                            }
                        });
                    });
                });

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

            // Start observing the chat
            observeChat();

        } catch (error) {
            console.error("[Chat User Monitor] Initialization failed:", error);
        }
    }

    // Start the initialization process
    init();

})();