您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Intercept WebSocket messages on StumbleChat and display them in a resizable and collapsible table overlay
当前为
// ==UserScript== // @name Stumblechat Stalker // @namespace http://tampermonkey.net/ // @version 1.03 // @description Intercept WebSocket messages on StumbleChat and display them in a resizable and collapsible table overlay // @author MeKLiN // @match https://stumblechat.com/room/* // @icon https://www.google.com/s2/favicons?sz=64&domain=stumblechat.com // @grant none // @license MIT // ==/UserScript== //revert // ==UserScript== // @name Stumblechat Stalker // @namespace http://tampermonkey.net/ // @version 1.02 // @description Intercept WebSocket messages on StumbleChat and display them in a resizable and collapsible table overlay // @author MeKLiN // @match https://stumblechat.com/room/* // @icon https://www.google.com/s2/favicons?sz=64&domain=stumblechat.com // @grant none // @license MIT (function() { 'use strict'; let pingCount = 0; let messageSections = {}; // Store sections by handle let handleMessages = {}; // Track handles and associated messages const reopenButton = document.createElement("button"); reopenButton.textContent = "Open WebSocket Log"; reopenButton.style.position = "fixed"; reopenButton.style.bottom = "10px"; reopenButton.style.left = "10px"; reopenButton.style.padding = "10px 15px"; reopenButton.style.backgroundColor = "#4CAF50"; reopenButton.style.color = "white"; reopenButton.style.border = "none"; reopenButton.style.cursor = "pointer"; reopenButton.style.display = "inline-block"; // Ensure the button is visible by default reopenButton.style.zIndex = "10000"; // High z-index to ensure visibility on top document.body.appendChild(reopenButton); function createWebSocketOverlay() { const overlay = document.createElement("div"); overlay.id = "webSocketOverlay"; overlay.style.position = "fixed"; overlay.style.top = "10%"; overlay.style.left = "10%"; overlay.style.fontSize = "12px"; overlay.style.width = "80%"; // Adjust to 80% width overlay.style.maxHeight = "80%"; // Adjust max height to 80% of the window overlay.style.backgroundColor = "rgba(0, 0, 0, 0.7)"; overlay.style.zIndex = "9999"; // Ensure overlay is just below the button overlay.style.display = "none"; // Hidden by default overlay.style.alignItems = "center"; overlay.style.justifyContent = "center"; overlay.style.padding = "10px"; overlay.style.resize = "both"; overlay.style.overflowY = "auto"; // Allow vertical scrolling within the overlay overlay.style.boxSizing = "border-box"; overlay.style.cursor = "move"; let offsetX, offsetY, isDragging = false; overlay.addEventListener('mousedown', (e) => { isDragging = true; offsetX = e.clientX - overlay.offsetLeft; offsetY = e.clientY - overlay.offsetTop; document.addEventListener('mousemove', dragOverlay); document.addEventListener('mouseup', () => { isDragging = false; document.removeEventListener('mousemove', dragOverlay); }); }); function dragOverlay(e) { if (isDragging) { overlay.style.left = (e.clientX - offsetX) + 'px'; overlay.style.top = (e.clientY - offsetY) + 'px'; } } const closeButton = document.createElement("button"); closeButton.textContent = "Close"; closeButton.style.position = "absolute"; closeButton.style.top = "10px"; closeButton.style.right = "10px"; closeButton.style.padding = "5px 10px"; closeButton.style.backgroundColor = "#ff4b5c"; closeButton.style.color = "white"; closeButton.style.border = "none"; closeButton.style.cursor = "pointer"; closeButton.addEventListener('click', () => { overlay.style.display = "none"; reopenButton.style.display = "inline-block"; // Show the reopen button again }); overlay.appendChild(closeButton); const table = document.createElement("table"); table.style.width = "100%"; table.style.backgroundColor = "white"; table.style.borderCollapse = "collapse"; overlay.appendChild(table); document.body.appendChild(overlay); return { overlay, table }; } function createCollapsibleSection(handle, username, symbol) { const section = document.createElement("tr"); const header = document.createElement("td"); header.colSpan = 2; header.style.padding = "10px"; header.style.cursor = "pointer"; header.style.backgroundColor = "#f2f2f2"; header.innerHTML = `<strong style="color: red;">${symbol}</strong> ${handle} (${username})`; header.addEventListener("click", () => { const contentRow = section.querySelector(".contentRow"); const contentCell = contentRow.querySelector("td"); if (contentRow.style.display === "none") { contentRow.style.display = "table-row"; // Scroll to the top of the overlay when opening a section const overlay = document.getElementById('webSocketOverlay'); overlay.scrollTop = 0; // Scroll to the top } else { contentRow.style.display = "none"; } }); section.appendChild(header); const contentRow = document.createElement("tr"); contentRow.classList.add("contentRow"); contentRow.style.display = "none"; const contentCell = document.createElement("td"); contentCell.colSpan = 2; contentCell.style.padding = "10px"; contentCell.style.whiteSpace = "normal"; contentCell.style.wordWrap = "break-word"; contentCell.style.overflowWrap = "break-word"; contentCell.style.maxWidth = "100%"; contentCell.style.overflowX = "auto"; // Allow horizontal scrolling when needed contentCell.style.border = "1px solid #ccc"; contentCell.style.backgroundColor = "#f9f9f9"; contentRow.appendChild(contentCell); section.appendChild(contentRow); return section; } function breakLongText(content) { // Split long strings into chunks and insert <br> after every 200 characters const maxLength = 200; let formattedContent = content; const regex = /.{1,200}(?:\s|$)/g; formattedContent = formattedContent.replace(regex, '$&<br>'); return formattedContent; } function getUserNicknameByHandle(handle) { const userList = document.querySelector("#userlist .list"); if (userList) { const userElement = userList.querySelector(`[user-id='${handle}']`); if (userElement) { const nickname = userElement.querySelector(".nickname"); return nickname ? nickname.textContent : "Unknown"; } } return "Unknown"; } function displayWebSocketMessage(message, table) { let data; try { data = JSON.parse(message); } catch (e) { // Handle non-JSON messages console.log('Non-JSON message received:', message); data = { handle: "unknown", stumble: "msg", content: message }; // Placeholder for non-JSON message } const handle = data.handle; const username = getUserNicknameByHandle(handle); let symbol = "🔵"; switch (data.stumble) { case "sysmsg": symbol = "🔴"; break; case "consume": symbol = "🟠"; break; case "connect": symbol = "🟣"; break; case "msg": symbol = "🔵"; // For general messages break; default: break; } // Create a section for each handle if it doesn't exist already let section; if (!handleMessages[handle]) { section = createCollapsibleSection(handle, username, symbol); table.appendChild(section); handleMessages[handle] = section; } else { section = handleMessages[handle]; } // Display the message content in the section const contentRow = section.querySelector(".contentRow td"); const contentText = breakLongText(message); contentRow.innerHTML += `<p>${contentText}</p>`; } const originalWebSocket = window.WebSocket; window.WebSocket = function(url, protocols) { console.log('WebSocket URL:', url); const ws = new originalWebSocket(url, protocols); const { overlay, table } = createWebSocketOverlay(); ws.addEventListener('message', event => { displayWebSocketMessage(event.data, table); }); reopenButton.addEventListener("click", () => { overlay.style.display = "flex"; reopenButton.style.display = "none"; // Hide the reopen button once overlay is open }); return ws; }; })();