v2ex 快速屏蔽用户和帖子

在用户评论区域添加一个 block 按钮,不用点进用户主页,点击即可 block 用户。

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         v2ex 快速屏蔽用户和帖子
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  在用户评论区域添加一个 block 按钮,不用点进用户主页,点击即可 block 用户。
// @author       3989364
// @match        *://*.v2ex.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=v2ex.com
// @grant        none
// @license      MIT
// @note         0.3 实现屏蔽帖子功能
// @note         0.2 扩展 v2ex.com 的域名匹配规则
// ==/UserScript==

// TODO:
// 1. 帖子外面加个屏蔽用户按钮
// 2. 直接在用户头像链接里面取 userID
// 3. 判断用户是否登录,如果没登录直接跳过所有功能

const BASE_URL = window.location.origin;

if (!BASE_URL.includes("v2ex.com")) {
  throw Error("This script only works at v2ex.com, " + BASE_URL);
}

class OnceCode {
  /**
   * onceCode 类似验证码,目测用一次就会失效;目前获取 once code 的方式是请求设置页面提取出 once code
   */
  static async getOnceCode() {
    const resp = await fetch(`/settings/block`);
    const text = await resp.text();
    const html = document.createElement("html");
    html.innerHTML = text;

    const fn = html
      .querySelector('input[value="一键清除所有忽略主题"]')
      .onclick.toString();

    return this.parseOnceCode(fn);
  }

  static parseOnceCodeURL(text) {
    return text.match("location.href ?= ?'(.+)'")[1];
  }

  static parseOnceCode(text) {
    return text.match(/once=(\d+)/)[1];
  }
}

class User {
  async fetchUserPage(username) {
    const resp = await fetch(`/member/${username}`);
    return await resp.text();
  }

  parseBlockURL(page) {
    const html = document.createElement("html");
    html.innerHTML = page;
    const fn = html.querySelector('input[value="Block"]').onclick;

    if (fn) {
      // /block/xxxx?once=19604
      return OnceCode.parseOnceCodeURL(fn.toString());
    }

    return null;
  }

  async blockUser(username) {
    const page = await this.fetchUserPage(username);
    const url = await this.parseBlockURL(page);

    if (url) {
      // just dont redirect
      try {
        await fetch(url, {
          redirect: "error",
        });
      } catch {}
    }
  }

  hideUserAllReply(username) {
    if (!username) {
      return;
    }

    document.querySelectorAll("#Main .cell").forEach((cell) => {
      const u = cell.querySelector("strong> a")?.textContent;
      if (u === username) {
        cell.remove();
      }
    });
  }
}

class Topic {
  async ignoreTopic(topicId) {
    const onceCode = await OnceCode.getOnceCode();
    try {
      await fetch(`/ignore/topic/${topicId}?once=${onceCode}`, {
        redirect: "error",
      });
    } catch {}
  }
}

function createBlockButton(
  text = "block",
  className = "thank",
  styleText = ""
) {
  const blockButton = document.createElement("a");
  blockButton.textContent = text;
  blockButton.className = className;
  blockButton.style.marginLeft = "0.5rem";
  styleText && (blockButton.style = styleText);

  blockButton.href = "#;";

  return blockButton;
}

function initUserBlockFunction() {
  function addChildInThankArea(node, thankArea) {
    thankArea.appendChild(node, thankArea);
  }

  const user = new User();

  document.querySelectorAll(".thank_area").forEach((thankArea) => {
    const blockButton = createBlockButton();

    addChildInThankArea(blockButton, thankArea);

    blockButton.addEventListener("click", async function (event) {
      event.preventDefault();
      const wrapper = thankArea.parentNode.parentNode;
      const username = wrapper.querySelector("strong > a").textContent;

      if (username && confirm(`确认要屏蔽 ${username} ?`)) {
        await user.blockUser(username);
        user.hideUserAllReply(username);
      }
    });
  });
}

function initTopicBlockFunction() {
  const topic = new Topic();

  document
    .querySelectorAll('#Main .cell.item td[valign="middle"]')
    .forEach((cell) => {
      const titleElement = cell.querySelector(".item_title > a");
      const infoElement = cell.querySelector(".topic_info");

      if (!titleElement) {
        return;
      }

      const topicId = titleElement.href.match(/\/t\/(\d+)/)[1];
      const ignoreButton = createBlockButton(
        "忽略",
        "title",
        "color: #ccc; float: right;"
      );

      infoElement.appendChild(ignoreButton);

      ignoreButton.addEventListener("click", async function () {
        if (confirm("确定不想再看到这个主题?")) {
          // debugger
          await topic.ignoreTopic(topicId);

          // delete this topic
          let element = cell.parentElement;
          const body = document.body;

          // 向上找到 className = "cell item" 的元素即认为找到了帖子元素
          while (element.className !== "cell item" && element !== body) {
            element = element.parentElement;
          }

          if (element !== body) {
            element.remove();
          } else {
            throw Error("Can't find topic cell");
          }
        }
      });
    });
}

(function () {
  "use strict";
  if (location.href.match(/\/t\/\d+/)) {
    console.log("Init user block function");
    initUserBlockFunction();
  }

  initTopicBlockFunction();
})();