Hides Semprot replies that lack images or links, keeping the main OP post safe.
// ==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 });
})();