哔哩哔哩 - 屏蔽指定内容

实现可分别按用户名、关键字或正则表达式对视频(或直播间/相薄)和评论(或回复)进行屏蔽; 将鼠标移至网页右下角弹出悬浮按钮

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==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/arrive@2.4.1/minified/arrive.min.js
// @require      https://greasyfork.org/scripts/407543-block-obj/code/Block_Obj.js?version=963893
// @require      https://unpkg.com/dayjs@1.8.21/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 result = null;
    if (selector) {
      if (Array.isArray(selector)) {
        for (const e of selector) {
          if (ele.querySelector(e)) {
            result = ele.querySelector(e);
            break;
          }
        }
      } else {
        result = ele.querySelector(selector);
      }
    }
    return result;
  }
  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(/&nbsp;/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;
  }
})();