动漫花园评论区屏蔽助手

屏蔽动漫花园评论区的用户和关键词

当前为 2025-01-15 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         动漫花园评论区屏蔽助手
// @name:en      DMHY Comment Block
// @namespace    https://github.com/xkbkx5904/dmhy-comment-block
// @version      1.0.0
// @description  屏蔽动漫花园评论区的用户和关键词
// @description:en  Block users and keywords in dmhy comment section
// @author       xkbkx5904
// @license      MIT
// @match        *://share.dmhy.org/topics/view/*
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-end
// @noframes
// @supportURL   https://github.com/xkbkx5904/dmhy-comment-block/issues
// @homepageURL  https://github.com/xkbkx5904/dmhy-comment-block
// ==/UserScript==

// 用户黑名单列表
let UserBlockList = [];

// 从本地存储加载黑名单
function loadBlockList() {
    try {
        const saved = GM_getValue('dmhy_comment_blocklist', []);
        UserBlockList = Array.isArray(saved) ? saved.map(item => {
            if (item.type === 'keywords') {
                return {
                    type: 'keywords',
                    values: item.values.map(k => {
                        if (typeof k === 'string' && k.startsWith('/') && k.endsWith('/')) {
                            try {
                                return new RegExp(k.slice(1, -1));
                            } catch (e) {
                                return k;
                            }
                        }
                        return k;
                    })
                };
            }
            return item;
        }) : [];
    } catch (err) {
        UserBlockList = [];
    }
}

// 保存黑名单到本地存储
function saveBlockList() {
    try {
        const listToSave = UserBlockList.map(item => {
            if (item.type === 'keywords') {
                return {
                    type: 'keywords',
                    values: item.values.map(k => {
                        if (k instanceof RegExp) {
                            return `/${k.source}/`;
                        }
                        return k;
                    })
                };
            }
            return item;
        });
        GM_setValue('dmhy_comment_blocklist', listToSave);
    } catch (err) {
        console.error('保存黑名单失败:', err);
    }
}

// 缓存常用的 DOM 选择器结果
const SELECTORS = {
    COMMENT_TABLE: '#comment_recent',
    COMMENT_ROW: 'tr[id^="comment"]',
    USERNAME: '.username',
    CONTENT: '.comment_con span:last-child'
};

// 优化处理评论的函数
function handleComments() {
    const comments = document.querySelectorAll(SELECTORS.COMMENT_ROW);
    if (!comments.length) return;

    // 预先获取黑名单数据,避免重复查找
    const userList = UserBlockList.find(item => item.type === 'users')?.values || [];
    const blockedKeywords = UserBlockList.find(item => item.type === 'keywords')?.values || [];

    comments.forEach(comment => {
        try {
            const commentId = comment.id.replace('comment', '');
            const usernameEl = comment.querySelector(SELECTORS.USERNAME);
            if (!usernameEl) return;

            const username = usernameEl.textContent.trim();
            const content = comment.querySelector(SELECTORS.CONTENT)?.textContent?.trim() || '';

            // 处理用户名链接(如果还没有处理过)
            if (!usernameEl.querySelector('a')) {
                const userLink = document.createElement('a');
                userLink.href = `/topics/list?keyword=${encodeURIComponent(username)}`;
                userLink.className = 'user-link';
                userLink.style.cssText = 'color:blue;text-decoration:underline;cursor:pointer;';
                userLink.textContent = username;
                
                userLink.onclick = (e) => {
                    e.preventDefault();
                    window.open(userLink.href, '_blank');
                };

                userLink.oncontextmenu = (e) => {
                    e.preventDefault();
                    showContextMenu(e, commentId);
                    return false;
                };

                usernameEl.innerHTML = '';
                usernameEl.appendChild(userLink);
            }

            // 检查是否需要屏蔽
            if (shouldBlockComment(username, content, commentId, userList, blockedKeywords)) {
                comment.style.display = 'none';
            }
        } catch (err) {
            console.error('Error processing comment:', err);
        }
    });
}

// 优化屏蔽判断函数
function shouldBlockComment(username, content, commentId, userList, blockedKeywords) {
    if (!username) return false;

    // 检查用户名和ID
    const isBlocked = userList.some(user => {
        // 如果匹配到用户名但没有ID,自动绑定ID
        if (user.username === username && !user.userId && commentId) {
            user.userId = parseInt(commentId);
            saveBlockList();  // 保存更新后的黑名单
        }
        // 通过用户名或ID匹配
        return user.username === username || (user.userId && user.userId === parseInt(commentId));
    });

    if (isBlocked) return true;

    // 检查关键词
    if (content && blockedKeywords.length) {
        return blockedKeywords.some(keyword => {
            if (typeof keyword === 'string') {
                return content.toLowerCase().includes(keyword.toLowerCase());
            }
            return keyword instanceof RegExp && keyword.test(content);
        });
    }

    return false;
}

// 修改显示上下文菜单的函数
function showContextMenu(event, commentId) {
    const menu = document.getElementById('dmhy-comment-context-menu');
    if (!menu) return;

    // 获取鼠标位置
    const x = event.clientX;
    const y = event.clientY;

    // 获取视窗大小
    const viewportWidth = window.innerWidth;
    const viewportHeight = window.innerHeight;

    // 获取菜单大小
    const menuWidth = menu.offsetWidth;
    const menuHeight = menu.offsetHeight;

    // 计算菜单位置,确保不会超出视窗
    let left = x;
    let top = y;

    if (x + menuWidth > viewportWidth) {
        left = viewportWidth - menuWidth;
    }

    if (y + menuHeight > viewportHeight) {
        top = viewportHeight - menuHeight;
    }

    // 设置菜单位置,使用 fixed 定位相对于视窗
    menu.style.position = 'fixed';
    menu.style.left = left + 'px';
    menu.style.top = top + 'px';
    menu.style.display = 'block';
    
    // 获取用户名
    const username = event.target.textContent;
    
    const blockUserOption = document.getElementById('block-comment-user');
    if (blockUserOption) {
        blockUserOption.onclick = function() {
            addUserToBlocklist(commentId, username);
            menu.style.display = 'none';
        };
    }

    // 添加点击事件监听器来关闭菜单
    const closeMenu = (e) => {
        if (!menu.contains(e.target)) {
            menu.style.display = 'none';
            document.removeEventListener('click', closeMenu);
        }
    };

    // 延迟添加点击监听器,避免立即触发
    setTimeout(() => {
        document.addEventListener('click', closeMenu);
    }, 0);
}

// 添加一个初始化后的检查
function checkAndRetryHandleComments() {
    console.log('Checking comments...');  // 添加调试日志
    const comments = document.querySelectorAll('#comment_list .comment-item');
    if (comments.length > 0) {
        console.log('Found comments, processing...');  // 添加调试日志
        handleComments();
    }
}

// 添加管理界面
function addBlocklistUI() {
    // 检查是否存在主屏蔽脚本的UI
    const mainBlocklistUI = document.getElementById('dmhy-blocklist-ui');
    
    if (mainBlocklistUI) {
        // 建议添加延迟确保主脚本按钮已完全初始化
        setTimeout(() => {
            const mainButton = mainBlocklistUI.querySelector('button');
            if (mainButton) {
                mainButton.textContent = '管理种子黑名单';
            }
            
            // 检查是否已存在评论黑名单按钮,避免重复添加
            if (!document.getElementById('show-comment-blocklist')) {
                const button = document.createElement('button');
                button.id = 'show-comment-blocklist';
                button.textContent = '管理评论黑名单';
                button.style.marginTop = '5px';
                button.style.display = 'block'; // 确保按钮垂直排列
                mainBlocklistUI.appendChild(button);
                button.addEventListener('click', showBlocklistManager);
            }
        }, 100);
    } else {
        // 如果不存在主脚本,创建独立的UI
        const uiHtml = `
            <div id="dmhy-comment-blocklist-ui" style="position:fixed;left:10px;top:10px;z-index:9999;">
                <button id="show-comment-blocklist">管理评论黑名单</button>
            </div>
        `;
        document.body.insertAdjacentHTML('beforeend', uiHtml);
        document.getElementById('show-comment-blocklist')?.addEventListener('click', showBlocklistManager);
    }
}

// 修改显示黑名单管理界面的函数
function showBlocklistManager() {
    // 如果已存在管理界面,先移除
    const existingManager = document.getElementById('comment-blocklist-manager');
    const existingOverlay = document.getElementById('comment-blocklist-overlay');
    if (existingManager) existingManager.remove();
    if (existingOverlay) existingOverlay.remove();

    const managerHtml = `
        <div id="comment-blocklist-manager" style="position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);
            background:white;padding:20px;border:1px solid #ccc;border-radius:5px;z-index:10000;
            width:500px;max-height:80vh;overflow-y:auto;">
            <h3 style="margin-top:0;">评论区黑名单管理</h3>
            <div style="margin-bottom:10px;">
                <label>用户黑名单(注意是用户名,用分号分隔):</label><br>
                <textarea id="blocked-usernames" style="width:100%;height:60px;margin-top:5px;resize:none;"></textarea>
            </div>
            <div style="margin-bottom:10px;">
                <label>关键词屏蔽(用分号分隔):</label><br>
                <textarea id="comment-keywords" style="width:100%;height:60px;margin-top:5px;resize:none;"></textarea>
                <div style="color:#666;font-size:12px;margin-top:5px;">
                    提示:支持普通文本和正则表达式<br>
                    - 普通文本直接输入,用分号分隔<br>
                    - 正则表达式用 / 包裹,例如:/\\d+/<br>
                    - 示例:文本1;/user\\d+/;文本2
                </div>
            </div>
            <div style="margin-top:10px;text-align:right;">
                <button id="save-comment-blocklist">保存</button>
                <button id="close-comment-manager">关闭</button>
            </div>
        </div>
        <div id="comment-blocklist-overlay" style="position:fixed;top:0;left:0;right:0;bottom:0;
            background:rgba(0,0,0,0.5);z-index:9999;"></div>
    `;

    document.body.insertAdjacentHTML('beforeend', managerHtml);

    // 填充现有数据
    const userList = UserBlockList.find(item => item.type === 'users')?.values || [];
    const keywords = UserBlockList.find(item => item.type === 'keywords')?.values || [];

    // 填充用户名
    document.getElementById('blocked-usernames').value = userList
        .map(user => user.username)
        .filter(username => username) // 过滤掉空值
        .join(';');

    // 填充关键词
    document.getElementById('comment-keywords').value = keywords.map(k => {
        if (k instanceof RegExp) {
            return `/${k.source}/`;
        }
        return k;
    }).join(';');

    // 绑定关闭按钮事件
    document.getElementById('close-comment-manager').addEventListener('click', function() {
        document.getElementById('comment-blocklist-manager')?.remove();
        document.getElementById('comment-blocklist-overlay')?.remove();
    });

    // 绑定遮罩层点击事件
    document.getElementById('comment-blocklist-overlay').addEventListener('click', function(e) {
        if (e.target === this) {
            document.getElementById('comment-blocklist-manager')?.remove();
            document.getElementById('comment-blocklist-overlay')?.remove();
        }
    });

    // 绑定保存按钮事件
    document.getElementById('save-comment-blocklist').addEventListener('click', function() {
        // 处理用户名
        const usernames = document.getElementById('blocked-usernames').value
            .split(/[;;]/)
            .map(name => name.trim())
            .filter(name => name);

        // 更新用户列表
        let userList = UserBlockList.find(item => item.type === 'users');
        if (!userList) {
            userList = { type: 'users', values: [] };
            UserBlockList.push(userList);
        }

        // 保留现有用户的ID信息
        const existingUsers = new Map(userList.values.map(user => [user.username, user.userId]));
        
        // 更新用户列表,保留已有ID并尝试查找新用户的ID
        userList.values = usernames.map(username => {
            const existingId = existingUsers.get(username);
            if (existingId) {
                // 如果已有ID,保留它
                return { username, userId: existingId };
            } else {
                // 尝试查找新用户的ID
                const newId = findUserIdByUsername(username);
                return { username, userId: newId ? parseInt(newId) : null };
            }
        });

        // 处理关键词
        const keywords = document.getElementById('comment-keywords').value
            .split(/[;;]/)
            .map(k => k.trim())
            .filter(k => k);

        // 更新关键词
        let keywordItem = UserBlockList.find(item => item.type === 'keywords');
        if (!keywordItem) {
            keywordItem = { type: 'keywords', values: [] };
            UserBlockList.push(keywordItem);
        }
        keywordItem.values = keywords;

        saveBlockList();
        document.getElementById('comment-blocklist-manager')?.remove();
        document.getElementById('comment-blocklist-overlay')?.remove();
        handleComments();
        showNotification('黑名单已更新');
    });
}

// 修改右键菜单的样式
function addContextMenu() {
    const menuHtml = `
        <div id="dmhy-comment-context-menu" style="
            display: none;
            position: fixed;
            background: white;
            border: 1px solid #ccc;
            border-radius: 3px;
            padding: 5px;
            box-shadow: 2px 2px 5px rgba(0,0,0,0.2);
            z-index: 10000;
            min-width: 150px;
        ">
            <div id="block-comment-user" style="
                padding: 8px 12px;
                cursor: pointer;
                white-space: nowrap;
            ">
                添加评论用户到黑名单
            </div>
        </div>
    `;
    document.body.insertAdjacentHTML('beforeend', menuHtml);

    // 添加悬停效果
    const blockUserOption = document.getElementById('block-comment-user');
    if (blockUserOption) {
        blockUserOption.addEventListener('mouseover', () => {
            blockUserOption.style.backgroundColor = '#f0f0f0';
        });
        blockUserOption.addEventListener('mouseout', () => {
            blockUserOption.style.backgroundColor = '';
        });
    }
}

// 添加通知提示函数
function showNotification(message) {
    const notification = document.createElement('div');
    notification.style.cssText = `
        position: fixed;
        top: 20px;
        right: 20px;
        background: rgba(0, 0, 0, 0.8);
        color: white;
        padding: 10px 20px;
        border-radius: 4px;
        z-index: 10001;
        font-size: 14px;
        transition: opacity 0.3s;
    `;
    notification.textContent = message;
    document.body.appendChild(notification);

    // 2秒后自动消失
    setTimeout(() => {
        notification.style.opacity = '0';
        setTimeout(() => notification.remove(), 300);
    }, 2000);
}

// 修改添加用户到黑名单的函数
function addUserToBlocklist(commentId, username) {
    if (!username) return;
    
    // 查找或创建用户列表
    let userList = UserBlockList.find(item => item.type === 'users');
    if (!userList) {
        userList = { type: 'users', values: [] };
        UserBlockList.push(userList);
    }

    // 检查是否已存在
    const existingUser = userList.values.find(u => u.username === username);
    if (existingUser) {
        // 如果存在且没有ID,则更新ID
        if (!existingUser.userId && commentId) {
            existingUser.userId = parseInt(commentId);
            showNotification(`已更新用户 ${username} 的ID信息`);
        }
    } else {
        // 添加新用户,包含ID(如果有)
        userList.values.push({
            username,
            userId: commentId ? parseInt(commentId) : null
        });
        showNotification(`已添加用户 ${username} 到黑名单`);
    }

    saveBlockList();
    handleComments();
}

// 优化等待评论区加载的函数
function waitForComments() {
    return new Promise((resolve) => {
        const commentTable = document.querySelector(SELECTORS.COMMENT_TABLE);
        if (commentTable?.querySelector(SELECTORS.USERNAME)) {
            resolve();
            return;
        }

        let attempts = 0;
        const maxAttempts = 20;
        const interval = setInterval(() => {
            const commentTable = document.querySelector(SELECTORS.COMMENT_TABLE);
            if (commentTable?.querySelector(SELECTORS.USERNAME)) {
                clearInterval(interval);
                resolve();
                return;
            }

            attempts++;
            if (attempts >= maxAttempts) {
                clearInterval(interval);
                resolve();
            }
        }, 500);
    });
}

// 根据用户名查找用户ID
function findUserIdByUsername(username) {
    const comments = document.querySelectorAll(SELECTORS.COMMENT_ROW);
    for (const comment of comments) {
        const usernameEl = comment.querySelector(SELECTORS.USERNAME);
        if (usernameEl && usernameEl.textContent.trim() === username) {
            return comment.id.replace('comment', '');
        }
    }
    return null;
}

// 修改初始化函数
(function() {
    'use strict';
    
    // 1. 首先加载黑名单数据
    loadBlockList();
    
    // 2. 添加UI界面
    addBlocklistUI();
    addContextMenu();

    // 3. 等待评论区出现后再处理
    waitForComments().then(() => {
        handleComments();

        // 4. 设置评论区监听器
        const commentList = document.querySelector('#comment_list');
        if (commentList) {
            const observer = new MutationObserver((mutations) => {
                for (const mutation of mutations) {
                    if (mutation.addedNodes.length > 0) {
                        handleComments();
                    }
                }
            });

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