☰

Semprot.com Bomber

Hides Semprot replies that lack images or links, keeping the main OP post safe.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램을 먼저 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Semprot.com Bomber
// @namespace    Violentmonkey Scripts
// @version      1.1.0
// @description  Hides Semprot replies that lack images or links, keeping the main OP post safe.
// @license      MIT
// @match        https://www.semprot.com/threads/*
// @grant        none
// @run-at       document-end
// ==/UserScript==

(async function() {
    'use strict';

    const cleanPath = window.location.pathname.replace(/\/page-\d+.*$/, '');
    const storageKey = 'xf_true_op_' + cleanPath;

    function getPostAuthor(post) {
        if (!post) return null;
        const dataAuthor = post.getAttribute('data-author');
        if (dataAuthor) return dataAuthor.trim();

        // Search for username elements, but explicitly ignore any inside quote blocks
        const usernames = post.querySelectorAll('.username, .message-name');
        for (let el of usernames) {
            if (!el.closest('.bbCodeBlock--quote')) {
                return el.textContent.trim();
            }
        }
        return null;
    }

    async function getOrFetchOpAuthor() {
        let opAuthor = sessionStorage.getItem(storageKey);
        if (opAuthor) return opAuthor;

        const posts = document.querySelectorAll('article.message--post');
        if (posts.length > 0 && !window.location.pathname.includes('/page-')) {
            opAuthor = getPostAuthor(posts[0]);
            if (opAuthor) {
                sessionStorage.setItem(storageKey, opAuthor);
                return opAuthor;
            }
        }

        try {
            const res = await fetch(cleanPath);
            const html = await res.text();
            const doc = new DOMParser().parseFromString(html, 'text/html');
            const firstPost = doc.querySelector('article.message--post');
            if (firstPost) {
                opAuthor = getPostAuthor(firstPost);
                if (opAuthor) {
                    sessionStorage.setItem(storageKey, opAuthor);
                    return opAuthor;
                }
            }
        } catch (err) {
            console.error('Could not fetch root OP:', err);
        }

        if (posts.length > 0) {
            opAuthor = getPostAuthor(posts[0]);
            if (opAuthor) sessionStorage.setItem(storageKey, opAuthor);
        }
        return opAuthor;
    }

    function hasMediaOrLink(post) {
        const messageBody = post.querySelector('.message-body, .bbWrapper');
        if (!messageBody) return false;

        // Check for real images/media, ignoring smilies
        const realImages = messageBody.querySelectorAll('img:not(.smilie), .bbImage, video, iframe, .bbMedia, picture');
        if (realImages.length > 0) return true;

        // Check for links, ignoring quotes and user mentions
        const links = messageBody.querySelectorAll('a');
        for (let a of links) {
            if (a.closest('.bbCodeBlock--quote')) continue;
            if (a.classList.contains('username') || a.hasAttribute('data-user-id')) continue;

            const href = a.getAttribute('href');
            if (href && !href.startsWith('#') && !href.startsWith('javascript:')) {
                return true;
            }
        }

        return false;
    }

    async function filterPosts() {
        const opAuthor = await getOrFetchOpAuthor();
        if (!opAuthor) return;

        const posts = document.querySelectorAll('article.message--post');
        const isPageOne = !window.location.pathname.includes('/page-') || window.location.pathname.endsWith('/page-1');

        posts.forEach((post, index) => {
            if (post.dataset.opProcessed) return;
            post.dataset.opProcessed = 'true';

            const postAuthor = getPostAuthor(post);

            // Nuke anyone who isn't the true OP
            if (postAuthor !== opAuthor) {
                post.style.display = 'none';
                return;
            }

            // Always keep page 1 opening post safe
            if (isPageOne && index === 0) {
                return;
            }

            // Nuke OP text-only replies that lack media or links
            if (!hasMediaOrLink(post)) {
                post.style.display = 'none';
            }
        });
    }

    await filterPosts();

    const observer = new MutationObserver(() => {
        filterPosts();
    });
    observer.observe(document.body, { childList: true, subtree: true });
})();