Kick.com Combined Chatter and Message Counter

Kick.com Combined Chatter and Message Counter - TRUTH EDITION

目前为 2024-01-10 提交的版本。查看 最新版本

// ==UserScript==
// @name     Kick.com Combined Chatter and Message Counter
// @version  3.06
// @description Kick.com Combined Chatter and Message Counter - TRUTH EDITION
// @author   Graph1ks
// @match    https://kick.com/*
// @grant    none
// @namespace https://gf.qytechs.cn/users/1235079
// ==/UserScript==

(function () {
  'use strict';

  const config = {
    interval: 5000,
    watchTimeUpdateInterval: 1000,
    topPosition: '0px',
    rightPosition: '60px',
    backgroundColor: '#24272C',
    textColor: '#fff',
    logging: false, // Set to false to deactivate logging
    clearIdsInterval: 150000, // Clear unique IDs every 2.5 minutes (in milliseconds)
  };

  let uniqueUserIds = {};
  let uniqueMessageIds = {};
  let totalMessages = 0;
  let totalChatters = 0;
  let messagesPerMinute = 0;
  let chattersPerMinute = 0;
  let scriptStartTime = Date.now();
  let last60SecondsMessages = 0;
  let last60SecondsChatters = 0;
  let userIdTimestamps = {};
  let currentStreamSrc = null;
  let pageURL = window.location.href;
  let streamerName = extractStreamerNameFromURL();

  const uiContainer = document.createElement('div');
  uiContainer.style.position = 'fixed';
  uiContainer.style.top = config.topPosition;
  uiContainer.style.right = config.rightPosition;
  uiContainer.style.padding = '0px';
  uiContainer.style.background = config.backgroundColor;
  uiContainer.style.color = config.textColor;
  uiContainer.style.cursor = 'pointer';
  uiContainer.style.zIndex = '9999';
  uiContainer.style.textAlign = 'left';
  uiContainer.style.width = '285px';
  document.body.appendChild(uiContainer);

  let prevMessagesPerMinute = 0;
  let prevChattersPerMinute = 0;

  function log(message) {
    if (config.logging) {
      console.log(message);
    }
  }

  function updateWatchTime() {
    const watchTimeElement = document.getElementById('watchTime');

    if (watchTimeElement) {
      watchTimeElement.textContent = formatTimeElapsed(scriptStartTime);
    }
  }

  function updateUI() {
    if (isChatAvailable()) {
      uiContainer.style.display = 'block';
      uiContainer.innerHTML = `
        <div style="font-size: 10px; width: 285px; text-align: left;">
          <table style="width:100%; table-layout: fixed; border-collapse: collapse;">
            <colgroup>
              <col style="width: 30%;" />
              <col style="width: 20%;" />
              <col style="width: 25%;" />
              <col style="width: 25%;" />
            </colgroup>
            <tr>
              <td style="height: 14px;">Unique Chatters:</td>
              <td style="height: 14px;"><span style="color: #FFA500;">${totalChatters}</span></td>
              <td style="height: 14px;">per min:</td>
              <td style="height: 14px;">${getColoredText(chattersPerMinute, prevChattersPerMinute)}</td>
            </tr>
            <tr>
              <td style="height: 14px;">Messages:</td>
              <td style="height: 14px;"><span style="color: #FFA500;">${totalMessages}</span></td>
              <td style="height: 14px;">per min:</td>
              <td style="height: 14px;">${getColoredText(messagesPerMinute, prevMessagesPerMinute)}</td>
            </tr>
            <tr>
              <td style="height: 14px;">Watch Time:</td>
              <td style="height: 14px;" id="watchTime">${formatTimeElapsed(scriptStartTime)}</td>
              <td style="height: 14px;">~Viewers:</td>
              <td style="height: 14px;">${getViewerCount()}</td>
            </tr>
          </table>
        </div>
      `;

      prevMessagesPerMinute = messagesPerMinute;
      prevChattersPerMinute = chattersPerMinute;

      if (Math.floor((Date.now() - scriptStartTime) / config.interval) % 2 === 0) {
        displayDifference();
      }
    } else {
      uiContainer.style.display = 'none';
    }
  }

  function getColoredText(value, previousValue) {
    const difference = value - previousValue;
    const color = difference > 0 ? '#00FF00' : difference < 0 ? '#FF0000' : '#FFA500';
    const indicatorColor = difference > 0 ? '#00FF00' : difference < 0 ? '#FF0000' : '';

    const indicator = difference !== 0 ? `<span style="color: ${indicatorColor};">${getIndicatorSymbol(difference)} (${difference > 0 ? '+' : ''}${Math.abs(difference)})</span>` : '';

    return `<span style="color: ${color};">${value} ${indicator}</span>`;
  }

  function getIndicatorSymbol(difference) {
    if (difference > 0) {
      return '▲';
    } else if (difference < 0) {
      return '▼';
    } else {
      return '';
    }
  }

  function calculateDifference(currentValue, prevValue) {
    const difference = currentValue - prevValue;
    const indicator = difference > 0 ? '+' : difference < 0 ? '-' : '';
    return `${indicator}${Math.abs(difference)}`;
  }

  function displayDifference() {
    const chattersDifference = chattersPerMinute - prevChattersPerMinute;
    const messagesDifference = messagesPerMinute - prevMessagesPerMinute;

    const chattersIndicator = getIndicator(chattersDifference);
    const messagesIndicator = getIndicator(messagesDifference);

    uiContainer.innerHTML += `<div style="font-size: 10px; margin-top: 5px;">${chattersIndicator} ${messagesIndicator}</div>`;
  }

  function getIndicator(difference) {
    const color = difference > 0 ? '#00FF00' : difference < 0 ? '#FF0000' : '';
    const indicator = difference !== 0 ? `<span style="color: ${color};">${difference > 0 ? '+' : ''}${difference}</span>` : '';

    return indicator;
  }

  function countUniqueUserIdsAndMessages() {
    const userIdElements = document.querySelectorAll('[data-chat-entry-user-id]');
    const messageElements = document.querySelectorAll('[data-chat-entry]');

    const currentTime = Date.now();
    const elapsedMilliseconds = currentTime - scriptStartTime;

    userIdElements.forEach((element) => {
      const userId = element.getAttribute('data-chat-entry-user-id');
      uniqueUserIds[userId] = currentTime;
    });

    messageElements.forEach((element) => {
      const messageId = element.getAttribute('data-chat-entry');
uniqueMessageIds[messageId] = true;
    });

    totalMessages = Object.keys(uniqueMessageIds).length;
    totalChatters = Object.keys(uniqueUserIds).length;

    const elapsedSeconds = elapsedMilliseconds / 1000;

    const last60SecondsMessagesCount = Math.max(totalMessages - last60SecondsMessages, 0);
    const last60SecondsChattersCount = Math.max(totalChatters - last60SecondsChatters, 0);

    let currentMinuteMessages = 0;
    let currentMinuteChatters = 0;

    const currentMinuteStartTimestamp = currentTime - (currentTime % 60000);

    for (const userId in uniqueUserIds) {
      const lastChatTime = uniqueUserIds[userId];
      const currentMinute = Math.floor((currentMinuteStartTimestamp - lastChatTime) / 60000);

      if (currentMinute === 0) {
        currentMinuteMessages++;
        currentMinuteChatters++;
      }
    }

    messagesPerMinute = Math.round((last60SecondsMessagesCount + currentMinuteMessages) / Math.max(elapsedSeconds / 60, 1));
    chattersPerMinute = Math.round((last60SecondsChattersCount + currentMinuteChatters) / Math.max(elapsedSeconds / 60, 1));

    updateUI();
  }

  function checkStreamChange() {
    const videoElement = document.querySelector('video.vjs-tech');
    if (videoElement) {
      const newStreamSrc = videoElement.getAttribute('src');
      const newPageURL = window.location.href;

      if (newStreamSrc !== currentStreamSrc || newPageURL !== pageURL) {
        resetScript();
        currentStreamSrc = newStreamSrc;
        pageURL = newPageURL;
      }
    }
  }

  function resetScript() {
    uniqueUserIds = {};
    uniqueMessageIds = {};
    totalMessages = 0;
    scriptStartTime = Date.now();
    userIdTimestamps = {};
    streamerName = extractStreamerNameFromURL();
  }

function clearIds() {
  const currentTime = Date.now();

  // Clear expired user IDs
  for (const userId in uniqueUserIds) {
    if (currentTime - uniqueUserIds[userId] > config.clearIdsInterval) {
      delete uniqueUserIds[userId];
    }
  }

  // Clear expired message IDs
  for (const messageId in uniqueMessageIds) {
    if (currentTime - uniqueMessageIds[messageId] > config.clearIdsInterval) {
      delete uniqueMessageIds[messageId];
    }
  }

  log(`Cleared IDs. Remaining: Users - ${Object.keys(uniqueUserIds).length} | Messages - ${Object.keys(uniqueMessageIds).length}`);
}


  function isChatAvailable() {
    return document.querySelector('[data-chat-entry]') !== null;
  }

  function extractStreamerNameFromURL() {
    const urlParts = window.location.href.split('/');
    const streamerName = urlParts[urlParts.length - 1];
    return streamerName.charAt(0).toUpperCase() + streamerName.slice(1);
  }

  function formatTimeElapsed(startTime) {
    const elapsedMilliseconds = Date.now() - startTime;
    const seconds = Math.floor(elapsedMilliseconds / 1000);
    const hours = Math.floor(seconds / 3600);
    const minutes = Math.floor((seconds % 3600) / 60);
    const remainingSeconds = seconds % 60;

    return `${hours}h ${minutes}m ${remainingSeconds}s`;
  }

  // Add these variables at the beginning of your script
  let previousViewerCount = 'N/A';
  let invalidViewerCountCount = 0;

  // Update the getViewerCount function
  function getViewerCount() {
    const viewerCountElement = document.querySelector('span.odometer');

    if (viewerCountElement) {
      const viewerDigits = viewerCountElement.querySelectorAll('.odometer-value');
      let actualViewerCount = '';

      viewerDigits.forEach((digit) => {
        actualViewerCount += digit.textContent.trim();
      });

      const parsedViewerCount = parseInt(actualViewerCount);

      // Check if the viewer count has a reasonable number of digits (6 or less)
      if (!isNaN(parsedViewerCount) && parsedViewerCount.toString().length <= 6) {
        previousViewerCount = parsedViewerCount;
        invalidViewerCountCount = 0;
        return parsedViewerCount;
      } else {
        // Increment the count of consecutive invalid viewer counts
        invalidViewerCountCount++;

        // If the count reaches a threshold, reset the previousViewerCount to 'N/A'
        if (invalidViewerCountCount >= 3) {
          previousViewerCount = 'N/A';
        }

        return previousViewerCount;
      }
    } else {
      return previousViewerCount;
    }
  }

  uiContainer.addEventListener('click', copyToClipboard);

  function copyToClipboard() {
    const viewerCount = getViewerCount();

    const textToCopy = `Streamer: ${streamerName}\nUnique Chatters: ${totalChatters} | per min: ${chattersPerMinute}\nMessages: ${totalMessages} | per min: ${messagesPerMinute}\nWatch Time: ${formatTimeElapsed(scriptStartTime)}\n~Viewers: ${viewerCount}`;

    navigator.clipboard.writeText(textToCopy).then(() => {
      uiContainer.innerHTML += '<div style="font-size: 12px; color: #0f0; margin-top: 5px;">Copied to clipboard!</div>';

      setTimeout(() => {
        uiContainer.innerHTML = uiContainer.innerHTML.replace(/<div style="font-size: 12px; color: #0f0; margin-top: 5px;">Copied to clipboard!<\/div>/, '');
      }, 1000);
    });
  }

  document.addEventListener('chatMessageReceived', (event) => {
    const userId = event.detail.userId;
    userIdTimestamps[userId] = Date.now();
    countUniqueUserIdsAndMessages();
  });

  countUniqueUserIdsAndMessages();
  checkStreamChange();

function logCurrentIdCounts() {
  const currentUserIdsCount = Object.keys(uniqueUserIds).length;
  const currentMessageIdsCount = Object.keys(uniqueMessageIds).length;

  log(`Current IDs in Memory: Users - ${currentUserIdsCount} | Messages - ${currentMessageIdsCount}`);
}

setInterval(() => {
  logCurrentIdCounts();
}, 60000); // Log every minute

  setInterval(() => {
    countUniqueUserIdsAndMessages();
    checkStreamChange();
  }, config.interval);

  setInterval(() => {
    if (Math.floor((Date.now() - scriptStartTime) / config.interval) % 2 === 0) {
      displayDifference();
    }
  }, config.interval);

  setInterval(() => {
    updateWatchTime();
  }, config.watchTimeUpdateInterval);

  setInterval(() => {
    clearIds();
  }, config.clearIdsInterval);

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址