置顶折叠 - NGA Topped Collapse

折叠版头,版块标题是切换按钮。

另有选项可选择是否折叠子版块和广告。

// ==UserScript==
// @name        NGA Topped Collapse
// @namespace   https://greasyfork.org/users/263018
// @version     1.0.1
// @author      snyssss
// @description 置顶折叠
 
// @match       *://bbs.nga.cn/*
// @match       *://ngabbs.com/*
// @match       *://nga.178.com/*
 
// @grant       GM_registerMenuCommand
// @grant       GM_setValue
// @grant       GM_getValue
// @noframes
// ==/UserScript==
 
((ui) => {
  if (!ui) return;
 
  // KEY
  const COLLAPSE_SUBFORUMS_ENABLE_KEY = "COLLAPSE_SUBFORUMS_ENABLE";
  const COLLAPSE_ADS_ENABLE_KEY = "COLLAPSE_ADS_ENABLE";
 
  // 折叠子版块
  const collapseSubForumsEnable =
    GM_getValue(COLLAPSE_SUBFORUMS_ENABLE_KEY) || false;
  
  // 折叠广告
  const collapseAdsEnable =
    GM_getValue(COLLAPSE_ADS_ENABLE_KEY) || false;
 
  // 钩子
  const hookFunction = (object, functionName, callback) => {
    ((originalFunction) => {
      object[functionName] = function () {
        const returnValue = originalFunction.apply(this, arguments);
 
        callback.apply(this, [returnValue, originalFunction, arguments]);
 
        return returnValue;
      };
    })(object[functionName]);
  };
 
  // 是否折叠
  let collapsed = true;
 
  // 主函数
  const execute = () => {
    const topped = document.querySelector("#toptopics");
 
    if (topped) {
      const postrow = topped.querySelector(".postrow");
      const subForums = collapseSubForumsEnable ? document.querySelectorAll(
        "#sub_forums_c, #more_sub_forums_c"
      ) : [];
      const ads = collapseAdsEnable ? document.querySelectorAll(
        "[id*=bbs_ads]"
      ) : [];
 
      const collapse = () => {
        [postrow, ...subForums, ...ads].forEach(
          (element) => {
            element.style = `display: ${collapsed ? "none" : "block"}`;
          }
        );
      };
 
      const button = topped.querySelector("A");
 
      button.onclick = () => {
        collapsed = !collapsed;
 
        collapse();
 
        return false;
      };
 
      collapse();
    }
  };
 
  // 绑定事件
  (() => {
    let initialized = false;
 
    hookFunction(ui, "eval", () => {
      if (initialized) return;
 
      if (ui.parseToppedTopic) {
        hookFunction(ui, "parseToppedTopic", execute);
 
        initialized = true;
      }
    });
 
    execute();
  })();
 
  // 菜单项
  (() => {
    // 折叠子版块
    if (collapseSubForumsEnable) {
      GM_registerMenuCommand("折叠子版块:启用", () => {
        GM_setValue(COLLAPSE_SUBFORUMS_ENABLE_KEY, false);
        location.reload();
      });
    } else {
      GM_registerMenuCommand("折叠子版块:禁用", () => {
        GM_setValue(COLLAPSE_SUBFORUMS_ENABLE_KEY, true);
        location.reload();
      });
    }
    
    // 折叠广告
    if (collapseAdsEnable) {
      GM_registerMenuCommand("折叠广告:启用", () => {
        GM_setValue(COLLAPSE_ADS_ENABLE_KEY, false);
        location.reload();
      });
    } else {
      GM_registerMenuCommand("折叠广告:禁用", () => {
        GM_setValue(COLLAPSE_ADS_ENABLE_KEY, true);
        location.reload();
      });
    }
  })();
})(commonui);

简单的自动翻页 - NGA Auto Pagerize

简单的自动翻页

附加功能:让帖子的附件标识更加明显。

// ==UserScript==
// @name        NGA Auto Pagerize
// @namespace   https://greasyfork.org/users/263018
// @version     1.1.4
// @author      snyssss
// @description 简单的自动翻页
 
// @match       *://bbs.nga.cn/*
// @match       *://ngabbs.com/*
// @match       *://nga.178.com/*
 
// @grant       GM_registerMenuCommand
// @grant       GM_setValue
// @grant       GM_getValue
// @noframes
// ==/UserScript==
 
((ui) => {
  if (!ui) return;
  
  // KEY
  const ATTACHMENT_STYLE_ENABLE_KEY = "ATTACHMENT_STYLE_ENABLE";
  
  // 附件样式
  const attachmentStyleEnable = GM_getValue(ATTACHMENT_STYLE_ENABLE_KEY) || false;
  
  // 钩子
  const hookFunction = (object, functionName, callback) => {
    ((originalFunction) => {
      object[functionName] = function () {
        const returnValue = originalFunction.apply(this, arguments);
 
        callback.apply(this, [returnValue, originalFunction, arguments]);
 
        return returnValue;
      };
    })(object[functionName]);
  };
 
  // 翻页
  if (ui.pageBtn) {
    const delay = (interval) =>
      new Promise((resolve) => setTimeout(resolve, interval));
 
    const retry = async (fn, retriesLeft = 10, interval = 160) => {
      try {
        return await fn();
      } catch (error) {
        await delay(interval);
 
        if (retriesLeft > 0) {
          return await retry(fn, retriesLeft - 1, interval);
        }
      }
    };
 
    const execute = (() => {
      const observer = new IntersectionObserver((entries) => {
        if (entries.find((item) => item.isIntersecting)) {
          retry(() => {
            if (ui.loadReadHidden.lock) {
              throw new Error();
            }
 
            ui.loadReadHidden(0, 2);
          });
        }
      });
 
      return () => {
        const anchor = document.querySelector('[title="加载下一页"]');
 
        if (anchor) {
          observer.observe(anchor);
        } else {
          observer.disconnect();
        }
      };
    })();
 
    hookFunction(ui, "pageBtn", execute);
 
    execute();
  }
 
  // 移除重复内容
  if (ui.topicArg)
  {
    const execute = () => {
      ui.topicArg.data = ui.topicArg.data.reduce((accumulator, currentValue) => {
        const index = accumulator.findIndex((item) => item[8] === currentValue[8]);
 
        if (index < 0) {
          return [...accumulator, currentValue];
        }
 
        currentValue[0].closest("TBODY").remove();
 
        return accumulator;
      }, []);
    };
    
    hookFunction(ui.topicArg, "loadAll", execute);
 
    execute();
  }
  
  // 附件样式
  if (ui.topicArg && attachmentStyleEnable)
  {
    const execute = () => {
      const elements = document.querySelectorAll('[title="主题中有附件"]');
      
      elements.forEach((element) => {
          element.className = "block_txt white nobr vertmod";
          element.style = "background-color: #BD7E6D";
          element.innerHTML = "附件";
      })
    };
 
    hookFunction(ui.topicArg, "loadAll", execute);
 
    execute();
  }
  
  if (attachmentStyleEnable) {
    GM_registerMenuCommand('附件样式:启用', () => {
      GM_setValue(ATTACHMENT_STYLE_ENABLE_KEY, false);
      location.reload();
    });
  } else {
    GM_registerMenuCommand('附件样式:禁用', () => {
      GM_setValue(ATTACHMENT_STYLE_ENABLE_KEY, true);
      location.reload();
    });
  }
})(commonui);

狗叔,咱们有自己的论坛了 - NGA x Saraba1st Smiles

如果一个论坛发言像s1

表情像s1

还经常出现在s1微博中

那么它就是s1

// ==UserScript==
// @name        NGA x Saraba1st Smiles
// @namespace   https://greasyfork.org/users/263018
// @version     1.1.4
// @author      snyssss
// @description 狗叔,咱们有自己的论坛了
 
// @match       *://bbs.nga.cn/*
// @match       *://ngabbs.com/*
// @match       *://nga.178.com/*
 
// @noframes
// ==/UserScript==
 
((ui, poster) => {
  if (!ui) return;
  if (!poster) return;
 
  const hookFunction = (object, functionName, callback) => {
    ((originalFunction) => {
      object[functionName] = function () {
        const returnValue = originalFunction.apply(this, arguments);
 
        callback.apply(this, [returnValue, originalFunction, arguments]);
 
        return returnValue;
      };
    })(object[functionName]);
  };
 
  const smiles = [
    [
      "麻将",
      [
        "./mon_202011/26/-9lddQ5-ga9hK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-18dlK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-7ymuK0Sw-w.gif",
        "./mon_202011/26/-9lddQ5-dtlrK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-jmfjK2Sw-w.gif",
        "./mon_202011/26/-9lddQ5-57p6K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-bvn7K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-jeaeK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-4y0mK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-bwgvK2S12-w.gif",
        "./mon_202011/26/-9lddQ5-jbw3K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-6y0rK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-cmumK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-icanK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-3eklK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-bk1dK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-hj2hK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-21htK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-7oc9K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-dn7aK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-k91uK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-4y43K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-bszeK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-hrc1K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-2j95K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-8ge1K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-eqmxK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-kpz5K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-4n7iK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-akkzK0Sw-w.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-9yu9K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-ga6xK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-tzyK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-78i4K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-dot4K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-jf0iK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-47cmK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-ati2K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-fz6rK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-laiaK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-5r2bK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-bhmbK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-ho52K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-1artK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-6pr7K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-bmh9K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-hrpuK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-2lw1K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-90wpK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-estbK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-jv2mK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-3sfoK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-9e5hK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-feezK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-lbsaK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-50bqK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-av8vK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-gi6iK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-iefK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-6rbjK0Sw-w.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-3uK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-69lsK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-cfdrK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-hwm9K4T8Sw-w.gif",
        "./mon_202011/26/-9lddQ5-2mavK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-87fpK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-eirbK0S10-x.png",
        "./mon_202011/26/-9lddQ5-k0t5K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-4p0uK1S10-y.gif",
        "./mon_202011/26/-9lddQ5-bf27K1S10-x.gif",
        "./mon_202011/26/-9lddQ5-gtjxK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-2ncbK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-8evwK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-e1mvK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-kghcK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-6213K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-chfbK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-ix46K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-3o6kK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-anv6K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-h6hxK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-2xh1K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-9rhkK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-gm72K1Sw-w.png",
        "./mon_202011/26/-9lddQ5-20f1K1Sw-w.png",
        "./mon_202011/26/-9lddQ5-aaieK0Sw-11.png",
        "./mon_202011/26/-9lddQ5-h6dwK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-1lngK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-7u18K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-e2ljK1Sw-w.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-bkqdK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-hitfK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-23kkK0S14-10.png",
        "./mon_202011/26/-9lddQ5-dl5bK1S1j-1c.png",
        "./mon_202011/26/-9lddQ5-v1nK1Sw-y.png",
        "./mon_202011/26/-9lddQ5-gnt0K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-eyt2K1Sw-w.png",
        "./mon_202011/26/-9lddQ5-8qqsK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-frefK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-jh6vK2Sw-w.gif",
        "./mon_202011/26/-9lddQ5-iv4jKbT8Sw-w.gif",
        "./mon_202011/26/-9lddQ5-d5c7K5T8Sw-w.gif",
        "./mon_202011/26/-9lddQ5-jsihK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-467aK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-gxnuK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-ai3tK0Sq-w.png",
        "./mon_202011/26/-9lddQ5-dwt7K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-b578K1Sw-w.png",
        "./mon_202011/26/-9lddQ5-1oncK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-bzhdK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-em4cK0Sw-14.png",
        "./mon_202011/26/-9lddQ5-8e3dK1Sw-14.png",
        "./mon_202011/26/-9lddQ5-9ujxK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-jticK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-1u7xK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-268hK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-cbq1K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-1o9lK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-hesqK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-e5awK0Sw-w.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-istjK0Sy-14.png",
        "./mon_202011/26/-9lddQ5-kzq9K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-5fvtK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-fgymK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-l0uzK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-d9fdK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-2bkfK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-4as6K7T8Sw-z.gif",
        "./mon_202011/26/-9lddQ5-dkn2K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-341uK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-k3kvK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-gnigK2Sw-w.gif",
        "./mon_202011/26/-9lddQ5-h39uK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-cl00K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-54i3K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-htnyK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-8uxmK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-1daK0Sw-w.gif",
        "./mon_202011/26/-9lddQ5-b0qkK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-212jK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-gf6uK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-9kp7K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-5nc1K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-5ze4K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-8vz7K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-eckxK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-e3dhK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-asxzK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-8p4nK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-2v9nK0Sw-w.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-2d4tK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-apb3K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-2n6fK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-2n4fK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-47g8K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-ablrK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-evp3K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-l0oqK1Sw-w.gif",
        "./mon_202011/26/-9lddQ5-7ikzK0Sw-y.png",
        "./mon_202011/26/-9lddQ5-bhe1K0S18-w.png",
        "./mon_202011/26/-9lddQ5-6k33K0S12-w.png",
        "./mon_202011/26/-9lddQ5-j4ihK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-bct3K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-2byqK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-dpp8K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-cgj7K0Sw-12.png",
        "./mon_202011/26/-9lddQ5-5pv8K0Sz-w.png",
        "./mon_202011/26/-9lddQ5-7bs3K0S12-w.png",
        "./mon_202011/26/-9lddQ5-jwadK0S12-12.png",
        "./mon_202011/26/-9lddQ5-9ccsK0S12-w.png",
        "./mon_202011/26/-9lddQ5-kuuyK0S12-16.png",
        "./mon_202011/26/-9lddQ5-af09K0S12-w.png",
        "./mon_202011/26/-9lddQ5-27cfK0S12-w.png",
        "./mon_202011/26/-9lddQ5-8buhK0S12-w.png",
        "./mon_202011/26/-9lddQ5-2bihK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-huv9K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-fekuK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-daxrK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-64uoK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-jvk5K0Sw-w.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-cyvrK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-6dk1K0Sw-x.png",
        "./mon_202011/26/-9lddQ5-kprxK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-btueK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-5mukK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-j6wwK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-emybK0Sw-x.png",
        "./mon_202011/26/-9lddQ5-70neK5T8S16-u.gif",
        "./mon_202011/26/-9lddQ5-1ln9K0S12-16.png",
        "./mon_202011/26/-9lddQ5-ds6cK0Sw-w.gif",
        "./mon_202011/26/-9lddQ5-kjcqK1Sw-w.gif",
        "./mon_202011/26/-9lddQ5-c421K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-ilvK1Sw-w.gif",
        "./mon_202011/26/-9lddQ5-bwplK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-h20cK0Sy-10.png",
        "./mon_202011/26/-9lddQ5-l3g6K1Sy-10.png",
        "./mon_202011/26/-9lddQ5-86b4K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-g1rxK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-go7fK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-ikb0K1Sw-w.gif",
        "./mon_202011/26/-9lddQ5-8iizK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-kxprK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-l5q1K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-8xjnK1S10-17.png",
        "./mon_202011/26/-9lddQ5-izreK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-6yjbK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-curmK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-ih7kK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-53wwK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-fuflK0Sw-z.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-428aK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-fudaK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-gukK1S1c-10.png",
        "./mon_202011/26/-9lddQ5-7kfsK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-80gsK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-3m1eK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-ka7rK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-2m74K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-7sk4K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-d6trK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-h8b0K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-b670K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-ftzxK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-1pzdK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-6yprK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-caa2K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-hwnxK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-7v2rK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-2n1qK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-bpp0K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-gfvoK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-kaubK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-8a1oKhT8S18-18.gif",
        "./mon_202011/26/-9lddQ5-cuzeK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-itlyK1S10-14.png",
        "./mon_202011/26/-9lddQ5-olzK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-7r6yK7T8S16-t.gif",
        "./mon_202011/26/-9lddQ5-bgn0K2S1f-1b.gif",
        "./mon_202011/26/-9lddQ5-ekg3K2S1f-1b.gif",
        "./mon_202011/26/-9lddQ5-kzbcK2S1j-12.gif",
      ],
      [
        "./mon_202011/26/-9lddQ5-6eujK2S1j-18.gif",
        "./mon_202011/26/-9lddQ5-c93kK8T8S1j-18.gif",
        "./mon_202011/26/-9lddQ5-i81zK7T8S1j-18.gif",
        "./mon_202011/26/-9lddQ5-5s5vK4T8Sw-z.gif",
        "./mon_202011/26/-9lddQ5-e1n1KcT8Sw-w.gif",
        "./mon_202011/26/-9lddQ5-kq85K1S11-14.gif",
        "./mon_202011/26/-9lddQ5-8b7zK1Sx-10.png",
        "./mon_202011/26/-9lddQ5-hpxmK1Sx-10.png",
        "./mon_202011/26/-9lddQ5-6e81K3Sw-11.gif",
        "./mon_202011/26/-9lddQ5-cbjtK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-g9qyK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-167pK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-7453K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-erzpK3S14-12.gif",
        "./mon_202011/26/-9lddQ5-g7lmK3Sw-14.gif",
        "./mon_202011/26/-9lddQ5-88o7K1Sw-z.png",
        "./mon_202011/26/-9lddQ5-73ynK0Sw-w.png",
      ],
      [
        "./mon_202105/27/-9lddQ2o-9f2hK0Sw-w.png",
        "./mon_202105/27/-9lddQ2o-ku8sK0Sw-w.png",
      ]
    ],
    [
      "角色",
      [
        "./mon_202011/26/-9lddQ5-dp6K1Sw-w.png",
        "./mon_202011/26/-9lddQ5-9u7qK0S10-y.png",
        "./mon_202011/26/-9lddQ5-j408K1Sw-10.png",
        "./mon_202011/26/-9lddQ5-yt8K1Sy-12.png",
        "./mon_202011/26/-9lddQ5-k7szK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-gnwK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-bwmwK1Sw-11.png",
        "./mon_202011/26/-9lddQ5-229yK1Sy-11.png",
        "./mon_202011/26/-9lddQ5-8nr0K1Sy-10.png",
        "./mon_202011/26/-9lddQ5-49ukK1Sw-x.png",
        "./mon_202011/26/-9lddQ5-hk3rK0Sw-y.png",
        "./mon_202011/26/-9lddQ5-1v65K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-9kloK0S12-11.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-f5roK1Sw-10.png",
        "./mon_202011/26/-9lddQ5-e187K1S10-y.png",
        "./mon_202011/26/-9lddQ5-gub3K1Sw-12.png",
        "./mon_202011/26/-9lddQ5-j9m9K2S14-w.gif",
        "./mon_202011/26/-9lddQ5-fmgaK1Sy-1a.png",
        "./mon_202011/26/-9lddQ5-jcg9K2S14-13.gif",
        "./mon_202011/26/-9lddQ5-2j1cK1S14-17.png",
        "./mon_202011/26/-9lddQ5-2wvqK1S14-17.png",
        "./mon_202011/26/-9lddQ5-b1sxK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-48wvK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-dcpyK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-twnK0Sw-y.png",
        "./mon_202011/26/-9lddQ5-9mveK1Sz-y.png",
        "./mon_202011/26/-9lddQ5-hdtuK1Sy-y.png",
        "./mon_202011/26/-9lddQ5-deoyK1Sy-y.png",
        "./mon_202011/26/-9lddQ5-uwfK1Sw-y.png",
        "./mon_202011/26/-9lddQ5-ahasK1S10-13.png",
        "./mon_202011/26/-9lddQ5-aqirK1Sw-y.png",
        "./mon_202011/26/-9lddQ5-khr4K1Sw-14.png",
        "./mon_202011/26/-9lddQ5-8pszK0Sw-y.png",
        "./mon_202011/26/-9lddQ5-hguqK0Sw-y.png",
        "./mon_202011/26/-9lddQ5-i3kwK0Sw-y.png",
        "./mon_202011/26/-9lddQ5-6hbiK0Sy-y.png",
        "./mon_202011/26/-9lddQ5-gh22K0Sw-y.png",
        "./mon_202011/26/-9lddQ5-6llvK0Sw-y.png",
        "./mon_202011/26/-9lddQ5-5bndK0Sw-y.png",
        "./mon_202011/26/-9lddQ5-bqinK1Sw-y.png",
        "./mon_202011/26/-9lddQ5-j1zjK1Sy-12.png",
        "./mon_202011/26/-9lddQ5-66f7K1Sy-12.png",
        "./mon_202011/26/-9lddQ5-a80eK1Sy-12.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-ad3rK1Sy-12.png",
        "./mon_202011/26/-9lddQ5-g45vK1Sw-z.png",
        "./mon_202011/26/-9lddQ5-j6lzK1Sw-z.png",
        "./mon_202011/26/-9lddQ5-5lm8K1Sw-z.png",
        "./mon_202011/26/-9lddQ5-acb2K1Sx-10.png",
        "./mon_202011/26/-9lddQ5-edg5K1Sw-10.png",
        "./mon_202011/26/-9lddQ5-hqxgK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-28viK1S10-10.gif",
        "./mon_202011/26/-9lddQ5-4zpzK1Sw-y.png",
        "./mon_202011/26/-9lddQ5-9ac6K1Sw-z.png",
        "./mon_202011/26/-9lddQ5-cvkmK1Sw-1c.png",
        "./mon_202011/26/-9lddQ5-fn51K1Sw-z.png",
        "./mon_202011/26/-9lddQ5-hzbwK1Sw-z.png",
        "./mon_202011/26/-9lddQ5-lrqK1Sw-10.png",
        "./mon_202011/26/-9lddQ5-4g03K1Sw-y.png",
        "./mon_202011/26/-9lddQ5-8ooaK0Sw-10.png",
        "./mon_202011/26/-9lddQ5-bzs2K0Sy-w.png",
        "./mon_202011/26/-9lddQ5-hk0nK1Sy-w.png",
        "./mon_202011/26/-9lddQ5-20jK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-i9s0K1S10-y.png",
        "./mon_202011/26/-9lddQ5-lc4kK0Sw-y.png",
        "./mon_202011/26/-9lddQ5-8kksK0Sw-x.png",
        "./mon_202011/26/-9lddQ5-b2asK0Sw-11.png",
        "./mon_202011/26/-9lddQ5-e9wrK0Sw-11.png",
        "./mon_202011/26/-9lddQ5-gvppK1Sy-11.png",
        "./mon_202011/26/-9lddQ5-jebfK1S12-10.png",
        "./mon_202011/26/-9lddQ5-1augK0Sw-10.png",
        "./mon_202011/26/-9lddQ5-4uisK1Sy-14.png",
        "./mon_202011/26/-9lddQ5-9bhzK1Sw-13.png",
        "./mon_202011/26/-9lddQ5-553zK1Sw-10.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-15huK1Sw-12.png",
        "./mon_202011/26/-9lddQ5-4djsK1Sw-13.png",
        "./mon_202011/26/-9lddQ5-97fgK1Sw-14.png",
        "./mon_202011/26/-9lddQ5-cawgK1Sw-18.png",
        "./mon_202011/26/-9lddQ5-gas1K1Sw-18.png",
        "./mon_202011/26/-9lddQ5-hyczK1S12-18.png",
        "./mon_202011/26/-9lddQ5-3mf2K1Sw-y.png",
        "./mon_202011/26/-9lddQ5-6aruK1Sw-10.png",
        "./mon_202011/26/-9lddQ5-9zw4K1Sw-z.png",
        "./mon_202011/26/-9lddQ5-cujeK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-f0zlK1Sw-12.png",
        "./mon_202011/26/-9lddQ5-icy1K1Sy-10.png",
        "./mon_202011/26/-9lddQ5-6eipK1Sy-14.png",
        "./mon_202011/26/-9lddQ5-affsK1Sw-12.png",
        "./mon_202011/26/-9lddQ5-csaqK0Sw-y.png",
        "./mon_202011/26/-9lddQ5-gru6K0Sw-y.png",
        "./mon_202011/26/-9lddQ5-k4jtK1Sw-y.png",
        "./mon_202011/26/-9lddQ5-2q1fK1Sy-14.png",
        "./mon_202011/26/-9lddQ5-5jimK1S1c-14.gif",
        "./mon_202011/26/-9lddQ5-9pybK2S1c-14.gif",
        "./mon_202011/26/-9lddQ5-cgj8K2S10-10.gif",
        "./mon_202011/26/-9lddQ5-h8bdK3S1e-18.gif",
        "./mon_202011/26/-9lddQ5-k86xK3S10-1a.gif",
        "./mon_202011/26/-9lddQ5-zw3K1S12-12.gif",
        "./mon_202011/26/-9lddQ5-5jjnK3S14-1a.gif",
        "./mon_202011/26/-9lddQ5-8pvjK5T8S19-1a.gif",
        "./mon_202011/26/-9lddQ5-c7dvK1Sw-10.gif",
        "./mon_202011/26/-9lddQ5-eyg6K1Sy-16.png",
        "./mon_202011/26/-9lddQ5-hr06K1Sy-16.png",
        "./mon_202011/26/-9lddQ5-gdvhK1Sw-12.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-fir8K1Sw-12.png",
        "./mon_202011/26/-9lddQ5-10ghK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-3khdK1Sw-y.png",
        "./mon_202011/26/-9lddQ5-8lnaK1Sw-x.png",
        "./mon_202011/26/-9lddQ5-au9sK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-f5mqK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-jtjwK1Sw-y.png",
        "./mon_202011/26/-9lddQ5-xdnK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-597bK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-8uwfK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-geo8K1Sw-w.png",
        "./mon_202011/26/-9lddQ5-juijK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-jimdK4T8Sy-10.gif",
        "./mon_202011/26/-9lddQ5-1yegK1S16-10.png",
        "./mon_202011/26/-9lddQ5-4twvK0Sy-y.png",
        "./mon_202011/26/-9lddQ5-a60pK0Sy-y.png",
        "./mon_202011/26/-9lddQ5-3sjwK1S1c-1e.png",
        "./mon_202011/26/-9lddQ5-cgn5K0Sw-10.png",
        "./mon_202011/26/-9lddQ5-9feoK1Sw-14.png",
        "./mon_202011/26/-9lddQ5-hpgK0Sw-13.png",
        "./mon_202011/26/-9lddQ5-8846K1Sy-13.png",
        "./mon_202011/26/-9lddQ5-jjq1K0Sw-z.png",
        "./mon_202011/26/-9lddQ5-2vjuK1Sw-x.png",
        "./mon_202011/26/-9lddQ5-cnl2K0Sy-10.png",
        "./mon_202011/26/-9lddQ5-hy49K0Sy-10.png",
        "./mon_202011/26/-9lddQ5-15dfK0Sy-12.png",
        "./mon_202011/26/-9lddQ5-bls0K0Sy-12.png",
        "./mon_202011/26/-9lddQ5-kpa1K1Sw-11.png",
        "./mon_202011/26/-9lddQ5-987vK1Sw-11.png",
        "./mon_202011/26/-9lddQ5-gmlyK0Sw-11.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-8rxdK1S14-18.png",
        "./mon_202011/26/-9lddQ5-dq5rK0Sw-14.png",
        "./mon_202011/26/-9lddQ5-h16xK1Sw-14.png",
        "./mon_202011/26/-9lddQ5-vhkK0Sw-14.png",
        "./mon_202011/26/-9lddQ5-csxfK1Sw-14.png",
        "./mon_202011/26/-9lddQ5-a65vK1S12-11.png",
        "./mon_202011/26/-9lddQ5-e9k1K1S12-11.png",
        "./mon_202011/26/-9lddQ5-jc8yK1S12-11.png",
        "./mon_202011/26/-9lddQ5-37q8K1S14-14.png",
        "./mon_202011/26/-9lddQ5-7owaK1Sw-15.png",
        "./mon_202011/26/-9lddQ5-e7s5K1Sw-15.png",
        "./mon_202011/26/-9lddQ5-iq1rK1S12-15.png",
        "./mon_202011/26/-9lddQ5-1jhlK1S12-15.png",
        "./mon_202011/26/-9lddQ5-almaK0Sy-11.png",
        "./mon_202011/26/-9lddQ5-dr9lK0Sy-11.png",
        "./mon_202011/26/-9lddQ5-hg9xK1Sw-y.png",
        "./mon_202011/26/-9lddQ5-sr5K1Sy-11.png",
        "./mon_202011/26/-9lddQ5-dqszK1Sy-11.png",
        "./mon_202011/26/-9lddQ5-kqoaK1Sy-11.png",
        "./mon_202011/26/-9lddQ5-2qgeK0Sw-y.png",
        "./mon_202011/26/-9lddQ5-6dacK1Sw-y.png",
        "./mon_202011/26/-9lddQ5-blszK0Sw-y.png",
        "./mon_202011/26/-9lddQ5-4hn0K1S12-16.png",
        "./mon_202011/26/-9lddQ5-ai87K1S12-11.png",
        "./mon_202011/26/-9lddQ5-dkh4K1S18-14.png",
        "./mon_202011/26/-9lddQ5-gfkhK2S17-1d.gif",
        "./mon_202011/26/-9lddQ5-e2orK2S17-1d.gif",
        "./mon_202011/26/-9lddQ5-idmsK0St-13.png",
        "./mon_202011/26/-9lddQ5-2iqeK0St-13.png",
        "./mon_202011/26/-9lddQ5-66zjK0St-13.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-b4stK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-e43qK0Sw-10.png",
        "./mon_202011/26/-9lddQ5-ied4K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-1t6nK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-8ow6K1Sw-10.png",
        "./mon_202011/26/-9lddQ5-howlK1Sw-y.gif",
        "./mon_202011/26/-9lddQ5-3j2qK1Sw-y.png",
        "./mon_202011/26/-9lddQ5-cgafK1Sw-y.png",
        "./mon_202011/26/-9lddQ5-ksz1K1S10-12.png",
        "./mon_202011/26/-9lddQ5-6424K1S10-12.png",
        "./mon_202011/26/-9lddQ5-d7oaK1S10-12.png",
        "./mon_202011/26/-9lddQ5-c2vwK1S10-12.png",
        "./mon_202011/26/-9lddQ5-60ecK1S10-12.png",
        "./mon_202011/26/-9lddQ5-ihwcK1S10-12.png",
        "./mon_202011/26/-9lddQ5-cvuuK1S10-12.png",
        "./mon_202011/26/-9lddQ5-6d0vK1S10-12.png",
        "./mon_202011/26/-9lddQ5-65exK1S10-12.png",
        "./mon_202011/26/-9lddQ5-bv3K1S10-12.png",
        "./mon_202011/26/-9lddQ5-5hmgK1S10-12.png",
        "./mon_202011/26/-9lddQ5-7hi3K1Sy-16.png",
        "./mon_202011/26/-9lddQ5-gqevK1Sy-16.png",
        "./mon_202011/26/-9lddQ5-bakrK0Sw-14.png",
        "./mon_202011/26/-9lddQ5-jwzcK1Sw-1q.png",
        "./mon_202011/26/-9lddQ5-93h0K1S12-12.png",
        "./mon_202011/26/-9lddQ5-iv2xK1Sw-10.png",
        "./mon_202011/26/-9lddQ5-d2zhK1Sw-10.png",
        "./mon_202011/26/-9lddQ5-1xbwK0Sw-12.png",
        "./mon_202011/26/-9lddQ5-ebhfK1Sw-14.png",
        "./mon_202011/26/-9lddQ5-2dyyK0S12-16.png",
        "./mon_202011/26/-9lddQ5-c878K1S16-1a.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-2hkwK1Sw-y.png",
        "./mon_202011/26/-9lddQ5-bx4yK0Sw-y.png",
        "./mon_202011/26/-9lddQ5-kybjK1S10-12.png",
        "./mon_202011/26/-9lddQ5-btrkK1Sy-13.png",
        "./mon_202011/26/-9lddQ5-5kjeK1Sy-14.png",
        "./mon_202011/26/-9lddQ5-emouK1Sy-14.png",
        "./mon_202011/26/-9lddQ5-34x7K1Sy-1a.png",
        "./mon_202011/26/-9lddQ5-cgkhK1S10-10.png",
        "./mon_202011/26/-9lddQ5-1002K1Sy-10.png",
        "./mon_202011/26/-9lddQ5-i8hsK0Sw-y.png",
        "./mon_202011/26/-9lddQ5-673aK1Su-12.png",
        "./mon_202011/26/-9lddQ5-ernjK1Sw-10.png",
        "./mon_202011/26/-9lddQ5-g7faK1Sw-10.png",
        "./mon_202011/26/-9lddQ5-4lohK1S12-17.png",
        "./mon_202011/26/-9lddQ5-e15gK0Sw-w.gif",
        "./mon_202011/26/-9lddQ5-7q5hK1Sy-14.png",
        "./mon_202011/26/-9lddQ5-gvacK1S10-14.png",
        "./mon_202011/26/-9lddQ5-53p6K1S10-14.png",
        "./mon_202011/26/-9lddQ5-f35lK1Sw-10.png",
        "./mon_202011/26/-9lddQ5-2h7kK0Sw-10.png",
        "./mon_202011/26/-9lddQ5-8ogcK1S11-14.png",
        "./mon_202011/26/-9lddQ5-hlbvK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-6op8K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-abemK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-jzrfK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-82g6K1S17-17.png",
        "./mon_202011/26/-9lddQ5-80ogK1S12-10.gif",
        "./mon_202011/26/-9lddQ5-aa5yK1S12-10.png",
        "./mon_202011/26/-9lddQ5-jumjK1Sw-12.png",
        "./mon_202011/26/-9lddQ5-7r97K1S12-y.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-fz78K1Sw-w.png",
        "./mon_202011/26/-9lddQ5-4n2pK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-ecanK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-dll6K1Sw-10.png",
        "./mon_202011/26/-9lddQ5-ero6K1Sw-w.png",
        "./mon_202011/26/-9lddQ5-xxmK1S10-14.png",
        "./mon_202011/26/-9lddQ5-ajmyK1S10-15.png",
        "./mon_202011/26/-9lddQ5-kg5qK1Sy-14.png",
        "./mon_202011/26/-9lddQ5-9566K1Sy-14.png",
        "./mon_202011/26/-9lddQ5-iyjjK1Sy-14.png",
        "./mon_202011/26/-9lddQ5-58owK1S10-14.png",
        "./mon_202011/26/-9lddQ5-7292K1S12-10.png",
        "./mon_202011/26/-9lddQ5-hb3oK1S10-1e.png",
        "./mon_202011/26/-9lddQ5-azqtK1S14-1a.png",
        "./mon_202011/26/-9lddQ5-cmwbK1S14-16.png",
        "./mon_202011/26/-9lddQ5-odtK1Sw-x.png",
        "./mon_202011/26/-9lddQ5-9udtK1Sw-12.png",
        "./mon_202011/26/-9lddQ5-cqzcK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-f2j0K1Sy-y.png",
        "./mon_202011/26/-9lddQ5-4sznK1S10-14.png",
        "./mon_202011/26/-9lddQ5-eyrhK1S14-14.png",
        "./mon_202011/26/-9lddQ5-39veK1Sy-14.gif",
        "./mon_202011/26/-9lddQ5-ctt3K1Sy-14.png",
        "./mon_202011/26/-9lddQ5-107sK1S14-14.png",
        "./mon_202011/26/-9lddQ5-b8jiK1S14-14.png",
        "./mon_202011/26/-9lddQ5-cfy2K1S14-14.png",
        "./mon_202011/26/-9lddQ5-1ul6K1S14-14.png",
        "./mon_202011/26/-9lddQ5-375aK1S10-14.png",
        "./mon_202011/26/-9lddQ5-db2zK1S10-14.png",
        "./mon_202011/26/-9lddQ5-1lauK3S10-16.gif",
      ],
      [
        "./mon_202011/26/-9lddQ5-1au8K4T8S15-16.gif",
        "./mon_202011/26/-9lddQ5-bgocK4T8S15-16.gif",
        "./mon_202011/26/-9lddQ5-k697K1S12-1e.png",
        "./mon_202011/26/-9lddQ5-97paK1S14-19.png",
        "./mon_202011/26/-9lddQ5-j6w0K1S14-1e.png",
        "./mon_202011/26/-9lddQ5-bsjzK1S13-18.png",
        "./mon_202011/26/-9lddQ5-42rK1S13-18.png",
        "./mon_202011/26/-9lddQ5-9xfjK1S13-18.png",
        "./mon_202011/26/-9lddQ5-jdzhK1S18-1e.png",
        "./mon_202011/26/-9lddQ5-8jcxK1Sy-1a.png",
        "./mon_202011/26/-9lddQ5-ipezK1Sy-1a.png",
        "./mon_202011/26/-9lddQ5-7327K1Sy-1c.png",
        "./mon_202011/26/-9lddQ5-gn9aK2S10-16.png",
        "./mon_202011/26/-9lddQ5-4t0pK1S10-16.png",
        "./mon_202011/26/-9lddQ5-7823K1S16-18.png",
        "./mon_202011/26/-9lddQ5-4jkaK1S16-18.png",
        "./mon_202011/26/-9lddQ5-a28oK1S10-18.png",
        "./mon_202011/26/-9lddQ5-l9m2K1S10-10.png",
        "./mon_202011/26/-9lddQ5-aj62K1Sw-y.png",
        "./mon_202011/26/-9lddQ5-keibK0Sw-y.png",
        "./mon_202011/26/-9lddQ5-99atK1Sw-18.png",
        "./mon_202011/26/-9lddQ5-ejo9K1Sw-18.png",
        "./mon_202011/26/-9lddQ5-2yhcK1S10-1c.png",
        "./mon_202011/26/-9lddQ5-380wK1Sw-12.png",
        "./mon_202011/26/-9lddQ5-cuk0K1Sw-12.png",
        "./mon_202011/26/-9lddQ5-1k2sK1Sw-12.png",
        "./mon_202011/26/-9lddQ5-ji5vK1S14-12.png",
        "./mon_202011/26/-9lddQ5-7z09K1S14-12.png",
        "./mon_202011/26/-9lddQ5-i7huK2S14-12.gif",
        "./mon_202011/26/-9lddQ5-620oK1Sw-1i.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-6doxK1Sw-z.png",
        "./mon_202011/26/-9lddQ5-gfffK1Su-10.png",
        "./mon_202011/26/-9lddQ5-4ufcK0Su-11.png",
        "./mon_202011/26/-9lddQ5-el4bK0Sw-y.png",
        "./mon_202011/26/-9lddQ5-48k9K0Sw-y.png",
        "./mon_202011/26/-9lddQ5-el5pK1Sx-y.png",
        "./mon_202011/26/-9lddQ5-309hK4T8S34-32.png",
        "./mon_202011/26/-9lddQ5-de8tK0Ss-w.png",
        "./mon_202011/26/-9lddQ5-1zr0K7T8S14-14.gif",
        "./mon_202011/26/-9lddQ5-ar83K1S19-10.gif",
        "./mon_202011/26/-9lddQ5-kmi7K1S10-13.png",
        "./mon_202011/26/-9lddQ5-lbywK1Sy-12.png",
        "./mon_202011/26/-9lddQ5-9mpbK1Sy-10.png",
        "./mon_202011/26/-9lddQ5-jcmqK1Sy-10.png",
        "./mon_202011/26/-9lddQ5-7xysK3Sw-14.gif",
        "./mon_202011/26/-9lddQ5-gqknK0Sw-10.png",
        "./mon_202011/26/-9lddQ5-8rxaK3Sw-12.gif",
        "./mon_202011/26/-9lddQ5-gw90K1S10-14.png",
        "./mon_202011/26/-9lddQ5-426oK1Sy-12.png",
        "./mon_202011/26/-9lddQ5-heyaK1S10-14.png",
        "./mon_202011/26/-9lddQ5-25m2K0Sw-10.png",
        "./mon_202011/26/-9lddQ5-c08kK1Sw-10.png",
        "./mon_202011/26/-9lddQ5-bjsK0Sw-y.png",
        "./mon_202011/26/-9lddQ5-2lsvK3Sw-w.gif",
        "./mon_202011/26/-9lddQ5-7f2sK1Sy-12.png",
        "./mon_202011/26/-9lddQ5-cp1bK0Sw-13.png",
        "./mon_202011/26/-9lddQ5-upbK0Sw-1d.png",
        "./mon_202011/26/-9lddQ5-a4toK3Sw-y.gif",
        "./mon_202011/26/-9lddQ5-1acbK1Sz-y.png",
        "./mon_202011/26/-9lddQ5-4j4fK1Sw-y.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-a8qiK1Sw-12.png",
        "./mon_202011/26/-9lddQ5-5pm8K1S12-1k.png",
        "./mon_202011/26/-9lddQ5-eipgK1S10-16.png",
        "./mon_202011/26/-9lddQ5-28plK0Sw-14.png",
        "./mon_202011/26/-9lddQ5-bog6K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-lcc0K1Sy-12.png",
        "./mon_202011/26/-9lddQ5-8gtcK0Sw-z.png",
        "./mon_202011/26/-9lddQ5-ht9zK0Sw-z.png",
        "./mon_202011/26/-9lddQ5-5ax2K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-5n1zK1Sx-14.png",
        "./mon_202011/26/-9lddQ5-ddpaK0Sw-16.png",
        "./mon_202011/26/-9lddQ5-ejfsK1Sy-16.png",
        "./mon_202011/26/-9lddQ5-4if0K1S12-14.png",
        "./mon_202011/26/-9lddQ5-e2mfK1S10-14.png",
        "./mon_202011/26/-9lddQ5-kz6dK1Sy-11.png",
        "./mon_202011/26/-9lddQ5-2wfaK0Sw-y.png",
        "./mon_202011/26/-9lddQ5-cj27K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-ik4K0Sw-z.png",
        "./mon_202011/26/-9lddQ5-9mmbK0S10-z.png",
        "./mon_202011/26/-9lddQ5-igm6K1Sw-x.png",
        "./mon_202011/26/-9lddQ5-61q3K1S16-1c.png",
        "./mon_202011/26/-9lddQ5-ighoK1Sw-12.png",
        "./mon_202011/26/-9lddQ5-bp98K1S12-18.png",
        "./mon_202011/26/-9lddQ5-9s5K1Sw-17.png",
        "./mon_202011/26/-9lddQ5-kn08K1S1e-10.png",
        "./mon_202011/26/-9lddQ5-7m2rK1S12-1b.png",
        "./mon_202011/26/-9lddQ5-gga7K0Sw-18.gif",
        "./mon_202011/26/-9lddQ5-47p8K1Sy-12.png",
        "./mon_202011/26/-9lddQ5-8eafK1S10-13.png",
        "./mon_202011/26/-9lddQ5-gun7K1S10-11.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-y7pK1Sy-11.png",
        "./mon_202011/26/-9lddQ5-inwyKeT8S14-18.gif",
        "./mon_202011/26/-9lddQ5-6lmaK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-g3kqK1Sw-14.png",
        "./mon_202011/26/-9lddQ5-6fu7K1S1c-12.png",
      ],
      [
        "./mon_202105/27/-9lddQ2o-ekkqK1Sw-10.png",
        "./mon_202105/27/-9lddQ2o-1g3sK1S10-18.png",
        "./mon_202105/27/-9lddQ2o-dav0K1Sz-11.png",
        "./mon_202105/27/-9lddQ2o-kfg8K1Sw-z.png",
      ]
    ],
    [
      "动物",
      [
        "./mon_202011/26/-9lddQ5-e97hK1Sw-12.png",
        "./mon_202011/26/-9lddQ5-1g80K1Sw-12.png",
        "./mon_202011/26/-9lddQ5-bewkK1Sw-10.png",
        "./mon_202011/26/-9lddQ5-kw3pK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-kkb9K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-7t6qK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-gs6xK0Sw-13.png",
        "./mon_202011/26/-9lddQ5-4goaK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-kejeK0Sw-y.png",
        "./mon_202011/26/-9lddQ5-femlK1Sw-11.png",
        "./mon_202011/26/-9lddQ5-47b5K1Sw-10.png",
        "./mon_202011/26/-9lddQ5-crv3K0Sw-10.png",
        "./mon_202011/26/-9lddQ5-4ke5K0Sw-11.png",
        "./mon_202011/26/-9lddQ5-dda7K1Sw-w.png",
        "./mon_202011/26/-9lddQ5-170yK0Sw-11.png",
        "./mon_202011/26/-9lddQ5-amrqK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-bzykK1Sz-11.png",
        "./mon_202011/26/-9lddQ5-l44gK0Sw-x.png",
        "./mon_202011/26/-9lddQ5-9knoK1Sw-11.png",
        "./mon_202011/26/-9lddQ5-iruoK1Sw-11.png",
        "./mon_202011/26/-9lddQ5-6c2jK1Sx-10.png",
        "./mon_202011/26/-9lddQ5-gcboK1Sw-10.png",
        "./mon_202011/26/-9lddQ5-5307K0Sw-12.png",
        "./mon_202011/26/-9lddQ5-9v1cK1Sw-10.png",
        "./mon_202011/26/-9lddQ5-a58uK1Sw-y.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-gxxqK1Sw-z.png",
        "./mon_202011/26/-9lddQ5-4il9K1Sw-14.png",
        "./mon_202011/26/-9lddQ5-c0tlK1Sw-w.png",
      ],
      [
        "./mon_202105/27/-9lddQ2o-57a5K1S16-1c.png",
        "./mon_202105/27/-9lddQ2o-gz1mK0Sw-u.png",
      ]
    ],
    [
      "硬件",
      [
        "./mon_202011/26/-9lddQ5-ld2cK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-5dwqK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-k4lwK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-88byK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-hwc7K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-5xxyK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-9zlrK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-238xK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-a2j5K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-i7noK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-4o5tK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-d9oyK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-hexK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-9upkK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-ab9oK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-jhqvK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-7xasK1Sw-12.gif",
        "./mon_202011/26/-9lddQ5-g91sK0S10-w.png",
        "./mon_202011/26/-9lddQ5-4iezK0S10-w.png",
        "./mon_202011/26/-9lddQ5-e9gnK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-faeK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-98muK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-itd9K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-serK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-9opdK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-j989K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-59s2K0Sw-w.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-cjnbK0Sw-16.png",
        "./mon_202011/26/-9lddQ5-ij83K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-cp84K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-9m1vK0Sw-x.png",
        "./mon_202011/26/-9lddQ5-ivu7K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-6evrK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-fb7fK0Sw-11.png",
        "./mon_202011/26/-9lddQ5-23ojK0Sw-11.png",
        "./mon_202011/26/-9lddQ5-a4v0K0Sw-x.png",
        "./mon_202011/26/-9lddQ5-islaK0Sw-x.png",
        "./mon_202011/26/-9lddQ5-18hmK0Sw-x.png",
        "./mon_202011/26/-9lddQ5-cwoxK0Sw-x.png",
        "./mon_202011/26/-9lddQ5-au8jK0Sw-x.png",
        "./mon_202011/26/-9lddQ5-4ydsK1Sw-10.png",
        "./mon_202011/26/-9lddQ5-eygtK0Sw-x.png",
        "./mon_202011/26/-9lddQ5-aeznK0Sw-x.png",
        "./mon_202011/26/-9lddQ5-jdouK0Sw-x.png",
      ],
    ],
    [
      "白鹅",
      [
        "./mon_202011/26/-9lddQ5-8hnkK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-g18pK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-4rn9K1Sw-w.png",
        "./mon_202011/26/-9lddQ5-60f5K1Sw-w.png",
        "./mon_202011/26/-9lddQ5-reyK0Sy-w.png",
        "./mon_202011/26/-9lddQ5-as2xK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-8r5kK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-3yjwK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-d8vxK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-1dteK0Sx-14.png",
        "./mon_202011/26/-9lddQ5-goqcK0Sw-14.png",
        "./mon_202011/26/-9lddQ5-5xelK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-fxmrK1Sw-w.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-frzrK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-432jK1S12-11.png",
        "./mon_202011/26/-9lddQ5-dtocK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-lc5dK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-dkwbK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-6ffaK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-hk7pK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-dj9tK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-kf9xK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-9zpqK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-6gkcK1Sz-z.png",
        "./mon_202011/26/-9lddQ5-g7e6K1Sw-w.png",
        "./mon_202011/26/-9lddQ5-436lK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-j2x1K1Sw-w.png",
        "./mon_202011/26/-9lddQ5-ehy8K1Sw-w.png",
        "./mon_202011/26/-9lddQ5-fmq8K1Sw-w.png",
        "./mon_202011/26/-9lddQ5-a6ldK0Sw-z.png",
        "./mon_202011/26/-9lddQ5-cbv0K1Sw-w.png",
        "./mon_202011/26/-9lddQ5-3v9pK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-cuzhK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-dr3zK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-9f9iK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-54jzK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-1fthK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-ab22K1Sw-w.png",
        "./mon_202011/26/-9lddQ5-5kdiK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-feo8K2T8S1q-1s.png",
        "./mon_202011/26/-9lddQ5-6j1eK2S18-18.png",
        "./mon_202011/26/-9lddQ5-doimK0S14-w.png",
        "./mon_202011/26/-9lddQ5-ck3K1Sw-w.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-co05K1Sw-w.png",
        "./mon_202011/26/-9lddQ5-f5n9K1Sw-w.png",
        "./mon_202011/26/-9lddQ5-gii1K1Sw-w.png",
        "./mon_202011/26/-9lddQ5-hujoK0Sy-w.png",
        "./mon_202011/26/-9lddQ5-5mu9K0Sy-w.png",
        "./mon_202011/26/-9lddQ5-d2yvK1S12-11.png",
        "./mon_202011/26/-9lddQ5-iof5K0Sy-y.png",
        "./mon_202011/26/-9lddQ5-3evwK0Sz-z.png",
        "./mon_202011/26/-9lddQ5-ax7vK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-fn5jK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-kxdxK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-7ovsK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-f4usK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-hsmdK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-318aK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-6u4sK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-ey5qK1S18-18.png",
        "./mon_202011/26/-9lddQ5-isebK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-4tm0K1Sw-w.png",
        "./mon_202011/26/-9lddQ5-d42oK0Sw-z.png",
        "./mon_202011/26/-9lddQ5-j0cyK0Sz-w.png",
        "./mon_202011/26/-9lddQ5-7nuyK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-dfnkK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-e0rK1S12-11.png",
        "./mon_202011/26/-9lddQ5-5aejK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-e0maK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-j7isK1S18-18.png",
        "./mon_202011/26/-9lddQ5-2zkoK0S10-x.png",
        "./mon_202011/26/-9lddQ5-aem3K0Sy-18.png",
        "./mon_202011/26/-9lddQ5-iptfK0Sy-10.png",
      ],
      ["./mon_202011/26/-9lddQ5-g7bsK0Sz-11.png"],
    ],
    [
      "高达",
      [
        "./mon_202011/26/-9lddQ5-cuvK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-626bK0S11-y.png",
        "./mon_202011/26/-9lddQ5-c7leK1Sw-10.png",
        "./mon_202011/26/-9lddQ5-gy97K0Sw-13.png",
        "./mon_202011/26/-9lddQ5-46uuK0Sw-13.png",
        "./mon_202011/26/-9lddQ5-cgrlK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-l2wcK0Sw-11.png",
        "./mon_202011/26/-9lddQ5-5l6fK0Sw-11.png",
        "./mon_202011/26/-9lddQ5-b967K0Sw-11.png",
        "./mon_202011/26/-9lddQ5-jj5aK0Sw-11.png",
        "./mon_202011/26/-9lddQ5-4pbeK0Sw-12.png",
        "./mon_202011/26/-9lddQ5-apdqK0Sw-12.png",
        "./mon_202011/26/-9lddQ5-hzlgK0Sw-12.png",
        "./mon_202011/26/-9lddQ5-4s8nK0Sw-z.png",
        "./mon_202011/26/-9lddQ5-cw5hK0Sw-z.png",
        "./mon_202011/26/-9lddQ5-ldplK0Sw-z.png",
        "./mon_202011/26/-9lddQ5-7mh6K0Sw-z.png",
        "./mon_202011/26/-9lddQ5-dwvkK0Sw-z.png",
        "./mon_202011/26/-9lddQ5-8j7yK0Sw-12.png",
        "./mon_202011/26/-9lddQ5-h9yxK0Sw-11.png",
        "./mon_202011/26/-9lddQ5-3obmK0Sw-10.png",
        "./mon_202011/26/-9lddQ5-8vknK1Sw-11.png",
        "./mon_202011/26/-9lddQ5-flfjK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-10oK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-55b4K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-cl30K0Sw-w.png",
        "./mon_202011/26/-9lddQ5-huzmK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-2qxnK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-ah2hK1Sw-w.png",
      ],
      [
        "./mon_202011/26/-9lddQ5-311qK0Sw-10.png",
        "./mon_202011/26/-9lddQ5-8e4iK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-dv7kK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-l6qbK1Sw-w.png",
        "./mon_202011/26/-9lddQ5-8q3rK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-fybzK0Sw-w.png",
        "./mon_202011/26/-9lddQ5-fippK0Sw-w.png",
      ],
    ],
  ];
 
  const loadSmiles = (loaded) => {
    if (loaded) return;
    
    const { correctAttachUrl } = ui;
 
    const tabs = poster.selectSmilesw._.__c.firstElementChild;
    const contents = poster.selectSmilesw._.__c.lastElementChild;
 
    smiles.forEach((item) => {
      const name = item[0];
      const list = item.flatMap((i) => (typeof i === "object" ? [...i] : []));
 
      const pageSize = 60;
      const pageCount = Math.ceil(list.length / pageSize);
 
      for (let i = 0; i < pageCount; i++) {
        const tab = document.createElement("BUTTON");
        const content = document.createElement("DIV");
 
        tab.className = "block_txt_big";
        tab.innerText = `${name}${
          pageCount > 1 ? `(${i + 1}/${pageCount})` : ""
        }`;
        tab.onclick = () => {
          tabs.firstChild.innerHTML = `<a href="https://bbs.saraba1st.com/2b/" target="_blank" style="color: inherit;">《粪海狂蛆》</a>`;
 
          contents.childNodes.forEach((c) => {
            if (c !== content) {
              c.style.display = "none";
            } else {
              c.style.display = "";
            }
          });
 
          if (content.childNodes.length === 0) {
            list.slice(pageSize * i, pageSize * (i + 1)).forEach((s) => {
              const smile = document.createElement("IMG");
              smile.src = correctAttachUrl(s);
              smile.style = "max-width: 60px";
              smile.onclick = () => {
                poster.selectSmilesw._.hide();
                poster.addText(`[img]${s}[/img]`);
              };
 
              content.appendChild(smile);
            });
          }
        };
 
        tabs.appendChild(tab);
        contents.appendChild(content);
      }
    });
  };
 
  hookFunction(poster, "selectSmiles", (returnValue) =>
    loadSmiles(returnValue)
  );
 
  if (location.pathname === "/post.php") {
    const corsServices = [
      "https://cors.bridged.cc/",
    ];
 
    const { attach_url, fid, auth } = poster.currentPostStat;
 
    const upload = async (url, corsIndex = 0) => {
      const corsService = corsServices[corsIndex];
 
      const fetch_retry = async (url, options, n) => {
        let error;
        for (let i = 0; i < n; i++) {
          try {
            return await fetch(url, options).then((res) => {
              if (!res.ok) {
                throw Error(res.statusText);
              }
              return res;
            });
          } catch (err) {
            error = err;
          }
        }
        throw error;
      };
 
      return fetch_retry(corsService + url, { 
        headers: {
          "X-Requested-With": "XMLHttpRequest"
        }}, 3)
        .then((res) => res.blob())
        .then((blob) => {
          const filename = url.replace(/^.*[\\\/]/, "");
 
          const file = new File([blob], filename, {
            type: blob.type,
            lastModified: Date.now(),
          });
 
          const formData = new FormData();
 
          formData.append("v2", "1");
          formData.append("func", "upload");
          formData.append("attachment_file1", file);
          formData.append(
            "attachment_file1_url_utf8_name",
            poster.rawUrlEncode(filename)
          );
          formData.append("attachment_file1_img", "1");
          formData.append("attachment_file1_dscp", "");
          formData.append("attachment_file1_auto_size", "0");
          formData.append("fid", fid);
          formData.append("auth", auth);
          formData.append("__output", "11");
 
          return fetch(attach_url, { method: "POST", body: formData });
        })
        .then((res) => res.json())
        .then((res) => {
          poster.add1Attach(
            res.attachments,
            res.attachments_check,
            res.url,
            res.isImg,
            res.thumb
          );
 
          return res.url;
        })
        .catch(() => "");
    };
 
    const migration = (task, limit = 0, length = 60) => {
      const SOURCEURL =
        "https://bbs.saraba1st.com/2b/data/cache/common_smilies_var.js";
      const STATICURL = "https://static.saraba1st.com/";
 
      const interval = 100;
 
      fetch(corsServices[0] + SOURCEURL)
        .then((res) => res.text())
        .then(async (res) => {
          eval(res);
 
          const pair = Object.entries(smilies_type).find(
            ([key, value]) => value[0] === task
          );
 
          if (pair) {
            const typeid = pair[0].substr(1);
            const typename = pair[1][0];
            const typepath = pair[1][1];
 
            poster.addText(
              `正在搬运[${typename}]:${limit + 1} - ${limit + length}\n`
            );
 
            const array = smilies_array[typeid]
              .flatMap((item) => [...item])
              .slice(limit, limit + length);
 
            const result = [];
 
            for (let item in array) {
              const filename = array[item][2];
 
              const smilieimg =
                STATICURL + "image/smiley/" + typepath + "/" + filename;
 
              const url = await upload(smilieimg);
 
              if (url) {
                result.push(`./${url}`);
              } else {
                poster.addText(`搬运失败:${smilieimg}\n`);
              }
 
              await new Promise((resolve) => setTimeout(resolve, interval));
            }
 
            if (result.length) {
              poster.addText(
                `[collapse=${typename}][code]${JSON.stringify(
                  result
                )}[/code]${result
                  .map((item) => `[img]${item}[/img]`)
                  .join("")}[/collapse]\n`
              );
            }
          }
        });
    };
 
    poster.migration = migration;
  }
})(commonui, postfunc);

显示被点赞和粉丝数量 - NGA Likes Support

在回帖列表中显示每个楼层用户的总被点赞数量

v1.0.1 修复了贴条的BUG;不再显示匿名用户;现在总点赞数为0的也会被显示(方便给同情分
v1.0.2 不再显示匿名用户
v1.0.3 增加了关注数量的显示(不显示0关注),优化了加载方式
v1.0.4 修改了部分代码以兼容关注脚本

// ==UserScript==
// @name        NGA Likes Support
// @namespace   https://greasyfork.org/users/263018
// @version     1.1.1
// @author      snyssss
// @description 显示被点赞和粉丝数量
 
// @match       *://bbs.nga.cn/*
// @match       *://ngabbs.com/*
// @match       *://nga.178.com/*
 
// @noframes
// ==/UserScript==
 
((ui) => {
  if (!ui) return;
 
  // 钩子
  const hookFunction = (object, functionName, callback) => {
    ((originalFunction) => {
      object[functionName] = function () {
        const returnValue = originalFunction.apply(this, arguments);
 
        callback.apply(this, [returnValue, originalFunction, arguments]);
 
        return returnValue;
      };
    })(object[functionName]);
  };
 
  class UserInfo {
    execute(task) {
      task().finally(() => {
        if (this.waitingQueue.length) {
          const next = this.waitingQueue.shift();
 
          this.execute(next);
        } else {
          this.isRunning = false;
        }
      });
    }
 
    enqueue(task) {
      if (this.isRunning) {
        this.waitingQueue.push(task);
      } else {
        this.isRunning = true;
 
        this.execute(task);
      }
    }
 
    rearrange() {
      if (this.data) {
        const list = Object.values(this.children);
 
        for (let i = 0; i < list.length; i++) {
          if (list[i].source === undefined) {
            list[i].create(this.data);
          }
 
          Object.entries(this.container).forEach((item) => {
            list[i].clone(this.data, item);
          });
        }
      }
    }
 
    reload() {
      this.enqueue(async () => {
        this.data = await new Promise((resolve) => {
          fetch(`/nuke.php?lite=js&__lib=ucp&__act=get&uid=${this.uid}`)
            .then((res) => res.blob())
            .then((blob) => {
              const reader = new FileReader();
 
              reader.onload = () => {
                const text = reader.result;
                const result = JSON.parse(
                  text.replace("window.script_muti_get_var_store=", "")
                );
 
                resolve(result.data[0]);
              };
 
              reader.readAsText(blob, "GBK");
            })
            .catch(() => {
              resolve();
            });
        });
 
        Object.values(this.children).forEach((item) => item.destroy());
 
        this.rearrange();
      });
    }
 
    constructor(id) {
      this.uid = id;
 
      this.waitingQueue = [];
      this.isRunning = false;
 
      this.container = {};
      this.children = {};
 
      this.reload();
    }
  }
 
  class UserInfoWidget {
    destroy() {
      if (this.source) {
        this.source = undefined;
      }
 
      if (this.target) {
        Object.values(this.target).forEach((item) => {
          if (item.parentNode) {
            item.parentNode.removeChild(item);
          }
        });
      }
    }
 
    clone(data, [argid, container]) {
      if (this.source) {
        if (this.target[argid] === undefined) {
          this.target[argid] = this.source.cloneNode(true);
 
          if (this.callback) {
            this.callback(data, this.target[argid]);
          }
        }
 
        container.appendChild(this.target[argid]);
      }
    }
 
    constructor(func, callback) {
      this.create = (data) => {
        this.destroy();
 
        this.source = func(data);
        this.target = {};
      };
 
      this.callback = callback;
    }
  }
 
  ui.sn = ui.sn || {};
  ui.sn.userInfo = ui.sn.userInfo || {};
 
  ((info) => {
    const execute = (argid) => {
      const args = ui.postArg.data[argid];
 
      if (args.comment) return;
 
      const uid = +args.pAid;
 
      if (uid > 0) {
        if (info[uid] === undefined) {
          info[uid] = new UserInfo(uid);
        }
 
        if (document.contains(info[uid].container[argid]) === false) {
          info[uid].container[argid] = args.uInfoC.querySelector(
            "[name=uid]"
          ).parentNode;
        }
 
        info[uid].enqueue(async () => {
          if (info[uid].children[8] === undefined) {
            info[uid].children[8] = new UserInfoWidget((data) => {
              const value =
                Object.values(data.more_info || {}).find((item) => item.type === 8)
                  ?.data || 0;
 
              const element = document.createElement("SPAN");
 
              element.className =
                "small_colored_text_btn stxt block_txt_c2 vertmod";
              element.style.cursor = "default";
              element.innerHTML = `<span class="white"><span style="font-family: comm_glyphs; -webkit-font-smoothing: antialiased; line-height: 1em;">⯅</span>&nbsp;${value}</span>`;
 
              return element;
            });
          }
 
          if (info[uid].children[16] === undefined) {
            info[uid].children[16] = new UserInfoWidget((data) => {
              const value = data.follow_by_num || 0;
 
              const element = document.createElement("SPAN");
 
              element.className =
                "small_colored_text_btn stxt block_txt_c2 vertmod";
              element.style.cursor = "default";
              element.innerHTML = `<span class="white"><span style="font-family: comm_glyphs; -webkit-font-smoothing: antialiased; line-height: 1em;">★</span>&nbsp;${value}</span>`;
 
              return element;
            });
          }
 
          info[uid].rearrange();
        });
      }
    };
 
    if (ui.postArg) {
      Object.keys(ui.postArg.data).forEach((i) => execute(i));
    }
 
    let initialized = false;
 
    hookFunction(ui, "eval", () => {
      if (initialized) return;
 
      if (ui.postDisp) {
        hookFunction(
          ui,
          "postDisp",
          (returnValue, originalFunction, arguments) => execute(arguments[0])
        );
 
        initialized = true;
      }
    });
  })(ui.sn.userInfo);
})(commonui);

同步客户端关注功能 - NGA Watcher

事到如今我也不用装什么正人君子了.jpg

v1.1.0 采用官方接口实现网页版关注功能,与客户端同步。故原有的关键词筛选功能和消息提示功能暂已取消,请谨慎选择更新。
v1.2.0 关键词筛选功能和消息提示功能回归,现在支持单独设置筛选条件。
v1.2.2 关注动态数量提示。
v1.3.0 解决官方接口会返回已取消关注用户动态的问题。重写了关注动态数量提示逻辑,现在会提示筛选后的准确数量了。

// ==UserScript==
// @name        NGA Watcher
// @namespace   https://greasyfork.org/users/263018
// @version     1.3.1
// @author      snyssss
// @description 同步客户端关注功能
 
// @match       *://bbs.nga.cn/*
// @match       *://ngabbs.com/*
// @match       *://nga.178.com/*
 
// @grant       GM_addStyle
// @grant       GM_setValue
// @grant       GM_getValue
// @grant       GM_addValueChangeListener
// @grant       GM_registerMenuCommand
 
// @noframes
// ==/UserScript==
 
((ui, self) => {
  if (!ui || !self) return;
 
  // 钩子
  const hookFunction = (object, functionName, callback) => {
    ((originalFunction) => {
      object[functionName] = function () {
        const returnValue = originalFunction.apply(this, arguments);
 
        callback.apply(this, [returnValue, originalFunction, arguments]);
 
        return returnValue;
      };
    })(object[functionName]);
  };
 
  // 用户信息
  class UserInfo {
    execute(task) {
      task().finally(() => {
        if (this.waitingQueue.length) {
          const next = this.waitingQueue.shift();
 
          this.execute(next);
        } else {
          this.isRunning = false;
        }
      });
    }
 
    enqueue(task) {
      if (this.isRunning) {
        this.waitingQueue.push(task);
      } else {
        this.isRunning = true;
 
        this.execute(task);
      }
    }
 
    rearrange() {
      if (this.data) {
        const list = Object.values(this.children);
 
        for (let i = 0; i < list.length; i++) {
          if (list[i].source === undefined) {
            list[i].create(this.data);
          }
 
          Object.entries(this.container).forEach((item) => {
            list[i].clone(this.data, item);
          });
        }
      }
    }
 
    reload() {
      this.enqueue(async () => {
        this.data = await new Promise((resolve) => {
          fetch(`/nuke.php?lite=js&__lib=ucp&__act=get&uid=${this.uid}`)
            .then((res) => res.blob())
            .then((blob) => {
              const reader = new FileReader();
 
              reader.onload = () => {
                const text = reader.result;
                const result = JSON.parse(
                  text.replace("window.script_muti_get_var_store=", "")
                );
 
                resolve(result.data[0]);
              };
 
              reader.readAsText(blob, "GBK");
            })
            .catch(() => {
              resolve();
            });
        });
 
        Object.values(this.children).forEach((item) => item.destroy());
 
        this.rearrange();
      });
    }
 
    constructor(id) {
      this.uid = id;
 
      this.waitingQueue = [];
      this.isRunning = false;
 
      this.container = {};
      this.children = {};
 
      this.reload();
    }
  }
 
  // 用户信息组件
  class UserInfoWidget {
    destroy() {
      if (this.source) {
        this.source = undefined;
      }
 
      if (this.target) {
        Object.values(this.target).forEach((item) => {
          if (item.parentNode) {
            item.parentNode.removeChild(item);
          }
        });
      }
    }
 
    clone(data, [argid, container]) {
      if (this.source) {
        if (this.target[argid] === undefined) {
          this.target[argid] = this.source.cloneNode(true);
 
          if (this.callback) {
            this.callback(data, this.target[argid]);
          }
        }
 
        container.appendChild(this.target[argid]);
      }
    }
 
    constructor(func, callback) {
      this.create = (data) => {
        this.destroy();
 
        this.source = func(data);
        this.target = {};
      };
 
      this.callback = callback;
    }
  }
 
  ui.sn = ui.sn || {};
  ui.sn.userInfo = ui.sn.userInfo || {};
 
  ((info) => {
    // 扩展规则
    const extraData = (() => {
      const key = `EXTRA_DATA`;
      const data = GM_getValue(key) || {
        [0]: {
          time: 0,
        },
      };
 
      const save = () => {
        GM_setValue(key, data);
      };
 
      const setValue = (uid, value) => {
        data[uid] = value;
 
        save();
      };
 
      const getValue = (uid) => data[uid];
 
      const remove = (uid) => {
        delete data[uid];
 
        save();
      };
 
      const specialList = () => {
        const result = Object.entries(data).filter(
          ([key, value]) => value.level
        );
 
        if (Object.keys(result)) {
          return result;
        }
 
        return null;
      };
 
      GM_addValueChangeListener(key, function (_, prev, next) {
        Object.assign(data, next);
      });
 
      return {
        specialList,
        setValue,
        getValue,
        remove,
      };
    })();
 
    // 获取用户信息
    const get_user_info = (uid) =>
      new Promise((resolve, reject) => {
        fetch(`/nuke.php?lite=js&__lib=ucp&__act=get&uid=${uid}`)
          .then((res) => res.blob())
          .then((blob) => {
            const reader = new FileReader();
 
            reader.onload = () => {
              const text = reader.result;
              const result = eval(`
                (${text.replace("window.script_muti_get_var_store=", "")})
              `);
 
              if (result.data) {
                resolve(result.data[0]);
              } else {
                reject(result.error[0]);
              }
            };
 
            reader.readAsText(blob, "GBK");
          })
          .catch(() => {
            reject();
          });
      });
 
    // 获取用户发帖列表
    const get_user_topic_list = (uid) =>
      new Promise((resolve) => {
        fetch(`/thread.php?lite=js&authorid=${uid}`)
          .then((res) => res.blob())
          .then((blob) => {
            const reader = new FileReader();
 
            reader.onload = () => {
              const text = reader.result;
              const result = eval(`
                (${text.replace("window.script_muti_get_var_store=", "")})
              `);
 
              if (result.data) {
                resolve(result.data.__T);
              } else {
                resolve({});
              }
            };
 
            reader.readAsText(blob, "GBK");
          })
          .catch(() => {
            resolve({});
          });
      });
 
    // 获取用户回帖列表
    const get_user_post_list = (uid) =>
      new Promise((resolve, reject) => {
        fetch(`/thread.php?lite=js&authorid=${uid}&searchpost=1`)
          .then((res) => res.blob())
          .then((blob) => {
            const reader = new FileReader();
 
            reader.onload = () => {
              const text = reader.result;
              const result = eval(`
                (${text.replace("window.script_muti_get_var_store=", "")})
              `);
 
              if (result.data) {
                resolve(result.data.__T);
              } else {
                resolve({});
              }
            };
 
            reader.readAsText(blob, "GBK");
          })
          .catch(() => {
            resolve({});
          });
      });
 
    // 关注
    const follow = (uid) =>
      new Promise((resolve, reject) => {
        fetch(
          `/nuke.php?lite=js&__lib=follow_v2&__act=follow&id=${uid}&type=1`,
          {
            method: "post",
          }
        )
          .then((res) => res.blob())
          .then((blob) => {
            const reader = new FileReader();
 
            reader.onload = () => {
              const text = reader.result;
              const result = eval(`
                (${text.replace("window.script_muti_get_var_store=", "")})
              `);
 
              if (result.data) {
                resolve(result.data[0]);
              } else {
                reject(result.error[0]);
              }
            };
 
            reader.readAsText(blob, "GBK");
          })
          .catch(() => {
            reject();
          });
      });
 
    // 取消关注
    const un_follow = (uid) =>
      new Promise((resolve, reject) => {
        fetch(
          `/nuke.php?lite=js&__lib=follow_v2&__act=follow&id=${uid}&type=8`,
          {
            method: "post",
          }
        )
          .then((res) => res.blob())
          .then((blob) => {
            const reader = new FileReader();
 
            reader.onload = () => {
              const text = reader.result;
              const result = eval(`
                (${text.replace("window.script_muti_get_var_store=", "")})
              `);
 
              if (result.data) {
                resolve(result.data[0]);
              } else {
                reject(result.error[0]);
              }
            };
 
            reader.readAsText(blob, "GBK");
          })
          .catch(() => {
            reject();
          });
      });
 
    // 移除粉丝
    const un_follow_fans = (uid) =>
      new Promise((resolve, reject) => {
        fetch(
          `/nuke.php?lite=js&__lib=follow_v2&__act=follow&id=${uid}&type=256`,
          {
            method: "post",
          }
        )
          .then((res) => res.blob())
          .then((blob) => {
            const reader = new FileReader();
 
            reader.onload = () => {
              const text = reader.result;
              const result = eval(`
                (${text.replace("window.script_muti_get_var_store=", "")})
              `);
 
              if (result.data) {
                resolve(result.data[0]);
              } else {
                reject(result.error[0]);
              }
            };
 
            reader.readAsText(blob, "GBK");
          })
          .catch(() => {
            reject();
          });
      });
 
    // 获取关注列表
    const follow_list = (page) =>
      new Promise((resolve, reject) => {
        fetch(
          `/nuke.php?lite=js&__lib=follow_v2&__act=get_follow&page=${page}`,
          {
            method: "post",
          }
        )
          .then((res) => res.blob())
          .then((blob) => {
            const reader = new FileReader();
 
            reader.onload = () => {
              const text = reader.result;
              const result = eval(`
                (${text.replace("window.script_muti_get_var_store=", "")})
              `);
 
              if (result.data) {
                resolve(result.data[0]);
              } else {
                reject(result.error[0]);
              }
            };
 
            reader.readAsText(blob, "GBK");
          })
          .catch(() => {
            reject();
          });
      });
 
    // 获取粉丝列表
    const follow_by_list = (page) =>
      new Promise((resolve, reject) => {
        fetch(
          `/nuke.php?lite=js&__lib=follow_v2&__act=get_follow_by&page=${page}`,
          {
            method: "post",
          }
        )
          .then((res) => res.blob())
          .then((blob) => {
            const reader = new FileReader();
 
            reader.onload = () => {
              const text = reader.result;
              const result = eval(`
                (${text.replace("window.script_muti_get_var_store=", "")})
              `);
 
              if (result.data) {
                resolve(result.data[0]);
              } else {
                reject(result.error[0]);
              }
            };
 
            reader.readAsText(blob, "GBK");
          })
          .catch(() => {
            reject();
          });
      });
 
    // 获取关注动态
    const follow_dymanic_list = (page) =>
      new Promise((resolve, reject) => {
        fetch(
          `/nuke.php?lite=js&__lib=follow_v2&__act=get_push_list&page=${page}`,
          {
            method: "post",
          }
        )
          .then((res) => res.blob())
          .then((blob) => {
            const reader = new FileReader();
 
            reader.onload = () => {
              const text = reader.result;
              const result = eval(`
                (${text.replace("window.script_muti_get_var_store=", "")})
              `);
 
              if (result.data) {
                resolve(result.data);
              } else {
                reject(result.error[0]);
              }
            };
 
            reader.readAsText(blob, "GBK");
          })
          .catch(() => {
            reject();
          });
      });
 
    // 切换关注
    const handleSwitchFollow = (uid, isFollow) => {
      if (isFollow) {
        if (confirm("取消关注?")) {
          un_follow(uid).then(() => {
            info[uid]?.reload();
            u.refresh();
          });
        }
      } else {
        follow(uid).then(() => {
          info[uid]?.reload();
          u.refresh();
        });
      }
    };
 
    // 移除粉丝
    const handleRemoveFans = (uid) => {
      if (confirm("移除粉丝?")) {
        un_follow_fans(uid).then(() => {
          u.refresh();
        });
      }
    };
 
    // STYLE
    GM_addStyle(`
      .s-user-info-container:not(:hover) .ah {
        display: none !important;
      }
      .s-table-wrapper {
        height: calc((2em + 10px) * 11 + 3px);
        overflow-y: auto;
      }
      .s-table {
        margin: 0;
      }
      .s-table th,
      .s-table td {
        position: relative;
        white-space: nowrap;
      }
      .s-table th {
        position: sticky;
        top: 2px;
        z-index: 1;
      }
      .s-table input:not([type]), .s-table input[type="text"] {
        margin: 0;
        box-sizing: border-box;
        height: 100%;
        width: 100%;
      }
      .s-input-wrapper {
        position: absolute;
        top: 6px;
        right: 6px;
        bottom: 6px;
        left: 6px;
      }
      .s-text-ellipsis {
        display: flex;
      }
      .s-text-ellipsis > * {
        flex: 1;
        width: 1px;
        overflow: hidden;
        text-overflow: ellipsis;
      }
      .s-button-group {
        margin: -.1em -.2em;
      }
    `);
 
    // MENU
    const m = (() => {
      const container = document.createElement("DIV");
 
      container.className = `td`;
      container.innerHTML = `<a class="mmdefault" href="javascript: void(0);" style="white-space: nowrap;">关注</a>`;
 
      const content = container.querySelector("A");
 
      const create = (onclick) => {
        const anchor = document.querySelector("#mainmenu .td:last-child");
 
        anchor.before(container);
 
        content.onclick = onclick;
      };
 
      const update = (count) => {
        if (count) {
          content.innerHTML = `关注 <span class="small_colored_text_btn stxt block_txt_c0 vertmod">${count}</span>`;
        } else {
          content.innerHTML = `关注`;
        }
      };
 
      return {
        create,
        update,
      };
    })();
 
    // UI
    const u = (() => {
      const modules = {};
 
      const createView = () => {
        const tabContainer = (() => {
          const c = document.createElement("div");
 
          c.className = "w100";
          c.innerHTML = `
            <div class="right_" style="margin-bottom: 5px;">
                <table class="stdbtn" cellspacing="0">
                    <tbody>
                        <tr></tr>
                    </tbody>
                </table>
            </div>
            <div class="clear"></div>
            `;
 
          return c;
        })();
 
        const tabPanelContainer = (() => {
          const c = document.createElement("div");
 
          c.style = "width: 800px;";
 
          return c;
        })();
 
        const content = (() => {
          const c = document.createElement("div");
 
          c.append(tabContainer);
          c.append(tabPanelContainer);
 
          return c;
        })();
 
        const addModule = (() => {
          const tc = tabContainer.getElementsByTagName("tr")[0];
          const cc = tabPanelContainer;
 
          return (module) => {
            const tabBox = document.createElement("td");
 
            tabBox.innerHTML = `<a href="javascript:void(0)" class="nobr silver">${module.name}</a>`;
 
            const tab = tabBox.childNodes[0];
 
            const toggle = () => {
              Object.values(modules).forEach((item) => {
                if (item.tab === tab) {
                  item.tab.className = "nobr";
                  item.content.style = "display: block";
                  item.visible = true;
                } else {
                  item.tab.className = "nobr silver";
                  item.content.style = "display: none";
                  item.visible = false;
                }
              });
 
              module.refresh();
            };
 
            tc.append(tabBox);
            cc.append(module.content);
 
            tab.onclick = (() => {
              let timeout;
 
              return () => {
                if (timeout > 0) {
                  return;
                }
 
                timeout = setTimeout(() => {
                  timeout = 0;
                }, 320);
 
                toggle();
              };
            })();
 
            modules[module.name] = {
              ...module,
              tab,
              toggle,
              visible: false,
            };
 
            return modules[module.name];
          };
        })();
 
        return {
          content,
          addModule,
        };
      };
 
      const refresh = () => {
        Object.values(modules)
          .find((item) => item.visible)
          ?.refresh();
      };
 
      return {
        modules,
        createView,
        refresh,
      };
    })();
 
    // 我的关注
    {
      const name = "我的关注";
 
      const content = (() => {
        const c = document.createElement("div");
 
        c.style.display = "none";
        c.innerHTML = `
        <div class="s-table-wrapper">
          <table class="s-table forumbox">
            <thead>
              <tr class="block_txt_c0">
                <th class="c1" width="1">用户</th>
                <th class="c2">过滤规则</th>
                <th class="c3" width="1">特别关注</th>
                <th class="c4" width="1">操作</th>
              </tr>
            </thead>
            <tbody></tbody>
          </table>
        </div>
        <div class="silver" style="margin-top: 5px;">特别关注功能需要占用额外的资源,请谨慎开启</div>
        `;
 
        return c;
      })();
 
      let page = 0;
      let hasNext = false;
      let isFetching = false;
 
      const box = content.querySelector("DIV");
 
      const list = content.querySelector("TBODY");
 
      const wrapper = content.querySelector(".s-table-wrapper");
 
      const fetchData = () => {
        isFetching = true;
 
        follow_list(page)
          .then((res) => {
            hasNext = Object.keys(res).length > 0;
 
            for (let i in res) {
              const { uid, username } = res[i];
 
              const data = extraData.getValue(uid) || {};
 
              if (list.querySelector(`[data-id="${uid}"]`)) {
                continue;
              }
 
              const item = document.createElement("TR");
 
              item.className = `row${
                (list.querySelectorAll("TR").length % 2) + 1
              }`;
 
              item.setAttribute("data-id", uid);
 
              item.innerHTML = `
                <td class="c1">
                  <a href="/nuke.php?func=ucp&uid=${uid}" class="b nobr">${username}</a>
                </td>
                <td class="c2">
                  <div class="s-input-wrapper">
                    <input value="${data.rule || ""}" />
                  </div>
                </td>
                <td class="c3">
                  <div style="text-align: center;">
                    <input type="checkbox" ${
                      data.level ? `checked="checked"` : ""
                    } />
                  </div>
                </td>
                <td class="c4">
                  <div class="s-button-group">
                    <button>重置</button>
                    <button>移除</button>
                  </div>
                </td>
              `;
 
              const ruleElement = item.querySelector("INPUT");
              const levelElement = item.querySelector(`INPUT[type="checkbox"]`);
              const actions = item.querySelectorAll("BUTTON");
 
              const save = () => {
                extraData.setValue(uid, {
                  rule: ruleElement.value,
                  level: levelElement.checked ? 1 : 0,
                });
              };
 
              const clear = () => {
                ruleElement.value = "";
                levelElement.checked = false;
 
                save();
              };
 
              ruleElement.onchange = save;
 
              levelElement.onchange = save;
 
              actions[0].onclick = () => clear();
              actions[1].onclick = () => handleSwitchFollow(uid, 1);
 
              list.appendChild(item);
            }
          })
          .finally(() => {
            isFetching = false;
          });
      };
 
      box.onscroll = () => {
        if (isFetching || !hasNext) {
          return;
        }
 
        if (
          box.scrollHeight - box.scrollTop - box.clientHeight <=
          wrapper.clientHeight
        ) {
          page = page + 1;
 
          fetchData();
        }
      };
 
      const refresh = () => {
        list.innerHTML = "";
 
        page = 1;
        hasNext = false;
 
        fetchData();
      };
 
      hookFunction(u, "createView", (view) => {
        view.addModule({
          name,
          content,
          refresh,
        });
      });
    }
 
    // 我的粉丝
    {
      const name = "我的粉丝";
 
      const content = (() => {
        const c = document.createElement("div");
 
        c.style.display = "none";
        c.innerHTML = `
        <div class="s-table-wrapper">
          <table class="s-table forumbox">
            <thead>
              <tr class="block_txt_c0">
                <th class="c1">用户</th>
                <th class="c2" width="1">操作</th>
              </tr>
            </thead>
            <tbody></tbody>
          </table>
        </div>
        `;
 
        return c;
      })();
 
      let page = 0;
      let hasNext = false;
      let isFetching = false;
 
      const box = content.querySelector("DIV");
 
      const list = content.querySelector("TBODY");
 
      const wrapper = content.querySelector(".s-table-wrapper");
 
      const fetchData = () => {
        isFetching = true;
 
        follow_by_list(page)
          .then((res) => {
            hasNext = Object.keys(res).length > 0;
 
            for (let i in res) {
              const { uid, username } = res[i];
 
              if (list.querySelector(`[data-id="${uid}"]`)) {
                continue;
              }
 
              const item = document.createElement("TR");
 
              item.className = `row${
                (list.querySelectorAll("TR").length % 2) + 1
              }`;
 
              item.setAttribute("data-id", uid);
 
              item.innerHTML = `
                <td class="c1">
                  <a href="/nuke.php?func=ucp&uid=${uid}" class="b nobr">${username}</a>
                </td>
                <td class="c2">
                  <div class="s-button-group">
                    <button>移除</button>
                  </div>
                </td>
              `;
 
              const action = item.querySelector("BUTTON");
 
              action.onclick = () => handleRemoveFans(uid);
 
              list.appendChild(item);
            }
          })
          .finally(() => {
            isFetching = false;
          });
      };
 
      box.onscroll = () => {
        if (isFetching || !hasNext) {
          return;
        }
 
        if (
          box.scrollHeight - box.scrollTop - box.clientHeight <=
          wrapper.clientHeight
        ) {
          page = page + 1;
 
          fetchData();
        }
      };
 
      const refresh = () => {
        list.innerHTML = "";
 
        page = 1;
        hasNext = false;
 
        fetchData();
      };
 
      hookFunction(u, "createView", (view) => {
        view.addModule({
          name,
          content,
          refresh,
        });
      });
    }
 
    // 关注动态
    {
      const name = "关注动态";
 
      const content = (() => {
        const c = document.createElement("div");
 
        c.style.display = "none";
        c.innerHTML = `
        <div class="s-table-wrapper">
          <table class="s-table forumbox">
            <thead>
              <tr class="block_txt_c0">
                <th class="c1" width="1">时间</th>
                <th class="c2">内容</th>
              </tr>
            </thead>
            <tbody></tbody>
          </table>
        </div>
        `;
 
        return c;
      })();
 
      let page = 0;
      let hasNext = false;
      let isFetching = false;
 
      const box = content.querySelector("DIV");
 
      const list = content.querySelector("TBODY");
 
      const wrapper = content.querySelector(".s-table-wrapper");
 
      const fetchData = () => {
        isFetching = true;
 
        follow_dymanic_list(page)
          .then((res) =>
            Promise.all(
              Object.keys(res[1]).map((uid) =>
                get_user_info(uid).then((item) => {
                  if (item.follow) {
                    const info = extraData.getValue(uid) || {
                      rule: "",
                      level: 0,
                    };
 
                    extraData.setValue(uid, {
                      ...info,
                    });
                  } else {
                    extraData.remove(uid);
                  }
                })
              )
            ).then(() => {
              return res;
            })
          )
          .then((res) => {
            hasNext = res[2] > res[3];
 
            extraData.setValue(0, {
              time: Math.floor(new Date() / 1000),
              unread: 0,
            });
 
            return res;
          })
          .then((res) => {
            const filtered = Object.values(res[0])
              .map((item) => ({
                id: item[0],
                uid: item[2],
                info: item[4]
                  ? res[4][`${item[3]}_${item[4]}`]
                  : res[4][item[3]],
                time: item[6],
                summary: item.summary
                  .replace(
                    /\[uid=(\d+)\](.+)\[\/uid\]/,
                    `<a href="/nuke.php?func=ucp&uid=${item[2]}" class="b nobr">$2</a>`
                  )
                  .replace(
                    /\[pid=(\d+)\](.+)\[\/pid\](\s?)/,
                    `<a href="/read.php?pid=${item[4]}" class="b nobr">回复</a>`
                  )
                  .replace(
                    /\[tid=(\d+)\](.+)\[\/tid\]/,
                    item[4] === 0
                      ? `<a href="/read.php?tid=${item[3]}" title="$2" class="b nobr">$2</a>`
                      : `<a href="/read.php?pid=${item[4]}&opt=128" title="$2" class="b nobr">$2</a>`
                  ),
              }))
              .filter((item) => {
                const { uid, info } = item;
 
                const data = extraData.getValue(uid);
 
                if (data) {
                  const { rule } = data;
 
                  if (rule) {
                    return (
                      info.subject.search(rule) >= 0 ||
                      info.content.search(rule) >= 0
                    );
                  }
 
                  return true;
                }
 
                return false;
              });
 
            return filtered;
          })
          .then((res) => {
            for (let i in res) {
              const { id, time, summary } = res[i];
 
              if (list.querySelector(`[data-id="${id}"]`)) {
                continue;
              }
 
              const item = document.createElement("TR");
 
              item.className = `row${
                (list.querySelectorAll("TR").length % 2) + 1
              }`;
 
              item.setAttribute("data-id", id);
              item.setAttribute("data-time", time);
 
              item.innerHTML = `
                <td class="c1">
                  <span class="nobr">${ui.time2dis(time)}</span>
                </td>
                <td class="c2">
                  <div class="s-text-ellipsis">
                    <span>${summary}</span>
                  </div>
                </td>
              `;
 
              list.appendChild(item);
            }
 
            if (box.scrollHeight === box.clientHeight && hasNext) {
              page = page + 1;
 
              fetchData();
            }
          })
          .finally(() => {
            isFetching = false;
          });
      };
 
      box.onscroll = () => {
        if (isFetching || !hasNext) {
          return;
        }
 
        if (
          box.scrollHeight - box.scrollTop - box.clientHeight <=
          wrapper.clientHeight
        ) {
          page = page + 1;
 
          fetchData();
        }
      };
 
      const refresh = () => {
        list.innerHTML = "";
 
        page = 1;
        hasNext = false;
 
        fetchData();
      };
 
      hookFunction(u, "createView", (view) => {
        view.addModule({
          name,
          content,
          refresh,
        });
      });
    }
 
    // 打开菜单
    const handleCreateView = (() => {
      let view, window;
 
      return () => {
        if (view === undefined) {
          view = u.createView();
        }
 
        u.modules["关注动态"].toggle();
        m.update(0);
 
        if (window === undefined) {
          window = ui.createCommmonWindow();
        }
 
        window._.addContent(null);
        window._.addTitle(`关注`);
        window._.addContent(view.content);
        window._.show();
      };
    })();
 
    // 扩展用户信息
    (() => {
      const execute = (argid) => {
        const args = ui.postArg.data[argid];
 
        if (args.comment) return;
 
        const uid = +args.pAid;
 
        if (uid > 0) {
          if (info[uid] === undefined) {
            info[uid] = new UserInfo(uid);
          }
 
          if (document.contains(info[uid].container[argid]) === false) {
            info[uid].container[argid] = args.uInfoC.querySelector(
              "[name=uid]"
            ).parentNode;
          }
 
          info[uid].enqueue(async () => {
            args.uInfoC.className =
              args.uInfoC.className + " s-user-info-container";
 
            if (info[uid].children[16]) {
              info[uid].children[16].destroy();
            }
 
            info[uid].children[16] = new UserInfoWidget(
              (data) => {
                const value = data.follow_by_num || 0;
 
                const element = document.createElement("SPAN");
 
                if (uid === self || data.follow) {
                  element.className =
                    "small_colored_text_btn stxt block_txt_c2 vertmod";
                } else {
                  element.className =
                    "small_colored_text_btn stxt block_txt_c2 vertmod ah";
                }
 
                element.style.cursor = "default";
                element.innerHTML = `<span class="white"><span style="font-family: comm_glyphs; -webkit-font-smoothing: antialiased; line-height: 1em;">★</span>&nbsp;${value}</span>`;
 
                element.style.cursor = "pointer";
 
                return element;
              },
              (data, element) => {
                element.onclick = () => {
                  if (data.uid === self) {
                    handleCreateView();
                  } else {
                    handleSwitchFollow(data.uid, data.follow);
                  }
                };
              }
            );
 
            info[uid].rearrange();
          });
        }
      };
 
      let initialized = false;
 
      if (ui.postArg) {
        Object.keys(ui.postArg.data).forEach((i) => execute(i));
      }
 
      hookFunction(ui, "eval", () => {
        if (initialized) return;
 
        if (ui.postDisp) {
          hookFunction(
            ui,
            "postDisp",
            (returnValue, originalFunction, arguments) => execute(arguments[0])
          );
 
          initialized = true;
        }
      });
    })();
 
    // 提醒关注
    (async () => {
      // 增加菜单项
      m.create(handleCreateView);
 
      // 获取动态
      (() => {
        const cache = extraData.getValue(0) || {
          time: 0,
          unread: 0,
        };
 
        const fetchData = async (page = 1, result = {}) =>
          new Promise((resolve) => {
            follow_dymanic_list(page).then(async (res) => {
              const list = Object.values(res[0]);
              const prefiltered = list
                .map((item) => ({
                  id: item[0],
                  uid: item[2],
                  info: item[4]
                    ? res[4][`${item[3]}_${item[4]}`]
                    : res[4][item[3]],
                  time: item[6],
                }))
                .filter((item) => item.time > (cache.time || 0))
                .filter((item) => {
                  if (result[item.id]) {
                    return false;
                  }
 
                  result[item.id] = item;
                  return true;
                });
 
              if (prefiltered.length) {
                await Promise.all(
                  Object.keys(res[1]).map((uid) =>
                    get_user_info(uid).then((item) => {
                      if (item.follow) {
                        const info = extraData.getValue(uid) || {
                          rule: "",
                          level: 0,
                        };
 
                        extraData.setValue(uid, {
                          ...info,
                        });
                      } else {
                        extraData.remove(uid);
                      }
                    })
                  )
                );
 
                const hasNext =
                  prefiltered.length === list.length && res[2] > res[3];
 
                if (hasNext) {
                  const withNext = await fetchData(page + 1, result);
 
                  resolve(withNext);
                }
              }
 
              resolve(result);
            });
          });
 
        fetchData().then((res) => {
          const filtered = Object.values(res).filter((item) => {
            const { uid, info } = item;
 
            const data = extraData.getValue(uid);
 
            if (data) {
              const { rule } = data;
 
              if (rule) {
                return (
                  info.subject.search(rule) >= 0 ||
                  info.content.search(rule) >= 0
                );
              }
 
              return true;
            }
 
            return false;
          });
 
          const unread = (cache.unread || 0) + filtered.length;
 
          extraData.setValue(0, {
            time: Math.floor(new Date() / 1000),
            unread: unread,
          });
 
          m.update(unread);
        });
      })();
 
      // 特别关注
      {
        const fetchData = async (uid, value) => {
          // 请求用户信息
          const { username, follow, posts } = await get_user_info(uid);
 
          // 用户缓存
          const { rule, time, postNum } = value;
 
          // 已取消关注
          if (follow === 0) {
            extraData.remove(uid);
            return [];
          }
 
          // 判断是否有新活动
          if (posts <= (postNum || 0)) {
            return [];
          }
 
          // 是否匹配
          const isMatch = (text) => {
            if (rule) {
              return text.search(rule) >= 0;
            }
 
            return true;
          };
 
          // 请求发帖记录
          const ts = await get_user_topic_list(uid).then((res) =>
            Object.values(res)
              .filter(
                (item) => item.postdate > (time || 0) && isMatch(item.subject)
              )
              .map((item) => ({
                0: 5,
                1: item.authorid,
                2: item.author,
                5: item.subject,
                6: item.tid,
                9: item.postdate,
                10: 1,
              }))
          );
 
          // 请求回帖记录
          const ps = await get_user_post_list(uid).then((res) =>
            Object.values(res)
              .filter(
                (item) =>
                  item.__P.postdate > (time || 0) && isMatch(item.__P.content)
              )
              .map((item) => ({
                0: 6,
                1: uid,
                2: username,
                5: item.subject,
                6: item.__P.tid,
                7: item.__P.pid,
                9: item.__P.postdate,
                10: 1,
              }))
          );
 
          // 更新缓存
          extraData.setValue(uid, {
            ...value,
            time: Math.floor(new Date() / 1000),
            postNum: posts,
          });
 
          // 返回结果
          return [...ts, ...ps];
        };
 
        const data = (
          await Promise.all(
            extraData
              .specialList()
              .map(async ([key, value]) => await fetchData(key, value))
          )
        )
          .flat()
          .sort((a, b) => a[9] - b[9]);
 
        if (Object.keys(data).length) {
          const func = () => {
            // 修复 NGA 脚本错误
            TPL[KEY["_BIT_SYS"]][KEY["_TYPE_KEYWORD_WATCH_REPLY"]] = function (
              x
            ) {
              return x[KEY["_ABOUT_ID_4"]]
                ? "{_U} 在{_T1} {_R2} 中的 {_R5} 触发了关键词监视<br/>"
                : "{_U} 在主题 {_T} 中的 {_R5} 触发了关键词监视<br/>";
            };
 
            // 推送消息
            for (let i in data) {
              ui.notification._add(1, data[i], 1);
            }
 
            // 打开窗口
            ui.notification.openBox();
          };
 
          if (ui.notification) {
            func();
          } else {
            ui.loadNotiScript(() => {
              func();
            });
          }
        }
      }
    })();
  })(ui.sn.userInfo);
})(commonui, __CURRENT_UID);

troll must die - NGA Filter

NGA屏蔽脚本

v0.1 简单做了屏蔽逻辑,根据trollArray里的用户ID删除相关帖子或者楼层
v0.2 不再直接删除帖子或楼层,改为标题用删除线区分,楼层折叠。屏蔽方式为楼层ID边上的灰色UID按钮,红色为已屏蔽,再次点击取消屏蔽。
v0.3 简单的帖子标题关键词过滤,在代码里修改
v0.4 加入了一个菜单用于切换过滤模式为删除或者标记。
v0.5 加入了一个菜单用于修改过滤关键词。
v0.6 现在点击屏蔽按钮时按住ctrl键可以增加标记。
v1.0 简单写了个UI,支持多个标记,支持标记但不屏蔽,增加了用户信息页的操作。入口集成在论坛右上角主菜单。
v1.1 更新过滤逻辑,解决NGA新框架下翻页等情况会导致过滤失效的问题。
v1.2 优化了过滤方式的设置,并可以针对某个用户或者标记采用单独的过滤规则。过滤优先级:用户>标记>全局。
v1.3 升级了关键词屏蔽机制,现在可以更好的设置关键词并采用单独的过滤规则,同时支持帖子内容过滤。

附加功能

v0.6.1 屏蔽左上角广告相关的错误提示
v0.6.2 NGA去掉了广告提示,代码回滚
v0.7.0 显示原始的赞踩数量
v0.7.1 赞踩相关接口失效,代码回滚

// ==UserScript==
// @name        NGA Filter
// @namespace   https://greasyfork.org/users/263018
// @version     1.3.2
// @author      snyssss
// @description troll must die
 
// @match       *://bbs.nga.cn/*
// @match       *://ngabbs.com/*
// @match       *://nga.178.com/*
 
// @grant       GM_addStyle
// @grant       GM_setValue
// @grant       GM_getValue
 
// @noframes
// ==/UserScript==
 
((n, self) => {
  if (n === undefined) return;
 
  const key = "NGAFilter";
 
  // 过滤提示
  const FILTER_TIPS =
    "过滤顺序:用户 &gt; 标记 &gt; 关键字<br/>过滤级别:隐藏 &gt; 遮罩 &gt; 标记 &gt; 继承 &gt; 显示<br/>多个标记或者关键字按最高级别过滤";
 
  // 过滤方式
  const FILTER_MODE = ["继承", "标记", "遮罩", "隐藏", "显示"];
 
  // 切换过滤方式
  const switchFilterMode = (value) => {
    const next = FILTER_MODE.indexOf(value) + 1;
 
    if (next >= FILTER_MODE.length) {
      return FILTER_MODE[0];
    }
 
    return FILTER_MODE[next];
  };
 
  // 数据
  const data = (() => {
    const d = {
      tags: {},
      users: {},
      keywords: {},
      options: {
        filterMode: "隐藏",
      },
    };
 
    const v = GM_getValue(key);
 
    if (typeof v !== "object") {
      return d;
    }
 
    return Object.assign(d, v);
  })();
 
  // 保存数据
  const saveData = () => {
    GM_setValue(key, data);
  };
 
  // 增加标记
  const addTag = (name) => {
    const tag = Object.values(data.tags).find((item) => item.name === name);
 
    if (tag) return tag.id;
 
    const id =
      Math.max(...Object.values(data.tags).map((item) => item.id), 0) + 1;
 
    const hash = (() => {
      let h = 5381;
      for (var i = 0; i < name.length; i++) {
        h = ((h << 5) + h + name.charCodeAt(i)) & 0xffffffff;
      }
      return h;
    })();
 
    const hex = Math.abs(hash).toString(16) + "000000";
 
    const hsv = [
      `0x${hex.substr(2, 2)}` / 255,
      `0x${hex.substr(2, 2)}` / 255 / 2 + 0.25,
      `0x${hex.substr(4, 2)}` / 255 / 2 + 0.25,
    ];
 
    const rgb = n.hsvToRgb(hsv[0], hsv[1], hsv[2]);
 
    const color = ["#", ...rgb].reduce((a, b) => {
      return a + ("0" + b.toString(16)).slice(-2);
    });
 
    data.tags[id] = {
      id,
      name,
      color,
      filterMode: FILTER_MODE[0],
    };
 
    saveData();
 
    return id;
  };
 
  // 增加用户
  const addUser = (id, name = null, tags = [], filterMode = FILTER_MODE[0]) => {
    if (data.users[id]) return data.users[id];
 
    data.users[id] = {
      id,
      name,
      tags,
      filterMode,
    };
 
    saveData();
 
    return data.users[id];
  };
 
  // 增加关键字
  const addKeyword = (
    keyword,
    filterMode = FILTER_MODE[0],
    filterLevel = 0
  ) => {
    const id =
      Math.max(...Object.values(data.keywords).map((item) => item.id), 0) + 1;
 
    data.keywords[id] = {
      id,
      keyword,
      filterMode,
      filterLevel,
    };
 
    saveData();
 
    return id;
  };
 
  // 旧版本数据迁移
  {
    const dataKey = "troll_data";
    const modeKey = "troll_mode";
    const keywordKey = "troll_keyword";
 
    if (localStorage.getItem(dataKey)) {
      let trollMap = (function () {
        try {
          return JSON.parse(localStorage.getItem(dataKey)) || {};
        } catch (e) {}
 
        return {};
      })();
 
      let filterMode = ~~localStorage.getItem(modeKey);
 
      let filterKeyword = localStorage.getItem(keywordKey) || "";
 
      // 整理标签
      [...new Set(Object.values(trollMap).flat())].forEach((item) =>
        addTag(item)
      );
 
      // 整理用户
      Object.keys(trollMap).forEach((item) => {
        addUser(
          item,
          null,
          (typeof trollMap[item] === "object" ? trollMap[item] : []).map(
            (tag) => addTag(tag)
          )
        );
      });
 
      data.options.filterMode = filterMode ? "隐藏" : "标记";
      data.options.keyword = filterKeyword;
 
      localStorage.removeItem(dataKey);
      localStorage.removeItem(modeKey);
      localStorage.removeItem(keywordKey);
 
      saveData();
    }
 
    // v1.1.0 -> v1.1.1
    {
      Object.values(data.users).forEach(({ id, name, tags, enabled }) => {
        if (enabled !== undefined) {
          data.users[id] = {
            id,
            name,
            tags,
            filterMode: enabled ? "继承" : "显示",
          };
        }
      });
 
      Object.values(data.tags).forEach(({ id, name, color, enabled }) => {
        if (enabled !== undefined) {
          data.tags[id] = {
            id,
            name,
            color,
            filterMode: enabled ? "继承" : "显示",
          };
        }
      });
 
      if (data.options.filterMode === 0) {
        data.options.filterMode = "隐藏";
      } else if (data.options.filterMode === 1) {
        data.options.filterMode = "标记";
      }
 
      saveData();
    }
 
    // v1.2.x -> v1.3.0
    {
      if (data.options.keyword) {
        addKeyword(data.options.keyword);
 
        delete data.options.keyword;
 
        saveData();
      }
    }
  }
 
  // 编辑用户标记
  const editUser = (() => {
    let window;
    return (uid, name, callback) => {
      if (window === undefined) {
        window = n.createCommmonWindow();
      }
 
      const user = data.users[uid];
 
      const content = document.createElement("div");
 
      const size = Math.floor((screen.width * 0.8) / 200);
 
      const items = Object.values(data.tags).map(
        (tag, index) => `
          <td class="c1">
            <label for="s-tag-${index}" style="display: block; cursor: pointer;">
              <b class="block_txt nobr" style="background:${
                tag.color
              }; color:#fff; margin: 0.1em 0.2em;">${tag.name}</b>
            </label>
          </td>
          <td class="c2" width="1">
              <input id="s-tag-${index}" type="checkbox" value="${tag.id}" ${
          user && user.tags.find((item) => item === tag.id) && "checked"
        }/>
          </td>
        `
      );
 
      const rows = [...new Array(Math.ceil(items.length / size))].map(
        (item, index) =>
          `
          <tr class="row${(index % 2) + 1}">
            ${items.slice(size * index, size * (index + 1)).join("")}
          </tr>
          `
      );
 
      content.className = "w100";
      content.innerHTML = `
        <div class="filter-table-wrapper" style="width: 80vw;">
          <table class="filter-table forumbox">
            <tbody>
              ${rows.join("")}
            </tbody>
          </table>
        </div>
        <div style="margin: 10px 0;">
            <input placeholder="一次性添加多个标记用&quot;|&quot;隔开,不会添加重名标记" style="width: -webkit-fill-available;" />
        </div>
        <div style="margin: 10px 0;">
            <span>过滤方式:</span>
            <button>${(user && user.filterMode) || FILTER_MODE[0]}</button>
            <div class="right_">
                <button>删除</button>
                <button>保存</button>
            </div>
        </div>
        <div class="silver" style="margin-top: 5px;">${FILTER_TIPS}</div>
    `;
 
      const actions = content.getElementsByTagName("button");
 
      actions[0].onclick = () => {
        actions[0].innerText = switchFilterMode(
          actions[0].innerText || FILTER_MODE[0]
        );
      };
 
      actions[1].onclick = () => {
        if (confirm("是否确认?")) {
          delete data.users[uid];
 
          saveData();
 
          callback && callback();
 
          window._.hide();
        }
      };
 
      actions[2].onclick = () => {
        if (confirm("是否确认?")) {
          const values = [...content.getElementsByTagName("input")];
          const newTags = values[values.length - 1].value
            .split("|")
            .filter((item) => item.length)
            .map((item) => addTag(item));
          const tags = [
            ...new Set(
              values
                .filter((item) => item.type === "checkbox" && item.checked)
                .map((item) => ~~item.value)
                .concat(newTags)
            ),
          ].sort();
 
          if (user) {
            user.tags = tags;
            user.filterMode = actions[0].innerText;
          } else {
            addUser(uid, name, tags, actions[0].innerText);
          }
 
          saveData();
 
          callback && callback();
 
          window._.hide();
        }
      };
 
      if (user === undefined) {
        actions[1].style = "display: none;";
      }
 
      window._.addContent(null);
      window._.addTitle(`编辑标记 - ${name ? name : "#" + uid}`);
      window._.addContent(content);
      window._.show();
    };
  })();
 
  // 判断过滤方式
  const getFilterMode = (uid, content, level) => {
    let result = -1;
 
    const user = data.users[uid];
 
    const tags = user ? user.tags.map((tag) => data.tags[tag]) : [];
 
    const keywords = Object.values(data.keywords);
 
    if (user) {
      const filterMode = FILTER_MODE.indexOf(user.filterMode);
 
      if (filterMode > 0) {
        return filterMode;
      }
 
      result = filterMode;
    }
 
    if (tags.length) {
      const filterMode = (() => {
        if (tags.some((tag) => tag.filterMode !== "显示")) {
          return tags
            .filter((tag) => tag.filterMode !== "显示")
            .map((tag) => FILTER_MODE.indexOf(tag.filterMode) || 0)
            .sort((a, b) => b - a)[0];
        }
 
        return FILTER_MODE.indexOf("显示");
      })();
 
      if (filterMode > 0) {
        return filterMode;
      }
 
      result = filterMode;
    }
 
    if (keywords.length) {
      const filterMode = (() => {
        const r = keywords
          .filter((item) => item.keyword && item.filterMode !== "显示")
          .filter((item) => (item.filterLevel || 0) >= level)
          .sort(
            (a, b) =>
              FILTER_MODE.indexOf(b.filterMode) -
              FILTER_MODE.indexOf(a.filterMode)
          )
          .find((item) => content.search(item.keyword) >= 0);
 
        if (r) {
          return FILTER_MODE.indexOf(r.filterMode);
        }
 
        return result;
      })();
 
      if (filterMode > 0) {
        return filterMode;
      }
 
      result = filterMode;
    }
 
    return result;
  };
 
  // 处理引用
  const handleQuote = (content) => {
    const quotes = content.querySelectorAll(".quote");
 
    quotes.forEach((quote) => {
      const uid = (() => {
        const ele = quote.querySelector("a[href^='/nuke.php']");
 
        if (ele) {
          const res = ele.getAttribute("href").match(/uid=(\S+)/);
 
          if (res) {
            return res[1];
          }
        }
 
        return 0;
      })();
 
      if (uid) {
        const filterMode = (() => {
          const mode = getFilterMode(uid, quote.innerText, 1);
 
          if (mode === 0) {
            return data.options.filterMode;
          }
 
          if (mode > 0) {
            return FILTER_MODE[mode];
          }
 
          return "";
        })();
 
        if (filterMode === "标记") {
          quote.innerHTML = `
            <div class="lessernuke" style="background: #81C7D4; border-color: #66BAB7; ">
                <span class="crimson">Troll must die.</span>
                <a href="javascript:void(0)" onclick="[...document.getElementsByName('troll_${uid}')].forEach(item => item.style.display = '')">点击查看</a>
                <div style="display: none;" name="troll_${uid}">
                    ${quote.innerHTML}
                </div>
            </div>`;
        } else if (filterMode === "遮罩") {
          const source = document.createElement("DIV");
 
          source.innerHTML = quote.innerHTML;
          source.style.display = "none";
 
          const caption = document.createElement("CAPTION");
 
          caption.className = "filter-mask filter-mask-block";
 
          caption.innerHTML = `<span class="crimson">Troll must die.</span>`;
          caption.onclick = () => {
            quote.removeChild(caption);
 
            source.style.display = "";
          };
 
          quote.innerHTML = "";
          quote.appendChild(source);
          quote.appendChild(caption);
        } else if (filterMode === "隐藏") {
          quote.innerHTML = "";
        }
      }
    });
  };
 
  // 过滤
  const reFilter = () => {
    const tPage = location.pathname === "/thread.php";
    const pPage = location.pathname === "/read.php";
    const uPage = location.pathname === "/nuke.php";
 
    if (tPage) {
      const tData = n.topicArg.data;
 
      Object.values(tData).forEach((item) => {
        if (item.containerC) return;
 
        const uid =
          item[2].search.match(/uid=(\S+)/) &&
          item[2].search.match(/uid=(\S+)/)[1];
 
        const filterMode = (() => {
          const mode = getFilterMode(uid, item[1].innerText, 0);
 
          if (mode === 0) {
            return data.options.filterMode;
          }
 
          if (mode > 0) {
            return FILTER_MODE[mode];
          }
 
          return "";
        })();
 
        item.contentC = item[1];
 
        item.contentB = item.contentB || item.contentC.innerHTML;
 
        item.containerC =
          item.containerC || item.contentC.parentNode.parentNode;
 
        item.containerC.style = "";
        item.contentC.style = "";
        item[1].className = item[1].className.replace(" filter-mask", "");
        item[2].className = item[2].className.replace(" filter-mask", "");
 
        if (filterMode === "标记") {
          item.contentC.style = "text-decoration: line-through;";
        } else if (filterMode === "遮罩") {
          item[1].className += " filter-mask";
          item[2].className += " filter-mask";
        } else if (filterMode === "隐藏") {
          item.containerC.style = "display: none;";
        }
      });
    } else if (pPage) {
      const pData = n.postArg.data;
 
      Object.values(pData).forEach((item) => {
        if (~~item.pAid === self) return;
        if (item.containerC) return;
 
        if (typeof item.i === "number") {
          item.actionC =
            item.actionC ||
            (() => {
              const ele = item.uInfoC.querySelector('[name="uid"]');
 
              ele.onclick = null;
 
              return ele;
            })();
 
          item.tagC =
            item.tagC ||
            (() => {
              const tc = document.createElement("div");
 
              tc.className = "filter-tags";
 
              item.uInfoC.appendChild(tc);
 
              return tc;
            })();
        }
 
        item.pName =
          item.pName ||
          item.uInfoC.getElementsByClassName("author")[0].innerText;
 
        item.reFilter =
          item.reFilter ||
          (() => {
            const uid = item.pAid;
 
            const filterMode = (() => {
              const mode = getFilterMode(uid, item.contentC.innerText, 1);
 
              if (mode === 0) {
                return data.options.filterMode;
              }
 
              if (mode > 0) {
                return FILTER_MODE[mode];
              }
 
              return "";
            })();
 
            item.avatarC =
              item.avatarC ||
              (() => {
                const tc = document.createElement("div");
 
                const avatar = document.getElementById(`posteravatar${item.i}`);
 
                if (avatar) {
                  avatar.parentNode.insertBefore(tc, avatar.nextSibling);
 
                  tc.appendChild(avatar);
                }
 
                return tc;
              })();
 
            item.contentB = item.contentB || item.contentC.innerHTML;
 
            item.containerC =
              item.containerC ||
              (() => {
                let temp = item.contentC;
 
                if (item.i >= 0) {
                  while (temp.nodeName !== "TBODY") {
                    temp = temp.parentNode;
                  }
                } else {
                  while (temp.nodeName !== "DIV") {
                    temp = temp.parentNode;
                  }
                }
 
                return temp;
              })();
 
            item.avatarC.style.display = "";
            item.containerC.style.display = "";
            item.contentC.innerHTML = item.contentB;
 
            if (item.actionC) {
              item.actionC.style = "background: #aaa;";
            }
 
            if (filterMode === "标记") {
              item.avatarC.style.display = "none";
              item.contentC.innerHTML = `
                <div class="lessernuke" style="background: #81C7D4; border-color: #66BAB7; ">
                    <span class="crimson">Troll must die.</span>
                    <a href="javascript:void(0)" onclick="[...document.getElementsByName('troll_${uid}')].forEach(item => item.style.display = '')">点击查看</a>
                    <div style="display: none;" name="troll_${uid}">
                        ${item.contentB}
                    </div>
                </div>`;
 
              if (item.actionC && data.users[uid]) {
                item.actionC.style = "background: #cb4042;";
              }
            } else if (filterMode === "遮罩") {
              const caption = document.createElement("CAPTION");
 
              if (item.i >= 0) {
                caption.className = "filter-mask filter-mask-block";
              } else {
                caption.className = "filter-mask filter-mask-block left";
                caption.style = "width: 47%;";
              }
 
              caption.innerHTML = `<span class="crimson">Troll must die.</span>`;
              caption.onclick = () => {
                item.containerC.parentNode.removeChild(caption);
                item.containerC.style.display = "";
              };
 
              item.containerC.parentNode.insertBefore(caption, item.containerC);
              item.containerC.style.display = "none";
 
              if (item.actionC && data.users[uid]) {
                item.actionC.style = "background: #cb4042;";
              }
            } else if (filterMode === "隐藏") {
              item.containerC.style.display = "none";
            } else {
              handleQuote(item.contentC);
            }
 
            if (item.tagC) {
              const tags = data.users[uid]
                ? data.users[uid].tags.map((tag) => data.tags[tag]) || []
                : [];
 
              item.tagC.style.display = tags.length ? "" : "none";
              item.tagC.innerHTML = tags
                .map(
                  (tag) =>
                    `<b class="block_txt nobr" style="background:${tag.color}; color:#fff; margin: 0.1em 0.2em;">${tag.name}</b>`
                )
                .join("");
            }
          });
 
        if (item.actionC) {
          item.actionC.onclick =
            item.actionC.onclick ||
            ((e) => {
              if (item.pAid < 0) return;
 
              const user = data.users[item.pAid];
 
              if (e.ctrlKey === false) {
                editUser(item.pAid, item.pName, item.reFilter);
              } else {
                if (user) {
                  delete data.users[user.id];
                } else {
                  addUser(item.pAid, item.pName);
                }
 
                saveData();
                item.reFilter();
              }
            });
        }
 
        item.reFilter();
      });
    } else if (uPage) {
      const container = document.getElementById("ucp_block");
 
      if (container.firstChild) {
        const uid = container.innerText.match(/用户ID\s*:\s*(\S+)/)[1];
 
        const name = container.innerText.match(/用户名\s*:\s*(\S+)/)[1];
 
        container.tagC =
          container.tagC ||
          (() => {
            const c = document.createElement("span");
 
            c.innerHTML = `
                    <h2 class="catetitle">:: ${name} 的标记 ::</h2>
                    <div class="cateblock" style="text-align: left; line-height: 1.8em;">
                        <div class="contentBlock" style="padding: 5px 10px;">
                            <span>
                                <ul class="actions" style="padding: 0px; margin: 0px;">
                                    <li style="padding-right: 5px;">
                                        <span>
                                            <a href="javascript: void(0);">[编辑 ${name} 的标记]</a>
                                        </span>
                                    </li>
                                    <div class="clear"></div>
                                </ul>
                            </span>
                            <div class="filter-tags"></div>
                            <div class="clear"></div>
                        </div>
                    </div>
                `;
 
            c.getElementsByTagName("a")[0].onclick = () => {
              editUser(uid, name, container.refresh);
            };
 
            container.firstChild.insertBefore(
              c,
              container.firstChild.childNodes[1]
            );
 
            return c.getElementsByClassName("filter-tags")[0];
          })();
 
        container.refresh = () => {
          container.tagC.innerHTML = data.users[uid].tags
            .map(
              (tag) =>
                `<b class="block_txt nobr" style="background:${data.tags[tag].color}; color:#fff; margin: 0.1em 0.2em;">${data.tags[tag].name}</b>`
            )
            .join("");
        };
 
        container.refresh();
      }
    }
  };
 
  // STYLE
  GM_addStyle(`
    .filter-table-wrapper {
        max-height: 80vh;
        overflow-y: auto;
    }
    .filter-table {
        margin: 0;
    }
    .filter-table th,
    .filter-table td {
        position: relative;
        white-space: nowrap;
    }
    .filter-table th {
        position: sticky;
        top: 2px;
        z-index: 1;
    }
    .filter-table input:not([type]), .filter-table input[type="text"] {
        margin: 0;
        box-sizing: border-box;
        height: 100%;
        width: 100%;
    }
    .filter-input-wrapper {
        position: absolute;
        top: 6px;
        right: 6px;
        bottom: 6px;
        left: 6px;
    }
    .filter-text-ellipsis {
        display: flex;
    }
    .filter-text-ellipsis > * {
        flex: 1;
        width: 1px;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .filter-button-group {
        margin: -.1em -.2em;
    }
    .filter-tags {
        margin: 2px -0.2em 0;
        text-align: left;
    }
    .filter-mask {
        margin: 1px; 
        color: #81C7D4;
        background: #81C7D4;
    }
    .filter-mask-block {
        display: block;
        border: 1px solid #66BAB7;
        text-align: center !important;
    }
    .filter-input-wrapper {
      position: absolute;
      top: 6px;
      right: 6px;
      bottom: 6px;
      left: 6px;
    }
  `);
 
  // UI
  const u = (() => {
    const modules = {};
 
    const tabContainer = (() => {
      const c = document.createElement("div");
 
      c.className = "w100";
      c.innerHTML = `
          <div class="right_" style="margin-bottom: 5px;">
              <table class="stdbtn" cellspacing="0">
                  <tbody>
                      <tr></tr>
                  </tbody>
              </table>
          </div>
          <div class="clear"></div>
          `;
 
      return c;
    })();
 
    const tabPanelContainer = (() => {
      const c = document.createElement("div");
 
      c.style = "width: 80vw;";
 
      return c;
    })();
 
    const content = (() => {
      const c = document.createElement("div");
 
      c.append(tabContainer);
      c.append(tabPanelContainer);
 
      return c;
    })();
 
    const addModule = (() => {
      const tc = tabContainer.getElementsByTagName("tr")[0];
      const cc = tabPanelContainer;
 
      return (module) => {
        const tabBox = document.createElement("td");
 
        tabBox.innerHTML = `<a href="javascript:void(0)" class="nobr silver">${module.name}</a>`;
 
        const tab = tabBox.childNodes[0];
 
        const toggle = () => {
          Object.values(modules).forEach((item) => {
            if (item.tab === tab) {
              item.tab.className = "nobr";
              item.content.style = "display: block";
              item.refresh();
            } else {
              item.tab.className = "nobr silver";
              item.content.style = "display: none";
            }
          });
        };
 
        tc.append(tabBox);
        cc.append(module.content);
 
        tab.onclick = toggle;
 
        modules[module.name] = {
          ...module,
          tab,
          toggle,
        };
 
        return modules[module.name];
      };
    })();
 
    return {
      content,
      modules,
      addModule,
    };
  })();
 
  // 用户
  const userModule = (() => {
    const content = (() => {
      const c = document.createElement("div");
 
      c.style = "display: none";
      c.innerHTML = `
        <div class="filter-table-wrapper">
          <table class="filter-table forumbox">
            <thead>
              <tr class="block_txt_c0">
                <th class="c1" width="1">昵称</th>
                <th class="c2">标记</th>
                <th class="c3" width="1">过滤方式</th>
                <th class="c4" width="1">操作</th>
              </tr>
            </thead>
            <tbody></tbody>
          </table>
        </div>
      `;
 
      return c;
    })();
 
    const refresh = (() => {
      const container = content.getElementsByTagName("tbody")[0];
 
      const func = () => {
        container.innerHTML = "";
 
        Object.values(data.users).forEach((item) => {
          const tc = document.createElement("tr");
 
          tc.className = `row${
            (container.querySelectorAll("TR").length % 2) + 1
          }`;
 
          tc.refresh = () => {
            if (data.users[item.id]) {
              tc.innerHTML = `
                <td class="c1">
                    <a href="/nuke.php?func=ucp&uid=${
                      item.id
                    }" class="b nobr">[${
                item.name ? "@" + item.name : "#" + item.id
              }]</a>
                </td>
                <td class="c2">
                    ${item.tags
                      .map((tag) => {
                        if (data.tags[tag]) {
                          return `<b class="block_txt nobr" style="background:${data.tags[tag].color}; color:#fff; margin: 0.1em 0.2em;">${data.tags[tag].name}</b>`;
                        }
                      })
                      .join("")}
                </td>
                <td class="c3">
                    <div class="filter-table-button-group">
                      <button>${item.filterMode || FILTER_MODE[0]}</button>
                    </div>
                </td>
                <td class="c4">
                    <div class="filter-table-button-group">
                      <button>编辑</button>
                      <button>删除</button>
                    </div>
                </td>
              `;
 
              const actions = tc.getElementsByTagName("button");
 
              actions[0].onclick = () => {
                data.users[item.id].filterMode = switchFilterMode(
                  data.users[item.id].filterMode || FILTER_MODE[0]
                );
 
                actions[0].innerHTML = data.users[item.id].filterMode;
 
                saveData();
                reFilter();
              };
 
              actions[1].onclick = () => {
                editUser(item.id, item.name, tc.refresh);
              };
 
              actions[2].onclick = () => {
                if (confirm("是否确认?")) {
                  delete data.users[item.id];
                  container.removeChild(tc);
 
                  saveData();
                  reFilter();
                }
              };
            } else {
              tc.remove();
            }
          };
 
          tc.refresh();
 
          container.appendChild(tc);
        });
      };
 
      return func;
    })();
 
    return {
      name: "用户",
      content,
      refresh,
    };
  })();
 
  // 标记
  const tagModule = (() => {
    const content = (() => {
      const c = document.createElement("div");
 
      c.style = "display: none";
      c.innerHTML = `
        <div class="filter-table-wrapper">
          <table class="filter-table forumbox">
            <thead>
              <tr class="block_txt_c0">
                <th class="c1" width="1">标记</th>
                <th class="c2">列表</th>
                <th class="c3" width="1">过滤方式</th>
                <th class="c4" width="1">操作</th>
              </tr>
            </thead>
            <tbody></tbody>
          </table>
        </div>
      `;
 
      return c;
    })();
 
    const refresh = (() => {
      const container = content.getElementsByTagName("tbody")[0];
 
      const func = () => {
        container.innerHTML = "";
 
        Object.values(data.tags).forEach((item) => {
          const tc = document.createElement("tr");
 
          tc.className = `row${
            (container.querySelectorAll("TR").length % 2) + 1
          }`;
 
          tc.innerHTML = `
            <td class="c1">
                <b class="block_txt nobr" style="background:${
                  item.color
                }; color:#fff; margin: 0.1em 0.2em;">${item.name}</b>
            </td>
            <td class="c2">
                <button>${
                  Object.values(data.users).filter((user) =>
                    user.tags.find((tag) => tag === item.id)
                  ).length
                }
                </button>
                <div style="white-space: normal; display: none;">
                    ${Object.values(data.users)
                      .filter((user) =>
                        user.tags.find((tag) => tag === item.id)
                      )
                      .map(
                        (user) =>
                          `<a href="/nuke.php?func=ucp&uid=${
                            user.id
                          }" class="b nobr">[${
                            user.name ? "@" + user.name : "#" + user.id
                          }]</a>`
                      )
                      .join("")}
                </div>
            </td>
            <td class="c3">
                <div class="filter-table-button-group">
                  <button>${item.filterMode || FILTER_MODE[0]}</button>
                </div>
            </td>
            <td class="c4">
                <div class="filter-table-button-group">
                  <button>删除</button>
                </div>
            </td>
          `;
 
          const actions = tc.getElementsByTagName("button");
 
          actions[0].onclick = (() => {
            let hide = true;
            return () => {
              hide = !hide;
              actions[0].nextElementSibling.style.display = hide
                ? "none"
                : "block";
            };
          })();
 
          actions[1].onclick = () => {
            data.tags[item.id].filterMode = switchFilterMode(
              data.tags[item.id].filterMode || FILTER_MODE[0]
            );
 
            actions[1].innerHTML = data.tags[item.id].filterMode;
 
            saveData();
            reFilter();
          };
 
          actions[2].onclick = () => {
            if (confirm("是否确认?")) {
              delete data.tags[item.id];
 
              Object.values(data.users).forEach((user) => {
                const index = user.tags.findIndex((tag) => tag === item.id);
                if (index >= 0) {
                  user.tags.splice(index, 1);
                }
              });
 
              container.removeChild(tc);
 
              saveData();
              reFilter();
            }
          };
 
          container.appendChild(tc);
        });
      };
 
      return func;
    })();
 
    return {
      name: "标记",
      content,
      refresh,
    };
  })();
 
  // 关键字
  const keywordModule = (() => {
    const content = (() => {
      const c = document.createElement("div");
 
      c.style = "display: none";
      c.innerHTML = `
        <div class="filter-table-wrapper">
          <table class="filter-table forumbox">
            <thead>
              <tr class="block_txt_c0">
                <th class="c1">列表</th>
                <th class="c2" width="1">过滤方式</th>
                <th class="c3" width="1">包括内容</th>
                <th class="c4" width="1">操作</th>
              </tr>
            </thead>
            <tbody></tbody>
          </table>
        </div>
        <div class="silver" style="margin-top: 10px;">支持正则表达式。比如同类型的可以写在一条规则内用&quot;|&quot;隔开,&quot;ABC|DEF&quot;即为屏蔽带有ABC或者DEF的内容。</div>
      `;
 
      return c;
    })();
 
    const refresh = (() => {
      const container = content.getElementsByTagName("tbody")[0];
 
      const func = () => {
        container.innerHTML = "";
 
        Object.values(data.keywords).forEach((item) => {
          const tc = document.createElement("tr");
 
          tc.className = `row${
            (container.querySelectorAll("TR").length % 2) + 1
          }`;
 
          tc.innerHTML = `
            <td class="c1">
                <div class="filter-input-wrapper">
                  <input value="${item.keyword || ""}" />
                </div>
            </td>
            <td class="c2">
                <div class="filter-table-button-group">
                  <button>${item.filterMode || FILTER_MODE[0]}</button>
                </div>
            </td>
            <td class="c3">
              <div style="text-align: center;">
                <input type="checkbox" ${
                  item.filterLevel ? `checked="checked"` : ""
                } />
              </div>
            </td>
            <td class="c4">
                <div class="filter-table-button-group">
                    <button>保存</button>
                    <button>删除</button>
                </div>
            </td>
          `;
 
          const inputElement = tc.querySelector("INPUT");
          const levelElement = tc.querySelector(`INPUT[type="checkbox"]`);
          const actions = tc.getElementsByTagName("button");
 
          actions[0].onclick = () => {
            actions[0].innerHTML = switchFilterMode(actions[0].innerHTML);
          };
 
          actions[1].onclick = () => {
            if (inputElement.value) {
              data.keywords[item.id] = {
                id: item.id,
                keyword: inputElement.value,
                filterMode: actions[0].innerHTML,
                filterLevel: levelElement.checked ? 1 : 0,
              };
 
              saveData();
              refresh();
            }
          };
 
          actions[2].onclick = () => {
            if (confirm("是否确认?")) {
              delete data.keywords[item.id];
 
              saveData();
              refresh();
            }
          };
 
          container.appendChild(tc);
        });
 
        {
          const tc = document.createElement("tr");
 
          tc.className = `row${
            (container.querySelectorAll("TR").length % 2) + 1
          }`;
 
          tc.innerHTML = `
            <td class="c1">
                <div class="filter-input-wrapper">
                  <input value="" />
                </div>
            </td>
            <td class="c2">
                <div class="filter-table-button-group">
                  <button>${FILTER_MODE[0]}</button>
                </div>
            </td>
            <td class="c3">
              <div style="text-align: center;">
                <input type="checkbox" />
              </div>
            </td>
            <td class="c4">
                <div class="filter-table-button-group">
                  <button>添加</button>
                </div>
            </td>
          `;
 
          const inputElement = tc.querySelector("INPUT");
          const levelElement = tc.querySelector(`INPUT[type="checkbox"]`);
          const actions = tc.getElementsByTagName("button");
 
          actions[0].onclick = () => {
            actions[0].innerHTML = switchFilterMode(actions[0].innerHTML);
          };
 
          actions[1].onclick = () => {
            if (inputElement.value) {
              addKeyword(
                inputElement.value,
                actions[0].innerHTML,
                levelElement.checked ? 1 : 0
              );
 
              saveData();
              refresh();
            }
          };
 
          container.appendChild(tc);
        }
      };
 
      return func;
    })();
 
    return {
      name: "关键字",
      content,
      refresh,
    };
  })();
 
  // 通用设置
  const commonModule = (() => {
    const content = (() => {
      const c = document.createElement("div");
 
      c.style = "display: none";
 
      return c;
    })();
 
    const refresh = (() => {
      const container = content;
 
      const func = () => {
        container.innerHTML = "";
 
        // 默认过滤方式
        {
          const tc = document.createElement("div");
 
          tc.innerHTML += `
            <div>默认过滤方式</div>
            <div></div>
            <div class="silver" style="margin-top: 10px;">${FILTER_TIPS}</div>
          `;
 
          ["标记", "遮罩", "隐藏"].forEach((item, index) => {
            const ele = document.createElement("SPAN");
 
            ele.innerHTML += `
            <input id="s-fm-${index}" type="radio" name="filterType" ${
              data.options.filterMode === item && "checked"
            }>
            <label for="s-fm-${index}" style="cursor: pointer;">${item}</label>
            `;
 
            const inp = ele.querySelector("input");
 
            inp.onchange = () => {
              if (inp.checked) {
                data.options.filterMode = item;
                saveData();
                reFilter();
              }
            };
 
            tc.querySelectorAll("div")[1].append(ele);
          });
 
          container.appendChild(tc);
        }
 
        // 删除没有标记的用户
        {
          const tc = document.createElement("div");
 
          tc.innerHTML += `
            <br/>
            <div>
                <button>删除没有标记的用户</button>
            </div>
          `;
 
          const actions = tc.getElementsByTagName("button");
 
          actions[0].onclick = () => {
            if (confirm("是否确认?")) {
              Object.values(data.users).forEach((item) => {
                if (item.tags.length === 0) {
                  delete data.users[item.id];
                }
              });
 
              saveData();
              reFilter();
            }
          };
 
          container.appendChild(tc);
        }
 
        // 删除没有用户的标记
        {
          const tc = document.createElement("div");
 
          tc.innerHTML += `
            <br/>
            <div>
                <button>删除没有用户的标记</button>
            </div>
          `;
 
          const actions = tc.getElementsByTagName("button");
 
          actions[0].onclick = () => {
            if (confirm("是否确认?")) {
              Object.values(data.tags).forEach((item) => {
                if (
                  Object.values(data.users).filter((user) =>
                    user.tags.find((tag) => tag === item.id)
                  ).length === 0
                ) {
                  delete data.tags[item.id];
                }
              });
 
              saveData();
              reFilter();
            }
          };
 
          container.appendChild(tc);
        }
      };
 
      return func;
    })();
 
    return {
      name: "通用设置",
      content,
      refresh,
    };
  })();
 
  u.addModule(userModule).toggle();
  u.addModule(tagModule);
  u.addModule(keywordModule);
  u.addModule(commonModule);
 
  // 增加菜单项
  (() => {
    const title = "过滤设置";
 
    let window;
 
    n.mainMenu.addItemOnTheFly(title, null, () => {
      if (window === undefined) {
        window = n.createCommmonWindow();
      }
 
      window._.addContent(null);
      window._.addTitle(title);
      window._.addContent(u.content);
      window._.show();
    });
  })();
 
  // 执行过滤
  (() => {
    const hookFunction = (object, functionName, callback) => {
      ((originalFunction) => {
        object[functionName] = function () {
          const returnValue = originalFunction.apply(this, arguments);
 
          callback.apply(this, [returnValue, originalFunction, arguments]);
 
          return returnValue;
        };
      })(object[functionName]);
    };
 
    const initialized = {
      topicArg: false,
      postArg: false,
    };
 
    hookFunction(n, "eval", () => {
      if (Object.values(initialized).findIndex((item) => item === false) < 0) {
        return;
      }
 
      if (n.topicArg && initialized.topicArg === false) {
        hookFunction(n.topicArg, "add", reFilter);
 
        initialized.topicArg = true;
      }
 
      if (n.postArg && initialized.postArg === false) {
        hookFunction(n.postArg, "proc", reFilter);
 
        initialized.postArg = true;
      }
    });
 
    if (n.ucp) {
      hookFunction(n.ucp, "_echo", reFilter);
    }
 
    reFilter();
  })();
})(commonui, __CURRENT_UID);
文章目录