// ==UserScript==
// @name 违规检测
// @namespace http://ecmk48.free.mbbs.cc/
// @version 2.0.0
// @description 检测玩家的违规事件并记录
// @author EC
// @match https://wtf.haven.chat/
// @match https://game.elfive.cn:91/wtfgame/
// @grant none
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
// 违禁词列表
const bannedWords = ['神经', '骚', '逼', '外挂', '作弊', '王八蛋','操','全家','智障','fuck','Fuck','sb','SB'];
const violationRecords = new Map();
// 创建监控面板
function createMonitorPanels() {
// 侦测面板(蓝色)- 放在右下角底部
const detectPanel = document.createElement('div');
detectPanel.id = 'detect-panel';
detectPanel.style.position = 'fixed';
detectPanel.style.bottom = '20px';
detectPanel.style.right = '20px';
detectPanel.style.background = 'rgba(0, 50, 100, 0.9)';
detectPanel.style.color = 'white';
detectPanel.style.padding = '10px';
detectPanel.style.border = '2px solid #0088ff';
detectPanel.style.borderRadius = '8px';
detectPanel.style.zIndex = '10000';
detectPanel.style.width = '250px';
detectPanel.style.fontFamily = 'Arial, sans-serif';
detectPanel.style.fontSize = '12px';
detectPanel.style.userSelect = 'none';
detectPanel.innerHTML = `
🔍 侦测
📝 总消息: 0
👥 在线玩家: 0
⏰ 状态: 运行中
最后更新: -
`;
// 违规记录面板(红色)- 放在蓝色面板上方
const violationPanel = document.createElement('div');
violationPanel.id = 'violation-panel';
violationPanel.style.position = 'fixed';
violationPanel.style.bottom = '160px'; // 蓝色面板高度 + 间距
violationPanel.style.right = '20px';
violationPanel.style.background = 'rgba(100, 0, 0, 0.9)';
violationPanel.style.color = 'white';
violationPanel.style.padding = '10px';
violationPanel.style.border = '2px solid #ff4444';
violationPanel.style.borderRadius = '8px';
violationPanel.style.zIndex = '10001'; // 比蓝色面板更高
violationPanel.style.width = '250px';
violationPanel.style.fontFamily = 'Arial, sans-serif';
violationPanel.style.fontSize = '12px';
violationPanel.style.maxHeight = '150px'; // 限制高度避免重叠
violationPanel.style.overflowY = 'auto';
violationPanel.style.userSelect = 'none';
violationPanel.innerHTML = `
🚨 违规记录
🚫 违规次数: 0
👤 违规玩家: 0
`;
document.body.appendChild(detectPanel);
document.body.appendChild(violationPanel);
// 使面板可拖动
makeDraggable(detectPanel);
makeDraggable(violationPanel);
return { detectPanel, violationPanel };
}
// 修复的拖动功能 - 添加边界检查和防重叠
function makeDraggable(element) {
let isDragging = false;
let currentX;
let currentY;
let initialX;
let initialY;
let xOffset = 0;
let yOffset = 0;
const header = element.querySelector('div[style*="cursor:move"]');
if (!header) return;
header.style.cursor = 'move';
header.addEventListener("mousedown", dragStart);
document.addEventListener("mousemove", drag);
document.addEventListener("mouseup", dragEnd);
function dragStart(e) {
initialX = e.clientX - xOffset;
initialY = e.clientY - yOffset;
if (e.target === header) {
isDragging = true;
// 拖动时提高z-index确保在最前面
element.style.zIndex = '10002';
}
}
function drag(e) {
if (isDragging) {
e.preventDefault();
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
xOffset = currentX;
yOffset = currentY;
// 边界检查 - 确保面板不会移出视口
const maxX = window.innerWidth - element.offsetWidth;
const maxY = window.innerHeight - element.offsetHeight;
let newX = currentX;
let newY = currentY;
// 限制在视口范围内
if (newX < 0) newX = 0;
if (newY < 0) newY = 0;
if (newX > maxX) newX = maxX;
if (newY > maxY) newY = maxY;
setTranslate(newX, newY, element);
// 检查并防止面板重叠
preventOverlap(element);
}
}
function setTranslate(xPos, yPos, el) {
el.style.left = xPos + "px";
el.style.top = yPos + "px";
el.style.right = "auto";
el.style.bottom = "auto";
}
function dragEnd(e) {
initialX = currentX;
initialY = currentY;
isDragging = false;
// 恢复正常的z-index
if (element.id === 'violation-panel') {
element.style.zIndex = '10001';
} else {
element.style.zIndex = '10000';
}
}
// 防止面板重叠的功能
function preventOverlap(draggedPanel) {
const otherPanel = draggedPanel.id === 'detect-panel'
? document.getElementById('violation-panel')
: document.getElementById('detect-panel');
if (!otherPanel) return;
const draggedRect = draggedPanel.getBoundingClientRect();
const otherRect = otherPanel.getBoundingClientRect();
// 检查是否重叠
const isOverlapping = !(
draggedRect.right < otherRect.left ||
draggedRect.left > otherRect.right ||
draggedRect.bottom < otherRect.top ||
draggedRect.top > otherRect.bottom
);
if (isOverlapping) {
// 如果重叠,将另一个面板推开
const overlapX = Math.min(
Math.abs(draggedRect.right - otherRect.left),
Math.abs(draggedRect.left - otherRect.right)
);
const overlapY = Math.min(
Math.abs(draggedRect.bottom - otherRect.top),
Math.abs(draggedRect.top - otherRect.bottom)
);
// 选择重叠较小的方向进行移动
if (overlapX < overlapY) {
// 水平方向重叠较小,水平移动另一个面板
if (draggedRect.right > otherRect.left && draggedRect.left < otherRect.left) {
// 拖动面板在右侧重叠
otherPanel.style.left = (draggedRect.right + 10) + 'px';
otherPanel.style.right = 'auto';
} else {
// 拖动面板在左侧重叠
otherPanel.style.left = (draggedRect.left - otherRect.width - 10) + 'px';
otherPanel.style.right = 'auto';
}
} else {
// 垂直方向重叠较小,垂直移动另一个面板
if (draggedRect.bottom > otherRect.top && draggedRect.top < otherRect.top) {
// 拖动面板在下方重叠
otherPanel.style.top = (draggedRect.bottom + 10) + 'px';
otherPanel.style.bottom = 'auto';
} else {
// 拖动面板在上方重叠
otherPanel.style.top = (draggedRect.top - otherRect.height - 10) + 'px';
otherPanel.style.bottom = 'auto';
}
}
}
}
}
// 更新侦测面板
function updateDetectPanel(messageCount, playerCount) {
const messageElement = document.getElementById('detect-message-count');
const playerElement = document.getElementById('detect-player-count');
const updateElement = document.getElementById('detect-last-update');
if (messageElement) messageElement.textContent = messageCount;
if (playerElement) playerElement.textContent = playerCount;
if (updateElement) updateElement.textContent = new Date().toLocaleTimeString();
}
// 更新违规面板
function updateViolationPanel() {
const countElement = document.getElementById('violation-count');
const playerCountElement = document.getElementById('violation-player-count');
const listElement = document.getElementById('violation-list');
if (countElement) {
let totalViolations = 0;
for (const record of violationRecords.values()) {
totalViolations += record.count;
}
countElement.textContent = totalViolations;
}
if (playerCountElement) {
playerCountElement.textContent = violationRecords.size;
}
if (listElement) {
if (violationRecords.size === 0) {
listElement.innerHTML = '暂无违规记录
';
} else {
let html = '';
violationRecords.forEach((record, playerName) => {
html += `
${playerName}
违规${record.count}次 最后更新: ${record.lastTime.toLocaleTimeString()}
`;
});
listElement.innerHTML = html;
// 添加删除按钮事件监听
document.querySelectorAll('.delete-violation-btn').forEach(btn => {
btn.addEventListener('click', function() {
const playerName = this.getAttribute('data-player');
deleteViolationRecord(playerName);
});
});
}
}
}
// 删除违规记录
function deleteViolationRecord(playerName) {
if (violationRecords.has(playerName)) {
const record = violationRecords.get(playerName);
const uuid = record.uuid;
// 踢出
qc.wtf.ServerManager.instance.sendMessage('kick',{uuid:uuid});
// 删除记录
violationRecords.delete(playerName);
// 更新面板
updateViolationPanel();
console.log(`删除了 ${playerName} UUID: ${uuid}`);
}
}
// 主逻辑
var messageCount = 0,
playerCount = 0,
ren = [],
ws, hooksend;
var hook = window.WebSocket;
window.WebSocket = function(protocols) {
ws = new hook(protocols);
hooksend = ws.send;
ws.send = function(data) {
hooksend.call(ws, data);
};
ws.addEventListener('message', function(event) {
const datas = jie(event.data);
processMessage(datas);
});
return ws;
};
function send(message) {
let data = jia(message)
hooksend.call(ws, data);
}
function processMessage(e) {
try {
if (e.includes('msg')) {
// 处理聊天消息
const text = JSON.parse(e);
if (text.v.msg && text.v.name && text.v.uuid) {
messageCount++;
const playerName = text.v.name;
const playerUUID = text.v.uuid;
const message = text.v.msg.toLowerCase();
console.log('收到消息:', playerName, ':', message);
// 检查违禁词
const hasBannedWord = bannedWords.some(word =>
message.includes(word.toLowerCase())
);
if (hasBannedWord) {
handleViolation(playerName, playerUUID);
}
updateDetectPanel(messageCount, ren.length);
}
}
else if (e.includes('$newplayer')) {
// 玩家加入
const arr = parsePlayerData(e);
if (arr && arr.length > 0) {
ren.push(arr[0]);
playerCount = ren.length;
updateDetectPanel(messageCount, playerCount);
}
}
else if (e.includes('$leave')) {
// 玩家离开
const arr = parsePlayerData(e);
if (arr && arr.length > 0) {
ren = ren.filter(obj => obj.uuid !== arr[0].uuid);
playerCount = ren.length;
updateDetectPanel(messageCount, playerCount);
}
}
else if (e.includes('$playerdata')) {
// 玩家数据更新
const arr = parseMultiplePlayers(e);
if (arr && arr.length > 0) {
ren = arr;
playerCount = ren.length;
updateDetectPanel(messageCount, playerCount);
}
}
} catch (error) {
console.debug('消息处理错误:', error);
}
}
function parsePlayerData(data) {
try {
const Data = JSON.parse(data);
return [{
name: Data.v.name,
uuid: Data.v.uuid,
x: Data.v.x,
y: Data.v.y
}];
} catch (e) {
return [];
}
}
function parseMultiplePlayers(data) {
try {
const Data = JSON.parse(data);
return Data.v.map(item => ({
name: item.name,
uuid: item.uuid,
x: item.x,
y: item.y
}));
} catch (e) {
return [];
}
}
// 加密函数
function jia(str) {
return btoa(unescape(encodeURIComponent(str)));
}
// 解密函数
function jie(encodedStr) {
return decodeURIComponent(escape(atob(encodedStr)));
}
function handleViolation(playerName, uid) {
if (!violationRecords.has(playerName)) {
violationRecords.set(playerName, {
count: 0,
uuid: 0,
lastTime: new Date()
});
}
const record = violationRecords.get(playerName);
record.count++;
record.uuid = uid;
record.lastTime = new Date();
console.log(`玩家 ${playerName} 违规次数: ${record.count}`);
updateViolationPanel();
}
// 初始化
function init() {
// 等待页面加载完成
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
createMonitorPanels();
updateDetectPanel(0, 0);
updateViolationPanel();
console.log('违禁词检测系统已启动!');
});
} else {
createMonitorPanels();
updateDetectPanel(0, 0);
updateViolationPanel();
console.log('违禁词检测系统已启动!');
}
}
// 启动系统
init();
})();