全网内容屏蔽

屏蔽特定用户的帖子和评论

目前為 2024-08-16 提交的版本,檢視 最新版本

// ==UserScript==
// @name              全网内容屏蔽
// @name:zh           网络内容过滤器
// @namespace         Violentmonkey Scripts
// @match             *://*.weibo.com/*
// @match             *://*.weibo.cn/*
// @match             *://weibo.com/*
// @match             *://m.hupu.com/*
// @match             *://tieba.baidu.com/*
// @match             *://www.zhihu.com/*
// @match             *://www.zhihu.com
// @match             *://*.bilibili.com/*
// @exclude           *://weibo.com/tv*
// @grant             GM.getValue
// @grant             GM.setValue
// @version           4.0.1
// @author            no one
// @description       屏蔽特定用户的帖子和评论
// @description:zh    屏蔽特定用户的帖子和评论
// @run-at            document-start
// @require           https://update.gf.qytechs.cn/scripts/472943/1320613/Itsnotlupus%27%20MiddleMan.js
// ==/UserScript==

(() => {
  var __defProp = Object.defineProperty;
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
  var __decorateClass = (decorators, target, key, kind) => {
    var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
    for (var i = decorators.length - 1, decorator; i >= 0; i--)
      if (decorator = decorators[i])
        result = (kind ? decorator(target, key, result) : decorator(result)) || result;
    if (kind && result) __defProp(target, key, result);
    return result;
  };
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);

  // src/utils/store.ts
  var DomainStore = class _DomainStore {
    static NgListKey = "NgList";
    static domainKeyPrefix;
    static listener(user) {
      const store = new _DomainStore();
      store.addUser(user);
    }
    static {
      const domain = document.location.host;
      let segs = domain.split(".");
      if (segs.length > 2) segs = segs.slice(1);
      this.domainKeyPrefix = `${segs.join(".")}:${this.NgListKey}`;
    }
    static async init() {
      let blockedUsers = [];
      const value = await GM.getValue(_DomainStore.blockUsersKey());
      if (value === void 0) {
        const list = await this.loadBlackList();
        blockedUsers = list.map((name) => {
          if (this.domainKeyPrefix.includes("zhihu")) {
            return {
              id: name,
              name: "",
              blockedDate: /* @__PURE__ */ new Date()
            };
          } else {
            return {
              name,
              id: "",
              blockedDate: /* @__PURE__ */ new Date()
            };
          }
        });
        const value2 = JSON.stringify(blockedUsers);
        await GM.setValue(_DomainStore.blockUsersKey(), value2);
      } else {
        blockedUsers = JSON.parse(value);
        blockedUsers.forEach((user) => {
          user.name = user.name.trim();
        });
      }
      const val = await GM.getValue(_DomainStore.patternKey());
      const patterns = JSON.parse(val || "[]");
      return new _DomainStore(blockedUsers, patterns);
    }
    nameMap = /* @__PURE__ */ new Map();
    // private idMap: Map<string | number, BlockedUser> = new Map();
    excludePatterns = [];
    get userList() {
      return this.nameMap.keys();
    }
    get patternList() {
      return structuredClone(this.excludePatterns);
    }
    matchPattern(text) {
      text = text.trim();
      return this.excludePatterns.some((pattern) => {
        return text.toLocaleLowerCase().includes(pattern.toLocaleLowerCase());
      });
    }
    addPattern(text) {
      const s = new Set(this.excludePatterns).add(text.trim());
      this.excludePatterns = [...s];
      this.flush();
    }
    removePattern(text) {
      text = text.trim();
      this.excludePatterns = this.excludePatterns.filter((pattern) => {
        return pattern !== text;
      });
      this.flush();
    }
    hasUser({ name, id }) {
      if (!name) {
        return false;
      }
      return this.nameMap.has(name.trim());
    }
    addUser({ name, id }) {
      name = name.trim();
      const user = { name, id, blockedDate: /* @__PURE__ */ new Date() };
      if (user.name) this.nameMap.set(name, user);
      this.flush();
    }
    removeUser({ name, id }) {
      if (name) this.nameMap.delete(name.trim());
      this.flush();
    }
    static blockUsersKey() {
      return `${_DomainStore.domainKeyPrefix}:blockedusers`;
    }
    static patternKey() {
      return `${_DomainStore.domainKeyPrefix}:patterns`;
    }
    async flush() {
      const blockedUsers = [...this.nameMap.values()];
      const val = JSON.stringify(blockedUsers);
      await GM.setValue(_DomainStore.blockUsersKey(), val);
      const pv = JSON.stringify(this.excludePatterns);
      await GM.setValue(_DomainStore.patternKey(), pv);
    }
    static singleton;
    constructor(users, patterns) {
      if (_DomainStore.singleton) {
        return _DomainStore.singleton;
      }
      for (const user of users) {
        this.nameMap.set(user.name, user);
      }
      this.excludePatterns = patterns;
      _DomainStore.singleton = this;
    }
    // 获取屏蔽词列表
    static async loadBlackList() {
      const value = await GM.getValue(_DomainStore.domainKeyPrefix);
      if (!value) return [];
      const ret = JSON.parse(value.toString());
      if (!Array.isArray(ret)) return [];
      console.log(`gm value: ${ret}`);
      return ret;
    }
  };

  // src/utils/css.ts
  var BlockButtonClass = "block-button";
  var HoverButtonClass = "hover-button";
  var cssByCls = (cls) => `.${cls}`;
  var ButtonCSSText = `
      ${cssByCls(BlockButtonClass)} {
        cursor: pointer;
        height: 12px;
        width: 12px;
        margin-left: 1px;
        float: inherit;
        background: white;
        border-width: 0;
        padding: 0;
        line-height:0px;
        transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1);
        transform-origin: bottom;
      }

      ${cssByCls(BlockButtonClass)}:hover {
        transform: translateY(0) scale(1.5);
      }`;
  var css = `
      #add_ngList_btn {
        position: fixed;
        bottom: 2rem;
        left: 1rem;
        width: 2rem;
        height: 2rem;
        border-radius: 50%;
        border: 1px solid rgba(0, 0, 0, 0.5);
        background: white;
        display: flex;
        align-items: center;
        justify-content: center;
        cursor: pointer !important;
        z-index: 100;
      }

      #add_ngList_btn::before {
        content: '';
        position: absolute;
        width: 16px;
        height: 2px;
        background: rgba(0, 0, 0, 0.5);
        top: calc(50% - 1px);
        left: calc(50% - 8px);
      }

      #add_ngList_btn::after {
        content: '';
        position: absolute;
        height: 16px;
        width: 2px;
        background: rgba(0, 0, 0, 0.5);
        top: calc(50% - 8px);
        left: calc(50% - 1px);
      }

      .my-dialog__wrapper {
        position: fixed;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        overflow: auto;
        margin: 0;
        z-index: 10000;
        background: rgba(0, 0, 0, 0.3);
        display: none;
      }

      .my-dialog {
        position: relative;
        background: #FFFFFF;
        border-radius: 2px;
        box-shadow: 0 1px 3px rgb(0 0 0 / 30%);
        box-sizing: border-box;
        width: 50%;
        transform: none;
        left: 0;
        margin: 0 auto;
      }

      .my-dialog .my-dialog__header {
        border-bottom: 1px solid #e4e4e4;
        padding: 14px 16px 10px 16px;
      }

      .my-dialog__title {
        line-height: 24px;
        font-size: 18px;
        color: #303133;
      }

      .my-dialog__headerbtn {
        position: absolute;
        top: 20px;
        right: 20px;
        padding: 0;
        background: transparent;
        border: none;
        outline: none;
        cursor: pointer;
        font-size: 16px;
        width: 12px;
        height: 12px;
        transform: rotateZ(45deg);
      }

      .my-dialog .my-dialog__header .my-dialog__headerbtn {
        right: 16px;
        top: 16px;
      }

      .my-dialog__headerbtn .my-dialog__close::before {
        content: '';
        position: absolute;
        width: 12px;
        height: 1.5px;
        background: #909399;
        top: calc(50% - 0.75px);
        left: calc(50% - 6px);
        border-radius: 2px;
      }

      .my-dialog__headerbtn:hover .my-dialog__close::before {
        background: #1890ff;
      }

      .my-dialog__headerbtn .my-dialog__close::after {
        content: '';
        position: absolute;
        height: 12px;
        width: 1.5px;
        background: #909399;
        top: calc(50% - 6px);
        left: calc(50% - 0.75px);
        border-radius: 2px;
      }

      .my-dialog__headerbtn:hover .my-dialog__close::after {
        background: #1890ff;
      }

      .my-dialog__body {
        padding: 30px 20px;
        color: #606266;
        font-size: 14px;
        word-break: break-all;
      }

      .my-dialog__footer {
        padding: 20px;
        padding-top: 10px;
        text-align: right;
        box-sizing: border-box;
      }

      .my-dialog .my-dialog__footer {
        padding: 0px 16px 24px 16px;
        margin-top: 40px;
      }

      #ngList {
        display: flex;
        flex-wrap: wrap;
        justify-content: flex-start;
        max-height: 480px;
        overflow-y: scroll;
      }

      ${ButtonCSSText}

      .close-icon {
        width: 12px;
        height: 12px;
        border-radius: 50%;
        display: inline-block;
        position: relative;
        transform: rotateZ(45deg);
        margin-left: 8px;
        cursor: pointer;
      }

      .close-icon:hover {
        background: #409eff;
      }

      .close-icon::before {
        content: '';
        position: absolute;
        width: 8px;
        height: 2px;
        background: #409eff;
        top: calc(50% - 1px);
        left: calc(50% - 4px);
        border-radius: 2px;
      }

      .close-icon:hover::before {
        background: #fff;
      }

      .close-icon::after {
        content: '';
        position: absolute;
        height: 8px;
        width: 2px;
        background: #409eff;
        top: calc(50% - 4px);
        left: calc(50% - 1px);
        border-radius: 2px;
      }

      .close-icon:hover::after {
        background: #fff;
      }

      .ng_item {
        background-color: #ecf5ff;
        display: inline-flex;
        align-items: center;
        padding: 0 10px;
        font-size: 12px;
        color: #409eff;
        border: 1px solid #d9ecff;
        border-radius: 4px;
        box-sizing: border-box;
        white-space: nowrap;
        height: 28px;
        line-height: 26px;
        margin-left: 12px;
        margin-top: 8px;
      }
      
      .ng_pattern {
        background-color: #ff704d;
        display: inline-flex;
        align-items: center;
        padding: 0 10px;
        font-size: 12px;
        color: #000000;
        text-decoration: line-through;
        border: 1px solid #d9ecff;
        border-radius: 4px;
        box-sizing: border-box;
        white-space: nowrap;
        height: 28px;
        line-height: 26px;
        margin-left: 12px;
        margin-top: 8px;
      }


      .input_container {
        display: flex;
        align-items: center;
        margin-bottom: 12px;
      }

      .el-input {
        position: relative;
        font-size: 14px;
        display: inline-block;
        width: 100%;
      }

      .el-input__inner {
        -webkit-appearance: none;
        background-color: #fff;
        background-image: none;
        border-radius: 4px;
        border: 1px solid #dcdfe6;
        box-sizing: border-box;
        color: #606266;
        display: inline-block;
        font-size: inherit;
        height: 40px;
        line-height: 40px;
        outline: none;
        padding: 0 15px;
        transition: border-color .2s cubic-bezier(.645, .045, .355, 1);
        width: 100%;
        cursor: pointer;
        font-family: inherit;
      }

      .el-button {
        display: inline-block;
        line-height: 1;
        white-space: nowrap;
        cursor: pointer;
        background: #fff;
        border: 1px solid #dcdfe6;
        color: #606266;
        -webkit-appearance: none;
        text-align: center;
        box-sizing: border-box;
        outline: none;
        margin: 0;
        transition: .1s;
        font-weight: 500;
        -moz-user-select: none;
        -webkit-user-select: none;
        -ms-user-select: none;
        padding: 12px 20px;
        font-size: 14px;
        border-radius: 4px;
      }

      .el-button:focus,
      .el-button:hover {
        color: #409eff;
        border-color: #c6e2ff;
        background-color: #ecf5ff;
      }

      .el-button:active {
        color: #3a8ee6;
        border-color: #3a8ee6;
        outline: none;
      }

      .input_container .el-input {
        margin-right: 12px;
      }

      .tips {
        margin-top: 24px;
        font-size: 12px;
        color: #F56C6C;
      }
      
      
      .${HoverButtonClass}:hover {
        filter: opacity(1);
        transition: filter 0.1s linear 0.1s;
      }
      
      .${HoverButtonClass} {
        filter: opacity(0);
        z-index: 10;
        position: absolute;
        top: 8px;
        left: 8px;
        transition: filter 0.1s linear 0s;
      }
    `;
  var svgIcon = `
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
          <circle cx="12" cy="12" r="10" />
          <line x1="4" y1="4" x2="20" y2="20" />
      </svg>
    `;
  function createBlockButton({
    name,
    id,
    subclass,
    listeners,
    css: css2
  }) {
    name = name.trim().replace(/^@/, "").replace("\uFF1A", "");
    const wrapper = document.createElement("div");
    wrapper.innerHTML = svgIcon;
    wrapper.className = BlockButtonClass;
    if (subclass) wrapper.classList.add(subclass);
    wrapper.setAttribute("user-name", name);
    wrapper.setAttribute("user-id", id);
    if (css2) {
      const style = document.createElement("style");
      style.textContent = css2;
      wrapper.appendChild(style);
    }
    wrapper.addEventListener("click", (ev) => {
      ev.preventDefault();
      ev.stopPropagation();
      if (typeof listeners === "function") {
        listeners({ name, id });
      } else {
        listeners.forEach((listener) => {
          listener({ name, id });
        });
      }
    });
    return wrapper;
  }
  function addStyle(cssStyle) {
    if (!cssStyle) return;
    const head = document.querySelector("head");
    const style = document.createElement("style");
    style.type = "text/css";
    style.innerHTML = cssStyle;
    head?.appendChild(style);
  }

  // src/filters/base.ts
  var defaultNameFn = (elem) => {
    const href = elem.getAttribute("href");
    const id = href?.split("/").at(-1);
    return {
      name: elem?.textContent || elem?.getAttribute("aria-label"),
      id
    };
  };
  var defaultContentFn = (elem) => {
    return elem ? elem.textContent : "";
  };
  function newListAndUser(list, user, content) {
    return {
      listSelector: list,
      userSelector: user,
      contentSelector: content ? content : ""
    };
  }
  function RegisterSubclass(target) {
    WebsiteFilter.registerClass(target);
  }
  var WebsiteFilter = class _WebsiteFilter {
    static subclasses;
    static registerClass(FilterClass) {
      if (!_WebsiteFilter.subclasses) {
        _WebsiteFilter.subclasses = [];
      }
      _WebsiteFilter.subclasses.push(FilterClass);
    }
    static fromHost() {
      const host = document.location.host;
      for (const cls of _WebsiteFilter.subclasses) {
        if (host.includes(cls.host)) {
          return new cls();
        }
      }
    }
    renderers = [];
    proxyOpt = {
      requestRouters: [],
      responseRouters: []
    };
    registered = false;
    removeElements(selectors, root = document, nameFn = defaultNameFn, contentFn = defaultContentFn) {
      if (selectors.length < 1) {
        return;
      }
      const selector = selectors[0];
      const list = root.querySelectorAll(selector.listSelector);
      list.forEach((element) => {
        const user = element.querySelector(selector.userSelector);
        if (!user) {
          return;
        }
        let content;
        if (selector.contentSelector) {
          const contentElement = element.querySelector(selector.contentSelector);
          content = contentFn(contentElement);
        } else {
          content = "";
        }
        const poster = nameFn(user);
        if (this.inBlackList(poster, content)) {
          element?.parentNode?.removeChild(element);
        } else {
          this.removeElements(selectors.slice(1), element, nameFn, contentFn);
        }
      });
    }
    inBlackList(name, content = "") {
      const store = new DomainStore();
      return store.hasUser(name) || store.matchPattern(content);
    }
    registerHooks() {
      if (!this.registered) {
        const opt = this.proxyOpt;
        opt.requestRouters.forEach((router) => {
          middleMan.addHook(router.hookMeta.pattern, this.requestHandler(router));
        });
        opt.responseRouters.forEach((router) => {
          middleMan.addHook(
            router.hookMeta.pattern,
            this.responseHandler(router)
          );
        });
        this.registered = true;
      }
    }
    constructor() {
      this.addHooks(
        ...Object.getOwnPropertyNames(Object.getPrototypeOf(this)).filter((name) => Object.getPrototypeOf(this)[name].hookMeta).reduce((hooks, name) => {
          const pd = Object.getOwnPropertyDescriptor(
            Object.getPrototypeOf(this),
            name
          );
          const fn = pd.value.bind(this);
          fn.hookMeta = pd.value.hookMeta;
          hooks.push(fn);
          return hooks;
        }, [])
      );
      this.renderers = Object.getOwnPropertyNames(
        Object.getPrototypeOf(this)
      ).reduce((fns, name) => {
        const method = Object.getOwnPropertyDescriptor(
          Object.getPrototypeOf(this),
          name
        ).value;
        if (method.filterMeta) {
          const fn = method.bind(this);
          fn.filterMeta = method.filterMeta;
          fns.push(fn);
        }
        return fns;
      }, []);
      this.registerHooks();
    }
    addHooks(...hooks) {
      hooks.forEach((hook) => {
        switch (hook.hookMeta.type) {
          case "request":
            this.proxyOpt.requestRouters.push(hook);
            break;
          case "response":
            this.proxyOpt.responseRouters.push(hook);
            break;
        }
      });
    }
    requestHandler(hook) {
      return {
        async requestHandler(request) {
          const h = hook;
          return h(request);
        }
      };
    }
    responseHandler(hook) {
      return {
        async responseHandler(request, response, error) {
          if (error) {
            throw error;
          }
          const res = await response.json();
          const url = request.url;
          const h = hook;
          h(url, res);
          return Response.json(res);
        }
      };
    }
    injectButton({
      listSelector,
      userSelector,
      elementButtonFunc,
      doc = document,
      subListSelector,
      subUserSelector,
      nameFn = defaultNameFn
    }) {
      const list = doc.querySelectorAll(listSelector);
      list.forEach((element) => {
        let user = element.querySelector(userSelector);
        if (!user) {
          return;
        }
        const { name, id } = nameFn(user);
        if (elementButtonFunc)
          elementButtonFunc(element, { name, id });
        if (element.querySelector(`${cssByCls(BlockButtonClass)}`)) {
          return;
        }
        const btn = createBlockButton({
          name,
          id,
          listeners: [DomainStore.listener, this.render.bind(this)]
        });
        while (user && user.parentNode !== element) {
          if (user.parentNode.childElementCount > 1) {
            user.parentNode.appendChild(btn);
            break;
          }
          user = user.parentNode;
        }
        if (user && user.parentNode === element) {
          user.parentNode.appendChild(btn);
        }
      });
      list.forEach((element) => {
        if (subListSelector && subUserSelector) {
          this.injectButton({
            listSelector: subListSelector,
            userSelector: subUserSelector,
            doc: element,
            nameFn
          });
        }
      });
    }
    render() {
      this.renderers.forEach((f) => {
        if (typeof f.filterMeta === "boolean") {
          f();
        } else {
          const href = document.location.href;
          if (href.match(f.filterMeta.pattern)) {
            f();
          }
        }
      });
    }
  };
  function filterFunc(target, name, descriptor) {
    const fn = descriptor.value;
    fn.filterMeta = true;
  }
  function patternFilterFunc(pattern) {
    return (target, name, descriptor) => {
      descriptor.value.filterMeta = { pattern };
    };
  }
  function HookDecorator(type) {
    return (pattern) => {
      return (target, name, descriptor) => {
        descriptor.value.hookMeta = {
          type,
          pattern
        };
      };
    };
  }
  var reqHook = HookDecorator("request");
  var respHook = HookDecorator("response");

  // src/filters/zhihu.ts
  var userFunc = (element) => {
    const href = element.getAttribute("href");
    return {
      id: href.split("/").at(-1),
      name: element.textContent
    };
  };
  var FeedUserFunc = (e) => {
    const metaText = e.dataset.zaExtraModule;
    let id = "";
    if (metaText) {
      const meta = JSON.parse(metaText);
      id = meta?.card?.content?.author_member_hash_id;
    }
    const zop = e.dataset.zop;
    const name = JSON.parse(zop).authorName;
    return { id, name };
  };
  var ZhihuFilter = class extends WebsiteFilter {
    interceptAnalytics() {
      return Response.json({}, { status: 200 });
    }
    interceptTouch() {
      return Response.json({ success: true }, { status: 201 });
    }
    async filterComments() {
      const selector = {
        comments: "div.css-18ld3w0 > div[data-id]",
        commentUser: "div.css-jp43l4 a.css-10u695f",
        commentContent: "div.css-jp43l4 div.CommentContent.css-1jpzztt",
        replies: "div[data-id]",
        replyUser: "a.css-10u695f",
        replyContent: "div.CommentContent.css-1jpzztt"
      };
      this.removeElements(
        [
          newListAndUser(
            selector.comments,
            selector.commentUser,
            selector.commentContent
          ),
          newListAndUser(
            selector.replies,
            selector.replyUser,
            selector.replyContent
          )
        ],
        document,
        userFunc,
        ZhihuFilter.contentFunc
      );
      this.injectButton({
        listSelector: selector.comments,
        userSelector: selector.commentUser,
        subListSelector: selector.replies,
        subUserSelector: selector.replyUser,
        nameFn: userFunc
      });
    }
    static contentFunc(element) {
      return element ? element.textContent + element.querySelector("img")?.getAttribute("alt") : "";
    }
    async filterAnswerComments() {
      const selector = {
        comments: "div.css-16zdamy div[data-id]",
        commentUser: "div.css-1tww9qq a.css-10u695f",
        commentContent: "div.CommentContent.css-1jpzztt"
      };
      this.removeElements(
        [
          newListAndUser(
            selector.comments,
            selector.commentUser,
            selector.commentContent
          )
        ],
        document,
        userFunc,
        ZhihuFilter.contentFunc
      );
      this.injectButton({
        listSelector: selector.comments,
        userSelector: selector.commentUser,
        nameFn: userFunc
      });
    }
    async filterAnswers() {
      const cardSelector = {
        card: "div.Card.AnswerCard.css-0",
        user: "div.ContentItem.AnswerItem"
      };
      this.removeElements(
        [newListAndUser(cardSelector.card, cardSelector.user)],
        document,
        FeedUserFunc
      );
      this.injectButton({
        listSelector: cardSelector.card,
        userSelector: cardSelector.user,
        nameFn: FeedUserFunc
      });
      const selector = {
        answers: "div.List-item",
        answerUser: "div.ContentItem.AnswerItem"
      };
      this.removeElements(
        [newListAndUser(selector.answers, selector.answerUser)],
        document,
        FeedUserFunc
      );
      this.injectButton({
        listSelector: selector.answers,
        userSelector: selector.answerUser,
        nameFn: FeedUserFunc
      });
    }
    async filterRecommends() {
      const selector = {
        listSelector: "div.Card.TopstoryItem.TopstoryItem-isRecommend",
        userSelector: "div.ContentItem.AnswerItem"
      };
      this.removeElements(
        [newListAndUser(selector.listSelector, selector.userSelector)],
        document,
        FeedUserFunc
      );
      this.injectButton({
        listSelector: selector.listSelector,
        userSelector: selector.userSelector,
        nameFn: FeedUserFunc
      });
    }
    async filterQuestions() {
    }
    hookComments(url, res) {
      const comments = res.data;
      res.data = comments.filter((comment) => {
        const id = comment.author.id;
        const name = comment.author.name;
        const content = comment.content;
        return !this.inBlackList({ id, name }, content);
      });
      res.data.forEach((comment) => {
        comment.childComments = comment.childComments?.filter((childComment) => {
          const { id, name } = childComment.author;
          const content = childComment.content;
          return !this.inBlackList({ id, name }, content);
        });
      });
    }
    hookAnswers(url, res) {
      const answers = res.data;
      res.data = answers.filter((answer) => {
        const id = answer.target?.author?.id;
        const name = answer.target.author.name;
        const content = answer.target?.content;
        return !this.inBlackList({ name, id }, content);
      });
    }
    interceptTrends(url, res) {
      res.preset_words.words = [];
    }
    interceptHotList(url, res) {
      res.data = [];
      res.display_num = 0;
      res.display_first = false;
    }
    interceptRecommends(url, res) {
      res.data = [];
    }
  };
  __publicField(ZhihuFilter, "host", "zhihu.com");
  __decorateClass([
    reqHook(/zhihu-web-analytics|datahub|apm/)
  ], ZhihuFilter.prototype, "interceptAnalytics", 1);
  __decorateClass([
    reqHook(/lastread\/touch/)
  ], ZhihuFilter.prototype, "interceptTouch", 1);
  __decorateClass([
    filterFunc
  ], ZhihuFilter.prototype, "filterComments", 1);
  __decorateClass([
    filterFunc
  ], ZhihuFilter.prototype, "filterAnswerComments", 1);
  __decorateClass([
    filterFunc
  ], ZhihuFilter.prototype, "filterAnswers", 1);
  __decorateClass([
    filterFunc
  ], ZhihuFilter.prototype, "filterRecommends", 1);
  __decorateClass([
    filterFunc
  ], ZhihuFilter.prototype, "filterQuestions", 1);
  __decorateClass([
    respHook(/\/root_comment|\/child_comment/)
  ], ZhihuFilter.prototype, "hookComments", 1);
  __decorateClass([
    respHook(/questions\/\d+\/feeds/)
  ], ZhihuFilter.prototype, "hookAnswers", 1);
  __decorateClass([
    respHook(/search\/preset_words/)
  ], ZhihuFilter.prototype, "interceptTrends", 1);
  __decorateClass([
    respHook(/feed\/topstory\/hot-lists/)
  ], ZhihuFilter.prototype, "interceptHotList", 1);
  __decorateClass([
    respHook(/recommend_follow_people/)
  ], ZhihuFilter.prototype, "interceptRecommends", 1);
  ZhihuFilter = __decorateClass([
    RegisterSubclass
  ], ZhihuFilter);

  // src/filters/weibo.ts
  var WeiboFilter = class extends WebsiteFilter {
    apiBlackList = ["/female_version.mp3", "/intake/v2/rum/events"];
    mockRequests() {
      return Response.json({}, { status: 200 });
    }
    interceptRead() {
      const data = {
        data: [
          {
            act: "PC_real_read",
            duration: 1902,
            read_duration: 1902,
            itemid: "5067162012353224",
            type: "adMblog",
            rid: "0_0_1_5135480043505157444_0_0_0",
            page: "",
            root_id: "5058972623834652",
            uicode: 20000390,
            groupid: 20000390,
            fid: 232150,
            analysis_extra: "",
            __date: 1723590632,
            ext: "module:02",
            PC_real_read: 1
          }
        ],
        ok: 1
      };
      return Response.json(data);
    }
    constructor() {
      super();
      this.hideTrends();
    }
    async filterSearchResults() {
      const selector = {
        cards: "div.card-wrap",
        cardUser: "div.info a.name",
        cardContent: "div.feed_list_content",
        retweetedCards: "div.card-wrap div.card-comment",
        retweetedCardUser: "div.con a.name",
        comments: "div.card-review",
        commentUser: "div.content a.name",
        commentContent: "div.content div.txt"
      };
      this.injectButton({
        listSelector: selector.cards,
        userSelector: selector.cardUser,
        elementButtonFunc: this.createCommentButton.bind(this)
      });
      this.injectButton({
        listSelector: selector.retweetedCards,
        userSelector: selector.retweetedCardUser
      });
      this.removeElements([
        newListAndUser(selector.cards, selector.cardUser, selector.cardContent),
        newListAndUser(
          selector.comments,
          selector.commentUser,
          selector.commentContent
        )
      ]);
      this.removeElements([
        newListAndUser(selector.cards, selector.retweetedCardUser)
      ]);
    }
    async filterFeeds() {
      const selector = {
        cardListSelector: "div.vue-recycle-scroller__item-view",
        cardUserSelector: "div.Feed_body_3R0rO a.ALink_default_2ibt1",
        commentListSelector: "div.wbpro-list",
        commentUserSelector: "div.con1.woo-box-item-flex div.text a.ALink_default_2ibt1",
        replyListSelector: "div.item2",
        replyUserSelector: "div.con2 a.ALink_default_2ibt1"
      };
      this.removeElements([
        newListAndUser(selector.cardListSelector, selector.cardUserSelector),
        newListAndUser(
          selector.commentListSelector,
          selector.commentUserSelector
        ),
        newListAndUser(selector.replyListSelector, selector.replyUserSelector)
      ]);
      this.injectButton({
        listSelector: selector.cardListSelector,
        userSelector: selector.cardUserSelector
      });
      const feedWrappers = document.querySelectorAll(selector.cardListSelector);
      feedWrappers.forEach((feed) => {
        this.injectButton({
          listSelector: selector.commentListSelector,
          userSelector: selector.commentUserSelector,
          doc: feed,
          subListSelector: selector.replyListSelector,
          subUserSelector: selector.replyUserSelector
        });
      });
    }
    async filterReplies() {
      const reply = document.querySelector("div.ReplyModal_scroll3_2kADQ");
      if (!reply) {
        return;
      }
      const selector = {
        commentListSelector: "div.wbpro-list",
        commentUserSelector: "div.con1.woo-box-item-flex a.ALink_default_2ibt1",
        root: reply,
        replyListSelector: "div.vue-recycle-scroller__item-view",
        replyUserSelector: "div.con2 a.ALink_default_2ibt1"
      };
      this.removeElements(
        [
          newListAndUser(
            selector.commentListSelector,
            selector.commentUserSelector
          ),
          newListAndUser(selector.replyListSelector, selector.replyUserSelector)
        ],
        reply
      );
      this.injectButton({
        listSelector: selector.commentListSelector,
        userSelector: selector.commentUserSelector,
        doc: reply,
        subListSelector: selector.replyListSelector,
        subUserSelector: selector.replyUserSelector
      });
    }
    createCommentButton(card, user) {
      this.injectButton({
        listSelector: "div.card-together div.list div.card-review",
        userSelector: "a.name",
        doc: card
      });
    }
    async filterDetailComments() {
      const selector = {
        commentListSelector: "div.vue-recycle-scroller__item-view",
        commentUserSelector: "div.con1.woo-box-item-flex div.text a.ALink_default_2ibt1",
        replyListSelector: "div.item2",
        replyUserSelector: "div.con2 a.ALink_default_2ibt1"
      };
      this.removeElements([
        newListAndUser(
          selector.commentListSelector,
          selector.commentUserSelector
        ),
        newListAndUser(selector.replyListSelector, selector.replyUserSelector)
      ]);
      this.injectButton({
        listSelector: selector.commentListSelector,
        userSelector: selector.commentUserSelector,
        subListSelector: selector.replyListSelector,
        subUserSelector: selector.replyUserSelector
      });
    }
    async hideTrends() {
      let trend = document.querySelector("div.main-side");
      if (trend) trend.style.display = "none";
      if (trend = document.querySelector("div.Main_side_i7Vti"))
        trend.style.display = "none";
    }
    async createRetweetButton() {
      this.removeElements([
        newListAndUser(
          "div.vue-recycle-scroller__item-view",
          "div.retweet.Feed_retweet_JqZJb a.ALink_default_2ibt1"
        )
      ]);
      this.injectButton({
        listSelector: "div.retweet.Feed_retweet_JqZJb",
        userSelector: "a.ALink_default_2ibt1"
      });
    }
    filterComments(comments) {
      return comments.reduce((filtered, comment) => {
        const myText = comment.text || "";
        const ngWordInMyText = this.inBlackList(
          { name: comment.user?.screen_name },
          myText
        );
        if (!ngWordInMyText) {
          filtered.push(comment);
        }
        return filtered;
      }, []);
    }
    filterStatuses(statuses) {
      return statuses.reduce((acc, cur) => {
        if (cur.user.following) {
          const myText = cur.text || "";
          const ngWordInMyText = this.inBlackList(
            { name: cur.user?.screen_name },
            myText
          );
          if (!ngWordInMyText) {
            if (cur.retweeted_status) {
              const oriText = cur.retweeted_status.text || "";
              const ngWordInOriText = this.inBlackList(
                { name: cur.retweeted_status?.user?.screen_name },
                oriText
              );
              if (ngWordInOriText) return acc;
            }
            acc.push(cur);
          }
        }
        return acc;
      }, []);
    }
    filterSearchBand(searchBands) {
      return searchBands.reduce((acc, cur) => {
        if (!this.inBlackList({ name: "" }, cur.word)) {
          acc.push(cur);
        }
        return acc;
      }, []);
    }
    onFriendTimeline(url, res) {
      if (url.includes("m.weibo.cn")) {
        res = res;
        res.data.statuses = this.filterStatuses(res.data.statuses);
      } else {
        res = res;
        res.statuses = this.filterStatuses(res.statuses);
        console.log(
          `filtered url: ${url}, users: ${res.statuses.reduce((pre, cur) => {
            pre.push(cur.user.screen_name);
            return pre;
          }, [])}`
        );
      }
    }
    onSearchBand(url, res) {
      res.data.realtime = this.filterSearchBand(res.data.realtime);
    }
    onTrendsBand(url, res) {
      res.data.list = [];
    }
    hook_buildComments(url, res) {
      res.data = this.filterComments(res.data);
    }
  };
  __publicField(WeiboFilter, "host", "weibo.com");
  __decorateClass([
    reqHook(/female_version|rum\/events/)
  ], WeiboFilter.prototype, "mockRequests", 1);
  __decorateClass([
    reqHook(/log\/read/)
  ], WeiboFilter.prototype, "interceptRead", 1);
  __decorateClass([
    filterFunc
  ], WeiboFilter.prototype, "filterSearchResults", 1);
  __decorateClass([
    filterFunc
  ], WeiboFilter.prototype, "filterFeeds", 1);
  __decorateClass([
    filterFunc
  ], WeiboFilter.prototype, "filterReplies", 1);
  __decorateClass([
    filterFunc
  ], WeiboFilter.prototype, "filterDetailComments", 1);
  __decorateClass([
    filterFunc
  ], WeiboFilter.prototype, "hideTrends", 1);
  __decorateClass([
    filterFunc
  ], WeiboFilter.prototype, "createRetweetButton", 1);
  __decorateClass([
    respHook(/friendstimeline/)
  ], WeiboFilter.prototype, "onFriendTimeline", 1);
  __decorateClass([
    respHook(/\/searchBand/)
  ], WeiboFilter.prototype, "onSearchBand", 1);
  __decorateClass([
    respHook(/getIndexBand/)
  ], WeiboFilter.prototype, "onTrendsBand", 1);
  __decorateClass([
    respHook(/\/buildComments/)
  ], WeiboFilter.prototype, "hook_buildComments", 1);
  WeiboFilter = __decorateClass([
    RegisterSubclass
  ], WeiboFilter);

  // src/filters/hupu.ts
  var HupuFilter = class extends WebsiteFilter {
    async filterHupuComments() {
      const selector = {
        commentsSelector: "div.index_discuss-card__Nd4MK.hp-m-discuss-card.post-card",
        userSelector: "p.discuss-card__user span.discuss-card__username",
        repliesSelector: "div.index_discuss-card__Nd4MK.hp-m-discuss-card.discuss-card",
        quotesSelector: "div.discuss-card__quote-container-quote",
        quoteUserSelector: "div.discuss-card__quote-container-quote span.discuss-card__quote-container-discusser"
      };
      this.removeElements([
        newListAndUser(selector.commentsSelector, selector.userSelector)
      ]);
      this.injectButton({
        listSelector: selector.commentsSelector,
        userSelector: selector.userSelector
      });
      this.removeElements([
        newListAndUser(selector.commentsSelector, selector.quoteUserSelector)
      ]);
      this.injectButton({
        listSelector: selector.quotesSelector,
        userSelector: selector.quoteUserSelector
      });
      this.removeElements([
        newListAndUser(selector.repliesSelector, selector.userSelector)
      ]);
      this.injectButton({
        listSelector: selector.repliesSelector,
        userSelector: selector.userSelector
      });
    }
    hupuRouter(url, res) {
      function filterHupuReplies(replies) {
        return replies.reduce((filtered, reply) => {
          const user = reply.user.username;
          if (!this.inBlackList({ name: user })) {
            filtered.push(reply);
          }
          return filtered;
        }, []);
      }
      res.data.replies = filterHupuReplies(res.data.replies);
    }
  };
  __publicField(HupuFilter, "host", "hupu.com");
  __decorateClass([
    filterFunc
  ], HupuFilter.prototype, "filterHupuComments", 1);
  __decorateClass([
    respHook(/\/bbs-reply-detail/)
  ], HupuFilter.prototype, "hupuRouter", 1);
  HupuFilter = __decorateClass([
    RegisterSubclass
  ], HupuFilter);

  // src/utils/time.ts
  function sleep(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }

  // src/filters/tieba.ts
  var TiebaFilter = class extends WebsiteFilter {
    constructor() {
      super();
      this.hideLoginPopup();
    }
    async hideLoginPopup() {
      let login = null;
      while (!login) {
        login = document.querySelector("div.tieba-custom-pass-login");
        if (login) {
          const ob = new MutationObserver((mutations) => {
            const popup = document.querySelector("div.tieba-custom-pass-login");
            const closeButton = document.querySelector("span.close-btn");
            if (closeButton) {
              closeButton.click();
            } else {
              popup.style.display = "none";
            }
          });
          ob.observe(login, {
            attributeFilter: ["style"],
            subtree: true
          });
          break;
        }
        await sleep(10);
      }
    }
    async filterTiebaThreadListComments() {
      const selector = {
        threadsSelector: "li.j_thread_list.clearfix.thread_item_box",
        threadUserSelector: "span.tb_icon_author",
        content: "div.col2_right.j_threadlist_li_right"
      };
      const contentFn = (element) => {
        const title = element.querySelector(
          "div.threadlist_abs.threadlist_abs_onlyline"
        );
        const preview = element.querySelector(
          "div.threadlist_title.pull_left.j_th_tit"
        );
        return title?.textContent + preview?.textContent;
      };
      const fn = (user) => {
        const field = user.dataset.field;
        const id = JSON.parse(field)?.user_id;
        return {
          name: user.getAttribute("title")?.replace("\u4E3B\u9898\u4F5C\u8005: ", ""),
          id
        };
      };
      this.removeElements(
        [
          newListAndUser(
            selector.threadsSelector,
            selector.threadUserSelector,
            selector.content
          )
        ],
        document,
        fn,
        contentFn
      );
      this.injectButton({
        listSelector: selector.threadsSelector,
        userSelector: selector.threadUserSelector,
        nameFn: fn
      });
    }
    async filterTiebaThreadComments() {
      const selector = {
        commentsSelector: "div.l_post.l_post_bright.j_l_post.clearfix",
        commentUserSelector: "div.d_author li.d_name a.p_author_name.j_user_card",
        commentContent: "div.p_content div.d_post_content.j_d_post_content",
        repliesSelector: "div.j_lzl_c_b_a.core_reply_content li.lzl_single_post.j_lzl_s_p",
        replyUserSelector: "div.lzl_cnt a.at.j_user_card",
        replyContent: "div.lzl_cnt span.lzl_content_main"
      };
      this.removeElements([
        newListAndUser(
          selector.commentsSelector,
          selector.commentUserSelector,
          selector.commentContent
        ),
        newListAndUser(
          selector.repliesSelector,
          selector.replyUserSelector,
          selector.replyContent
        )
      ]);
      this.injectButton({
        listSelector: selector.commentsSelector,
        userSelector: selector.commentUserSelector,
        subListSelector: selector.repliesSelector,
        subUserSelector: selector.replyUserSelector
      });
    }
    async hideRightBar() {
      const rightBar = document.querySelector("div.right_section.right_bright");
      if (!rightBar) {
        return;
      }
      rightBar.parentNode.removeChild(rightBar);
    }
    filterTiebaReplies(data) {
      const userMap = /* @__PURE__ */ new Map();
      for (const user of Object.values(data.user_list)) {
        userMap.set(user.user_nickname_v2, true);
      }
      const comments = data.comment_list;
      data.comment_list = Object.entries(comments).reduce(
        (filtered, [postId, comment]) => {
          let replies = comment.comment_info;
          replies = replies.filter(
            (reply) => !this.inBlackList({ name: reply.show_nickname }, reply.content)
          );
          comment.comment_list_num = replies.length;
          comment.comment_info = replies;
          if (comment.comment_list_num > 0) {
            filtered = {
              ...filtered,
              [postId]: comment
            };
          }
          return filtered;
        },
        {}
      );
      return data;
    }
    hookReplies(url, res) {
      res.data = this.filterTiebaReplies(res.data);
    }
    hookTopicList(url, res) {
      res.data.user_his_topic.topic_list = [];
      res.data.sug_topic.topic_list = [];
      res.data.bang_topic.topic_list = [];
      res.data.manual_topic.topic_list = [];
    }
    hookSuggestion(url, res) {
      res.hottopic_list.search_data = [];
      res.query_tips.search_data = [];
    }
  };
  __publicField(TiebaFilter, "host", "tieba.baidu.com");
  __decorateClass([
    filterFunc
  ], TiebaFilter.prototype, "hideLoginPopup", 1);
  __decorateClass([
    filterFunc
  ], TiebaFilter.prototype, "filterTiebaThreadListComments", 1);
  __decorateClass([
    filterFunc
  ], TiebaFilter.prototype, "filterTiebaThreadComments", 1);
  __decorateClass([
    filterFunc
  ], TiebaFilter.prototype, "hideRightBar", 1);
  __decorateClass([
    respHook(/\/p\/totalComment/)
  ], TiebaFilter.prototype, "hookReplies", 1);
  __decorateClass([
    respHook(/\/topicList/)
  ], TiebaFilter.prototype, "hookTopicList", 1);
  __decorateClass([
    respHook(/\/suggestion/)
  ], TiebaFilter.prototype, "hookSuggestion", 1);
  TiebaFilter = __decorateClass([
    RegisterSubclass
  ], TiebaFilter);

  // src/filters/bilibili.ts
  var BilibiliFilter = class extends WebsiteFilter {
    render() {
      super.render();
    }
    commentsButton(name, id) {
      return createBlockButton({
        name,
        id,
        css: ButtonCSSText,
        listeners: [DomainStore.listener, this.render.bind(this)]
      });
    }
    hoverButtonFunc(selector) {
      return (element, user) => {
        const wrapper = element.querySelector(selector);
        if (element.querySelector(cssByCls(BlockButtonClass))) {
          return;
        }
        const hoverButton = createBlockButton({
          name: user.name,
          id: user.id,
          subclass: HoverButtonClass,
          listeners: [DomainStore.listener, this.render.bind(this)]
        });
        wrapper.appendChild(hoverButton);
      };
    }
    filterReplies(e) {
      let replies = e.shadowRoot?.querySelector("div#replies")?.querySelector("bili-comment-replies-renderer")?.shadowRoot;
      let replyRenderers = replies?.querySelectorAll(
        "bili-comment-reply-renderer"
      );
      replyRenderers?.forEach((replyShadowRoot) => {
        let reply = replyShadowRoot?.shadowRoot;
        let replyUser = reply?.querySelector("bili-comment-user-info")?.shadowRoot?.querySelector("#user-name");
        let name = replyUser?.textContent || "";
        let id = replyUser?.getAttribute("data-user-profile-id") || "";
        if (this.inBlackList({ name, id })) {
          replyShadowRoot.parentElement?.removeChild(replyShadowRoot);
        } else {
          if (replyUser.parentNode.querySelector(cssByCls(BlockButtonClass))) {
            return;
          }
          const button = this.commentsButton(name, id);
          replyUser.parentNode.appendChild(button);
        }
      });
    }
    filterComments() {
      const srw = document.querySelector(
        "#comment > div > bili-comments"
      )?.shadowRoot;
      if (!srw) {
        return;
      }
      const comments = srw.querySelectorAll(
        "#feed > bili-comment-thread-renderer"
      );
      comments?.forEach((e) => {
        this.filterReplies(e);
        const comment = e.shadowRoot?.querySelector(
          "bili-comment-renderer"
        )?.shadowRoot;
        let commentUser = comment?.querySelector(
          "#header > bili-comment-user-info"
        )?.shadowRoot;
        const userInfo = commentUser.querySelector("#user-name");
        const buttonWrapper = commentUser.querySelector("div#info");
        let userName = userInfo?.textContent;
        let userId = userInfo?.getAttribute("data-user-profile-id") || "";
        if (this.inBlackList({ name: userName, id: userId })) {
          e.parentNode.removeChild(e);
        } else {
          if (buttonWrapper.querySelector(cssByCls(BlockButtonClass))) {
            return;
          }
          const button = this.commentsButton(userName, userId);
          buttonWrapper.appendChild(button);
        }
      });
    }
    filterVideoList() {
      const selector = {
        videos: ".feed-card",
        author: ".bili-video-card__info--owner",
        title: ".bili-video-card__info--tit"
      };
      const nameFn = (element) => {
        const author = element.querySelector(".bili-video-card__info--author");
        const href = element.getAttribute("href");
        return { name: author.textContent, id: href.split("/").at(-1) };
      };
      this.removeElements(
        [newListAndUser(selector.videos, selector.author, selector.title)],
        document,
        nameFn
      );
      this.injectButton({
        listSelector: selector.videos,
        userSelector: selector.author,
        elementButtonFunc: this.hoverButtonFunc(".bili-video-card__image--wrap"),
        nameFn
      });
    }
    filterVideoPlaylist() {
      const selector = {
        videos: ".video-page-card-small",
        videoAuthor: ".upname a",
        title: "div.info a p.title"
      };
      const nameFn = (element) => {
        return {
          name: element.querySelector("span.name").textContent,
          id: element.getAttribute("href").split("/").at(-2)
        };
      };
      this.removeElements(
        [newListAndUser(selector.videos, selector.videoAuthor, selector.title)],
        document,
        nameFn
      );
      this.injectButton({
        listSelector: selector.videos,
        userSelector: selector.videoAuthor,
        nameFn,
        elementButtonFunc: this.hoverButtonFunc(".pic-box")
      });
    }
    hookMain(url, res) {
      return this.hookComments(url, res);
    }
    hookReplies(url, res) {
      return this.hookComments(url, res);
    }
    hookComments(url, res) {
      const filterReplyFunc = (reply) => {
        const id = reply.mid_str;
        const name = reply.member.uname;
        const content = reply.content.message;
        return !this.inBlackList({ name, id }, content);
      };
      let { replies } = res.data;
      replies = replies.filter(filterReplyFunc);
      replies.forEach((reply) => {
        const subReplies = reply.replies;
        reply.replies = subReplies.filter(filterReplyFunc);
      });
      res.data.replies = replies;
    }
    hookSearchVideo(url, res) {
      res.data.result = res.data.result.filter((item) => {
        const name = item.author;
        const id = item.mid;
        const desc = item.description;
        return !this.inBlackList({ name, id }, desc);
      });
    }
    hookSearchTrends(url, res) {
      res.data.trending.list = [
        {
          keyword: "fsd",
          show_name: "FSD",
          icon: "http://i0.hdslb.com/bfs/activity-plat/static/20221213/eaf2dd702d7cc14d8d9511190245d057/lrx9rnKo24.png",
          uri: "",
          goto: ""
        }
      ];
    }
  };
  __publicField(BilibiliFilter, "host", "bilibili.com");
  __decorateClass([
    filterFunc
  ], BilibiliFilter.prototype, "filterComments", 1);
  __decorateClass([
    patternFilterFunc(/search\.bilibili\.com|bilibili\.com\/\?/)
  ], BilibiliFilter.prototype, "filterVideoList", 1);
  __decorateClass([
    patternFilterFunc(/com\/video\/\w+/)
  ], BilibiliFilter.prototype, "filterVideoPlaylist", 1);
  __decorateClass([
    respHook(/reply\/wbi\/main/)
  ], BilibiliFilter.prototype, "hookMain", 1);
  __decorateClass([
    respHook(/\/reply\/reply/)
  ], BilibiliFilter.prototype, "hookReplies", 1);
  __decorateClass([
    respHook(/search\/type/)
  ], BilibiliFilter.prototype, "hookSearchVideo", 1);
  __decorateClass([
    respHook(/search\/square/)
  ], BilibiliFilter.prototype, "hookSearchTrends", 1);
  BilibiliFilter = __decorateClass([
    RegisterSubclass
  ], BilibiliFilter);

  // src/views/panel.ts
  var MainView = class _MainView {
    store;
    filter;
    static instance;
    constructor() {
      if (_MainView.instance) {
        return _MainView.instance;
      }
      this.filter = WebsiteFilter.fromHost();
      this.store = new DomainStore();
      addStyle(css);
      window.addEventListener("load", () => {
        appObserverInit();
      });
      _MainView.instance = this;
    }
    static DialogSelector = ".my-dialog__wrapper";
    dialogElement() {
      return document.querySelector(_MainView.DialogSelector);
    }
    freshPage() {
      this.filter.render();
    }
    render() {
      setInterval(() => {
        this.renderSettingButton();
        this.renderSettingPanel();
        this.filter.render();
      }, 1e3);
    }
    /* 生成添加屏蔽关键词的按钮 */
    async renderSettingButton() {
      if (!document.body) {
        return;
      }
      if (document.body.querySelector("#add_ngList_btn")) {
        return;
      }
      const btn = document.createElement("div");
      btn.title = "\u6DFB\u52A0\u5C4F\u853D\u5173\u952E\u8BCD";
      const span = document.createElement("span");
      span.innerText = "";
      btn.appendChild(span);
      btn.id = "add_ngList_btn";
      document.body.appendChild(btn);
      btn.addEventListener("click", () => {
        this.renderBlockedUsers();
        this.showDialog();
      });
    }
    async renderSettingPanel() {
      const dialogTemplate = `
      <div class="my-dialog" style="margin-top: 15vh; width: 40%;">
        <div class="my-dialog__header">
          <span class="my-dialog__title">\u5C4F\u853D\u8BCD\u5217\u8868</span>
          <button type="button" aria-label="Close" class="my-dialog__headerbtn">
            <i class="my-dialog__close"></i>
          </button>
        </div>
        <div class="my-dialog__body">
          <div class="input_container">
            <div class="el-input">
              <input id="ngWord_input" class="el-input__inner" type="text" />
            </div>
            <button type="button" class="el-button" id="add_btn">
              <span>\u6DFB\u52A0</span>
            </button>
          </div>
          <div id="ngList"></div>
          <p class="tips">\u6CE8\uFF1A1. \u53EF\u8FC7\u6EE4\u5305\u542B\u5C4F\u853D\u8BCD\u7684\u7528\u6237\u3001\u5FAE\u535A\u3001\u8BC4\u8BBA\u3001\u70ED\u641C\u3002 2. \u5173\u952E\u8BCD\u4FDD\u5B58\u5728\u672C\u5730\u7684local storage\u4E2D\u3002 3. \u66F4\u6539\u5173\u952E\u8BCD\u540E\u5237\u65B0\u9875\u9762\u751F\u6548\uFF08\u4E0D\u5237\u65B0\u9875\u9762\u7684\u60C5\u51B5\u4E0B\uFF0C\u53EA\u6709\u4E4B\u540E\u52A0\u8F7D\u7684\u5FAE\u535A\u624D\u4F1A\u751F\u6548\uFF09\u3002</p>
        </div>
        <div class="my-dialog__footer"></div>
      </div>
    `;
      if (!document.body) {
        return;
      }
      if (document.body.querySelector(".my-dialog__wrapper")) {
        return;
      }
      const wrapper = document.createElement("div");
      wrapper.classList.add("my-dialog__wrapper");
      wrapper.innerHTML = dialogTemplate;
      document.body.appendChild(wrapper);
      document.querySelector(".my-dialog__headerbtn").addEventListener("click", () => {
        this.hideDialog();
      });
      document.querySelector("#add_btn").addEventListener("click", () => {
        const ngWord_input = document.querySelector(
          "#ngWord_input"
        );
        if (ngWord_input && ngWord_input.value) {
          this.store.addPattern(ngWord_input.value);
          ngWord_input.value = "";
          this.renderBlockedUsers();
        }
      });
    }
    showDialog() {
      this.dialogElement().style.display = "initial";
    }
    hideDialog() {
      this.dialogElement().style.display = "none";
    }
    renderBlockedUsers() {
      let blockedUsersHTML = "";
      const users = [...this.store.userList];
      for (const [i, pattern] of this.store.patternList.reverse().entries()) {
        blockedUsersHTML += `<span class="ng_pattern" data-type="pattern">${pattern}<i class="close-icon" data-index=${i}></i></span>`;
      }
      for (const [i, item] of users.reverse().entries()) {
        blockedUsersHTML += `<span class="ng_item" data-type="user">${item}<i class="close-icon" data-index=${i}></i></span>`;
      }
      const ngListNode = document.querySelector("#ngList");
      if (ngListNode) {
        ngListNode.innerHTML = blockedUsersHTML;
        const buttons = ngListNode.querySelectorAll(".close-icon");
        for (const button of buttons) {
          button.addEventListener("click", () => {
            const name = button.parentNode.textContent;
            const type = button.parentElement.dataset.type;
            switch (type) {
              case "user":
                this.store.removeUser({ name, id: name });
                break;
              case "pattern":
                this.store.removePattern(name);
                break;
            }
            this.renderBlockedUsers();
          });
        }
      }
    }
  };
  function appObserverInit() {
    const targetNode = document.getElementById("app");
    if (!targetNode) {
      return;
    }
    const config = {
      childList: true,
      subtree: true
    };
    const callback = function() {
      const audioList = document.querySelectorAll(".AfterPatch_bg_34rqc");
      for (const audio of audioList) {
        audio.remove();
        console.log("\u79FB\u9664\u4E86\u5F31\u667A\u4E09\u8FDE");
      }
    };
    const observer = new MutationObserver(callback);
    observer.observe(targetNode, config);
  }

  // src/main.ts
  async function main() {
    await DomainStore.init();
    const controller = new MainView();
    controller.render();
  }
  main();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址