bagscript

bag anti bot script

当前为 2025-04-28 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        bagscript
// @description bag anti bot script
// @version     0.5
// @license     MIT
// @namespace   9e7f6239-592e-409b-913f-06e11cc5e545
// @include     https://8chan.moe/v/res/*
// @include     https://8chan.se/v/res/*
// @include     https://8chan.moe/barchive/res/*
// @include     https://8chan.se/barchive/res/*
// @grant       unsafeWindow
// @run-at      document-idle
// ==/UserScript==

// Script settings
const SPOILER_BORDER = "3px solid red";
const RUDE_FORMATS = ["JPEG", "JPG", "PNG"];
const THREAD_NAME_FILTER = "/bag/";

// Debug settings
const DISABLE_YOU_BYPASS = false;

// State
let manualBypass;
let defaultSpoilerSrc;
const settings = {};
let toolbarVisible = false;

// Loader
(new MutationObserver((_, observer) => {
  const threadTitle = document.querySelector("div.opHead > span.labelSubject");
  if (threadTitle) {
    observer.disconnect();

    if (!threadTitle.innerText.includes(THREAD_NAME_FILTER)) {
      return;
    }

    loadSettings();
    loadToolbar();

    const initialPosts = document.querySelectorAll(".postCell");
    initialPosts.forEach((post) => {
      handleSpoilers(post);
    });

    processAllPosts();
    postObserver.observe(document, {childList: true, subtree: true});
  }
})).observe(document, {childList: true, subtree: true});

// New post observer
const postObserver = new MutationObserver((mutations) => {
  for (const mutation of mutations) {
    for (const node of mutation.addedNodes) {
      if (node.nodeType === 1) {
        const isPost = node.classList.contains("postCell");
        const isHoverPost = node.classList.contains("quoteTooltip");
        const isInlineQuote = node.classList.contains("inlineQuote");


        if (isPost) {
          handleSpoilers(node);

          const id = postId(node);
          unsafeWindow.posting.idsRelation[id].forEach((innerPost) => {
            processAllPostsById(id);
          });

          node.querySelectorAll(".quoteLink").forEach((quoteLink) => {
            const quotedId = quoteLink.innerText.substring(2);
            const quotedPost = document.getElementById(quotedId);
            processSinglePost(quotedPost);
          });
        } else if (isHoverPost || isInlineQuote) {
          processSinglePost(node);
        }
      }
    }
  }
});

const processSinglePost = function(post) {
  const id = postId(post);
  const isNice = isNiceId(id) || isNicePost(post);
  if (isNice) {
    unblurPost(post);
  } else {
    blurPost(post);
  }
}

const processAllPosts = function() {
  for (const id in unsafeWindow.posting.idsRelation) {
    processAllPostsById(id);
  }

  document.querySelectorAll(".inlineQuote").forEach((inlineQuote) => {
    processSinglePost(inlineQuote);
  });

  const hoverPost = document.querySelector(".quoteTooltip");
  if (hoverPost) {
    processSinglePost(hoverPost);
  }
}

const processAllPostsById = function(id) {
  const innerPostsById = unsafeWindow.posting.idsRelation[id];
  let isNice = isNiceId(id);

  for (const innerPost of innerPostsById) {
    const post = innerPost.parentElement;

    if (!isNice) {
      isNice = isNicePost(post);
      if (isNice) break;
    }
  }

  innerPostsById.forEach(innerPost => handlePost(innerPost.parentElement, isNice));
}

const isNiceId = function(id) {
  if (!settings.enabled) return true;

  if (manualBypass[id]) return true;

  const innerPostsById = unsafeWindow.posting.idsRelation[id];

  const isOp = innerPostsById.some(innerPost => innerPost.parentElement.classList.contains("opCell"));
  if (isOp) return true;

  const idAboveThreshold = innerPostsById.length >= settings.postThreshold;
  if (idAboveThreshold) return true;

  return false;
}

const isNicePost = function(post) {
  const postIsByYou = DISABLE_YOU_BYPASS ? false : post.querySelector(".youName");
  if (postIsByYou) return true;

  const aboveBlThreshold = post.querySelectorAll(".postInfo > .panelBacklinks > a")?.length >= settings.backlinkThreshold;
  if (aboveBlThreshold) return true;

  if (settings.experimental) {
    const images = post.querySelectorAll("img");

    const noImages = images.length === 0;
    if (noImages) return true;

    const hasFunImage = Array.from(images).some((image) => {
      const spoilerImage = image.getAttribute("data-spoiler") === "true"
      if (spoilerImage) return true;

      const format = image?.parentElement?.href?.split("/")?.[4]?.split(".")?.[1]?.toUpperCase();
      if (format) {
        const notRudeImage = !RUDE_FORMATS.includes(format);
        if (notRudeImage) return true;
      }

      return false;
    });

    if (hasFunImage) return true;

    const hasFunText = post.querySelector(".doomText, .moeText, .redText, .pinkText, .diceRoll");
    if (hasFunText) return true;
  }

  return false;
}

const isRudeId = function(id) {
  return settings.experimental && unsafeWindow.posting.idsRelation[id].length === 3;
}

const handlePost = function(post, isNice) {
  let bypassButton = post.querySelector(".bypassButton");

  if (isNice) {
    unblurPost(post);

    if (bypassButton) {
      bypassButton.style.display = "none";
    }
  } else {
    blurPost(post);

    if (bypassButton) {
      bypassButton.style.display = "inline";

      if (isRudeId(postId(post))) {
        bypassButton.style.border = "1px solid red";
      }
    } else {
      bypassButton = bypassButtonForId(postId(post));
      post.querySelector(".postInfo.title").appendChild(bypassButton);
    }
  }
}

const handleSpoilers = function(post) {
  const spoilers = post.querySelectorAll(".imgLink > img[src*='spoiler']");

  if (!defaultSpoilerSrc) {
    defaultSpoilerSrc = spoilers[0]?.src;
  }

  spoilers.forEach(spoiler => {
    spoiler.setAttribute("data-spoiler", true);

    if (settings.revealSpoilers) {
      const fileName = spoiler.parentElement.href.split("/")[4].split(".")[0];
      spoiler.src = `/.media/t_${fileName}`;
      spoiler.style.border = SPOILER_BORDER;
    }
  });
}

const blurPost = function(post) {
  post.style.display = settings.hideFiltered ? "none" : "block";

  post.querySelectorAll("img").forEach((img) => {
    img.style.filter = `blur(${settings.blurStrength}px)`;
  });
}

const unblurPost = function(post) {
  post.style.display = "block";

  post.querySelectorAll("img").forEach((img) => {
    img.style.filter = "";
  });
}

const loadToolbar = function() {
  // Toolbar container
  const toolbar = document.createElement("div");
  document.querySelector("body").appendChild(toolbar);
  toolbar.style.backgroundColor = "var(--navbar-text-color)";
  toolbar.style.bottom = "0px";
  toolbar.style.color = "var(--navbar-text-color)";
  toolbar.style.display = "flex";
  toolbar.style.gap = "1px";
  toolbar.style.right = "0px";
  toolbar.style.padding = "1px";
  toolbar.style.position = "fixed";

  // Toolbar contents container
  const toolbarContents = document.createElement("div");
  toolbar.appendChild(toolbarContents);
  toolbarContents.style.display = "none";
  toolbarContents.style.flexDirection = "column";
  toolbarContents.style.gap = "1px";
  toolbarContents.style.padding = "1px 1px 0 1px";

  // Enable checkbox
  const enableContainer = container();
  toolbarContents.appendChild(enableContainer);

  const enableLabel = label("Enable Filter");
  enableContainer.appendChild(enableLabel);

  const enableCheckbox = checkbox(settings.enabled);
  enableContainer.appendChild(enableCheckbox);
  enableCheckbox.onchange = () => {
    settings.enabled = !settings.enabled;
    unsafeWindow.localStorage.setItem("bag_enabled", settings.enabled);

    if (settings.enabled) {
      processAllPosts();
      postObserver.observe(document, {childList: true, subtree: true});
    } else {
      postObserver.disconnect();
      processAllPosts();
    }
  }

  // Post threshold input
  const thresholdContainer = container();
  toolbarContents.appendChild(thresholdContainer);

  const thresholdLabel = label("Post Threshold");
  thresholdContainer.appendChild(thresholdLabel);

  const thresholdInput = input(settings.postThreshold);
  thresholdContainer.appendChild(thresholdInput);
  thresholdInput.onchange = () => {
    settings.postThreshold = thresholdInput.value;
    unsafeWindow.localStorage.setItem("bag_postThreshold", settings.postThreshold);

    processAllPosts();
  }

  // Backlink threshold input
  const blThresholdContainer = container();
  toolbarContents.appendChild(blThresholdContainer);

  const blThresholdLabel = label("Backlink Threshold");
  blThresholdContainer.appendChild(blThresholdLabel);

  const blThresholdInput = input(settings.backlinkThreshold);
  blThresholdContainer.appendChild(blThresholdInput);
  blThresholdInput.onchange = () => {
    settings.backlinkThreshold = blThresholdInput.value;
    setSetting("bag_backlinkThreshold", settings.backlinkThreshold);

    processAllPosts();
  }

  // Blur input
  const blurContainer = container();
  toolbarContents.appendChild(blurContainer);

  const blurLabel = label("Blur Strength");
  blurContainer.appendChild(blurLabel);

  const blurInput = input(settings.blurStrength);
  blurContainer.appendChild(blurInput);
  blurInput.onchange = () => {
    settings.blurStrength = blurInput.value;
    unsafeWindow.localStorage.setItem("bag_blurStrength", settings.blurStrength);

    processAllPosts();
  }

  // Experimental checkbox
  const experimentalContaner = container();
  toolbarContents.appendChild(experimentalContaner);

  const experimentalLabel = label("Experimental Heuristics");
  experimentalContaner.appendChild(experimentalLabel);

  const experimentalCheckbox = checkbox(settings.experimental);
  experimentalContaner.appendChild(experimentalCheckbox);
  experimentalCheckbox.onchange = () => {
    settings.experimental = !settings.experimental;
    unsafeWindow.localStorage.setItem("bag_experimental", settings.experimental);

    if (!settings.experimental) {
      document.querySelectorAll('.innerPost').forEach(innerPost => {
        innerPost.style.borderRight = "1px solid var(--horizon-sep-color)";
      });

      document.querySelectorAll(".bypassButton").forEach(bypassButton => {
        bypassButton.style.border = "1px solid var(--horizon-sep-color)";
      });
    }

    processAllPosts();
  }

  // Hide filtered checkbox
  const hideContainer = container();
  toolbarContents.appendChild(hideContainer);

  const hideLabel = label("Hide Filtered");
  hideContainer.appendChild(hideLabel);

  const hideCheckbox = checkbox(settings.hideFiltered);
  hideContainer.appendChild(hideCheckbox);
  hideCheckbox.onchange = () => {
    settings.hideFiltered = !settings.hideFiltered;
    unsafeWindow.localStorage.setItem("bag_hideFiltered", settings.hideFiltered);

    processAllPosts();
  }

  // Reveal spoilers checkbox
  const revealContainer = container();
  toolbarContents.appendChild(revealContainer);

  const revealLabel = label("Reveal Spoilers");
  revealContainer.appendChild(revealLabel);

  const revealCheckbox = checkbox(settings.revealSpoilers);
  revealContainer.appendChild(revealCheckbox);
  revealCheckbox.onchange = () => {
    settings.revealSpoilers = !settings.revealSpoilers;
    unsafeWindow.localStorage.setItem("bag_revealSpoilers", settings.revealSpoilers);

    if (!settings.revealSpoilers) {
      document.querySelectorAll('img[data-spoiler="true"]').forEach(spoiler => {
        spoiler.src = defaultSpoilerSrc;
        spoiler.style.border = "0";
      });
    }

    processAllPosts();
  }

  // Toolbar toggle button
  const toggleButton = button();
  toolbar.appendChild(toggleButton);
  toggleButton.innerText = "<<"
  toggleButton.style.backgroundColor = "var(--background-color)"
  toggleButton.onclick = () => {
    toolbarVisible = !toolbarVisible;
    toolbarContents.style.display = toolbarVisible ? "flex" : "none";
    toggleButton.innerText = toolbarVisible ? ">>" : "<<";
  }
}

// Post helpers
const postId = function(post) {
  return post.querySelector('.labelId').innerText;
}

// LocalStorage Helpers
const loadSettings = function() {
  manualBypass = getManualBypass();

  settings.backlinkThreshold = getIntSetting("bag_backlinkThreshold", 3);
  settings.blurStrength = getIntSetting("bag_blurStrength", 10);
  settings.enabled = getBoolSetting("bag_enabled", true);
  settings.experimental = getBoolSetting("bag_experimental", true);
  settings.hideFiltered = getBoolSetting("bag_hideFiltered", false);
  settings.postThreshold = getIntSetting("bag_postThreshold", 4);
  settings.revealSpoilers = getBoolSetting("bag_revealSpoilers", false);
}

function setSetting(name, value) {
  unsafeWindow.localStorage.setItem(name, value);
}

function getSetting(name) {
  return unsafeWindow.localStorage.getItem(name);
}

function getBoolSetting(name, defaultValue) {
  const value = getSetting(name, defaultValue);
  if (value === null) return defaultValue;
  return value == "true";
}

function getIntSetting(name, defaultValue) {
  const value = getSetting(name, defaultValue);
  if (value === null) return defaultValue;
  return parseInt(value);
}

function getManualBypass() {
  const threadId = unsafeWindow.location.pathname.match(/\/v\/res\/(\d+)/)[1];
  const bypassVar = "bag_bypass_" + threadId;
  const bp = getSetting(bypassVar);
  return (bp === null) ? {} : JSON.parse(bp);
}

function setManualBypass() {
  const threadId = unsafeWindow.location.pathname.match(/\/v\/res\/(\d+)/)[1];
  const bypassVar = "bag_bypass_" + threadId;
  const bypassData = JSON.stringify(manualBypass);
  unsafeWindow.localStorage.setItem(bypassVar, bypassData);
}

// HTML Helpers
function container() {
  const container = document.createElement("div");
  container.style.alignItems = "center";
  container.style.backgroundColor = "var(--background-color)";
  container.style.display = "flex";
  container.style.gap = "0.25rem";
  container.style.justifyContent = "space-between";
  container.style.padding = "0.25rem";
  return container;
}

function label(text) {
  const label = document.createElement("div");
  label.innerText = text;
  label.style.color = "white";
  return label;
}

function checkbox(initialValue) {
  const checkbox = document.createElement("input");
  checkbox.type = "checkbox";
  checkbox.style.cursor = "pointer";
  checkbox.checked = initialValue;
  return checkbox;
}

function input(initialValue) {
  const input = document.createElement("input");
  input.size = 4;
  input.value = initialValue;
  return input;
}

function button() {
  const button = document.createElement("div");
  button.style.alignItems = "center";
  button.style.color = "var(--link-color)";
  button.style.cursor = "pointer";
  button.style.display = "flex";
  button.style.padding = "0.25rem 0.75rem";
  button.style.userSelect = "none";
  return button;
}

function bypassButtonForId(id) {
  const border = isRudeId(id)
    ? "1px solid red"
    : "1px solid var(--horizon-sep-color)";

  const bypassButton = button();
  bypassButton.className = "bypassButton";
  bypassButton.innerText = "+";
  bypassButton.style.display = "inline";
  bypassButton.style.marginLeft = "auto";
  bypassButton.style.border = border;
  bypassButton.onclick = () => {
    bypassButton.style.display = "none";
    manualBypass[id] = true;
    setManualBypass();

    unsafeWindow.posting.idsRelation[id].forEach(otherPostInner => {
      unblurPost(otherPostInner.parentElement);
    });
  };

  return bypassButton;
}