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.04
// @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: '55px',
    backgroundColor: '#24272C',
    textColor: '#fff',
  };

  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 = '275px';
  document.body.appendChild(uiContainer);

  let prevMessagesPerMinute = 0;
  let prevChattersPerMinute = 0;

  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: 275px; 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 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`;
  }

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();
    });

    return parseInt(actualViewerCount);
  } else {
    return 'N/A';
  }
}



  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();

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

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

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

})();

QingJ © 2025

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