NGA论坛WOW封禁检测脚本

检测NGA论坛中的WOW角色是否被封禁(基于RMT处罚名单)

当前为 2025-09-11 提交的版本,查看 最新版本

// ==UserScript==
// @name         NGA论坛WOW封禁检测脚本
// @namespace    http://tampermonkey.net/
// @version      2.0.0
// @description  检测NGA论坛中的WOW角色是否被封禁(基于RMT处罚名单)
// @author       逗逗你德
// @license      GNU GPLv3
// @match        https://ngabbs.com/read.php?tid=*
// @match        https://bbs.nga.cn/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
// @run-at       document-idle
// @resource     banList https://gist.githubusercontent.com/Hans0924/146ac44e638e86c1b1320c39531b4184/raw/b45f546563794bd234809ea5aa7cc7dd1d141c84/wow-cn-rmt-ban-list.json
// ==/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, #ff6b6b, #ff5252);
                color: white;
                padding: 6px 12px;
                margin: 5px 0;
                border-radius: 20px;
                font-size: 12px;
                font-weight: bold;
                text-align: center;
                box-shadow: 0 2px 8px rgba(255, 107, 107, 0.3);
                border: 2px solid #ff4444;
                animation: banWarningPulse 2s ease-in-out infinite alternate;
            }

            @keyframes banWarningPulse {
                0% { box-shadow: 0 2px 8px rgba(255, 107, 107, 0.3); }
                100% { box-shadow: 0 4px 16px rgba(255, 107, 107, 0.6); }
            }

            .nga-ban-warning:before {
                content: "⚠️ ";
                font-size: 14px;
            }

            .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;
            }
        `);
    }

    // 加载封禁名单数据
    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);
                    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);
                    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) {
        // 找到目标容器: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被处罚<br><small>来源: <a href="${banRecord.source_url}" target="_blank" style="color: #ffeb3b; text-decoration: underline;">${banRecord.source_url}</a></small>`;
        warningEl.title = `封禁记录:角色名 ${banRecord.character_name},服务器 ${banRecord.server_name}\n数据来源:${banRecord.source_url}\n提取时间:${new Date(banRecord.extracted_date).toLocaleString()}`;

        // 添加到目标容器
        targetContainer.appendChild(warningEl);
    }

    // 显示检测统计
    function showDetectionStats(newChecked, newBanned) {
        let statsEl = document.getElementById('nga-detection-stats');
        if (!statsEl) {
            statsEl = document.createElement('div');
            statsEl.id = 'nga-detection-stats';
            statsEl.className = 'nga-detection-stats';
            document.body.appendChild(statsEl);
        }

        // 计算总被封禁数量
        const totalBanned = document.querySelectorAll('.nga-ban-warning').length;

        statsEl.innerHTML = `
            <div>🛡️ WOW封禁检测</div>
            <div>总检查: ${detectedCharacters.size}</div>
            <div>新检查: ${newChecked}</div>
            <div>封禁角色: ${bannedCharacters.size}</div>
            <div>封禁标记: ${totalBanned}</div>
            <div>名单记录: ${banList.length}</div>
        `;

        // 重新设置透明度为完全可见
        statsEl.style.opacity = '1';

        // 3秒后自动变为半透明
        setTimeout(() => {
            if (statsEl) {
                statsEl.style.opacity = '0.3';
            }
        }, 3000);
    }

    // 设置页面变化监听
    function setupPageObserver() {
        const observer = new MutationObserver(function(mutations) {
            let shouldRedetect = false;

            mutations.forEach(function(mutation) {
                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                    // 检查是否有新的角色信息元素
                    for (let node of mutation.addedNodes) {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            if (node.classList?.contains('block_txt_c3') ||
                                node.querySelector?.('span.block_txt_c3')) {
                                shouldRedetect = true;
                                break;
                            }
                        }
                    }
                }
            });

            if (shouldRedetect) {
                console.log('检测到页面内容变化,重新执行封禁检测...');
                setTimeout(detectBannedPlayers, 500);
            }
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }


    // 启动脚本
    init();

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址