// ==UserScript==
// @name [MWI] WebSocket 双向日志查看器
// @namespace http://tampermonkey.net/
// @version 1.0.0
// @description 显示 WebSocket 收发的 JSON 数据,并支持手动发包和屏蔽指定类型
// @author XIxixi297
// @license MIT
// @match https://www.milkywayidle.com/*
// @match https://test.milkywayidle.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=milkywayidle.com
// @grant none
// @run-at document-start
// ==/UserScript==
(function () {
'use strict';
// 全局 WebSocket 实例存储
window.wsInstances = [];
window.currentWS = null;
// 屏蔽列表 - 存储需要屏蔽的 type,支持持久化
const STORAGE_KEY = 'ws_blocked_types';
// 从localStorage加载屏蔽列表
function loadBlockedTypes() {
try {
const stored = localStorage.getItem(STORAGE_KEY);
return stored ? new Set(JSON.parse(stored)) : new Set();
} catch (e) {
console.warn('加载屏蔽列表失败,使用空列表:', e);
return new Set();
}
}
// 保存屏蔽列表到localStorage
function saveBlockedTypes() {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(Array.from(window.wsBlockedTypes)));
} catch (e) {
console.error('保存屏蔽列表失败:', e);
}
}
window.wsBlockedTypes = loadBlockedTypes();
const OriginalWebSocket = window.WebSocket;
window.WebSocket = new Proxy(OriginalWebSocket, {
construct(target, args) {
const ws = new target(...args);
const url = args[0];
// 保存到全局变量
window.wsInstances.push(ws);
window.currentWS = ws; // 最新的连接
// 拦截 .send()
const originalSend = ws.send;
ws.send = function (data) {
try {
const parsed = JSON.parse(data);
const msgType = parsed.type || '未知类型';
// 检查是否需要屏蔽
if (!window.wsBlockedTypes.has(msgType)) {
console.groupCollapsed(`%c→ 发送: ${msgType}`, 'color: #03A9F4; font-weight: bold;');
console.log(parsed);
console.groupEnd();
}
} catch (e) {
console.log('%c→ 发送非JSON:', 'color: #03A9F4;', data);
}
return originalSend.call(this, data);
};
// 拦截 .onmessage
ws.addEventListener("message", function (event) {
try {
const parsed = JSON.parse(event.data);
const msgType = parsed.type || '未知类型';
// 检查是否需要屏蔽
if (!window.wsBlockedTypes.has(msgType)) {
console.groupCollapsed(`%c← 接收: ${msgType}`, 'color: #4CAF50; font-weight: bold;');
console.log(parsed);
console.groupEnd();
}
} catch (e) {
console.log('%c← 接收非JSON:', 'color: #4CAF50;', event.data);
}
});
ws.addEventListener("open", function () {
console.info('%cWebSocket 连接已建立: ' + url, 'color: gray;');
});
ws.addEventListener("close", function (e) {
console.warn('%cWebSocket 连接关闭', 'color: orange;', e);
// 从数组中移除关闭的连接
const index = window.wsInstances.indexOf(ws);
if (index > -1) {
window.wsInstances.splice(index, 1);
}
if (window.currentWS === ws) {
window.currentWS = window.wsInstances[window.wsInstances.length - 1] || null;
}
});
ws.addEventListener("error", function (e) {
console.error('%cWebSocket 错误', 'color: red;', e);
});
return ws;
}
});
// 便捷发包函数
window.sendWS = function(data) {
if (!window.currentWS) {
console.error('没有可用的 WebSocket 连接');
return false;
}
if (window.currentWS.readyState !== WebSocket.OPEN) {
console.error('WebSocket 连接未打开');
return false;
}
const jsonData = typeof data === 'string' ? data : JSON.stringify(data);
window.currentWS.send(jsonData);
console.log('%c✅ 手动发送成功:', 'color: #FF9800; font-weight: bold;', data);
return true;
};
// 显示所有 WebSocket 连接
window.listWS = function() {
console.log('%cWebSocket 连接列表:', 'color: #9C27B0; font-weight: bold;');
window.wsInstances.forEach((ws, index) => {
const status = ws.readyState === WebSocket.OPEN ? '✅打开' :
ws.readyState === WebSocket.CONNECTING ? '🔄连接中' :
ws.readyState === WebSocket.CLOSING ? '🔄关闭中' : '❌已关闭';
console.log(`[${index}] ${ws.url} - ${status}`);
});
console.log('当前活跃连接:', window.currentWS?.url || '无');
};
// 切换当前 WebSocket
window.switchWS = function(index) {
if (index >= 0 && index < window.wsInstances.length) {
window.currentWS = window.wsInstances[index];
console.log('%c切换到 WebSocket:', 'color: #9C27B0;', window.currentWS.url);
} else {
console.error('无效的索引');
}
};
// 添加屏蔽类型
window.blockType = function(type) {
if (typeof type === 'string') {
window.wsBlockedTypes.add(type);
saveBlockedTypes();
console.log('%c🚫 已屏蔽类型:', 'color: #F44336; font-weight: bold;', type);
} else if (Array.isArray(type)) {
type.forEach(t => window.wsBlockedTypes.add(t));
saveBlockedTypes();
console.log('%c🚫 已屏蔽类型:', 'color: #F44336; font-weight: bold;', type);
} else {
console.error('类型必须是字符串或字符串数组');
}
};
// 移除屏蔽类型
window.unblockType = function(type) {
if (typeof type === 'string') {
window.wsBlockedTypes.delete(type);
saveBlockedTypes();
console.log('%c✅ 已取消屏蔽类型:', 'color: #4CAF50; font-weight: bold;', type);
} else if (Array.isArray(type)) {
type.forEach(t => window.wsBlockedTypes.delete(t));
saveBlockedTypes();
console.log('%c✅ 已取消屏蔽类型:', 'color: #4CAF50; font-weight: bold;', type);
} else {
console.error('类型必须是字符串或字符串数组');
}
};
// 显示当前屏蔽列表
window.listBlockedTypes = function() {
if (window.wsBlockedTypes.size === 0) {
console.log('%c当前没有屏蔽任何类型', 'color: #607D8B;');
} else {
console.log('%c当前屏蔽的类型:', 'color: #F44336; font-weight: bold;');
Array.from(window.wsBlockedTypes).forEach((type, index) => {
console.log(` [${index + 1}] ${type}`);
});
}
};
// 清空屏蔽列表
window.clearBlockedTypes = function() {
const count = window.wsBlockedTypes.size;
window.wsBlockedTypes.clear();
saveBlockedTypes();
console.log(`%c✅ 已清空屏蔽列表 (共清除 ${count} 个类型)`, 'color: #4CAF50; font-weight: bold;');
};
console.info('%c[MWI WS Logger] WebSocket 拦截器已启用 - 增强版', 'color: purple; font-weight: bold;');
// 显示加载的屏蔽列表
if (window.wsBlockedTypes.size > 0) {
console.info(`%c已加载 ${window.wsBlockedTypes.size} 个屏蔽类型:`, 'color: #FF9800; font-weight: bold;', Array.from(window.wsBlockedTypes));
}
console.info('%c使用方法:', 'color: #2196F3; font-weight: bold;');
console.info('%c sendWS({type: "messageType", data: "your data"}) - 发送消息', 'color: #2196F3;');
console.info('%c listWS() - 查看所有 WebSocket 连接', 'color: #2196F3;');
console.info('%c switchWS(index) - 切换当前连接', 'color: #2196F3;');
console.info('%c blockType("typeName") - 屏蔽指定类型的消息', 'color: #F44336;');
console.info('%c blockType(["type1", "type2"]) - 屏蔽多个类型', 'color: #F44336;');
console.info('%c unblockType("typeName") - 取消屏蔽指定类型', 'color: #4CAF50;');
console.info('%c listBlockedTypes() - 查看当前屏蔽列表', 'color: #607D8B;');
console.info('%c clearBlockedTypes() - 清空所有屏蔽', 'color: #607D8B;');
})();