// ==UserScript== // @name NGA论坛WOW封禁检测脚本 // @namespace http://tampermonkey.net/ // @version 2.0.5 // @description 检测NGA论坛中的WOW角色是否被封禁(基于RMT处罚名单) // @author 逗逗你德 // @license GNU GPLv3 // @match https://ngabbs.com/read.php?tid=* // @match https://bbs.nga.cn/read.php?tid=* // @match https://nga.178.com/read.php?tid=* // @match http://nga.178.com/read.php?tid=* // @match http://ngabbs.com/read.php?tid=* // @match http://bbs.nga.cn/read.php?tid=* // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @grant GM_getResourceText // @grant GM_xmlhttpRequest // @run-at document-idle // @resource banList https://gist.githubusercontent.com/Hans0924/146ac44e638e86c1b1320c39531b4184/raw/wow-cn-rmt-ban-list.json // @downloadURL https://update.greasyfork.icu/scripts/549119/NGA%E8%AE%BA%E5%9D%9BWOW%E5%B0%81%E7%A6%81%E6%A3%80%E6%B5%8B%E8%84%9A%E6%9C%AC.user.js // @updateURL https://update.greasyfork.icu/scripts/549119/NGA%E8%AE%BA%E5%9D%9BWOW%E5%B0%81%E7%A6%81%E6%A3%80%E6%B5%8B%E8%84%9A%E6%9C%AC.meta.js // ==/UserScript== (function() { 'use strict'; console.log('NGA论坛WOW封禁检测脚本已加载'); // 封禁名单数据 let banList = []; let isDataLoaded = false; // 已检测的角色记录(避免重复检测) let detectedCharacters = new Set(); // 已知被封禁的角色记录(用于给新回复添加警告) let bannedCharacters = new Map(); // 使用Map存储角色ID和封禁记录 // 初始化函数 function init() { console.log('初始化WOW封禁检测功能...'); // 添加自定义样式 addCustomStyles(); // 加载封禁名单数据 loadBanList(); // 等待页面加载完成 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', startDetection); } else { startDetection(); } } // 添加自定义样式 function addCustomStyles() { GM_addStyle(` /* WOW封禁检测样式 */ .nga-ban-warning { background: linear-gradient(135deg, #ff5722 0%, #ff6b6b 50%, #ff5252 100%); color: white; padding: 12px 16px; margin: 8px 0; border-radius: 12px; font-size: 13px; font-weight: 600; text-align: left; box-shadow: 0 4px 16px rgba(255, 107, 107, 0.25); border: 1px solid rgba(255, 255, 255, 0.2); position: relative; overflow: hidden; } .nga-ban-warning:after { content: "⚠️"; position: absolute; top: 8px; right: 12px; font-size: 16px; opacity: 0.8; } .nga-ban-warning .ban-title { display: block; font-size: 14px; font-weight: 700; margin-bottom: 6px; text-shadow: 0 1px 2px rgba(0,0,0,0.3); } .nga-ban-warning small { display: block; margin-top: 8px; font-size: 11px; opacity: 0.9; line-height: 1.3; } .nga-ban-warning a { color: #ffeb3b !important; text-decoration: underline !important; font-weight: 500; } .nga-ban-warning a:hover { color: #fff176 !important; text-shadow: 0 0 4px rgba(255, 235, 59, 0.5); } .nga-loading-status { position: fixed; top: 10px; right: 10px; background: #2196F3; color: white; padding: 8px 16px; border-radius: 20px; font-size: 12px; z-index: 10000; box-shadow: 0 2px 8px rgba(33, 150, 243, 0.3); } .nga-detection-stats { position: fixed; bottom: 10px; right: 10px; background: rgba(0, 0, 0, 0.8); color: white; padding: 8px 16px; border-radius: 15px; font-size: 11px; z-index: 10000; max-width: 200px; } .nga-wcl-query-btn { background: linear-gradient(135deg, #4CAF50, #66BB6A); color: white; border: none; padding: 8px 16px; margin: 8px 0 5px 0; border-radius: 18px; font-size: 11px; cursor: pointer; transition: all 0.3s ease; font-weight: 600; box-shadow: 0 2px 6px rgba(76, 175, 80, 0.3); display: inline-block; min-width: 120px; text-align: center; } .nga-wcl-query-btn:hover { background: linear-gradient(135deg, #66BB6A, #4CAF50); transform: translateY(-1px); box-shadow: 0 4px 12px rgba(76, 175, 80, 0.4); } .nga-wcl-query-btn:disabled { background: linear-gradient(135deg, #bdbdbd, #9e9e9e); cursor: not-allowed; transform: none; box-shadow: 0 2px 4px rgba(158, 158, 158, 0.2); opacity: 0.7; } .nga-wcl-progress { background: linear-gradient(135deg, rgba(76, 175, 80, 0.08), rgba(76, 175, 80, 0.15)); border: 1px solid rgba(76, 175, 80, 0.3); border-left: 4px solid #4CAF50; padding: 10px 12px; margin: 8px 0 5px 0; border-radius: 8px; font-size: 12px; color: white; box-shadow: 0 2px 8px rgba(76, 175, 80, 0.1); } .nga-wcl-progress strong { color: white; font-size: 13px; } .nga-wcl-loading { background: linear-gradient(135deg, rgba(255, 152, 0, 0.08), rgba(255, 152, 0, 0.15)); border: 1px solid rgba(255, 152, 0, 0.3); border-left: 4px solid #ff9800; padding: 10px 12px; margin: 8px 0 5px 0; border-radius: 8px; color: white; font-style: normal; font-weight: 500; font-size: 12px; display: flex; align-items: center; box-shadow: 0 2px 8px rgba(255, 152, 0, 0.1); } .nga-wcl-loading:before { content: "🔄"; margin-right: 8px; animation: spin 1s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .nga-wcl-error { background: linear-gradient(135deg, rgba(244, 67, 54, 0.08), rgba(244, 67, 54, 0.15)); border: 1px solid rgba(244, 67, 54, 0.3); border-left: 4px solid #f44336; padding: 10px 12px; margin: 8px 0 5px 0; border-radius: 8px; color: white; font-style: normal; font-weight: 500; font-size: 12px; box-shadow: 0 2px 8px rgba(244, 67, 54, 0.1); } .nga-wcl-error strong { color: white; } .nga-wcl-progress-container { margin-top: 5px; } `); } // 加载封禁名单数据 function loadBanList() { showLoadingStatus('正在加载封禁名单...'); try { const resourceData = GM_getResourceText('banList'); if (resourceData) { banList = JSON.parse(resourceData); isDataLoaded = true; console.log(`封禁名单加载成功,共 ${banList.length} 条记录`); hideLoadingStatus(); startDetection(); } else { console.error('无法获取封禁名单资源'); showLoadingStatus('封禁名单加载失败', true); } } catch (error) { console.error('加载封禁名单失败:', error); showLoadingStatus('封禁名单加载失败', true); } } // 显示加载状态 function showLoadingStatus(message, isError = false) { let statusEl = document.getElementById('nga-loading-status'); if (!statusEl) { statusEl = document.createElement('div'); statusEl.id = 'nga-loading-status'; statusEl.className = 'nga-loading-status'; document.body.appendChild(statusEl); } statusEl.textContent = message; statusEl.style.background = isError ? '#ff5252' : '#2196F3'; } // 隐藏加载状态 function hideLoadingStatus() { const statusEl = document.getElementById('nga-loading-status'); if (statusEl) { statusEl.remove(); } } // 开始检测 function startDetection() { if (!isDataLoaded) { console.log('封禁名单未加载,等待中...'); return; } console.log('开始检测WOW角色封禁状态...'); detectBannedPlayers(); // 设置页面变化监听 setupPageObserver(); } // 检测被封禁的玩家 function detectBannedPlayers() { const spans = document.querySelectorAll('span.block_txt_c3'); console.log(`找到 ${spans.length} 个角色信息元素`); let checkedCount = 0; let bannedCount = 0; let newCheckedCount = 0; let newBannedCount = 0; let hasNewWarnings = false; // 追踪是否有新的警告被添加 spans.forEach(span => { const playerInfo = parsePlayerInfo(span); if (playerInfo) { const characterId = `${playerInfo.serverName}|${playerInfo.characterName}`; // 检查是否已经有封禁警告标记 if (span.closest('.clickextend')?.querySelector('.nga-ban-warning')) { return; // 跳过已经标记过的span } // 如果是已知的被封禁角色,直接添加警告(不重复计入统计) if (bannedCharacters.has(characterId)) { const banRecord = bannedCharacters.get(characterId); addBanWarning(span, banRecord, playerInfo); hasNewWarnings = true; console.log(`为已知被封禁角色添加警告: ${characterId}`); return; } // 如果是已检测过但未被封禁的角色,跳过 if (detectedCharacters.has(characterId)) { return; } // 新角色,进行检测 detectedCharacters.add(characterId); checkedCount++; newCheckedCount++; const isBanned = checkIfBanned(playerInfo); if (isBanned) { bannedCount++; newBannedCount++; bannedCharacters.set(characterId, isBanned); // 记录被封禁的角色 addBanWarning(span, isBanned, playerInfo); hasNewWarnings = true; console.log(`发现新的被封禁角色: ${characterId}`); } } }); // 显示检测统计 if (newCheckedCount > 0 || hasNewWarnings) { showDetectionStats(newCheckedCount, newBannedCount); if (newCheckedCount > 0) { console.log(`检测完成: 新检查了 ${newCheckedCount} 个角色,发现 ${newBannedCount} 个可能被封禁的角色`); console.log(`总计: 已检查 ${detectedCharacters.size} 个角色,已知封禁 ${bannedCharacters.size} 个角色`); } if (hasNewWarnings && newCheckedCount === 0) { console.log('为已知被封禁角色的新回复添加了警告标记'); } } else { console.log('本次检测未发现新的角色信息或封禁警告'); } } // 解析玩家信息 function parsePlayerInfo(span) { const title = span.getAttribute('title'); if (!title) return null; try { // 解析title格式:"正式服 熊猫酒仙; 暗夜精灵德鲁伊 不夜之心 "艾泽拉斯肝王"; 装备等级714 成就点数25610 史诗钥石3017; https://wow.blizzard.cn/character/#/pandaren/不夜之心" const parts = title.split(';'); if (parts.length < 2) return null; // 第一部分:版本和服务器 const firstPart = parts[0].trim(); if (!firstPart.startsWith('正式服')) { return null; // 只处理正式服 } const serverName = firstPart.replace('正式服', '').trim(); // 第二部分:职业和角色名 const secondPart = parts[1].trim(); const match = secondPart.match(/^(.+?)\s+(.+?)\s+/); if (!match) return null; const characterName = match[2]; return { serverName: serverName, characterName: characterName, element: span }; } catch (error) { console.error('解析玩家信息失败:', title, error); return null; } } // 检查是否被封禁 function checkIfBanned(playerInfo) { const { serverName, characterName } = playerInfo; // 在封禁名单中查找匹配的记录 const matches = banList.filter(banRecord => { // 服务器名必须完全匹配 if (banRecord.server_name !== serverName) { return false; } // 角色名匹配:考虑脱敏处理 return matchCharacterName(characterName, banRecord.character_name); }); return matches.length > 0 ? matches[0] : null; } // 匹配角色名(考虑脱敏处理) function matchCharacterName(actualName, bannedName) { if (!actualName || !bannedName) return false; // 长度必须相等 if (actualName.length !== bannedName.length) { return false; } // 如果长度小于3,需要完全匹配 if (actualName.length < 3) { return actualName === bannedName; } // 检查首尾字符是否匹配,中间字符是否为星号 const firstChar = actualName.charAt(0); const lastChar = actualName.charAt(actualName.length - 1); const bannedFirstChar = bannedName.charAt(0); const bannedLastChar = bannedName.charAt(bannedName.length - 1); // 首尾字符必须匹配 if (firstChar !== bannedFirstChar || lastChar !== bannedLastChar) { return false; } // 检查中间是否都是星号(脱敏处理的特征) const middlePart = bannedName.slice(1, -1); return /^\*+$/.test(middlePart); } // 添加封禁警告 function addBanWarning(span, banRecord, playerInfo) { // 找到目标容器:span的父元素的父元素(class为clickextend) let targetContainer = span.parentElement?.parentElement; // 如果没有找到clickextend,向上查找 if (!targetContainer || !targetContainer.classList.contains('clickextend')) { let current = span; while (current && current.parentElement) { current = current.parentElement; if (current.classList && current.classList.contains('clickextend')) { targetContainer = current; break; } } } if (!targetContainer) { console.warn('未找到目标容器clickextend,使用span的父元素'); targetContainer = span.parentElement || span; } // 检查是否已经添加过警告 if (targetContainer.querySelector('.nga-ban-warning')) { return; } // 创建警告元素 const warningEl = document.createElement('div'); warningEl.className = 'nga-ban-warning'; warningEl.innerHTML = ` 🚫 该玩家可能因RMT被处罚