Kick.com Combined Chatter and Message Counter

Kick.com Combined Chatter and Message Counter - TRUTH EDITION

// ==UserScript==
// @name     Kick.com Combined Chatter and Message Counter
// @version  4.01
// @description Kick.com Combined Chatter and Message Counter - TRUTH EDITION
// @author   Graph1ks
// @match    https://kick.com/*
// @grant        GM_setClipboard
// @grant        GM_xmlhttpRequest
// @namespace https://greasyfork.org/de/users/1245263
// ==/UserScript==

(function () {
    'use strict';
 let consoleLogsActive = true;
 let viewerCountValue = '<span class="viewer_countUI" style="color: orange;">calc</span>';
 let currentHttpRequest;
 let initialPath = window.location.pathname;
 let initialUsername = getStreamerUsernameFromURL();
 let startTime = window.startTime || new Date();

    const userData = {
        uniqueMessages: new Set(),
        chattersPerMinute: new Map(),
        messagesPerMinute: new Map(),
        totalChatters: [],
        totalMessages: [],
    };

    const logData = [];

    const uiContainer = document.createElement('div');
    uiContainer.style.position = 'fixed';
    uiContainer.style.top = '0px';
    uiContainer.style.right = '60px';
    uiContainer.style.padding = '0px';
    uiContainer.style.background = 'transparent';
    uiContainer.style.color = '#fff';
    uiContainer.style.cursor = 'pointer';
    uiContainer.style.zIndex = '9999';
    uiContainer.style.textAlign = 'left';
    uiContainer.style.width = '300px';
    document.body.appendChild(uiContainer);

    updateUIPanel();

    function addLogToData(log) {
        logData.push(log);
    }

    function toggleConsoleLogs() {
        consoleLogsActive = !consoleLogsActive;
        consoleLogsActive ? enableConsoleLogs() : disableConsoleLogs();
    }

    function enableConsoleLogs() {
        console.log = console._originalLog;
        console.warn = console._originalWarn;
        console.error = console._originalError;
    }

    function disableConsoleLogs() {
        console._originalLog = console.log;
        console._originalWarn = console.warn;
        console._originalError = console.error;

        console.log = function (...args) {
            const logMessage = args.join(' ');
            addLogToData(logMessage);
        };

        console.warn = function (...args) {
            const logMessage = args.join(' ');
            addLogToData(`Warning: ${logMessage}`);
        };

        console.error = function (...args) {
            const logMessage = args.join(' ');
            addLogToData(`Error: ${logMessage}`);
        };
    }

    function getStreamerUsernameFromURL() {
        const urlParts = window.location.href.split('/');
        const username = urlParts[urlParts.length - 1];
        console.log(`Streamer username extracted from URL: ${username}`);
        return username;
    }

    let viewerCount = 0;

    const fetchLivestreamViewerCount = () => {
        const apiUrl = `https://kick.com/api/v2/channels/${getStreamerUsernameFromURL()}`;

        GM_xmlhttpRequest({
            method: 'GET',
            url: apiUrl,
            responseType: 'json',
            onload: function (response) {
                if (response.status === 200) {
                    const responseData = JSON.parse(response.responseText);
                    const newViewerCount = responseData.livestream.viewer_count;

                    if (newViewerCount !== undefined) {
                        viewerCount = newViewerCount;
                        updateViewerCount();
                        console.log(`Viewer count: ${viewerCount}`);
                        viewerCountValue = viewerCount !== undefined
                            ? viewerCount
                            : '<span class="viewer_countUI" style="color: orange;">calc</span>';
                        updateUIPanel();
                    } else {
                        console.error('Failed to extract viewer count from API response.');
                    }
                } else {
                    console.error('Error fetching data from API. Status:', response.status);
                }
            },
            onerror: function (error) {
                console.error('Error making API request:', error);
            },
        });
    };

    function updateViewerCount() {
        const viewerCountElement = document.querySelector('.viewer_countUI');

        if (viewerCountElement) {
            viewerCountElement.innerHTML = viewerCount !== undefined
                ? viewerCount
                : '<span class="viewer_countUI" style="color: orange;">calc</span>';
            updateUIPanel();
        } else {
            console.error('Viewer count element not found. Unable to update viewer count.');
        }
    }

    function parseChatEntry(entryElement) {
        const currentTime = new Date().toLocaleTimeString();
        const entryId = entryElement.getAttribute('data-chat-entry');
        const usernameElement = entryElement.querySelector('.chat-entry-username');
        const contentElement = entryElement.querySelector('.chat-entry-content');

        if (entryId && usernameElement && contentElement) {
            const username = usernameElement.dataset.chatEntryUser;
            const content = contentElement.textContent.trim();

            const message = {
                timestamp: currentTime,
                entryId,
                username,
                content,
            };

            return message;
        }

        return null;
    }

function checkForNewChatEntries() {
    if (!consoleLogsActive) return;

    const chatEntries = document.querySelectorAll('[data-chat-entry]');
    const newChatters = new Set();
    const newMessages = new Set();

    chatEntries.forEach((entry) => {
        const chatEntry = parseChatEntry(entry);
        if (chatEntry && !userData.uniqueMessages.has(chatEntry.entryId)) {
            userData.uniqueMessages.add(chatEntry.entryId);
            newChatters.add(chatEntry.username);
            newMessages.add(chatEntry.entryId);
        }
    });

    console.log(`Number of unique messages stored: ${userData.uniqueMessages.size}`);

    const currentTime = new Date();
    const currentMinute = currentTime.getMinutes();

    if (!userData.chattersPerMinute.has(currentMinute)) {
        userData.chattersPerMinute.set(currentMinute, new Set());
    }

    const currentMinuteChatters = userData.chattersPerMinute.get(currentMinute);
    newChatters.forEach((username) => currentMinuteChatters.add(username));

    if (!userData.messagesPerMinute.has(currentMinute)) {
        userData.messagesPerMinute.set(currentMinute, new Set());
    }

    const currentMinuteMessages = userData.messagesPerMinute.get(currentMinute);
    newMessages.forEach((messageId) => currentMinuteMessages.add(messageId));

    console.log(`Chatters per minute: ${currentMinuteChatters.size}`);
    updateUIPanel();
}

    setInterval(checkForNewChatEntries, 1000);

function logChattersAndMessagesPerMinute() {
    if (!consoleLogsActive) return;

    const currentMinute = new Date().getMinutes();

    if (userData.chattersPerMinute.has(currentMinute)) {
        const currentMinuteChatters = userData.chattersPerMinute.get(currentMinute);
        userData.totalChatters.push(currentMinuteChatters.size);
    }

    if (userData.messagesPerMinute.has(currentMinute)) {
        const currentMinuteMessages = userData.messagesPerMinute.get(currentMinute);
        console.log(`Messages per minute (current minute): ${currentMinuteMessages.size}`);

        userData.totalMessages.push(currentMinuteMessages.size);
    }

    const averageChatters = calculateAverage(userData.totalChatters);
    console.log(`Average Chatters per minute: ${averageChatters !== undefined ? averageChatters : '<span style="color: orange;">calc</span>'}`);

    const averageMessages = calculateAverage(userData.totalMessages);
    console.log(`Average Messages per minute: ${averageMessages !== undefined ? averageMessages : '<span style="color: orange;">calc</span>'}`);

    console.log(`Total Chatters: ${userData.totalChatters.reduce((sum, chatters) => sum + chatters, 0)}`);
    console.log(`Average Chatters: ${averageChatters !== undefined ? averageChatters : '<span style="color: orange;">calc</span>'}`);
    console.log(`Total Messages: ${userData.totalMessages.reduce((sum, messages) => sum + messages, 0)}`);
    console.log(`Average Messages: ${averageMessages !== undefined ? averageMessages : '<span style="color: orange;">calc</span>'}`);

    requestAnimationFrame(updateUIPanel);
}


function shouldDisplayCalc(type) {
    const elapsedTime = (new Date() - startTime) / 1000; // Elapsed time in seconds

    // Display "calc" in orange for the first 60 seconds
    if (elapsedTime < 60) {
        return true;
    }

    // After 60 seconds, only display "calc" for the specified type
    switch (type) {
        case 'chatters':
            return userData.totalChatters.length === 0;
        // Add more cases for other types if needed

        default:
            return false;
    }
}

function updateUIPanel() {
    const chatEntries = document.querySelectorAll('[data-chat-entry]');

    // Check if there are any chat entries on the page
    const hasChatEntries = chatEntries.length > 0;

    // If there are no chat entries, hide the UI PANEL
    if (!hasChatEntries) {
        uiContainer.style.display = 'none';
        return;
    }

    uiContainer.style.display = ''; // Display the UI PANEL

    uiContainer.innerHTML = '';

    const tableStyle = `
        font-size: 10px;
        color: white;
        border-collapse: collapse;
        margin-bottom: 5px;
        width: 300px;
    `;

    const cellStyle = `
        border: none;
        padding: 2px;
        text-align: center;
        width: 100px;
    `;

    const numericalValueStyle = `color: #53FC18;`;

    const tableElement = document.createElement('table');
    tableElement.style = tableStyle;

    const row1 = tableElement.insertRow();
    const row2 = tableElement.insertRow();
    const row3 = tableElement.insertRow();

    const displayCalc = shouldDisplayCalc('chatters');

    const addInfo = (label, value, row) => {
        const labelCell = row.insertCell();
        const valueCell = row.insertCell();

        labelCell.style = cellStyle;
        labelCell.innerHTML = `<strong>${label}</strong>`;

        valueCell.style = `${cellStyle} ${numericalValueStyle}`;
        valueCell.innerHTML = displayCalc ? '<span style="color: orange;">calc</span>' : value;
    };

    addInfo('Chatters:', userData.totalChatters.reduce((sum, chatters) => sum + chatters, 0), row1);
    addInfo('per min:', calculateAverage(userData.totalChatters, true), row1);

    addInfo('Messages:', userData.uniqueMessages.size, row2);
    addInfo('per min:', calculateAverage(userData.totalMessages, true), row2);

    const labelCell = row3.insertCell();
    const valueCell = row3.insertCell();
    const labelCell2 = row3.insertCell();
    const elapsedTimeCell = row3.insertCell();

    labelCell.style = cellStyle;
    labelCell.innerHTML = '<strong> Viewers:</strong>';

    valueCell.style = `${cellStyle} ${numericalValueStyle}`;
    valueCell.innerHTML = viewerCountValue;

    labelCell2.style = cellStyle;
    labelCell2.innerHTML = '<strong> Timer:</strong>';

    elapsedTimeCell.style = `${cellStyle} ${numericalValueStyle}`;

    const elapsedTimeInSeconds = Math.floor((new Date() - startTime) / 1000);
    const hours = Math.floor(elapsedTimeInSeconds / 3600);
    const minutes = Math.floor((elapsedTimeInSeconds % 3600) / 60);
    const seconds = elapsedTimeInSeconds % 60;

    elapsedTimeCell.innerHTML = `${formatTime(hours)}h ${formatTime(minutes)}m ${formatTime(seconds)}s`;

    uiContainer.appendChild(tableElement);

    logData.forEach((log) => {
        if (!log.includes('Console Logs:')) {
            const logElement = document.createElement('div');
            logElement.style = tableStyle;
            logElement.textContent = log;
            uiContainer.appendChild(logElement);
        }
    });

    // Add an event listener to detect left mouse clicks on the uiContainer
    uiContainer.addEventListener('click', function(event) {
        if (event.button === 0) { // Check if it's a left mouse click
            // Copy the content of the uiContainer to the clipboard
            copyUIContentToClipboard();
        }
    });
}

// Helper function to format time components with leading zeros
function formatTime(time) {
    return time < 10 ? `0${time}` : `${time}`;
}

// Add this function to copy the content of the uiContainer to the clipboard
function copyUIContentToClipboard() {
    const streamerUsername = getStreamerUsernameFromURL();
    const capitalizedStreamerUsername = streamerUsername.charAt(0).toUpperCase() + streamerUsername.slice(1);

    const uiContent = `Streamer: ${capitalizedStreamerUsername}\n${uiContainer.innerText}`;
    GM_setClipboard(uiContent, 'text');

    // Get the current mouse position
    const mouseX = window.event.clientX;
    const mouseY = window.event.clientY;

    // Show confirmation window at the mouse pointer location
    const confirmationWindow = document.createElement('div');
    confirmationWindow.style.position = 'fixed';
    confirmationWindow.style.top = `${mouseY - 20}px`; // Slightly above the mouse pointer
    confirmationWindow.style.left = `${mouseX}px`;
    confirmationWindow.style.background = '#53FC18';
    confirmationWindow.style.color = 'black';
    confirmationWindow.style.padding = '5px';
    confirmationWindow.style.borderRadius = '5px'; // Border radius
    confirmationWindow.style.fontSize = '9px'; // Font size
    confirmationWindow.style.textTransform = 'uppercase'; // Text to uppercase
    confirmationWindow.style.fontWeight = 'bold'; // Bold text
    confirmationWindow.style.zIndex = '10000';
    confirmationWindow.textContent = 'Copied to clipboard';

    document.body.appendChild(confirmationWindow);

    // Remove confirmation window after a brief delay
    setTimeout(() => {
        document.body.removeChild(confirmationWindow);
    }, 1000); // Stay visible for 1 second
}

function calculateAverage(array, displayCalc) {
    if (array.length === 0) {
        return displayCalc ? '<span style="color: orange;">calc</span>' : 0;
    }
    const sum = array.reduce((acc, value) => acc + value, 0);
    const average = sum / array.length;
    return displayCalc ? Math.round(average) : average;
}



    function getChattersPerMinute() {
        const currentMinute = new Date().getMinutes();
        if (userData.chattersPerMinute.has(currentMinute)) {
            const currentMinuteChatters = userData.chattersPerMinute.get(currentMinute);
            return currentMinuteChatters.size;
        }
        return 0;
    }

    function addUIControls() {
        const controlsContainer = document.createElement('div');
        controlsContainer.style.marginTop = '5px';
        controlsContainer.style.textAlign = 'center';

        const toggleLogsButton = document.createElement('button');
        toggleLogsButton.textContent = 'Toggle Console Logs';
        toggleLogsButton.style.marginRight = '5px';
        toggleLogsButton.addEventListener('click', toggleConsoleLogs);

        const copyLogsButton = document.createElement('button');
        copyLogsButton.textContent = 'Copy Logs to Clipboard';
        copyLogsButton.addEventListener('click', copyLogsToClipboard);

        controlsContainer.appendChild(toggleLogsButton);
        controlsContainer.appendChild(copyLogsButton);

        uiContainer.appendChild(controlsContainer);
    }

    function copyLogsToClipboard() {
        const logsText = logData.join('\n');
        GM_setClipboard(logsText, 'text');
        console.log('Console logs copied to clipboard.');
    }

function checkChanges() {
    const currentPath = window.location.pathname;
    const currentUsername = getStreamerUsernameFromURL();

    if (currentPath !== initialPath || currentUsername !== initialUsername) {
        initialPath = currentPath;
        initialUsername = currentUsername;
        resetScript();
        fetchLivestreamViewerCount();
    }
}

function resetScript() {
    userData.uniqueMessages.clear();
    userData.chattersPerMinute.clear();
    userData.messagesPerMinute.clear();
    userData.totalChatters = [];
    userData.totalMessages = [];
    logData.length = 0;

    if (currentHttpRequest) {
        currentHttpRequest.abort();
    }

    viewerCount = 0;
    viewerCountValue = '<span class="viewer_countUI" style="color: orange;">calc</span>';
    updateUIPanel();
}

   setTimeout(() => {
        fetchLivestreamViewerCount();
    }, 500);

    const viewerCountInterval = setInterval(() => {
        fetchLivestreamViewerCount();
    }, 30000);

 const changesCheckInterval = setInterval(() => {
    checkChanges();
}, 1000);

    addUIControls();

    const apiUrl = `https://kick.com/api/v2/channels/${getStreamerUsernameFromURL()}`;
    fetchLivestreamViewerCount(apiUrl);

    setInterval(() => {
        fetchLivestreamViewerCount(apiUrl);
    }, 30000);

    setInterval(logChattersAndMessagesPerMinute, 60000);
})();