// ==UserScript== // @name:zh-CN 动漫花园评论区屏蔽助手 // @name DMHY Comment Block // @namespace https://github.com/xkbkx5904/dmhy-comment-block // @version 1.0.9 // @description:zh-CN 屏蔽动漫花园评论区的用户和关键词 // @description Block users and keywords in dmhy comment section // @author xkbkx5904 // @license MIT // @language zh-CN // @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 // @icon https://share.dmhy.org/favicon.ico // @compatible chrome // @compatible firefox // @compatible edge // @require https://cdn.jsdelivr.net/npm/sweetalert2@11 // @downloadURL https://update.greasyfork.icu/scripts/523808/DMHY%20Comment%20Block.user.js // @updateURL https://update.greasyfork.icu/scripts/523808/DMHY%20Comment%20Block.meta.js // ==/UserScript== // 用户黑名单列表 let UserBlockList = []; // 缓存常用的 DOM 选择器结果 const SELECTORS = { COMMENT_TABLE: '#comment_recent', COMMENT_ROW: 'tr[id^="comment"]', USERNAME: '.username', CONTENT: '.comment_con span:last-child' }; // 正则表达式工具类 const RegexUtils = { isValid(pattern) { if (!pattern.startsWith('/') || !pattern.endsWith('/')) return true; try { new RegExp(pattern.slice(1, -1)); return true; } catch (e) { return false; } }, toRegex(pattern) { if (pattern.startsWith('/') && pattern.endsWith('/')) { return new RegExp(pattern.slice(1, -1)); } return pattern; }, test(pattern, text) { if (pattern.startsWith('/') && pattern.endsWith('/')) { try { const regex = new RegExp(pattern.slice(1, -1)); return regex.test(text); } catch { return false; } } return false; } }; // 黑名单管理类 const BlockListManager = { addUser(username, commentId = null) { let userList = UserBlockList.find(item => item.type === 'users'); if (!userList) { userList = { type: 'users', values: [] }; UserBlockList.push(userList); } const user = { username, userId: username.startsWith('/') ? null : commentId }; userList.values.push(user); saveBlockList(); handleComments(); }, updateUsers(usernames) { let userList = UserBlockList.find(item => item.type === 'users'); if (!userList) { userList = { type: 'users', values: [] }; UserBlockList.push(userList); } userList.values = usernames.map(username => ({ username, userId: username.startsWith('/') ? null : userList.values.find(u => u.username === username)?.userId || null })); }, updateKeywords(keywords) { let keywordItem = UserBlockList.find(item => item.type === 'keywords'); if (!keywordItem) { keywordItem = { type: 'keywords', values: [] }; UserBlockList.push(keywordItem); } keywordItem.values = keywords.map(RegexUtils.toRegex); } }; // 从本地存储加载黑名单 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); } } // 处理评论显示 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.className = 'user-link'; userLink.style.cssText = 'color:blue;text-decoration:underline;cursor:pointer;'; userLink.textContent = username; userLink.oncontextmenu = (e) => { e.preventDefault(); showContextMenu(e, commentId); return false; }; usernameEl.innerHTML = ''; usernameEl.appendChild(userLink); } // 重置显示状态并检查是否需要屏蔽 comment.style.removeProperty('display'); 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; // 检查用户名 const isUserBlocked = userList.some(user => { // 正则匹配 if (user.username.startsWith('/')) { return RegexUtils.test(user.username, username); } // 普通用户名匹配 const isMatch = user.username === username; if (isMatch && !user.userId && commentId) { user.userId = parseInt(commentId); saveBlockList(); } return isMatch || (user.userId && user.userId === parseInt(commentId)); }); if (isUserBlocked) return true; // 检查关键词 return Boolean(content) && blockedKeywords.some(keyword => typeof keyword === 'string' ? content.toLowerCase().includes(keyword.toLowerCase()) : keyword.test(content) ); } // 显示右键菜单 function showContextMenu(event, commentId) { const menu = document.getElementById('dmhy-comment-context-menu'); if (!menu) return; menu.style.position = 'fixed'; menu.style.left = event.clientX + 'px'; menu.style.top = event.clientY + 'px'; menu.style.display = 'block'; const username = event.target.textContent; const blockUserOption = document.getElementById('block-comment-user'); if (blockUserOption) { blockUserOption.onclick = function(e) { e.stopPropagation(); BlockListManager.addUser(username, commentId); menu.style.display = 'none'; }; } // 改进关闭菜单的逻辑 const closeMenu = (e) => { if (!menu.contains(e.target)) { menu.style.display = 'none'; document.removeEventListener('click', closeMenu); document.removeEventListener('scroll', closeMenu); } }; window.addEventListener('scroll', closeMenu, { once: true }); setTimeout(() => { document.addEventListener('click', closeMenu); }, 0); } // 添加管理界面 function addBlocklistUI() { const maxAttempts = 5; let attempts = 0; function checkAndAddUI() { const mainBlocklistUI = document.getElementById('dmhy-blocklist-ui'); if (mainBlocklistUI) { 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); } } else { attempts++; if (attempts < maxAttempts) { setTimeout(checkAndAddUI, 200); } else { const uiHtml = `
评论区黑名单管理
- 普通文本直接输入,用分号分隔
- 正则表达式用 / 包裹,例如:/\\d+/
- 示例:文本1;/user\\d+/;文本2