您需要先安装一个扩展,例如 篡改猴、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.072 // @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 overlayVisible = true; // Flag to track overlay visibility let userMap = {}; // Maps handles to user info (username, nick) let selfHandle = null; // Store the handle of the current user // Function to update userMap based on "joined", "join", or "quit" events function updateUserMap(message) { if (message.stumble === "join" || message.stumble === "joined") { const { handle, username, nick, userlist } = message; if (userlist) { userlist.forEach(user => { if (user.handle && user.username) { userMap[user.handle] = { username: user.username, nick: user.nick }; } }); } if (handle && username) { userMap[handle] = { username, nick }; } if (handle && !selfHandle) { selfHandle = handle; // Store the user's handle if it's the first "joined" } } else if (message.stumble === "quit") { const { handle } = message; if (handle) { delete userMap[handle]; } } } // Function to display the user list in the joinTab function displayUserList() { const joinTab = document.getElementById('joinTab'); joinTab.innerHTML = ''; // Clear previous content Object.keys(userMap).forEach(handle => { const user = userMap[handle]; const userButton = document.createElement('button'); userButton.innerText = `${user.username} (${handle} / ${user.nick})`; joinTab.appendChild(userButton); }); } const originalWebSocket = window.WebSocket; window.WebSocket = function(url, protocols) { const ws = new originalWebSocket(url, protocols); ws.addEventListener('message', event => { try { const message = JSON.parse(event.data); if (["join", "joined", "quit"].includes(message.stumble)) { updateUserMap(message); displayUserList(); } else if (message.stumble === "msg") { displayWebSocketMessage(message); // Handle message event } } catch (error) { console.error("Error processing WebSocket message:", error); } }); return ws; }; // Create the overlay container const overlayDiv = document.createElement("div"); overlayDiv.id = "overlayContainer"; Object.assign(overlayDiv.style, { position: "fixed", top: "0", left: "0", zIndex: "9999", backgroundColor: "#222", color: "#fff", padding: "10px", borderRadius: "8px", maxWidth: "400px", maxHeight: "80vh", overflowY: "auto", display: "flex", flexDirection: "column", resize: "both", visibility: "visible" }); // Create the toggle button (separate from the overlay) const toggleButton = document.createElement("button"); toggleButton.innerHTML = "+"; Object.assign(toggleButton.style, { position: "fixed", top: "0px", left: "0px", zIndex: "10001", // Ensure it's on top of everything marginBottom: "10px", padding: "8px", cursor: "pointer", }); toggleButton.onclick = () => { overlayDiv.style.display = overlayVisible ? "none" : "flex"; overlayVisible = !overlayVisible; toggleButton.innerHTML = overlayVisible ? "-" : "+"; }; document.body.appendChild(toggleButton); // Add button to the body // Add the overlay content (tabs) const tabNames = ["msg", "join"]; // Reordered 'msg' to be first tabNames.forEach(tab => { const tabButton = document.createElement("button"); tabButton.innerHTML = tab.charAt(0).toUpperCase() + tab.slice(1); Object.assign(tabButton.style, { backgroundColor: "#444", border: "none", padding: "10px", margin: "2px", cursor: "pointer" }); tabButton.onclick = () => showTab(tab); overlayDiv.appendChild(tabButton); }); // Add the tab content const tabContentDivs = ["msg", "join"]; // Reordered 'msg' to be first tabContentDivs.forEach(tab => { const tabDiv = document.createElement("div"); tabDiv.id = `${tab}Tab`; Object.assign(tabDiv.style, { display: "none", padding: "10px", border: "1px solid #555", borderRadius: "5px", marginTop: "5px", maxHeight: "60vh", overflowY: "auto" }); overlayDiv.appendChild(tabDiv); }); document.body.appendChild(overlayDiv); // Function to display WebSocket messages in the "msg" tab function displayWebSocketMessage(message) { const { handle, text } = message; const username = userMap[handle] ? userMap[handle].username : handle; // Use handle if user not in map // Display the message in the "msg" tab const msgTab = document.getElementById("msgTab"); const messageDiv = document.createElement("div"); messageDiv.textContent = `${username}: ${text}`; msgTab.appendChild(messageDiv); // Scroll to the bottom of the messages div msgTab.scrollTop = msgTab.scrollHeight; } // Function to show the selected tab (join or msg) function showTab(tab) { tabContentDivs.forEach(tabName => { document.getElementById(`${tabName}Tab`).style.display = tabName === tab ? "block" : "none"; }); } showTab("join"); // Initially show the "join" tab })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址