实现可分别按用户名、关键字或正则表达式对视频(或直播间/相薄)和评论(或回复)进行屏蔽; 将鼠标移至网页右下角弹出悬浮按钮
// ==UserScript== // @name 哔哩哔哩 - 屏蔽指定内容 // @namespace https://greasyfork.org/zh-CN/users/193133-pana // @homepage https://greasyfork.org/zh-CN/users/193133-pana // @version 4.8.0 // @description 实现可分别按用户名、关键字或正则表达式对视频(或直播间/相薄)和评论(或回复)进行屏蔽; 将鼠标移至网页右下角弹出悬浮按钮 // @author pana // @include *://www.bilibili.com/* // @include *://search.bilibili.com/* // @include *://live.bilibili.com/* // @include *://space.bilibili.com/* // @include *://t.bilibili.com/* // @include *://h.bilibili.com/* // @include *://manga.bilibili.com/* // @include *://message.bilibili.com/* // @require https://cdn.jsdelivr.net/npm/[email protected]/minified/arrive.min.js // @require https://greasyfork.org/scripts/407543-block-obj/code/Block_Obj.js?version=963893 // @require https://unpkg.com/[email protected]/dayjs.min.js // @license GNU General Public License v3.0 or later // @grant GM_getValue // @grant GM.getValue // @grant GM_setValue // @grant GM.setValue // @grant GM_setClipboard // @grant GM.setClipboard // @grant GM_registerMenuCommand // @grant GM_addValueChangeListener // @run-at document-start // @noframes // @note ---------------------------------------------------------------- // @note 与"Bilibili 旧播放页"脚本(https://greasyfork.org/zh-CN/scripts/394296)的兼容问题: // @note 如果同时启用脚本后发现本脚本无法保存设置到存储中,请前往脚本管理器调整脚本执行顺序。 // @note 具体方法可参考: https://greasyfork.org/zh-CN/scripts/397669 // @note ---------------------------------------------------------------- // @note 关于 "读取仅拥有标题的视频的用户名信息" // @note 原理是通过 API: "https://api.bilibili.com/x/web-interface/view" // @note 这个方法本质上是不可靠的,因为可能会由于快速大量进行请求从而导致被拦截 (如:频繁地在页面内刷新) // @note ---------------------------------------------------------------- // @note 更新记录: // @note ver.4.8.0 优化关于动态的屏蔽功能 // @note ver.4.7.0 支持在动态、视频播放等页面按粉丝勋章屏蔽评论 // @note ver.4.6.4 补充覆盖视频播放页下部分内容 // @note ver.4.6.3 修复添加正则表达式中存在逗号时录入出错的问题 // @note ver.4.6.2 修复存在错误正则表达式时造成脚本失效的问题 // @note ver.4.6.1 修复白名单效果未作用至动态上的问题 // @note ver.4.6.0 支持用户白名单的功能 // @note ver.4.5.0 支持按粉丝勋章屏蔽弹幕评论 // @note ver.4.4.2 补充覆盖转发动态中的用户名 // @note ver.4.4.1 修复上次更新导致屏蔽评论失效的问题 // @note ver.4.4.0 优化脚本 // @note ver.4.3.5 修复在视频关键字/正则表达式中添加单个空格时会导致所有视频被屏蔽的情况 // @note ver.4.3.4 修复部分页面屏蔽失效的问题 // @note ver.4.3.3 补充覆盖当前在线页面 // @note ver.4.3.2 修复部分页面下按用户屏蔽失效的问题 // @note ver.4.3.1 修复了由于上一版本的改动导致的脚本失效的问题 // @note ver.4.3.0 兼容 Greasemonkey 4 // @note ver.4.2.0 添加支持允许作用于动态的功能 // @note ver.4.1.1 补充覆盖频道页面下内容 // @note ver.4.1.0 允许在评论区显示屏蔽用户和"爆炸"按钮 // @note ver.4.0.6 修复播放器网页全屏模式下的冲突问题 // @note ver.4.0.4 尝试修复播放器网页全屏模式下可能的冲突问题 // @note ver.4.0.3 修复已知的问题 // @note ver.4.0.2 更换依赖库的 CDN // @note ver.4.0.0 整理并优化代码; 修复对于新添加的正则表达式,其无法在其他同步数据的标签页上生效的问题 // @note ver.3.18.0 尝试通过 API 获取其他仅拥有标题的视频的用户名信息 // @note ver.3.17.1 尝试通过 API 的方式读取专区热门列表里视频的用户名 // @note ver.3.17.0 处理与"Bilibili 旧播放页"脚本不兼容的问题 // @note ver.3.16.4 补充之前忘记匹配消息中心页面的问题; 补充覆盖番剧区页内的评论区; 修复了其他已知的问题 // @note ver.3.16.0 增加 "消息中心 >> 回复我的" 的相关屏蔽,同时允许自动删除通知 // @note ver.3.14.4 补充覆盖漫画页内的评论区 // @note ver.3.14.3 增加相薄区的相关屏蔽; 完善部分未被覆盖的页面内容; 调整了使用关键字匹配表情的逻辑 // @note ver.3.13.2 补充覆盖动态首页内的评论区 // @note ver.3.13.1 修复可能无法匹配到用户动态页的问题 // @note ver.3.13.0 实现多标签页同步数据; 调整取消按钮的行为; 覆盖视频播放完毕后的推荐视频; 兼容"Bilibili 旧播放页"脚本; 依旧存在的兼容问题: 在主页和视频播放页等页面下同时开启脚本时,本脚本无法保存设置到存储中 // @note ver.3.11.0 增加直播区的相关屏蔽; 覆盖个人动态内的评论; 添加展开列表按钮 // @note ver.3.7.1 添加删除按钮; 完善部分未被覆盖的页面内容 // @note ver.3.5.3 修复部分页面下输入框内容看不清以及其他小问题 // @note ver.3.5.0 优化代码; 完善部分未被覆盖的页面内容; 悬浮图标自动隐藏等 // @note ver.2.2.0 添加允许将评论中的 b 站内置表情包转换成对应文字的功能 // @note ver.2.1.2 修复储存正则表达式出错的问题; 优化代码 // @note ver.2.1.0 添加允许按正则表达式进行屏蔽的功能 // @note ver.2.0.0 调整了添加与删除关键字的方式,方便操作; 将评论与视频标题的关键词分开作用 // @note ver.1.2.1 完善部分未被覆盖的页面内容 // @note ver.1.2.0 添加屏蔽评论的功能 // @note ver.1.1.2 调整屏蔽按钮的位置到右下角; 尝试处理脚本偶尔会失效的问题 // @note ver.1.1.1 修复搜索页面以关键字屏蔽无效的问题 // @note ver.1.1.0 匹配视频播放页面; 优化代码 // ==/UserScript== (async function () { 'use strict'; const OLD_URL = location.href; const MODULE = { USERNAME: { className: 'li_username', }, WHITELIST: { className: 'li_whitelist', }, VIDEO_KEYWORD: { className: 'li_video_keyword', }, COMMENT_KEYWORD: { className: 'li_comment_keyword', }, }; const BASIC_STYLE = ` .player-mode-webfullscreen, .mode-webfullscreen, .webfullscreen, .player-module { z-index: 100001 !important; } .bilibili_reply_bang_button, .bilibili_comment_bang_button, .bilibili_reply_user_block_button, .bilibili_comment_user_block_button { display: inline-block; padding: 0px 5px; border-radius: 4px; cursor: pointer; } .bilibili_reply_bang_button:hover, .bilibili_comment_bang_button:hover, .bilibili_reply_user_block_button:hover, .bilibili_comment_user_block_button:hover { color: #00a1d6; background-color: #e5e9ef; } `; const handler = [ { index: '.video-card-common', user: ['a.up', 'a.ex-up'], text: ['a.title', 'p.ex-title'], method: 1, }, { clientInformation: 0, index: '.video-card-reco', user: 'p.up', text: 'p.title', }, { index: '.van-slide div.item', user: null, text: 'p.title', }, { index: '.rank-wrap', user: 'span.name', text: ['p.f-title', 'p.title', 'div.txt a.link p'], }, { index: '.article-card', user: 'a.up', text: 'a.title', method: 1, }, { index: '.live-card', user: 'p.name', text: 'p.desc', method: 1, type: { live: true, }, }, { index: '.card-live-module', user: '.auther', text: 'p.t', method: 1, type: { live: true, }, }, { index: '.live-rank-item', user: 'div.txt > p', text: 'p.p2', method: 0, type: { live: true, }, }, { index: '.manga-card', user: null, text: 'p.manga-title', method: 1, }, { index: '.manga-spread-module', user: null, text: 'p.t', method: 1, }, { c: 1, index: '.groom-module', user: 'p.author', userReg: /^up主:/, text: 'p.title', }, { index: 'ul.vd-list li', user: 'a.v-author', text: 'a.title', }, { index: '.video-page-card, .video-page-operator-card', user: 'div.up', text: '.title', }, { index: '.rank-list li.item', user: null, text: '> a', }, { c: 2, index: '.storey-box .spread-module', bv: 'a', text: 'p.t', }, { index: '.ebox', user: '.author', text: '.etitle', url: ['www.bilibili.com/video/', 'www.bilibili.com/bangumi/'], comment: true, }, { index: '.article-list li', user: '.nick-name', text: '.article-title', url: 'www.bilibili.com/read/ranking', }, { index: '.rank-video-card, .video-card', user: '.up-name', text: '.video-name', url: ['www.bilibili.com/v/channel', 'www.bilibili.com/v/popular'], }, { index: '.video-item', user: 'a.up-name', text: 'a.title', url: 'search.bilibili.com', }, { index: '.live-user-item', user: '.uname', text: null, method: 0, type: { live: true, }, url: 'search.bilibili.com', }, { index: '.live-room-item', user: '.uname span', text: '.item-title', method: 0, type: { live: true, }, url: 'search.bilibili.com', }, { index: '.photo-item', user: '.up-name', text: '.title', method: 0, type: { pic: true, }, url: 'search.bilibili.com', }, { index: '.rank-item', user: '.room-anchor', text: '.room-title', method: 0, type: { live: true, }, url: 'live.bilibili.com', comment: true, }, { index: '.room-card-wrapper', user: '.room-anchor > span', text: '.room-title', method: 0, type: { live: true, }, url: 'live.bilibili.com', }, { index: '.ysly-room-ctnr li', user: '.uname', text: '.room-name', method: 0, type: { live: true, }, url: 'live.bilibili.com', }, { index: 'ul.list li', user: '.room-anchor > span', text: '.room-title', method: 0, type: { live: true, }, url: 'live.bilibili.com', }, { index: '.card-items li', user: '.uname', text: '.room-name', method: 0, type: { live: true, }, }, { index: '.content li', user: '.user-container a span', text: '.article-title a', method: 0, type: { pic: true, }, url: 'h.bilibili.com', comment: true, }, { index: '.rank-list > div', user: ['.name', '.user-name'], text: ['.title', '.work-name'], method: 0, type: { pic: true, }, url: 'h.bilibili.com', }, { index: '.canvas-card', user: '.user-container a span', text: '.article-title a', method: 1, type: { pic: true, }, url: 'h.bilibili.com', }, ]; let bilibiliConfig = { functionEnable: true, usernameEnable: true, keywordEnable: true, whitelistEnable: false, commentEnable: false, commentKeywordEnable: false, commentFans: false, convertEmojiEnable: false, showBlockUserBtnEnable: false, showBangBtnEnable: false, liveEnable: false, picEnable: false, messageReplyEnable: false, messageReplyDelEnable: false, dynamicVideo: false, dynamicContent: false, usernameArray: [], keywordArray: [], commentArray: [], whitelistArray: [], }; let infoRecord = []; const tempRecord = Block_Obj.GM.getValue('infoRecord', []); tempRecord.forEach(item => { if (dayjs().diff(item.time, 'd') <= 3) { infoRecord.push(item); } }); let delNum = 0; let recordButton = []; let requestTotal = 0; let sendStatus = true; const INTERVAL_TIME = 100; if (typeof Block_Obj !== 'function') { alert('Block_Obj.js was not loaded successfully.'); } else if (typeof Block_Obj.fn.compare !== 'function') { alert('The version of Block_Obj.js is too low.'); } let blockObj = new Block_Obj('bilibili_config', [ { key: 'keywordArray', ori: 'regArray', }, { key: 'commentArray', ori: 'commentRegArray', }, ]); await document.arrive('body', { fireOnAttributesModification: true, onceOnly: true, existing: true }, async function () { await blockObj.init({ id: 'bilibiliConfig', menu: 'bilibili_屏蔽设置', style: BASIC_STYLE, field: [ { id: 'functionEnable', label: '启用屏蔽功能', title: '总开关', type: 'c', default: true, }, { id: 'whitelistEnable', label: '启用白名单', title: '白名单用户的视频(或直播间/相薄)以及评论(或回复)不会被屏蔽', type: 'c', default: false, move_right: true, }, { label: '屏蔽视频(或直播间/相薄):', type: 's', }, { id: 'usernameEnable', label: '按用户名', title: '屏蔽指定用户发布的视频(或直播间/相薄)', type: 'c', default: true, }, { id: 'keywordEnable', label: '按关键字或正则', title: '屏蔽标题中包含指定关键字或匹配正则表达式的视频(或直播间/相薄)', type: 'c', default: true, move_right: true, }, { id: 'liveEnable', label: '直播间', title: '扩展作用范围以同时允许屏蔽直播间', type: 'c', default: false, move_right: true, }, { id: 'picEnable', label: '相薄', title: '扩展作用范围以同时允许屏蔽相薄', type: 'c', default: false, move_right: true, }, { id: 'dynamicVideo', label: '动态', title: '允许屏蔽转发、分享指定用户的动态\n允许屏蔽视频标题匹配关键字或正则的动态', type: 'c', default: false, move_right: true, }, { label: '屏蔽评论(或回复):', type: 's', }, { id: 'commentEnable', label: '按用户名', title: '屏蔽指定用户发布的评论(或回复)', type: 'c', default: false, }, { id: 'commentKeywordEnable', label: '按关键字或正则', title: '屏蔽内容中包含指定关键字或匹配正则表达式的评论(或回复)', type: 'c', default: false, move_right: true, }, { id: 'commentFans', label: '按粉丝勋章', title: '屏蔽直播间中挂有指定粉丝勋章用户发布的弹幕评论\n屏蔽动态、视频播放等页面中挂有指定粉丝勋章用户发布的评论', type: 'c', default: false, move_right: true, }, { id: 'dynamicContent', label: '动态', title: '允许屏蔽动态内容(包含转发、分享)匹配关键字或正则的动态', type: 'c', default: false, move_right: true, }, { type: 'br', }, { id: 'convertEmojiEnable', label: '表情转成文字', title: '判定时将表情包转换成对应的标识文字,例:[鸡腿]、[tv_白眼]等\n注意:使用关键字来匹配表情时,必须包含完整的中括号对;\n如 "鸡腿" 是无法匹配表情 [鸡腿] 的,需使用 "[鸡腿]" 进行匹配', type: 'c', default: false, }, { id: 'showBlockUserBtnEnable', label: '显示屏蔽用户按钮', title: '在评论在底部显示一个屏蔽该用户的按钮', type: 'c', default: false, move_right: true, }, { id: 'showBangBtnEnable', label: '显示"爆炸"按钮', title: '在评论底部显示一个可以拆分并选择文本内容的按钮', type: 'c', default: false, move_right: true, }, { type: 'br', }, { id: 'messageReplyEnable', label: '消息中心里的回复', title: '扩展作用范围以同时允许屏蔽消息中心里的回复', type: 'c', default: false, }, { id: 'messageReplyDelEnable', label: '自动删除回复通知', title: '同时将屏蔽的回复通知自动删除\n删除的记录可在控制台中查看\n请谨慎启用该选项,因为删除操作是不可逆的!', type: 'c', default: false, move_right: true, }, { type: 's', }, { type: 's', label: '白名单 (用户名):', classname: MODULE.WHITELIST.className, }, { id: 'whitelistInput', label: '输入:', placeholder: ' 同时输入多个时以半角逗号分隔 ', type: 'i', list_id: 'whitelistArray', classname: MODULE.WHITELIST.className, }, { id: 'whitelistArray', type: 'l', default: [], classname: MODULE.WHITELIST.className, }, { type: 's', }, { type: 's', label: '黑名单 (用户名/粉丝勋章名):', classname: MODULE.USERNAME.className, }, { id: 'usernameInput', label: '输入:', placeholder: ' 同时输入多个时以半角逗号分隔 ', type: 'i', list_id: 'usernameArray', classname: MODULE.USERNAME.className, }, { id: 'usernameArray', type: 'l', default: [], classname: MODULE.USERNAME.className, }, { type: 's', }, { type: 's', label: '视频(或直播间/相薄)关键字或正则:', classname: MODULE.VIDEO_KEYWORD.className, }, { id: 'videoKeywordInput', label: '输入:', placeholder: ' 正则表达式格式: /Pattern/Modifier ', type: 'i', list_id: 'keywordArray', classname: MODULE.VIDEO_KEYWORD.className, }, { id: 'keywordArray', type: 'l', default: [], classname: MODULE.VIDEO_KEYWORD.className, }, { type: 's', }, { type: 's', label: '评论(或回复)关键字或正则:', classname: MODULE.COMMENT_KEYWORD.className, }, { id: 'commentKeywordInput', label: '输入:', placeholder: ' 正则表达式格式: /Pattern/Modifier ', type: 'i', list_id: 'commentArray', classname: MODULE.COMMENT_KEYWORD.className, }, { id: 'commentArray', type: 'l', default: [], classname: MODULE.COMMENT_KEYWORD.className, }, { type: 's', }, ], events: { save: config => { bilibiliConfig = config; hideEvent(); }, change: config => { bilibiliConfig = config; hideEvent(); }, }, }); bilibiliConfig = blockObj.getConfig(); hideEvent(); try { let observer = new MutationObserver(() => { hideEvent(); }); observer.observe(document.querySelector('body'), { childList: true, subtree: true, }); } catch (e) { console.error(e); } if (/www\.bilibili\.com\/?(\/\?spm_id_from=.*)?$/.test(OLD_URL)) { document.querySelector('.btn.next') && document.querySelector('.btn.next').addEventListener('click', () => { setTimeout(() => { hideEvent(); }, 250); }); document.querySelector('.btn.prev') && document.querySelector('.btn.prev').addEventListener('click', () => { setTimeout(() => { hideEvent(); }, 250); }); document.body.arrive( '.manga-panel .btn-change', { fireOnAttributesModification: true, onceOnly: true, existing: true, }, item => { item.addEventListener('click', () => { setTimeout(() => { hideEvent(); }, 1000); }); } ); document.body.arrive( '.manga-panel .tab-switch-item', { fireOnAttributesModification: true, onceOnly: true, existing: true, }, item => { item.addEventListener('click', () => { setTimeout(() => { hideEvent(); }, 1000); }); } ); } if (/live\.bilibili\.com\/all/.test(OLD_URL)) { document.body.arrive( '.content-panel h1.title > span', { fireOnAttributesModification: true, onceOnly: true, existing: true, }, item => { item.addEventListener('click', () => { setTimeout(() => { hideEvent(); }, 1000); }); } ); } }); function displayDel(panelId, num) { if (document.getElementById(panelId)) { document.getElementById(panelId).textContent = ' (自动删除了 ' + num + ' 条通知)'; } else { const delPanel = document.createElement('span'); delPanel.id = panelId; delPanel.textContent = ' (自动删除了 ' + num + ' 条通知)'; document.querySelector('.space-right-top .title').appendChild(delPanel); } } function decideText( textValue, isComment = false, isLive = false, isPic = false, sourceText = null, isMessageReply = false, dynamicVideo = null, dynamic = null, repost = null ) { let isDecide = false; let isDecideComment = false; let isDecideDynamic = false; let isDecideDynamicTitle = false; let isDecideDynamicContent = false; let isDecideDynamicRepost = false; if (bilibiliConfig.functionEnable) { if (textValue) { if (isComment) { if (isMessageReply) { if (bilibiliConfig.messageReplyEnable) { isDecideComment = true; } } else { isDecideComment = true; } } else if (isLive) { if (bilibiliConfig.liveEnable) { isDecide = true; } } else if (isPic) { if (bilibiliConfig.picEnable) { isDecide = true; } } else { isDecide = true; } } else { if (bilibiliConfig.dynamicVideo && dynamicVideo) { isDecideDynamic = true; isDecideDynamicTitle = true; } if (bilibiliConfig.dynamicContent && dynamic) { isDecideDynamic = true; isDecideDynamicContent = true; } if (bilibiliConfig.dynamicContent && repost) { isDecideDynamic = true; isDecideDynamicRepost = true; } } } if (isDecide) { if (bilibiliConfig.keywordEnable) { for (let k of bilibiliConfig.keywordArray) { if (k) { if (typeof k === 'string' && textValue.includes(k)) { return true; } else { try { if (textValue.match(k)) { return true; } } catch (e) { console.error('存在错误的正则表达式: ', e); } } } } } } else if (isDecideComment) { if (bilibiliConfig.commentKeywordEnable) { for (let i of bilibiliConfig.commentArray) { if (i) { if (typeof i === 'string') { if (textValue.includes(i)) { if (sourceText) { if (sourceText.includes(i)) { return true; } else if (/\[.*\]/i.test(i)) { return true; } } else { return true; } } else if (sourceText && /\[.*\]/i.test(i)) { if (sourceText.includes(i)) { return true; } } } else { try { if (textValue.match(i)) { return true; } else if (sourceText.match(i)) { return true; } } catch (e) { console.error('存在错误的正则表达式: ', e); } } } } } } else if (isDecideDynamic) { let dynamicStatus = false; if (isDecideDynamicTitle) { if (bilibiliConfig.keywordEnable) { for (let o of bilibiliConfig.keywordArray) { if (o) { if (typeof o === 'string' && dynamicVideo.includes(o)) { dynamicStatus = true; break; } else { try { if (dynamicVideo.match(o)) { dynamicStatus = true; break; } } catch (e) { console.error('存在错误的正则表达式: ', e); } } } } } } if (!dynamicStatus && dynamic.content && isDecideDynamicContent) { if (bilibiliConfig.commentKeywordEnable) { for (const q of bilibiliConfig.commentArray) { if (q) { if (typeof q === 'string') { if (dynamic.content.includes(q)) { if (dynamic.sourceContent) { if (dynamic.sourceContent.includes(q)) { dynamicStatus = true; break; } else if (/\[.*\]/i.test(q)) { dynamicStatus = true; break; } } else { dynamicStatus = true; break; } } else if (dynamic.sourceContent && /\[.*\]/i.test(q)) { if (dynamic.sourceContent.includes(q)) { dynamicStatus = true; break; } } } else { try { if (dynamic.content.match(q)) { dynamicStatus = true; break; } else if (dynamic.sourceContent.match(q)) { dynamicStatus = true; break; } } catch (e) { console.error('存在错误的正则表达式: ', e); } } } } } } if (!dynamicStatus && repost.content && isDecideDynamicRepost) { if (bilibiliConfig.commentKeywordEnable) { for (const r of bilibiliConfig.commentArray) { if (r) { if (typeof r === 'string') { if (repost.content.includes(r)) { if (repost.sourceContent) { if (repost.sourceContent.includes(r)) { dynamicStatus = true; break; } else if (/\[.*\]/i.test(r)) { dynamicStatus = true; break; } } else { dynamicStatus = true; break; } } else if (repost.sourceContent && /\[.*\]/i.test(r)) { if (repost.sourceContent.includes(r)) { dynamicStatus = true; break; } } } else { try { if (repost.content.match(r)) { dynamicStatus = true; break; } else if (repost.sourceContent.match(r)) { dynamicStatus = true; break; } } catch (e) { console.error('存在错误的正则表达式: ', e); } } } } } } return dynamicStatus; } return false; } function decideUsername( username, isComment = false, isLive = false, isPic = false, isMessageReply = false, trueLove = null, repostUser = null ) { let isDecide = false; if (bilibiliConfig.functionEnable && username) { if (isComment) { if (bilibiliConfig.commentEnable) { if (isMessageReply) { if (bilibiliConfig.messageReplyEnable) { isDecide = true; } } else { isDecide = true; } } } else if (isLive) { if (bilibiliConfig.liveEnable) { if (bilibiliConfig.usernameEnable) { isDecide = true; } } } else if (isPic) { if (bilibiliConfig.picEnable) { if (bilibiliConfig.usernameEnable) { isDecide = true; } } } else { if (bilibiliConfig.usernameEnable) { isDecide = true; } } } if (isDecide) { if (bilibiliConfig.usernameArray.includes(username)) { return true; } } if (bilibiliConfig.functionEnable) { if (bilibiliConfig.commentFans && trueLove) { if (bilibiliConfig.usernameArray.includes(trueLove)) { return true; } } if (bilibiliConfig.dynamicVideo && repostUser) { if (bilibiliConfig.usernameArray.includes(repostUser)) { return true; } } } return false; } function isWhitelist(username) { if (username && bilibiliConfig.functionEnable && bilibiliConfig.whitelistEnable) { if (bilibiliConfig.whitelistArray.includes(username)) { return true; } } return false; } function hideHandler(itemNode, username, textValue, method = 0, type = {}) { if (username) { if (typeof username === 'object') { username = username.textContent; } username = username.trim(); } if (textValue) { if (typeof textValue === 'object') { textValue = textValue.textContent; } textValue = textValue.trim(); } const isComment = type.comment ? true : false; const isMessageReply = type.messageReply ? true : false; const delButton = type.delButton ? type.delButton : null; const isLive = type.live ? true : false; const isPic = type.pic ? true : false; const trueLove = type.trueLove ? type.trueLove.trim() : null; const dynamic = type.dynamic != null && typeof type.dynamic === 'object' ? type.dynamic : null; const dynamicVideo = type.dynamicVideo ? type.dynamicVideo.trim() : null; const repost = type.repost != null && typeof type.repost === 'object' ? type.repost : null; const repostUser = type.repostUser ? type.repostUser.trim() : null; const sourceText = type.sourceText ? type.sourceText : null; let hideStatus = false; if (isWhitelist(username)) { hideStatus = false; } else if (decideUsername(username, isComment, isLive, isPic, isMessageReply, trueLove, repostUser)) { hideStatus = true; } else if (decideText(textValue, isComment, isLive, isPic, sourceText, isMessageReply, dynamicVideo, dynamic, repost)) { hideStatus = true; } else { hideStatus = false; } if (itemNode.constructor == Array) { for (let eleNode of itemNode) { if (eleNode) { Block_Obj.fn.hideOperation(eleNode, hideStatus, method); } } } else { Block_Obj.fn.hideOperation(itemNode, hideStatus, method); } if (hideStatus) { if (delButton) { if (bilibiliConfig.messageReplyDelEnable && !recordButton.includes(delButton)) { recordButton.push(delButton); delButton.click(); console.info('%c自动删除通知:', 'color: purple;', '\n用户名:', username, '\n评论内容:', textValue); delNum++; displayDel('messageDelPanel', delNum); } } } } function extractEle(ele, selector) { let r###lt = null; if (selector) { if (Array.isArray(selector)) { for (const e of selector) { if (ele.querySelector(e)) { r###lt = ele.querySelector(e); break; } } } else { r###lt = ele.querySelector(selector); } } return r###lt; } function hideEvent() { handler.forEach(item => { const { c, index, user, text, method, type, userReg, url, comment, bv } = item; let status = false; if (url) { if (Array.isArray(url)) { for (let u of url) { if (OLD_URL.indexOf(u) !== -1) { status = true; break; } } } else { status = OLD_URL.indexOf(url) !== -1; } } else { status = OLD_URL.indexOf('www.bilibili.com') !== -1; } if (status) { const all = document.querySelectorAll(index); for (const ele of all) { if (c == 1) { hideHandler(ele, extractEle(ele, user).textContent.replace(userReg, '')); } else if (c == 2) { const bvNum = getBvNumber(ele.querySelector(bv).href); asyncUsernameHandle(bvNum, ele, extractEle(ele, text)); } else { hideHandler(ele, extractEle(ele, user), extractEle(ele, text), method, type); } } if (comment) { hideComment(); } } }); if (OLD_URL.indexOf('www.bilibili.com') !== -1) { try { const carouselModulePanel = document.querySelector('.carousel-module .panel'); if (carouselModulePanel) { const carouselModulePanelTitle = carouselModulePanel.querySelectorAll('ul.title a'); const carouselModulePanelPic = carouselModulePanel.querySelectorAll('ul.pic li'); const carouselModulePanelTrig = carouselModulePanel.querySelectorAll('ul.trig span'); for (let panelIndex = 0; panelIndex < carouselModulePanelTitle.length; panelIndex++) { hideHandler( [carouselModulePanelTitle[panelIndex], carouselModulePanelPic[panelIndex], carouselModulePanelTrig[panelIndex]], null, carouselModulePanelTitle[panelIndex], 3 ); } } } catch (e) { console.error('bilibili_BLock: Variable carouselModulePanel is error.'); console.error(e); } const rankItem = document.getElementsByClassName('rank-item'); for (const rankItemEle of rankItem) { let textValue = ''; if (rankItemEle.querySelector('p.ri-title')) { textValue = rankItemEle.querySelector('p.ri-title'); } if (rankItemEle.querySelector('a.title')) { textValue = rankItemEle.querySelector('a.title'); } if (rankItemEle.querySelector('.detail > a')) { hideHandler(rankItemEle, rankItemEle.querySelector('.detail > a'), textValue); } else if (rankItemEle.querySelector('a')) { const linkA = rankItemEle.querySelector('a'); const bvNum = getBvNumber(linkA.href); asyncUsernameHandle(bvNum, rankItemEle, textValue); } } const recentHot = document.querySelectorAll('div#recent_hot li'); for (const recentHotItem of recentHot) { const bvNum = getBvNumber(recentHotItem.querySelector('a').href); asyncUsernameHandle(bvNum, recentHotItem, recentHotItem.title); } const bilibiliPlayerRecommendVideo = document.getElementsByClassName('bilibili-player-recommend-video'); for (const bilibiliPlayerRecommendVideoItem of bilibiliPlayerRecommendVideo) { const bvNum = getBvNumber(bilibiliPlayerRecommendVideoItem.href); asyncUsernameHandle( bvNum, bilibiliPlayerRecommendVideoItem, bilibiliPlayerRecommendVideoItem.querySelector('.bilibili-player-recommend-title') ); } const bilibiliPlayerEndingPanelBoxRecommend = document.querySelectorAll('a.bilibili-player-ending-panel-box-recommend'); for (const bilibiliPlayerEndingPanelBoxRecommendItem of bilibiliPlayerEndingPanelBoxRecommend) { let bvNum = ''; try { bvNum = /(?:av|bv)(\w+)/i.exec(bilibiliPlayerEndingPanelBoxRecommendItem.getAttribute('data-bvid'))[1]; } catch (e) { bvNum = null; } if (!bvNum) { try { bvNum = getBvNumber(bilibiliPlayerEndingPanelBoxRecommendItem.href); } catch (e) { bvNum = null; } } asyncUsernameHandle( bvNum, bilibiliPlayerEndingPanelBoxRecommendItem, bilibiliPlayerEndingPanelBoxRecommendItem.querySelector('.bilibili-player-ending-panel-box-recommend-cover-title') ); } } else if (/(t|manga|space)\.bilibili\.com/.test(OLD_URL)) { const card = document.querySelectorAll('div.card'); for (const cardItem of card) { const contentFull = cardItem.querySelector('.content-full'); let sourceContent = null; let convertText = null; if (contentFull && !contentFull.closest('.repost')) { sourceContent = contentFull.textContent; if (bilibiliConfig.convertEmojiEnable) { convertText = getConvertText(contentFull.innerHTML); } } const title = cardItem.querySelector('.title'); let titleText = null; if (title) { titleText = title.textContent; } let repostUser = cardItem.querySelector('.repost .username'); repostUser = repostUser ? repostUser.textContent : null; let repostSourceText = null; let repostConvertText = null; const repostContent = cardItem.querySelector('.repost .content-full'); if (repostContent) { repostSourceText = repostContent.textContent; if (bilibiliConfig.convertEmojiEnable) { repostConvertText = getConvertText(repostContent.innerHTML); } } if (bilibiliConfig.convertEmojiEnable) { hideHandler(cardItem, null, null, 0, { dynamicVideo: titleText, dynamic: { content: convertText, sourceContent: sourceContent, }, repostUser, repost: { content: repostConvertText, sourceContent: repostSourceText, }, }); } else { hideHandler(cardItem, null, null, 0, { dynamicVideo: titleText, dynamic: { content: sourceContent, }, repostUser, repost: { content: repostSourceText, }, }); } } hideComment(); } else if (/message\.bilibili\.com\/#\/reply/.test(OLD_URL)) { const replyItem = document.getElementsByClassName('reply-item'); for (const replyItemEle of replyItem) { let nextNode = null; if (replyItemEle.nextElementSibling) { if (replyItemEle.nextElementSibling.classList.contains('divider')) { nextNode = replyItemEle.nextElementSibling; } } const sourceText = replyItemEle.querySelector('.text').textContent; if (bilibiliConfig.convertEmojiEnable) { const convertText = replyItemEle.querySelector('.text span').innerHTML.replace(/<img.*alt="(.*)".*>/g, '$1'); hideHandler([replyItemEle, nextNode], replyItemEle.querySelector('.name-field a'), convertText, 0, { comment: true, messageReply: true, sourceText: sourceText, delButton: replyItemEle.querySelector('.bl-button--primary'), }); } else { hideHandler([replyItemEle, nextNode], replyItemEle.querySelector('.name-field a'), sourceText, 0, { comment: true, messageReply: true, delButton: replyItemEle.querySelector('.bl-button--primary'), }); } } } else if (/live\.bilibili\.com\/\d+/.test(OLD_URL)) { const chatItems = document.querySelectorAll('#chat-items .chat-item'); chatItems.forEach(item => { const fansMedalContent = item.querySelector('.fans-medal-content'); hideHandler(item, null, null, 0, { trueLove: fansMedalContent ? fansMedalContent.textContent : null, }); }); } } function hideComment() { const commentList = document.querySelectorAll('.comment-list .list-item'); for (const commentListItem of commentList) { const sourceText = commentListItem.querySelector('.con > p.text').textContent; let trueLove = commentListItem.querySelector('.true-love'); trueLove = trueLove ? trueLove.firstChild.textContent : null; if (bilibiliConfig.convertEmojiEnable) { const convertText = getConvertText(commentListItem.querySelector('.con > p.text').innerHTML); hideHandler(commentListItem, commentListItem.querySelector('.con > .user a.name'), convertText, 0, { comment: true, sourceText: sourceText, trueLove, }); } else { hideHandler(commentListItem, commentListItem.querySelector('.con > .user a.name'), sourceText, 0, { comment: true, trueLove, }); } const commentReplyBtn = commentListItem.querySelector('.reply.btn-hover'); if (bilibiliConfig.showBlockUserBtnEnable) { commentReplyBtn && !commentListItem.querySelector('.bilibili_comment_user_block_button') && commentReplyBtn.after( blockObj.createBlockBtn( commentListItem.querySelector('.con > .user a.name').textContent, 'usernameArray', 'bilibili_comment_user_block_button', 'span', '屏蔽', '屏蔽该用户' ) ); } else { commentListItem.querySelector('.bilibili_comment_user_block_button') && commentListItem.querySelector('.bilibili_comment_user_block_button').remove(); } if (bilibiliConfig.showBangBtnEnable) { const commentBtn = commentListItem.querySelector('.bilibili_comment_user_block_button') || commentReplyBtn; commentBtn && !commentListItem.querySelector('.bilibili_comment_bang_button') && commentBtn.after( blockObj.createBigBangBtn( sourceText, 'commentArray', 'bilibili_comment_bang_button', 'span', '爆炸', '拆分并选择文本内容进行屏蔽' ) ); } else { commentListItem.querySelector('.bilibili_comment_bang_button') && commentListItem.querySelector('.bilibili_comment_bang_button').remove(); } } const replyCommentList = document.querySelectorAll('.comment-list .reply-item'); for (const replyCommentListItem of replyCommentList) { const replySourceText = replyCommentListItem.querySelector('.reply-con .text-con').textContent; if (bilibiliConfig.convertEmojiEnable) { const replyConvertText = getConvertText(replyCommentListItem.querySelector('.reply-con .text-con').innerHTML); hideHandler(replyCommentListItem, replyCommentListItem.querySelector('.reply-con .user a.name'), replyConvertText, 0, { comment: true, sourceText: replySourceText, }); } else { hideHandler(replyCommentListItem, replyCommentListItem.querySelector('.reply-con .user a.name'), replySourceText, 0, { comment: true, }); } const replyBtn = replyCommentListItem.querySelector('.reply.btn-hover'); if (bilibiliConfig.showBlockUserBtnEnable) { replyBtn && !replyCommentListItem.querySelector('.bilibili_reply_user_block_button') && replyBtn.after( blockObj.createBlockBtn( replyCommentListItem.querySelector('.reply-con .user a.name').textContent, 'usernameArray', 'bilibili_reply_user_block_button', 'span', '屏蔽', '屏蔽该用户' ) ); } else { replyCommentListItem.querySelector('.bilibili_reply_user_block_button') && replyCommentListItem.querySelector('.bilibili_reply_user_block_button').remove(); } if (bilibiliConfig.showBangBtnEnable) { const pBtn = replyCommentListItem.querySelector('.bilibili_reply_user_block_button') || replyBtn; pBtn && !replyCommentListItem.querySelector('.bilibili_reply_bang_button') && pBtn.after( blockObj.createBigBangBtn( replySourceText, 'commentArray', 'bilibili_reply_bang_button', 'span', '爆炸', '拆分并选择文本内容进行屏蔽' ) ); } else { replyCommentListItem.querySelector('.bilibili_reply_bang_button') && replyCommentListItem.querySelector('.bilibili_reply_bang_button').remove(); } } } function getConvertText(text) { return text .replace(/<img.*?alt="(.*?)".*?>/g, '$1') .replace(/<a.*?>(.*?)<\/\s*a>/g, '$1') .replace(/ /g, ' '); } function asyncUsernameHandle(bvNum, mainEle, textValue, hideMethod = 0, typeInfo = {}) { let userName = ''; if (bvNum) { let recordUser = false; infoRecord.forEach(item => { if (item.bv == bvNum) { userName = item.user; recordUser = true; } }); if (recordUser) { hideHandler(mainEle, userName, textValue, hideMethod, typeInfo); } else { infoRecord.push({ bv: bvNum, user: userName, time: dayjs().format('YYYY-MM-DD'), }); const apiUrl = bvNum.match(/^\d+$/) ? 'https://api.bilibili.com/x/web-interface/view?aid=' : 'https://api.bilibili.com/x/web-interface/view?bvid='; const xhr = new XMLHttpRequest(); xhr.open('GET', apiUrl + bvNum, true); xhr.responseType = 'json'; xhr.onload = () => { if (xhr.status == 200) { if (xhr.response.data && xhr.response.data.owner && xhr.response.data.owner['name']) { userName = xhr.response.data.owner['name']; } } else { sendStatus = false; console.info(apiUrl + bvNum + '\nresponse status: ' + xhr.status); } hideHandler(mainEle, userName, textValue, hideMethod, typeInfo); infoRecord.forEach(item => { if (item.bv == bvNum) { item.user = userName; } }); Block_Obj.GM.setValue('infoRecord', infoRecord); }; xhr.onerror = () => { console.info(apiUrl + bvNum + '\nerror.'); hideHandler(mainEle, userName, textValue, hideMethod, typeInfo); }; setTimeout(() => { sendStatus && xhr.send(); }, INTERVAL_TIME * requestTotal); requestTotal++; } } else { hideHandler(mainEle, userName, textValue, hideMethod, typeInfo); } } function getBvNumber(video_link) { let bvNum = ''; try { bvNum = /\/video\/(?:av|bv)(\w+)/i.exec(video_link)[1]; } catch (e) { bvNum = null; } return bvNum; } })();