Загрузка данных


// ==UserScript==
// @name         Двач: Фильтрация сообщений внутри тредов
// @namespace    http://tampermonkey.net
// @version      3.5
// @description  Скрывает отдельные ответы внутри тредов по ключевым словам. Не трогает общие ленты досок.
// @author       You
// @match        *://*.2ch.org/*
// @match        *://*.2ch.hk/*
// @match        *://*.m2ch.hk/*
// @match        *://*.2ch.life/*
// @match        *://*.2ch.su/*
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';
    function isInsideThread() {
        const url = window.location.href;
        return url.includes('/res/') || url.includes('.html');
    }

    const botShit = ['тарабс', 'какел', 'хохол',' хохлы', 'укро', 'тцк',
        'микол', 'мыкол', 'мивин', 'перемог', 'ципсо'
    ];

    function processReply(replyElement) {
        if (replyElement.dataset.checkedByFilter) return;

        replyElement.dataset.checkedByFilter = "true";
        const messageArticle = replyElement.querySelector('article.post__message');

        if (!messageArticle) return;

        let replyText = messageArticle.textContent.toLowerCase();

        const triggeredWord = botShit.find(word => replyText.includes(word.toLowerCase().trim()));

        if (triggeredWord) {
            const placeholder = document.createElement('div');
            placeholder.className = 'lahta-placeholder';
            placeholder.style.border = '1px dashed #777777'; // Рамка изменена на нейтральный темно-серый цвет
            placeholder.style.padding = '10px 14px';
            placeholder.style.margin = '4px 0 4px 20px'; 
            placeholder.style.borderRadius = '4px';
            placeholder.style.background = 'rgba(128, 128, 128, 0.08)'; // Фон изменен на полупрозрачный пастельно-серый
            placeholder.style.display = 'inline-block'; 
            placeholder.style.clear = 'both';

            // Установлен насыщенный коралловый цвет текста (#ff6f61) для идеального контраста на сером фоне
            placeholder.innerHTML = `<span style="color: #ff6f61; font-weight: bold; text-decoration: none !important;">УДАЛЕНА ЛАХТИНСКАЯ ВОШЬ: [ ${triggeredWord.trim()} ]</span>`;

            replyElement.parentNode.insertBefore(placeholder, replyElement);
            replyElement.style.setProperty('display', 'none', 'important');
        }
    }

    function scan() {
        if (!isInsideThread()) return;
        document.querySelectorAll('div.post.post_type_reply').forEach(processReply);
    }

    scan();

    new MutationObserver(scan).observe(document.body, { childList: true, subtree: true });
})();


--------------------------------------------------------------

// ==UserScript==
// @name         Большая кнопка Двача в конце блока
// @namespace    http://tampermonkey.net
// @version      2.5
// @description  Перенос кнопки скрытия с жирным черным крестом в самый конец блока post__details без текста
// @match        *://*.2ch.org/*
// @match        *://*.2ch.hk/*
// @match        *://*.m2ch.hk/*
// @match        *://*.2ch.life/*
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // 1. Внедряем CSS-стили для кнопки
    const style = document.createElement('style');
    style.textContent = `
        /* Настройка внешнего контейнера SVG кнопки */
        svg.post__btn.post__btn_type_hide.icon.desktop.js-processed-btn {
            display: inline-block !important;
            width: 50px !important;
            height: 50px !important;
            box-sizing: border-box !important;
            margin-left: 10px !important;
            border-radius: 8px !important;

            /* Базовое состояние: приглушенный сине-лиловый пиксельный стиль */
            filter: invert(40%) sepia(20%) saturate(1200%) hue-rotate(220deg) !important;
            image-rendering: pixelated !important;
            image-rendering: crisp-edges !important;
            
            /* Плавный переход для анимации при наведении (0.2 секунды) */
            transition: filter 0.2s ease-in-out, transform 0.2s ease-in-out !important;
        }

        /* ЭФФЕКТ ПРИ НАВЕДЕНИИ (HOVER) */
        /* Исходное состояние кнопки — добавляем быстрое время отклика */
        svg.post__btn.post__btn_type_hide.icon.desktop.js-processed-btn {
            transition: filter 0.15s ease-in-out, transform 0.15s ease-in-out !important;
        }

        /* Состояние при наведении */
        svg.post__btn.post__btn_type_hide.icon.desktop.js-processed-btn:hover {
            /* Смещаем спектр (hue-rotate) в сторону теплого пастельно-розового оттенка 
               и немного увеличиваем яркость (brightness) */
            filter: invert(40%) sepia(20%) saturate(1500%) hue-rotate(310deg) brightness(1.1) !important;
            
            /* Кнопка слегка и аккуратно увеличивается при наведении */
            transform: scale(1.01) !important; 
        }

        /* Настройки пикселизации для внутренних элементов */
        svg.post__btn.post__btn_type_hide.icon.desktop.js-processed-btn * {
            image-rendering: pixelated !important;
            image-rendering: crisp-edges !important;
        }
    `;

    document.head.appendChild(style);

    // 2. Функция поиска и перемещения кнопок
    function modifyButtons() {
        // Ищем SVG-кнопки скрытия, которые мы еще не обрабатывали
        let svgs = document.querySelectorAll('svg.post__btn.post__btn_type_hide.icon.desktop:not(.js-processed-btn)');

        svgs.forEach((svg) => {
            let parentDiv = svg.closest('.post__details');

            if (parentDiv) {
                // Помечаем SVG классом-маркером (CSS применится автоматически)
                svg.classList.add('js-processed-btn');

                // Переносим саму оригинальную SVG кнопку в самый конец родительского div
                // Браузер автоматически заберет её со старого места и вставит в конец
                parentDiv.appendChild(svg);
            }
        });
    }

    modifyButtons();

    // 3. Отслеживание динамической подгрузки постов в треде
    const observer = new MutationObserver(() => {
        if (document.querySelector('svg.post__btn.post__btn_type_hide.icon.desktop:not(.js-processed-btn)')) {
            modifyButtons();
        }
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });
})();


------------------------------------------------------

// ==UserScript==
// @name         Двач: Анти-Лахта Мод (Умная фильтрация)
// @namespace    http://tampermonkey.net
// @version      2.5
// @description  Скрывает бототреды только в общих лентах досок и никогда не трогает контент внутри самих тредов.
// @author       You
// @match        *://*.2ch.org/*
// @match        *://*.2ch.hk/*
// @match        *://*.m2ch.hk/*
// @match        *://*.2ch.life/*
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // Функция проверки: находимся ли мы внутри треда?
    function isInsideThread() {
        const url = window.location.href;
        // Если в URL есть "/res/" или имя файла заканчивается на ".html" (переход внутрь треда)
        // то скрипт не должен работать
        return url.includes('/res/') || url.includes('.html');
    }

    // Массив ключевых слов
    const botShit = [
        'webm', 'тяноч', 'отношен', 'секс', 'танцуль', 'Воскресной молитвы',
        'фурр', 'почему так', 'девушк', 'лесополос', 'ZASMOBOS', 'Это гуар.',
        'фапочно', 'покажи', 'цуиь', 'аниме', 'mp4', 'banana', 'понитре',
        'анимэ', 'дружботр', 'вацфу', 'вайф', 'тредю', 'зумер', 'тянка', 'тянко',
        'тянок', 'септемба', 'котосуббота', '#пкм', 'пустите в', 'fap', 'РИСОВАЧ',
        'рисовач тред', 'Храмом ГОЛОВЫ', 'храам голов', 'warcraft', 'war c',
        'Главный новостной', 'ШЛИК', 'fap', 'Оцени член', 'оцени', 'писяндрик'
    ];

    // Функция обработки ОП-поста
    function processPost(postElement) {
        // ЕСЛИ МЫ ВНУТРИ ТРЕДА — КАТЕГОРИЧЕСКИ НИЧЕГО НЕ СКРЫВАЕМ
        if (isInsideThread()) return;

        // Защита от повторной обработки одного и того же элемента
        if (postElement.dataset.checkedByLahta) return;
        postElement.dataset.checkedByLahta = "true";

        // Извлекаем текст
        let threadText = postElement.textContent.replace('(OP)', '').toLowerCase();
        threadText = threadText.replace(/\>+(\d+)>/g, '').replace(/\>+(\d+)/g, '');
        threadText = threadText.replace(/\s+/g, ' ').trim();

        // Ищем совпадение
        const triggeredWord = botShit.find(word => {
            const cleanWord = word.trim().toLowerCase();
            return cleanWord !== '' && threadText.includes(cleanWord);
        });

        if (triggeredWord) {
            // Находим контейнер всего треда на доске
            const threadContainer = postElement.closest('.thread') || postElement.closest('[id^="thread-"]') || postElement.closest('.post-wrapper');

            if (threadContainer) {
                // Создаем плашку-заглушку
                const placeholder = document.createElement('div');
                placeholder.className = 'lahta-placeholder';
                placeholder.style.border = '1px dashed #555555';
                placeholder.style.padding = '10px 14px';
                placeholder.style.margin = '12px 0';
                placeholder.style.borderRadius = '4px';
                placeholder.style.background = 'rgba(221, 161, 172, 0.05)';
                placeholder.style.display = 'block';
                placeholder.style.clear = 'both';
                placeholder.innerHTML = `<span style="color: #dda1ac; font-weight: bold; text-decoration: none !important;">УДАЛЕНА БОТОХУЕТА [ ${triggeredWord.trim()} ]</span>`;

                // Вставляем плашку перед скрываемым тредом
                threadContainer.parentNode.insertBefore(placeholder, threadContainer);

                // Скрываем оригинальный тред на главной странице доски
                threadContainer.style.setProperty('display', 'none', 'important');
            }
        }
    }

    // Запуск сканирования
    function scan() {
        if (isInsideThread()) return;

        const opPosts = document.querySelectorAll('.post_type_oppost, .oppost, .post.post_type_oppost');
        opPosts.forEach(post => processPost(post));
    }

    // Первый запуск при инициализации страницы
    scan();

    // Наблюдатель за динамической подгрузкой новых постов/тредов
    const observer = new MutationObserver((mutations) => {
        // Если пользователь перешел внутрь треда (пусть даже по AJAX без перезагрузки), прекращаем обработку
        if (isInsideThread()) return;

        let shouldScan = false;
        for (let mutation of mutations) {
            if (mutation.addedNodes.length > 0) {
                shouldScan = true;
                break;
            }
        }
        if (shouldScan) {
            scan();
        }
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });
})();