// ==UserScript==
// @name Desuarchive Word Filter
// @namespace -ACK!
// @version 1.0
// @description Word filter for Desuarchive posts, toggle with Shift+F or button
// @author NiggersTongueMyAnus
// @match https://desuarchive.org/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
const style = document.createElement('style');
style.textContent = `
.filter-toggle-container {
position: fixed;
top: 10px;
right: 10px;
z-index: 10001;
font-family: Arial, sans-serif;
}
.filter-toggle-btn {
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
background: #6c757d;
color: #fff;
border: none;
transition: background 0.2s;
}
.filter-toggle-btn:hover {
background: #5a6268;
}
`;
document.head.appendChild(style);
// Create filter toggle button
const filterContainer = document.createElement('div');
filterContainer.className = 'filter-toggle-container';
const filterBtn = document.createElement('button');
filterBtn.className = 'filter-toggle-btn';
filterBtn.textContent = 'Filter: On';
filterContainer.appendChild(filterBtn);
document.body.appendChild(filterContainer);
// Word filter for posts and their replacement
const targetWords = ['Akemi', 'Ack', '!Akemi', 'ack', 'ACK'];
const replacementPhrases = ['I love you', 'Marry me', 'Steamy night!', 'Have SEX with me', '*kisses you*', 'I KNEEL'];
let filterEnabled = true;
const originalTexts = new Map(); // Store original text node values
function getRandomReplacement() {
return replacementPhrases[Math.floor(Math.random() * replacementPhrases.length)];
}
function filterTextNode(textNode) {
if (!textNode.nodeValue || !textNode.parentNode) return;
if (!filterEnabled) {
// Restore original text if filter is disabled
if (originalTexts.has(textNode)) {
textNode.nodeValue = originalTexts.get(textNode);
}
return;
}
// Store original text if not already stored
if (!originalTexts.has(textNode)) {
originalTexts.set(textNode, textNode.nodeValue);
}
let text = textNode.nodeValue;
let modified = false;
targetWords.forEach((word) => {
// Regex to match whole words/phrases
const regex = new RegExp(`\\b${word.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')}\\b`, 'g');
if (regex.test(text)) {
text = text.replace(regex, getRandomReplacement());
modified = true;
}
});
if (modified) {
textNode.nodeValue = text;
//console.log('Filtered text node:', text);
}
}
function filterPostText(postTextElement) {
if (!postTextElement) return;
function processNode(node) {
// Skip greentext spans with backlinks
if (node.nodeType === Node.ELEMENT_NODE && node.classList.contains('greentext')) {
if (node.querySelector('a.backlink')) {
return; // Skip backlink greentext to prevent breaking format
}
}
if (node.nodeType === Node.TEXT_NODE) {
filterTextNode(node);
} else if (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
node.childNodes.forEach(processNode);
}
}
processNode(postTextElement);
}
// Apply filter to existing posts
function applyFilterToPosts() {
const postWrappers = document.querySelectorAll('.post_wrapper');
postWrappers.forEach((wrapper) => {
const postText = wrapper.querySelector('.text');
if (postText) {
filterPostText(postText);
}
});
//console.log(`Applied filter to ${postWrappers.length} posts`);
}
// Toggle filter
function toggleFilter() {
filterEnabled = !filterEnabled;
filterBtn.textContent = `Filter: ${filterEnabled ? 'On' : 'Off'}`;
//console.log(`Filter toggled to: ${filterEnabled ? 'On' : 'Off'}`);
applyFilterToPosts();
}
// Shift+F to toggle filter
filterBtn.addEventListener('click', toggleFilter);
document.addEventListener('keydown', (e) => {
if (e.shiftKey && e.key.toLowerCase() === 'f') {
e.preventDefault();
toggleFilter();
}
});
// Initial filter application
applyFilterToPosts();
// Observe for new posts to filter
const postObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.addedNodes.length) {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.matches('.post_wrapper')) {
const postText = node.querySelector('.text');
if (postText) {
filterPostText(postText);
}
} else {
const wrappers = node.querySelectorAll('.post_wrapper');
wrappers.forEach((wrapper) => {
const postText = wrapper.querySelector('.text');
if (postText) {
filterPostText(postText);
}
});
}
}
});
}
});
});
postObserver.observe(document.body, {
childList: true,
subtree: true
});
//console.log('Post MutationObserver set up for .post_wrapper');
})();