troll must die
当前为 
// ==UserScript==
// @name        NGA Filter
// @namespace   https://greatest.deepsurf.us/users/263018
// @version     1.0.4
// @author      snyssss
// @description troll must die
// @match       *bbs.nga.cn/thread.php?fid=*
// @match       *bbs.nga.cn/read.php?tid=*
// @match       *bbs.nga.cn/nuke.php?*
// @match       *ngabbs.com/thread.php?fid=*
// @match       *ngabbs.com/read.php?tid=*
// @match       *ngabbs.com/nuke.php?*
// @grant       GM_addStyle
// @grant       GM_setValue
// @grant       GM_getValue
// @noframes
// ==/UserScript==
((n, self) => {
  "use strict";
  if (n === undefined) return;
  const key = "NGAFilter";
  // 数据
  const data = (() => {
    const d = {
      tags: {},
      users: {},
      options: {
        filterMode: 0,
        keyword: ""
      }
    };
    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,
      enabled: true
    };
    saveData();
    return id;
  };
  // 增加用户
  const addUser = (id, name = null, tags = [], isEnabled = true) => {
    if (data.users[id]) return data.users[id];
    data.users[id] = {
      id,
      name,
      tags,
      enabled: isEnabled
    };
    saveData();
    return data.users[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,
          trollMap[item].map(tag => addTag(tag))
        );
      });
      data.options.filterMode = filterMode ? 0 : 1;
      data.options.keyword = filterKeyword;
      localStorage.removeItem(dataKey);
      localStorage.removeItem(modeKey);
      localStorage.removeItem(keywordKey);
      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");
      content.className = "w100";
      content.innerHTML = `
        <table class="filter-table" style="min-width: 400px;">
            <tbody>
                ${Object.values(data.tags)
                  .map(
                    tag => `
                        <tr>
                            <td>
                                <b class="block_txt nobr" style="background:${
                                  tag.color
                                }; color:#fff; margin: 0.1em 0.2em;">${
                      tag.name
                    }</b>
                            </td>
                            <td>
                                <input type="checkbox" value="${
                                  tag.id
                                }" ${user &&
                      user.tags.find(item => item === tag.id) &&
                      "checked"}/>
                            </td>
                        </tr>
                    `
                  )
                  .join("")}
            </tbody>
            <tfoot>
                <tr>
                <td colspan="2">
                    <input placeholder="一次性添加多个标记用"|"隔开,不会添加重名标记" style="width: -webkit-fill-available;" />
                </td>
                </tr>
            </tfoot>
        </table>
        <div style="margin: 10px 0;">
            <button>${user && user.enabled === false ? "启用" : "禁用"}</button>
            <div class="right_">
                <button>删除</button>
                <button>保存</button>
            </div>
        </div>
    `;
      const actions = content.getElementsByTagName("button");
      actions[0].onclick = () => {
        actions[0].innerText =
          actions[0].innerText === "禁用" ? "启用" : "禁用";
      };
      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.enabled = 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 reFilter = (() => {
    const tPage = location.pathname === "/thread.php";
    const pPage = location.pathname === "/read.php";
    const uPage = location.pathname === "/nuke.php";
    const func = (() => {
      if (tPage) {
        return () => {
          const tData = n.topicArg.data;
          Object.values(tData).forEach(item => {
            const uid =
              item[2].search.match(/uid=(\S+)/) &&
              item[2].search.match(/uid=(\S+)/)[1];
            const user = data.users[uid];
            const tags = user ? user.tags.map(tag => data.tags[tag]) : [];
            const isBlock =
              (user &&
                user.enabled &&
                (tags.length === 0 ||
                  tags.filter(tag => tag.enabled).length)) ||
              (data.options.keyword.length &&
                item[1].innerText.search(data.options.keyword) >= 0);
            item.contentC = item[1];
            item.contentB = item.contentB || item.contentC.innerHTML;
            item.containerC =
              item.containerC || item.contentC.parentNode.parentNode;
            item.containerC.style =
              isBlock && data.options.filterMode === 0 ? "display: none;" : "";
            item.contentC.style =
              isBlock && data.options.filterMode === 1
                ? "text-decoration: line-through;"
                : "";
          });
        };
      } else if (pPage) {
        return () => {
          const pData = n.postArg.data;
          Object.values(pData).forEach(item => {
            if (~~item.pAid === self) return;
            if (typeof item.i === "number") {
              item.actionC =
                item.actionC ||
                (() => {
                  const ele = item.uInfoC.firstElementChild.lastElementChild;
                  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 user = data.users[item.pAid];
                const tags = user ? user.tags.map(tag => data.tags[tag]) : [];
                const isBlock =
                  user &&
                  user.enabled &&
                  (tags.length === 0 || tags.filter(tag => tag.enabled).length);
                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;
                    while (
                      temp.className !== "forumbox postbox" &&
                      temp.className !== "comment_c left"
                    ) {
                      temp = temp.parentNode;
                    }
                    return temp;
                  })();
                item.containerC.style.display =
                  isBlock && data.options.filterMode === 0 ? "none" : "";
                item.contentC.innerHTML =
                  isBlock && data.options.filterMode === 1
                    ? `
                    <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_${user.id}')].forEach(item => item.style.display = '')">点击查看</a>
                        <div style="display: none;" name="troll_${user.id}">
                            ${item.contentB}
                        </div>
                    </div>`
                    : item.contentB;
                item.avatarC.style.display = isBlock ? "none" : "";
                if (item.actionC) {
                  item.actionC.style =
                    user && user.enabled
                      ? "background: #cb4042;"
                      : "background: #aaa;";
                }
                if (item.tagC) {
                  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) {
                    editUser(item.pAid, item.pName, item.reFilter);
                  } else {
                    if (user) {
                      if (user.tags.length) {
                        user.enabled = !user.enabled;
                        user.name = item.pName;
                      } else {
                        delete data.users[user.id];
                      }
                    } else {
                      addUser(item.pAid, item.pName);
                    }
                    saveData();
                    item.reFilter();
                  }
                });
            }
            item.reFilter();
          });
        };
      } else if (uPage) {
        return () => {
          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();
          }
        };
      }
      return () => {};
    })();
    const observer = new MutationObserver(mutations => {
      if (mutations.find(mutation => mutation.addedNodes.length)) {
        func();
      }
    });
    if (tPage) {
      observer.observe(document.getElementById("topicrows"), {
        childList: true
      });
    } else if (pPage) {
      observer.observe(document.getElementById("m_posts_c"), {
        childList: true
      });
    } else if (uPage) {
      observer.observe(document.getElementById("ucp_block"), {
        childList: true
      });
    }
    func();
    return func;
  })();
  // STYLE
  GM_addStyle(`
    .filter-tags {
        margin: 2px -0.2em 0;
        text-align: left;
    }
    .filter-table {
        border: 1px solid #ead5bc;
        border-left: none;
        border-bottom: none;
        width: 99.95%;
    }
    .filter-table thead {
        background-color: #591804;
        color: #fff8e7;
    }
    .filter-table tbody tr {
        background-color: #fff0cd;
    }
    .filter-table tbody tr:nth-of-type(odd) {
        background-color: #fff8e7;
    }
    .filter-table td {
        border: 1px solid #ead5bc;
        border-top: none;
        border-right: none;
        padding: 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 =
        "min-width: 20vw; max-width: 80vw; max-height: 80vh; overflow: auto;";
      c.innerHTML = `
            `;
      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 blockModule = (() => {
    const content = (() => {
      const c = document.createElement("div");
      c.style = "display: none";
      c.innerHTML = `
        <table class="filter-table">
            <thead>
                <tr>
                    <td>
                        <b style="margin: 0.1em 0.2em;">昵称</b>
                    </td>
                    <td>
                        <b style="margin: 0.1em 0.2em;">标记</b>
                    </td>
                    <td>
                        <b style="margin: 0.1em 0.2em;">操作</b>
                    </td>
                </tr>
            </thead>
            <tbody></tbody>
        </table>
            `;
      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.refresh = () => {
            if (data.users[item.id]) {
              tc.innerHTML = `
                <tr>
                    <td>
                        <a href="/nuke.php?func=ucp&uid=${
                          item.id
                        }" class="b nobr">[${
                item.name ? "@" + item.name : "#" + item.id
              }]</a>
                    </td>
                    <td>
                        ${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="nobr">
                        <button>编辑</button>
                        <button>${item.enabled ? "禁用" : "启用"}</button>
                        <button>删除</button>
                    </td>
                </tr>
                `;
              const actions = tc.getElementsByTagName("button");
              actions[0].onclick = () => {
                editUser(item.id, item.name, tc.refresh);
              };
              actions[1].onclick = () => {
                data.users[item.id].enabled = !data.users[item.id].enabled;
                actions[1].innerHTML = data.users[item.id].enabled
                  ? "禁用"
                  : "启用";
                saveData();
                reFilter();
              };
              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 = `
        <table class="filter-table">
            <thead>
                <tr>
                    <td>
                        <b style="margin: 0.1em 0.2em;">标记</b>
                    </td>
                    <td>
                        <b style="margin: 0.1em 0.2em;">列表</b>
                    </td>
                    <td>
                        <b style="margin: 0.1em 0.2em;">操作</b>
                    </td>
                </tr>
            </thead>
            <tbody></tbody>
        </table>
            `;
      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.innerHTML = `
            <tr>
                <td>
                    <b class="block_txt nobr" style="background:${
                      item.color
                    }; color:#fff; margin: 0.1em 0.2em;">${item.name}</b>
                </td>
                <td>
                    <button>${
                      Object.values(data.users).filter(user =>
                        user.tags.find(tag => tag === item.id)
                      ).length
                    }
                    </button>
                    <div style="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="nobr">
                    <button>${item.enabled ? "禁用" : "启用"}</button>
                    <button>删除</button>
                </td>
            </tr>
            `;
          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].enabled = !data.tags[item.id].enabled;
            actions[1].innerHTML = data.tags[item.id].enabled ? "禁用" : "启用";
            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 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>
                <input value="${data.options.keyword}"/>
                <button>确认</button>
            </div>
          `;
          const actions = tc.getElementsByTagName("button");
          actions[0].onclick = () => {
            const v = actions[0].previousElementSibling.value;
            data.options.keyword = v;
            saveData();
            reFilter();
          };
          container.appendChild(tc);
        }
        // 过滤方式
        {
          const tc = document.createElement("div");
          tc.innerHTML += `
            <br/>
            <div>过滤方式</div>
            <div>
                <input type="radio" name="filterType" ${data.options
                  .filterMode === 0 && "checked"}>
                <span>隐藏</span>
                <input type="radio" name="filterType" ${data.options
                  .filterMode === 1 && "checked"}>
                <span>标记</span>
                <button>确认</button>
            </div>
          `;
          const actions = tc.getElementsByTagName("button");
          actions[0].onclick = () => {
            const values = document.getElementsByName("filterType");
            for (let i = 0, length = values.length; i < length; i++) {
              if (values[i].checked) {
                data.options.filterMode = i;
                break;
              }
            }
            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.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(blockModule).toggle();
  u.addModule(tagModule);
  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();
    });
  })();
})(commonui, __CURRENT_UID);