哔哩哔哩 - 屏蔽指定内容

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

// ==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;
  }
})();