Kick.com Fullscreen Chat Overlay

Enhances the Kick.com viewing experience by providing a fullscreen chat overlay. Messages will flow from right to left, allowing for a seamless chat experience while watching content.

目前為 2023-10-01 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Kick.com Fullscreen Chat Overlay
// @namespace    Violentmonkey Scripts
// @match        *://*.kick.com/*
// @grant        none
// @version      0.1.3
// @author       spaghetto.be
// @description  Enhances the Kick.com viewing experience by providing a fullscreen chat overlay. Messages will flow from right to left, allowing for a seamless chat experience while watching content.
// @icon         https://s2.googleusercontent.com/s2/favicons?domain=kick.com&sz=32
// @license MI
// ==/UserScript==
window.onload = function() {
    // Initialize variables to track displayed messages and message queue
    const displayedMessages = {}; // Keeps track of messages displayed to avoid duplicates
    const lastPositionPerRow = []; // Keeps track of the last position of messages in each row
    const messageQueue = []; // Holds incoming messages

    // Generate a unique key based on sender ID and content
    function getMessageKey(key, value) {
        return key + "|" + value;
    }

    // Function to process message queue and append messages with a delay
    let queueRunning = false;

    function processMessageQueue() {
        if (messageQueue.length > 0 && queueRunning === false) {
            queueRunning = true;
            const data = messageQueue.shift();
            const eventType = data.event;

            try {
                if (eventType === "App\\Events\\ChatMessageEvent") {
                    createMessage(data.data);
                } else if (data.type === "message") {
                    createMessage(data);
                } else if (eventType === "App\\Events\\UserBannedEvent") {
                    createUserBanMessage(data.data);
                } else if (eventType === "App\\Events\\GiftedSubscriptionsEvent") {
                    createGiftedMessage(data.data);
                } else if (eventType === "App\\Events\\FollowersUpdated") {
                    createFollowersMessage(data.data);
                }

            } catch (error) {
                console.error("Error parsing message data: ", error);
            }

            let wait = 100 * (40 / messageQueue.length);
            if (messageQueue.length < 3) {
                wait = 2000;
            }

            setTimeout(function() {
                queueRunning = false;
                processMessageQueue();
            }, (wait));

        }
    }

    // Function to select the appropriate row for a new message
    function selectRow(messageContainer, messageWidth) {
        let selectedRow = 0;

        if (lastPositionPerRow.length > 0) {
            for (let i = 0; i < lastPositionPerRow.length; i++) {
                const lastMessage = lastPositionPerRow[i];
                if ((document.getElementById("chat-messages").offsetWidth * 2) - (lastMessage.offsetLeft + lastMessage.clientWidth + 20) >= messageWidth) {
                    selectedRow = i;
                    break;
                }
                selectedRow = i + 1;
            }
        }

        lastPositionPerRow[selectedRow] = messageContainer;
        return selectedRow;
    }

    // Function to append a message to the chat overlay
    function appendMessage(messageKey, messageContent) {
        if (displayedMessages[messageKey]) {
            return; // Ignore duplicate message
        }

        displayedMessages[messageKey] = true;

        const messageContainer = document.createElement("div");
        messageContainer.classList.add("chat-message", "chat-entry");

        messageContainer.innerHTML = messageContent;
        chatMessages.appendChild(messageContainer);

        const messageWidth = messageContainer.clientWidth;
        const messageHeight = messageContainer.clientHeight;

        let selectedRow = selectRow(messageContainer, messageWidth);
        const topPosition = selectedRow * (messageHeight + 2);

        messageContainer.style.top = topPosition + 2 + 'px';
        messageContainer.style.animation = "slide 15s linear";

        messageContainer.addEventListener("animationend", function() {
            chatMessages.removeChild(messageContainer);
            delete displayedMessages[messageKey];
        });
    }

    // Function to append a ban message to the chat overlay
    function createUserBanMessage(data) {
        const bannedUser = data.user.username;
        const messageKey = getMessageKey('-ban-', bannedUser);

        const banMessageContent = `
            <div class="chat-message-content">
                <span style="color:#FF0000">${bannedUser} 🚫 banned by 🚫 ${data.banned_by.username}</span>
            </div>
        `;

        appendMessage(messageKey, banMessageContent);
    }

    // Function to append a gifted message to the chat overlay
    function createGiftedMessage(data) {
        const gifterUsername = data.gifter_username;
        const giftedUsernames = data.gifted_usernames;
        const messageKey = getMessageKey('-gift-', gifterUsername + giftedUsernames[0]);

        const giftedContent = `
        <div class="chat-message-content">
            <span style="color:#00FF00">🎉 ${giftedUsernames.length} Subscriptions Gifted by ${gifterUsername}</span>
        </div>
    `;

        appendMessage(messageKey, giftedContent);
    }

    // Function to append a followers message to the chat overlay
    let lastFollowersCount = null; // Keeps track of the last followers count
    function createFollowersMessage(data) {
        const followersCount = data.followersCount;
        const messageKey = getMessageKey('-followers-', followersCount);

        if (lastFollowersCount !== null) {
            const followersDiff = followersCount - lastFollowersCount;
            if (followersDiff === 0) {
                return;
            }
            const messageKey = getMessageKey('-followers-', followersCount);
            const messageContent = `
            <div class="chat-message-content">
                🎉 Followers gained: ${followersDiff}
            </div>
        `;
            appendMessage(messageKey, messageContent);
        }

        lastFollowersCount = followersCount;
    }

    // Function to create a message and handle emotes
    function createMessage(data) {
        const sender = data.sender;
        const username = sender.username;
        const color = sender.identity.color;
        const content = data.content;
        const messageKey = getMessageKey(data.sender.id, data.content);

        // Replace [emote:ID:Name] with emoji image
        const replacedContent = content.replace(
            /\[emote:(\d+):(\w+)\]/g,
            (match, id, name) => {
                return `<img src="https://files.kick.com/emotes/${id}/fullsize" alt="${name}" class="emote-image" style="width: 20px; height: 20px;" />`;
            }
        );

        const messageContent = `
            <div class="chat-message-content">
                <span style="color:${color}" class="chat-message-identity">
                    ${username}:
                </span>
                <span style="color:#ffffff" class="chat-entry-content">${replacedContent}</span>
            </div>
        `;

        appendMessage(messageKey, messageContent);
    }

    // Function to initialize the chat overlay
    function initializeChat() {
        const existingSocket = window.Echo.connector.pusher;

        if (existingSocket) {
            // Create and append chat overlay elements
            const chatOverlay = document.createElement("div");
            chatOverlay.id = "chat-overlay";
            chatOverlay.innerHTML = `
                <div id="chat-messages"></div>
            `;

            // Append the chat container above the video player
            const videoPlayer = document.querySelector("video");
            videoPlayer.parentNode.insertBefore(chatOverlay, videoPlayer);

            // Add styles for the chat overlay
            const chatOverlayStyles = document.createElement("style");
            chatOverlayStyles.textContent = `
                #chat-overlay {
                    position: absolute;
                    top: 0;
                    left: 0;
                    width: 100%;
                    height: 100%;
                    pointer-events: none;
                    overflow: hidden;
                    z-index: 9999;
                }

                #chat-messages {
                    display: flex;
                    flex-direction: column-reverse;
                    align-items: flex-end;
                    height: 100%;
                    overflow-y: auto;
                }

                .chat-message {
                    position: absolute;
                    background-color: rgba(34, 34, 34, 0.5);
                    border-radius: 10px;
                    white-space: nowrap;
                    max-width: calc(100% - 20px);
                    overflow: hidden;
                    text-overflow: ellipsis;
                    height: 1rem;
                }

                .emote-image {
                    display: inline !important;
                    margin-right: 5px;
                }

                @keyframes slide {
                    0% {
                        right: -100%;
                    }

                    100% {
                        right: 100%;
                    }
                }
            `;
            document.head.appendChild(chatOverlayStyles);
            chatMessages = document.getElementById("chat-messages");

            // Handle incoming chat messages
            function handleChatMessageEvent(data) {
                messageQueue.push(data);
                processMessageQueue();
            }

            existingSocket.connection.bind("message", handleChatMessageEvent);
        } else {
            // Wait and try again after a short delay
            setTimeout(initializeChat, 1000);
        }
    }

    // Function to handle page changes and initialize chat
    function handlePageChange() {
        queueRunning = false;

        // Use a MutationObserver to watch for changes in the DOM
        const observer = new MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
                if (mutation.addedNodes) {
                    mutation.addedNodes.forEach(function(node) {
                        if (node.nodeName.toLowerCase() === "video") {
                            observer.disconnect();
                            initializeChat();

                            // Check if the page is a video page
                            if (window.location.href.includes("/video/")) {
                                // Use XMLHttpRequest code
                                let open = window.XMLHttpRequest.prototype.open;
                                window.XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
                                    this.addEventListener("load", function() {
                                        if (url.includes("/api/v2/channels/") && url.includes("/messages")) {
                                            // Process response
                                            const response = JSON.parse(this.responseText);
                                            if (response.data && response.data.messages) {
                                                response.data.messages.forEach(function(message) {
                                                    messageQueue.push(message);
                                                });
                                            }
                                            processMessageQueue();
                                        }
                                    }, false);
                                    open.apply(this, arguments);
                                };
                            }
                        }
                    });
                }
            });
        });

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

    // Attach the handlePageChange function to onpopstate event
    window.onpopstate = handlePageChange;

    // Call handlePageChange to initialize chat on page load
    handlePageChange();
};

QingJ © 2025

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