The 4chan Cleaner

Release 2026 Build 1

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name        The 4chan Cleaner
// @description Release 2026 Build 1
// @author      BoKu
// @version     2026.1
// @namespace   https://greasyfork.org/scripts/T4C - The 4chan Cleaner
// @icon        https://i.imgur.com/jsBhOii.gif
// @license 	The 4chan Cleaner is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
// @match       *://*.4chan.org/*
// @exclude     *://p.4chan.org/*
// @noframes
// @run-at      document-body
// @resource    bootstrapCss https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css#sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB
// @require     https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js#sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI
// @grant       GM.setValue
// @grant       GM.getValue
// @grant       GM.openInTab
// @grant       GM_getResourceText
// @grant       GM_addStyle
// @grant       GM_info
// @grant       unsafeWindow
// @grant       GM_download
// @connect     i.4cdn.org
// ==/UserScript==
'use strict';
const bootstrapCss = GM_getResourceText("bootstrapCss"),
      scriptVersion = GM_info.script.version,
      scriptDesc = GM_info.script.description,
      c = unsafeWindow,
      logo = "https://s.4cdn.org/image/fp/logo-transparent.png",
      h = c.document.querySelector("html"),
      s = c.document.querySelectorAll('head script, head style, link[rel*="stylesheet"]'),
      b = c.document.querySelector('body'),
      d = c.document.getElementById('doc'),
      f = c.document.getElementById('ft'),
      bt = c.document.querySelector("body > div.boardBanner > div.boardTitle")?.textContent || "4chan";
h.setAttribute('data-bs-theme','dark');
GM_addStyle(bootstrapCss);
h.style.background = '#212529';
b.style.background = '#212529';
f?.remove();

const b1 = c.document.querySelector("#delform > div.bottomCtrl.desktop > span.deleteform > input[type=submit]:nth-child(5)");
if (b1) {
    b1.classList.add("btn", "btn-danger");
    b1.style.margin = ".25rem .125rem";
}
const b2 = c.document.querySelector("#bottomReportBtn");
if (b2) {
    b2.classList.add("btn", "btn-warning");
    b2.style.margin = ".25rem .125rem";
}
const i1 = c.document.querySelector("#delform > div.bottomCtrl.desktop > span.deleteform > input[type=text]:nth-child(1)");
if (i1) {
    i1.classList.add("form-control");
}
const i2 = c.document.querySelector("#delform > div.bottomCtrl.desktop > span.deleteform > input[type=text]:nth-child(2)");
if (i2) {
    i2.classList.add("form-control");
}
const i3 = c.document.querySelector("#delPassword");
if (i3) {
    i3.classList.add("form-control");
}
const c1 = c.document.querySelector('#delform .bottomCtrl.desktop .deleteform input[type="checkbox"]:nth-child(3)');
if (c1) {
    c1.classList.add("form-check-input");
    Object.assign(c1.style, {
        minHeight: `calc(1em + 1rem + 2px)`,
        padding: `.5rem 1rem`,
        fontSize: `1.25rem`,
        marginTop: 'unset'
    })
}
const s1 = c.document.querySelector("#styleSelector");
if (s1) {
    s1.classList.add("form-select");
}

function whenReady(fn) {
    if (c.document.readyState !== "loading") {
        fn();
    } else {
        c.document.addEventListener("DOMContentLoaded", fn);
    }
}
function downloadAllImages() {
    const imageLinks = c.document.querySelectorAll('a.fileThumb');
    if (imageLinks.length === 0) {
        console.debug('No images found to download!');
        return;
    }
    const confirmationMessage = `Are you sure you want to download all the ${imageLinks.length} images?`;
    if (!c.confirm(confirmationMessage)) {
        return;
    }
    imageLinks.forEach((link, index) => {
    const imageUrl = link.href;
    // Cleans the URL path down to the pure filename string
    const fileName = imageUrl.substring(imageUrl.lastIndexOf('/') + 1);

    // Staggers requests by 150ms intervals to prevent file streaming bottlenecks
    setTimeout(() => {
      GM_download({
        url: imageUrl,
        name: fileName,
        onload: () => {
          console.log(`Successfully saved: ${fileName}`);
        },
        onerror: (error) => {
          console.error(`Download failed for ${fileName}:`, error.error);

          // Fallback Strategy: Triggers if your browser blocks native extension file generation
          if (error.error === 'not_enabled' || error.error === 'permission_denied') {
             const fallbackAnchor = c.document.createElement('a');
             fallbackAnchor.href = imageUrl;
             fallbackAnchor.download = fileName;
             c.document.body.appendChild(fallbackAnchor);
             fallbackAnchor.click();
             fallbackAnchor.remove();
          }
        }
      });
    }, index * 150);
  });
}
whenReady(() => {
    const nlD = c.document.querySelector("body > div.navLinks.desktop");
    if (!nlD) return;

    setTimeout(() => {
        if (nlD.childElementCount === 7) {
            try {
                const modalWrap = c.document.createElement("div");
                modalWrap.innerHTML = `
                <div class="modal fade" id="T4C_viewImages_modal" tabindex="-1" aria-hidden="true">
                  <div class="modal-dialog modal-dialog-centered modal-lg modal-dialog-scrollable" style="width:90vw!important;min-width:90vw!important;max-width:90vw!important;">
                    <div class="modal-content bg-dark text-light">
                      <div class="modal-header">
                        <h5 class="modal-title">View All Images</h5>
                        <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
                      </div>
                      <div class="modal-body" id="T4C_viewImages_modal_body"></div>
                      <div class="modal-footer" id="T4C_viewImages_modal_footer"><small>Doesn't support <b>webm</b> yet.</small></div>
                    </div>
                  </div>
                </div>`;
                c.document.body.appendChild(modalWrap.firstElementChild);

                const modalEl = c.document.getElementById("T4C_viewImages_modal"),
                      modalBody = c.document.getElementById("T4C_viewImages_modal_body");
                function buildThumbs() {
                    modalBody.replaceChildren();
                    const wrap = c.document.createElement("div");
                    wrap.className = "d-flex flex-wrap gap-2";
                    c.document.querySelectorAll("a.fileThumb").forEach((thumb) => {
                        const href = thumb.getAttribute("href") || "";
                        if (href.includes("webm")) return;
                        thumb.querySelectorAll(":scope > img").forEach((img) => {
                            img.classList.add("t4c-thumb");
                        });
                        wrap.appendChild(thumb.cloneNode(true));
                    });
                    modalBody.appendChild(wrap);
                }
                function clearThumbs() {
                    modalBody.replaceChildren();
                }
                nlD.appendChild(c.document.createTextNode("["));
                const vai = c.document.createElement("a");
                vai.id = "T4C_viewImages";
                vai.textContent = "View All Images";
                vai.style.cursor = "pointer";
                vai.dataset.bsBackdrop="static";
                vai.addEventListener("click", (event) => {
                    if (!modalEl) return;
                    const modal = bootstrap.Modal.getOrCreateInstance(modalEl, {
                        backdrop: true,
                        keyboard: true
                    });
                    modal.show();
                })
                nlD.appendChild(vai);
                modalEl.addEventListener("shown.bs.modal", buildThumbs);
                modalEl.addEventListener("hidden.bs.modal", clearThumbs);
                nlD.appendChild(c.document.createTextNode("] ["));
                const dai = c.document.createElement("a");
                dai.id = "T4C_downImages";
                dai.textContent = "Download All Images";
                dai.style.cursor = "pointer";
                dai.addEventListener("click", (event) => {
                    event.preventDefault();
                    downloadAllImages();
                })
                nlD.appendChild(dai);
                nlD.appendChild(c.document.createTextNode("]"));
            } catch (ex) {
                console.debug(ex);
            }
        }
    }, 500);
});

GM_addStyle(`
  * {
    scrollbar-color: rgba(255, 70, 87, 1) black;
  }
  html {
    font-size:75%!important;
  }
  #doc, #doc2, #doc3, #doc4 {
    display:flex;
    flex-direction:column;
    width:unset;
  }
  .boxcontent,
  .box-inner,
  .box-outer,
  #announce,
  #entries,
  #disclaimer-dialog,
  div.reply{
    background:#343a40;
    color:#f8f9fa;
  }
  div.reply{
    border: 1px solid #212529;
    border-left: none;
    border-top: none;
  }
  .boxbar,
  #entries th{
    background:#1a1d20;
    color:#dee2e6;
  }
  div#filter-btn,
  div#opts-btn {
    display:none;
  }
  .boxcontent {
    display:flex;
    flex-wrap:nowrap;
    justify-content:space-around;
    align-items:flex-start;
  }
  #boards .column {
    float:unset;
    width:100%;
  }
  span.warning sup {
    vertical-align:unset!important;
    top:unset;
  }
  div[class="box-outer top-box"],
  #announce{
    border:1px solid #212529;
  }

  /* Normal */
  a, a:visited,
  #boards a,
  div#boardNavDesktop a,
  a.replylink, a.replylink:not(:hover), div#absbot a:not(:hover),
  .quoteLink, .quotelink, .deadlink, .button
  {
    color: rgba(255, 70, 87, 1) !important;
  }
  /* Hover */
  a:hover,
  #boards a:hover,
  div#boardNavDesktop a:hover,
  a.replylink:hover, div.post div.postInfo span.postNum a:hover, .posteruid .hand:hover,
  a.quoteLink:hover, a.quotelink:hover, .button:hover
  {
    color: rgba(234, 134, 143, 1) !important;
  }

  .c-thread img {
    border: 1px solid rgba(220, 53, 69, 1) !important;
  }
  ol, ul {
    padding-left: unset;
  }
  #c-threads{
    display: flex;
    flex-wrap: nowrap;
    justify-content: space-evenly;
    flex-direction: row;
  }
  .c-thread{
    width:100%;
  }
  div#bannerCnt,
  hr.aboveMidAd,
  div.middlead,
  .adl,
  body > hr:nth-child(13),
  footer,
  #absbot,
  hr,
  #delform > div.bottomCtrl.desktop > span.stylechanger,
  #blotter{
    display:none!important;
  }
  hr{
    width:100%!important;
  }
  div.post div.postInfo span.subject {
    color: rgba(13, 202, 240, 1);
  }
  div.post div.postInfo span.nameBlock span.name {
    color: rgba(37, 200, 125, 1);
  }
  .nameBlock.capcodeMod span.name, span.capcodeMod a span.name, span.capcodeMod span.postertrip, span.capcodeMod strong.capcode {
    color: rgba(255, 126, 255,1) !important;
  }
  #delform > div.bottomCtrl.desktop{
    display: flex !important;
    width:100%;
    justify-content: flex-end;
    align-items: center;
  }

  #delform > div.bottomCtrl.desktop > span.deleteform
  {
    display: flex !important;
    align-items: center;
    justify-content: flex-end;
    gap: .5rem;
  }
  .form-check-input:focus {
    border-color: rgba(255, 70, 87, 1);
    outline: 0;
    box-shadow: 0 0 0 .25rem rgba(234, 134, 143, .25) !important;
  }
  .form-check-input:checked {
    background-color: rgba(255, 70, 87, 1);
    border-color: rgba(255, 70, 87, 1);
  }
  div.thread{
    border-bottom:2px solid rgba(255,255,255,.25);
    margin:1rem 0;
  }
  body > div.navLinks.desktop > div > span.ts-replies::before{
    content: "Replies: ";
  }body > div.navLinks.desktop > div > span.ts-images::before{
    content: "Images: ";
  }
  body > div.navLinks.desktop > div > span.ts-page::before{
    content: "Pages: ";
  }
  .fileThumb img,
  #T4C_viewImages_modal_body .t4c-thumb {
    max-height: 125px!important;
    width: auto !important;
  }
`);