YT Comment Filter

Unicode Blocker, menyembunyikan komentar dan balasan YouTube yang mengandung karakter khusus atau username spam.

// ==UserScript==
// @name         YT Comment Filter
// @namespace    https://www.youtube.com/@MochAdiCH
// @version      2.7
// @description  Unicode Blocker, menyembunyikan komentar dan balasan YouTube yang mengandung karakter khusus atau username spam.
// @author       Mochamad Adi MR (adimuham.mad)
// @match        *://www.youtube.com/watch?v=*
// @match        *://www.youtube.com/shorts/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant        none
// @license      MIT
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // --- Peningkatan 1: Regex Disederhanakan (Versi String) ---
    // Ditulis menggunakan 'new RegExp()' agar aman dari masalah 'line break' di editor
    // Perhatikan bahwa di dalam string, kita harus menggunakan backslash ganda '\\'
    const forbiddenRegex = new RegExp(
        "[^\\u0000-\\u007F\\u00A0-\\u00FF\\u0100-\\u017F\\u0180-\\u024F\\u0250-\\u02AF\\u02B0-\\u02FF\\u0370-\\u03FF\\u0400-\\u04FF\\u0500-\\u052F\\u0530-\\u058F\\u0590-\\u05FF\\u0600-\\u06FF\\u0700-\\u074F\\u0750-\\u077F\\u0780-\\u07BF\\u07C0-\\u07FF\\u0800-\\u083F\\u0840-\\u085F\\u0860-\\u087F\\u08A0-\\u08FF\\u0900-\\u097F\\u0980-\\u09FF\\u0A00-\\u0A7F\\u0A80-\\u0AFF\\u0B00-\\u0B7F\\u0B80-\\u0BFF\\u0C00-\\u0C7F\\u0C80-\\u0CFF\\u0D00-\\u0D7F\\u0D80-\\u0DFF\\u0E00-\\u0E7F\\u0E80-\\u0EFF\\u0F00-\\u0FFF\\u1000-\\u109F\\u10A0-\\u10FF\\u1100-\\u11FF\\u1200-\\u125F\\u1280-\\u12BF\\u13A0-\\u13FF\\u1400-\\u167F\\u1680-\\u169F\\u16A0-\\u16FF\\u1700-\\u171F\\u1720-\\u173F\\u1740-\\u175F\\u1760-\\u177F\\u1780-\\u17FF\\u1800-\\u18AF\\u1900-\\u194F\\u1950-\\u197F\\u1980-\\u19DF\\u19E0-\\u19FF\\u1A00-\\u1A1F\\u1A20-\\u1A5F\\u1A80-\\u1AFF\\u1B00-\\u1B7F\\u1B80-\\u1BBF\\u1BC0-\\u1BFF\\u1C00-\\u1C4F\\u1C50-\\u1C7F\\u1C90-\\u1CBF\\u1CC0-\\u1CCF\\u1CD0-\\u1CFF\\u1D00-\\u1D7F\\u1D80-\\u1DBF\\u1E00-\\u1EFF\\u1F00-\\u1FFF\\u2000-\\u206F\\u2070-\\u20CF\\u20D0-\\u20FF\\u2150-\\u218F\\u2190-\\u21FF\\u2200-\\u22FF\\u2300-\\u23FF\\u2400-\\u243F\\u2440-\\u245F\\u2500-\\u257F\\u2580-\\u259F\\u25A0-\\u25FF\\u2600-\\u26FF\\u2700-\\u27BF\\u27C0-\\u27EF\\u27F0-\\u27FF\\u2800-\\u28FF\\u2900-\\u297F\\u2980-\\u29FF\\u2A00-\\u2AFF\\u2B00-\\u2BFF\\u2C60-\\u2C7F\\u2C80-\\u2CFF\\u2D00-\\u2D2F\\u2D30-\\u2D7F\\u2D80-\\u2DDF\\u2DE0-\\u2DFF\\u2E00-\\u2E7F\\u2E80-\\u02EFF\\u2F00-\\u2FDF\\u2FF0-\\u2FFF\\u3000-\\u303F\\u3040-\\u309F\\u30A0-\\u30FF\\u3100-\\u312F\\u3130-\\u318F\\u3190-\\u319F\\u31A0-\\u31BF\\u31C0-\\u31EF\\u31F0-\\u31FF\\u3200-\\u32FF\\u3300-\\u33FF\\u3400-\\u4DBF\\u4DC0-\\u4DFF\\u4E00-\\u9FFF\\uA000-\\uA48F\\uA490-\\uA4CF\\uA4D0-\\uA4FF\\uA500-\\uA63F\\uA640-\\uA69F\\uA6A0-\\uA6FF\\uA700-\\uA71F\\uA720-\\uA7FF\\uA800-\\uA82F\\uA830-\\uA83F\\uA840-\\uA87F\\uA880-\\uA8DF\\uA8E0-\\uA8FF\\uA900-\\uA92F\\uA930-\\uA95F\\uA960-\\uA97F\\uA980-\\uA9DF\\uA9E0-\\uA9FF\\uAA00-\\uAA3F\\uAA40-\\uAA6F\\uAA70-\\uAAAB\\uAAAC-\\uAAAF\\uAAB0-\\uAABF\\uAAC0-\\uAADF\\uAAE0-\\uAAEF\\uAAF0-\\uAAFF\\uAB00-\\uAB2F\\uAB30-\\uAB6F\\uAB70-\\uABBF\\uABC0-\\uABFF\\uAC00-\\uD7AF\\uD7B0-\\uD7FF\\uF900-\\uFAFF\\uFB00-\\uFB4F\\uFB50-\\uFDFF\\uFE00-\\uFE0F\\uFE10-\\uFE1F\\uFE20-\\uFE2F\\uFE30-\\uFE4F\\uFE50-\\uFE6F\\uFE70-\\uFEFF\\uFF00-\\uFFEF]"
    );

    const forbiddenUsernames = [
        "4d", "vip", "hoki", "toto", "t0t0", "gacor", "maxwin", "pawpaw", "bonus", "persen", "aero", "wede",
        "search", "google", "daftar", "cari", "browser", "ketik", "pertamax7-hd", "l4w4nr3z1m", "pulauwin",
        // Tambahkan kata-kata lain di sini jika diperlukan
    ];

    // --- Peningkatan 2: Regex untuk Username ---
    // 'i' flag untuk case-insensitive, jadi tidak perlu .toLowerCase()
    const forbiddenUsernameRegex = new RegExp(forbiddenUsernames.join('|'), 'i');

    // --- Peningkatan 3 & 4: Fungsi terpisah untuk memfilter satu node ---
    /**
     * Memeriksa satu node komentar (ytd-comment-renderer) dan menyembunyikannya jika spam.
     * @param {Element} commentNode - Elemen <ytd-comment-renderer>
     */
    function filterCommentNode(commentNode) {
        // Cek apakah node ini sudah diproses/disembunyikan (optimasi)
        if (commentNode.style.display === 'none') {
            return;
        }

        // 1. Cek berdasarkan karakter spesial di teks komentar
        const commentText = commentNode.querySelector("#content-text");
        if (commentText && forbiddenRegex.test(commentText.innerText)) {
            commentNode.style.display = 'none';
            // console.log('Comment hidden (Unicode):', commentText.innerText.substring(0, 20)); // Untuk debug
            return; // Hentikan pengecekan jika sudah ketemu
        }

        // 2. Cek berdasarkan kata kunci di username
        const usernameElement = commentNode.querySelector("#author-text span");
        if (usernameElement && forbiddenUsernameRegex.test(usernameElement.innerText)) {
            commentNode.style.display = 'none';
            // console.log('Comment hidden (Username):', usernameElement.innerText); // Untuk debug
            return;
        }
    }

    // --- Peningkatan 4 & 5: Observer yang lebih efisien ---
    const observer = new MutationObserver((mutationsList) => {
        for (const mutation of mutationsList) {
            // Kita hanya peduli jika ada node baru yang ditambahkan
            if (mutation.type !== 'childList') {
                continue;
            }

            for (const node of mutation.addedNodes) {
                // Hanya proses Element node (abaikan text node, dll.)
                if (node.nodeType !== 1) {
                    continue;
                }

                // Cek apakah node yang ditambah itu sendiri adalah renderer
                if (node.matches('ytd-comment-renderer')) {
                    filterCommentNode(node);
                } else {
                    // Jika bukan, cek apakah node itu berisi renderer (misal, saat scroll)
                    // 'ytd-comment-renderer' adalah tag untuk kotak komentar individu (baik utama atau balasan)
                    node.querySelectorAll('ytd-comment-renderer').forEach(filterCommentNode);
                }
            }
        }
    });

    // --- Peningkatan 5: Menjalankan filter awal dan memulai observer ---

    // 1. Jalankan filter untuk komentar yang mungkin sudah ada saat script dimuat
    // Kita gunakan querySelectorAll ke 'ytd-comment-renderer' (target yang sudah diperbaiki)
    document.querySelectorAll('ytd-comment-renderer').forEach(filterCommentNode);

    // 2. Mulai observer untuk memantau node baru (komentar baru, balasan baru, scroll)
    observer.observe(document.body, { childList: true, subtree: true });

    // setTimeout(..., 3000) tidak diperlukan lagi.

})();