您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
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.
当前为
// ==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或关注我们的公众号极客氢云获取最新地址