您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Intercept WebSocket messages on StumbleChat and display them in tabs, sorted by sender's handle, with minimize and resize functionality
当前为
// ==UserScript== // @name Stumblechat Stalker // @namespace http://tampermonkey.net/ // @version 1.04 // @description Intercept WebSocket messages on StumbleChat and display them in tabs, sorted by sender's handle, with minimize and resize functionality // @author MeKLiN // @match https://stumblechat.com/room/* // @icon https://www.google.com/s2/favicons?sz=64&domain=stumblechat.com // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; let userMap = {}; // Store user data with handle as key and { username, nick } as value let overlayVisible = true; // Flag to track overlay visibility // Create a function to update the userMap based on join/quit events function updateUserMap(message) { if (message.stumble === "joined" || message.stumble === "join") { message.userlist.forEach(user => { userMap[user.handle] = { username: user.username || "Unknown", nick: user.nick || "Unknown" }; }); } else if (message.stumble === "quit") { // Remove the user from the map when they quit delete userMap[message.handle]; } } // Function to display WebSocket message under the appropriate tab function displayWebSocketMessage(message, tab) { const tabDiv = document.getElementById(tab); if (tabDiv) { // Group by sender's handle let senderDiv = document.getElementById(message.handle); if (!senderDiv) { senderDiv = document.createElement("div"); senderDiv.id = message.handle; // Retrieve the username from the user map const user = userMap[message.handle] || { username: "Unknown", nick: "Unknown" }; // Create the user's tab button const userTabButton = document.createElement("button"); userTabButton.innerText = `${user.username} (${message.handle} / ${user.nick})`; // Username, handle, and nick userTabButton.onclick = () => { // Toggle visibility of the user's message div if (senderDiv.style.display === "none") { senderDiv.style.display = "block"; } else { senderDiv.style.display = "none"; } }; // Add the button to the tab tabDiv.appendChild(userTabButton); // Create the user's message container const senderHeader = document.createElement("h4"); senderHeader.innerText = `${user.username} (${message.handle} / ${user.nick})`; senderDiv.appendChild(senderHeader); senderDiv.style.display = "none"; // Initially hidden // Add the message container to the tab tabDiv.appendChild(senderDiv); } const messageDiv = document.createElement("div"); messageDiv.innerHTML = message.text; // Show the message content senderDiv.appendChild(messageDiv); tabDiv.scrollTop = tabDiv.scrollHeight; // Auto-scroll } } // Override WebSocket constructor to intercept WebSocket creation const originalWebSocket = window.WebSocket; window.WebSocket = function(url, protocols) { console.log('WebSocket URL:', url); // Call original WebSocket constructor const ws = new originalWebSocket(url, protocols); // Event listener for receiving messages ws.addEventListener('message', event => { try { const message = JSON.parse(event.data); // Update userMap when users join or quit if (message.stumble === "join" || message.stumble === "joined" || message.stumble === "quit") { updateUserMap(message); } // Filter messages by the "stumble" type switch (message.stumble) { case "join": case "joined": displayWebSocketMessage(message, "joinTab"); break; case "msg": displayWebSocketMessage(message, "msgTab"); break; default: console.log("Unhandled message type:", message.stumble); break; } } catch (error) { console.error("Error processing WebSocket message:", error); } }); return ws; }; // Method to create the overlay container with tabs function createWebSocketTabs() { const overlayDiv = document.createElement("div"); overlayDiv.id = "overlayContainer"; overlayDiv.style.position = "fixed"; overlayDiv.style.top = "0"; overlayDiv.style.left = "0"; overlayDiv.style.zIndex = "9999"; overlayDiv.style.backgroundColor = "#222"; overlayDiv.style.color = "#fff"; overlayDiv.style.padding = "10px"; overlayDiv.style.borderRadius = "8px"; overlayDiv.style.maxWidth = "400px"; overlayDiv.style.maxHeight = "80vh"; overlayDiv.style.overflowY = "auto"; overlayDiv.style.display = "flex"; overlayDiv.style.flexDirection = "column"; overlayDiv.style.justifyContent = "flex-start"; overlayDiv.style.resize = "both"; overlayDiv.style.overflow = "hidden"; overlayDiv.style.visibility = "visible"; // Ensures the overlay is visible initially // Create a collapse/restore button const toggleButton = document.createElement("button"); toggleButton.innerHTML = "Collapse/Expand"; toggleButton.style.marginBottom = "10px"; toggleButton.style.padding = "8px"; toggleButton.style.cursor = "pointer"; toggleButton.style.zIndex = "10000"; // Set z-index to 10000 toggleButton.onclick = () => { // Toggle the overlay visibility based on the flag if (overlayVisible) { overlayDiv.style.display = "none"; // Hide overlay overlayVisible = false; } else { overlayDiv.style.display = "flex"; // Show overlay overlayVisible = true; } }; overlayDiv.appendChild(toggleButton); // Create Tabs const tabNames = ["join", "msg"]; tabNames.forEach(tab => { const tabButton = document.createElement("button"); tabButton.innerHTML = tab.charAt(0).toUpperCase() + tab.slice(1); tabButton.style.backgroundColor = "#444"; tabButton.style.border = "none"; tabButton.style.padding = "10px"; tabButton.style.margin = "2px"; tabButton.style.cursor = "pointer"; tabButton.onclick = () => { showTab(tab); // Show the clicked tab }; overlayDiv.appendChild(tabButton); }); // Create divs for each tab const tabContentDivs = ["join", "msg"]; tabContentDivs.forEach(tab => { const tabDiv = document.createElement("div"); tabDiv.id = tab + "Tab"; tabDiv.style.display = "none"; tabDiv.style.padding = "10px"; tabDiv.style.border = "1px solid #555"; tabDiv.style.borderRadius = "5px"; tabDiv.style.marginTop = "5px"; tabDiv.style.maxHeight = "60vh"; tabDiv.style.overflowY = "auto"; overlayDiv.appendChild(tabDiv); }); // Append the overlay to the body document.body.appendChild(overlayDiv); // Function to show the selected tab function showTab(tab) { tabContentDivs.forEach(tabName => { const tabDiv = document.getElementById(tabName + "Tab"); tabDiv.style.display = tabName === tab ? "block" : "none"; }); } // Default to showing the "join" tab showTab("join"); } // Call the functions to create the WebSocket tabs createWebSocketTabs(); })();