// ==UserScript== // @name 微博净化器·内容屏蔽助手 // @namespace http://tampermonkey.net/ // @version v6 // @description 信息流屏蔽+正文屏蔽+导航屏蔽+侧边栏屏蔽+评论用户屏蔽(完整线程)+自定义分组屏蔽 // @author MRBANK // @match https://weibo.com/* // @match https://*.weibo.com/* // @icon https://weibo.com/favicon.ico // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @license MIT // ==/UserScript== (function() { 'use strict'; // ================== 配置管理 ================== const CONFIG = { // 功能开关 blockAds: GM_getValue('blockAds', true), blockImages: GM_getValue('blockImages', true), blockTopNav: GM_getValue('blockTopNav', true), debugMode: GM_getValue('debugMode', false), // 信息流标签关键词(可增删) blockKeywords: GM_getValue('blockKeywords', [ { text: '荐读', enabled: true }, { text: '推荐', enabled: true }, { text: '公益', enabled: true }, { text: '广告', enabled: true } ]), // 正文关键词(可增删,默认空数组) contentKeywords: GM_getValue('contentKeywords', []), // 顶部导航屏蔽关键词(固定预设,不可增删) topNavKeywords: GM_getValue('topNavKeywords', [ { text: '首页', enabled: false }, { text: '消息', enabled: false }, { text: '推荐', enabled: true }, { text: '视频', enabled: true }, { text: '游戏', enabled: true }, { text: '无障碍', enabled: true } ]), // 侧边栏模块固定预设(用户不可增删,仅可开关) sidebarModules: GM_getValue('sidebarModules', [ { text: '你可能感兴趣的人', enabled: false }, { text: '创作者中心', enabled: false }, { text: '微博热搜', enabled: false }, { text: '自定义分组', enabled: false } // 新增预设 ]), // 评论用户屏蔽(可增删) blockCommentUsers: GM_getValue('blockCommentUsers', []), // 性能配置 observerDelay: 50, scrollThrottle: 500, batchSize: 10, // 选择器配置(已适配新版微博) selectors: { feedItem: 'article.Feed_wrap_3v9LH.Feed_normal_12A98, article:has(.wbpro-feed-content), article[class*="woo-panel"]', tagContainer: '.wbpro-tag-c2 div', feedContent: '[class*="_wbtext_"], [class*="Feed_content"] [class*="txt"], [class*="content"] [class*="txt"]', sidebarModule: '.wbpro-side', sidebarTitle: '.wbpro-side-tit .f16.fm.cla' } }; // ================== 临时配置(用于弹窗编辑)================== let TEMP_CONFIG = JSON.parse(JSON.stringify(CONFIG)); // ================== 工具函数 ================== function throttle(func, limit) { let inThrottle; return function(...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } function debounce(func, delay) { let timeoutId; return function(...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => func.apply(this, args), delay); }; } function log(message, type = 'info', data = null) { if (!CONFIG.debugMode && type !== 'error') return; const prefix = '【微博屏蔽 v5.3】'; const timestamp = new Date().toLocaleTimeString(); switch(type) { case 'error': console.error(`${prefix}[${timestamp}]`, message, data || ''); break; case 'warn': console.warn(`${prefix}[${timestamp}]`, message, data || ''); break; case 'success': console.log(`%c${prefix}[${timestamp}] ${message}`, 'color: green', data || ''); break; default: console.log(`${prefix}[${timestamp}]`, message, data || ''); } } const performanceMonitor = { stats: { blockedItems: 0, blockedImages: 0, blockedSidebars: 0, blockedTopNav: 0, blockedComments: 0, executionTime: 0 }, start() { return performance.now(); }, end(startTime, operation) { const duration = performance.now() - startTime; this.stats.executionTime += duration; if (CONFIG.debugMode) log(`${operation} 耗时: ${duration.toFixed(2)}ms`, 'info'); }, report() { log('性能统计', 'success', this.stats); } }; // ================== 核心屏蔽功能 ================== function injectStyles() { const styles = ` ${CONFIG.blockImages ? ` img[src*="shuakong.jpg"], img[src*="upload/1014/739/2023/08/03/shuakong.jpg"] { display: none !important; visibility: hidden !important; opacity: 0 !important; width: 0 !important; height: 0 !important; } ` : ''} .weibo-ad-blocked, .weibo-topnav-blocked, .weibo-sidebar-blocked, .weibo-comment-blocked { display: none !important; } .weibo-content-blocked { overflow: hidden !important; } .weibo-content-blocked header, .weibo-content-blocked footer, .weibo-content-blocked [class*="_head_"], .weibo-content-blocked [class*="_header_"], .weibo-content-blocked [class*="_info_"], .weibo-content-blocked [class*="_suffixbox_"], .weibo-content-blocked [class*="_content_wrap_"], .weibo-content-blocked [class*="_followbtn_"], .weibo-content-blocked [class*="_more_"], .weibo-content-blocked .woo-avatar-main, .weibo-content-blocked .woo-avatar-hover, .weibo-content-blocked .woo-avatar-img, .weibo-content-blocked .woo-button-main, .weibo-content-blocked [class*="_left_"], .weibo-content-blocked [class*="_main_"][class*="198pe"], .weibo-content-blocked [class*="_item_"], .weibo-content-blocked [class*="_wrap_"][class*="198pe"], .weibo-content-blocked [class*="_iconWrap_"], .weibo-content-blocked .woo-like-main, .weibo-content-blocked .woo-like-count, .weibo-content-blocked .woo-font--retweet, .weibo-content-blocked .woo-font--comment { display: none !important; } .weibo-content-blocked .wbpro-feed-content, .weibo-content-blocked [class*="_placebox_"], .weibo-content-blocked .picture, .weibo-content-blocked video, .weibo-content-blocked [class*="_row_"], .weibo-content-blocked [class*="_ogText_"], .weibo-content-blocked .retweet, .weibo-content-blocked [class*="_retweet_"], .weibo-content-blocked [class*="_reText_"], .weibo-content-blocked [class*="_retweetBar_"], .weibo-content-blocked [class*="_retweetHeadInfo_"] { display: none !important; } .weibo-content-blocked [class*="_body_"] { padding-bottom: 0 !important; } .weibo-content-blocked footer { margin-top: 0 !important; } ${CONFIG.debugMode ? ` .weibo-ad-blocked { display: block !important; opacity: 0.3 !important; border: 2px dashed red !important; background: rgba(255,0,0,0.1) !important; } .weibo-topnav-blocked { display: inline-block !important; opacity: 0.3 !important; border: 2px dashed blue !important; background: rgba(0,0,255,0.1) !important; } .weibo-sidebar-blocked { display: block !important; opacity: 0.3 !important; border: 2px dashed green !important; background: rgba(0,255,0,0.1) !important; } .weibo-comment-blocked { display: block !important; opacity: 0.3 !important; border: 2px dashed purple !important; background: rgba(128,0,128,0.1) !important; } ` : ''} `; GM_addStyle(styles); log('样式注入成功', 'success'); } // 获取微博正文文本 function getContentText(container) { try { let contentEl = container.querySelector('[class*="_wbtext_"]'); if (contentEl) { return contentEl.textContent.trim(); } contentEl = container.querySelector(CONFIG.selectors.feedContent); if (contentEl) { return contentEl.textContent.trim(); } return container.textContent.trim(); } catch (e) { log('获取正文文本失败', 'error', e); return ''; } } function markBlockedContent(container) { const article = container.closest('article') || container; article.classList.add('weibo-content-blocked'); return true; } // 信息流屏蔽 function processFeedItems() { if (!CONFIG.blockAds) return; const startTime = performanceMonitor.start(); const feedItems = document.querySelectorAll(`${CONFIG.selectors.feedItem}:not([data-processed="true"])`); let blockedCount = 0; const enabledContentKeywords = CONFIG.contentKeywords.filter(kw => kw.enabled); const enabledTagKeywords = CONFIG.blockKeywords.filter(kw => kw.enabled); Array.from(feedItems).slice(0, CONFIG.batchSize).forEach(container => { try { container.setAttribute('data-processed', 'true'); let shouldBlock = false; let blockReason = ''; const tagElement = container.querySelector(CONFIG.selectors.tagContainer); if (tagElement) { const tagText = tagElement.textContent.trim(); const matchedTag = enabledTagKeywords.find(kw => tagText.includes(kw.text)); if (matchedTag) { shouldBlock = true; blockReason = `标签:${matchedTag.text}`; } } if (!shouldBlock && enabledContentKeywords.length > 0) { const contentText = getContentText(container); if (contentText) { const matchedContent = enabledContentKeywords.find(kw => contentText.includes(kw.text)); if (matchedContent) { shouldBlock = true; blockReason = `正文:${matchedContent.text}`; } } } if (shouldBlock) { if (markBlockedContent(container)) { blockedCount++; performanceMonitor.stats.blockedItems++; log(`屏蔽信息流: ${blockReason}`, 'success'); } } } catch (e) { log('处理信息流出错', 'error', e); } }); if (blockedCount) { log(`本次屏蔽 ${blockedCount} 条内容`, 'info'); } performanceMonitor.end(startTime, '处理信息流'); if (feedItems.length > CONFIG.batchSize) setTimeout(processFeedItems, 50); } // 侧边栏模块屏蔽(包含旧版结构 + 新版自定义分组结构) function processSidebarModules() { const startTime = performanceMonitor.start(); let blockedCount = 0; try { // 1. 处理旧版/标准结构 (.wbpro-side) const modules = document.querySelectorAll(`${CONFIG.selectors.sidebarModule}:not([data-sidebar-processed="true"])`); modules.forEach(module => { module.setAttribute('data-sidebar-processed', 'true'); const titleEl = module.querySelector(CONFIG.selectors.sidebarTitle); if (!titleEl) return; const titleText = titleEl.textContent.trim(); const matchedModule = CONFIG.sidebarModules .filter(m => m.enabled) .find(m => titleText.includes(m.text)); if (matchedModule) { if (!module.classList.contains('weibo-sidebar-blocked')) { module.classList.add('weibo-sidebar-blocked'); blockedCount++; performanceMonitor.stats.blockedSidebars++; log(`屏蔽侧边栏模块(标准): "${titleText}"`, 'success'); } } }); // 2. 处理新版扁平结构(自定义分组等) // 逻辑:查找所有 h3 标题,如果匹配关键词,隐藏其父容器,并遍历隐藏后续的 a 标签 const enabledModules = CONFIG.sidebarModules.filter(m => m.enabled).map(m => m.text); if (enabledModules.length > 0) { // 获取侧边栏区域的所有 h3 标题 const h3Titles = document.querySelectorAll('main h3, [class*="side"] h3, [class*="Side"] h3'); h3Titles.forEach(h3 => { if (h3.hasAttribute('data-flat-processed')) return; h3.setAttribute('data-flat-processed', 'true'); const titleText = h3.textContent.trim(); const matchedModule = enabledModules.find(text => titleText.includes(text)); if (matchedModule) { // 找到标题所在的头部容器 (div.woo-box-flex) const headerContainer = h3.parentElement; if (headerContainer && !headerContainer.classList.contains('weibo-sidebar-blocked')) { // 屏蔽头部 headerContainer.classList.add('weibo-sidebar-blocked'); blockedCount++; log(`屏蔽侧边栏模块(扁平标题): "${titleText}"`, 'success'); } // 屏蔽后续兄弟节点(分组链接) let nextElement = headerContainer ? headerContainer.nextElementSibling : null; while (nextElement && nextElement.tagName === 'A') { if (!nextElement.classList.contains('weibo-sidebar-blocked')) { nextElement.classList.add('weibo-sidebar-blocked'); } nextElement = nextElement.nextElementSibling; } } }); } } catch (e) { log('处理侧边栏模块出错', 'error', e); } if (blockedCount) log(`本次屏蔽侧边栏模块 ${blockedCount} 个`, 'info'); performanceMonitor.end(startTime, '处理侧边栏模块'); } // 顶部导航屏蔽 function processTopNav() { if (!CONFIG.blockTopNav) return; const startTime = performanceMonitor.start(); let blockedCount = 0; try { const gameKeyword = CONFIG.topNavKeywords.find(kw => kw.text === '游戏'); if (gameKeyword && gameKeyword.enabled) { const gameLink = document.querySelector('a[title="游戏"]'); if (gameLink) { const gameBox = gameLink.closest('div[class*="_box_"]'); if (gameBox && !gameBox.classList.contains('weibo-topnav-blocked')) { gameBox.classList.add('weibo-topnav-blocked'); blockedCount++; performanceMonitor.stats.blockedTopNav++; log('屏蔽顶部导航: 游戏(含分隔线)', 'success'); } } } const accessKeyword = CONFIG.topNavKeywords.find(kw => kw.text === '无障碍'); if (accessKeyword && accessKeyword.enabled) { const accessibilityBtn = document.querySelector('button#cniil_wza'); if (accessibilityBtn) { const accessBox = accessibilityBtn.closest('div[class*="_box_"]'); if (accessBox && !accessBox.classList.contains('weibo-topnav-blocked')) { accessBox.classList.add('weibo-topnav-blocked'); blockedCount++; performanceMonitor.stats.blockedTopNav++; log('屏蔽顶部导航: 无障碍(含分隔线)', 'success'); } } } let navRoot = null; const recommendLink = document.querySelector('a[href="/hot"]'); if (recommendLink) { navRoot = recommendLink.closest('header, nav, div[class*="woo-tab"], div[class*="header"]'); } if (!navRoot) { navRoot = document.querySelector('.woo-tab-main, [class*="woo-tab-other"], div[class*="woo-tab"]'); } if (!navRoot) { navRoot = document.querySelector('header'); } if (navRoot) { const candidateSelector = [ 'a[title]', 'button[title]', 'button:not([title])', 'a[aria-label]', 'div[aria-label]', 'a[href*="/"]', '#cniil_wza' ].join(','); const candidates = navRoot.querySelectorAll(candidateSelector); const matchedTargets = new Set(); candidates.forEach(el => { if (el.closest('.weibo-topnav-blocked')) return; let matchText = ''; if (el.hasAttribute('aria-label')) { matchText = el.getAttribute('aria-label'); } else if (el.hasAttribute('title') && el.getAttribute('title').trim() !== '') { matchText = el.getAttribute('title'); } else { const span = el.querySelector('span:not(.woo-badge-box)'); if (span && span.textContent.trim()) { matchText = span.textContent.trim(); } else { matchText = el.textContent.trim(); } } if (!matchText) return; const shouldBlock = CONFIG.topNavKeywords .filter(kw => kw.enabled) .some(kw => matchText.includes(kw.text)); if (shouldBlock) { let target = el; const wrapper = el.closest('div[class*="_box_"]'); if (wrapper) { target = wrapper; } else if (el.tagName === 'SPAN' || el.tagName === 'SVG' || el.tagName === 'path') { target = el.closest('a, button, div[role="button"]') || el; } if (!matchedTargets.has(target)) { target.classList.add('weibo-topnav-blocked'); matchedTargets.add(target); blockedCount++; performanceMonitor.stats.blockedTopNav++; log(`屏蔽顶部导航: "${matchText}"`, 'success'); } } }); } } catch (error) { log('处理顶部导航按钮时出错', 'error', error); } if (blockedCount > 0) { log(`本次屏蔽顶部导航按钮 ${blockedCount} 个`, 'info'); } performanceMonitor.end(startTime, '处理顶部导航'); } // ========== 优化版:评论用户屏蔽(隐藏整个评论线程)========== function processComments() { // 如果没有启用的屏蔽用户,直接返回 const enabledUsers = CONFIG.blockCommentUsers.filter(u => u.enabled).map(u => u.text); if (enabledUsers.length === 0) return; const startTime = performanceMonitor.start(); let blockedCount = 0; try { // 只处理主评论内的用户链接 const userLinks = document.querySelectorAll('.item1 .text > a[href*="/u/"]'); userLinks.forEach(link => { const commentUnit = link.closest('.item1'); if (!commentUnit) return; // 如果该单元已经处理过,跳过 if (commentUnit.closest('.weibo-comment-blocked') || commentUnit.hasAttribute('data-comment-unit-processed')) return; const authorName = link.textContent.trim(); if (enabledUsers.includes(authorName)) { // 隐藏当前主评论 commentUnit.classList.add('weibo-comment-blocked'); commentUnit.setAttribute('data-comment-unit-processed', 'true'); // 隐藏后续所有兄弟元素,直到下一个 .item1 为止(即隐藏整个回复线程) let next = commentUnit.nextElementSibling; while (next && !next.classList.contains('item1')) { next.classList.add('weibo-comment-blocked'); next.setAttribute('data-comment-unit-processed', 'true'); next = next.nextElementSibling; } blockedCount++; performanceMonitor.stats.blockedComments++; log(`屏蔽评论线程: 用户 "${authorName}"`, 'success'); } else { // 未匹配的用户也标记为已处理,避免重复遍历 commentUnit.setAttribute('data-comment-unit-processed', 'true'); } }); } catch (e) { log('处理评论出错', 'error', e); } if (blockedCount) log(`本次屏蔽评论线程 ${blockedCount} 个`, 'info'); performanceMonitor.end(startTime, '处理评论'); } const processContent = debounce(() => { processFeedItems(); processSidebarModules(); processTopNav(); processComments(); }, CONFIG.observerDelay); // ================== 可视化配置弹窗 ================== let uiPanel = null; function createUI() { if (uiPanel) return uiPanel; uiPanel = document.createElement('div'); uiPanel.id = 'weibo-adblocker-panel'; uiPanel.innerHTML = `