Chat Spam Filter For fishtank.live

Chat Spam Filter

// ==UserScript==
// @name         Chat Spam Filter For fishtank.live
// @namespace    http://tampermonkey.net/
// @version      2.11
// @description  Chat Spam Filter
// @author       Blungs
// @match        https://*.fishtank.live/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=fishtank.live
// @license MIT
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Configuration
    const CONSECUTIVE_LIMIT = 4; // Maximum allowed consecutive identical characters

    // Function to remove repeated phrases (same words)
    function removeRepeatedText(text) {
        const words = text.split(' ');
        const seen = {};
        const result = [];

        words.forEach(word => {
            const lowerWord = word.toLowerCase();
            if (!seen[lowerWord]) {
                seen[lowerWord] = true;
                result.push(word);
            }
        });

        return result.join(' ');
    }

    // Function to filter out spammed consecutive letters (above a configurable limit)
    function filterConsecutiveLetters(text) {
        const regex = /(\w)\1{2,}/g; // Match any letter repeated 3 or more times
        return text.replace(regex, (match, char) => char.repeat(CONSECUTIVE_LIMIT)); // Only keep the first 4 repeated characters
    }

    // Function to collapse repeated words with spaces in between
    function filterRepeatedMessages(text) {
        const regex = /(\b\w+\b)(?=\s+\1)/g; // Match consecutive repeated words with spaces in between
        return text.replace(regex, '$1'); // Collapse repeated words into a single occurrence
    }

    // Function to collapse repeated messages without spaces (identical substrings repeated)
    function filterRepeatedMessagesWithoutSpaces(text) {
        let result = text;
        const len = text.length;

        // Check for repeating patterns of substrings (repeated identical parts)
        for (let i = 1; i <= Math.floor(len / 2); i++) {
            const substring = text.slice(0, i); // Take a substring of length i
            const repetitions = Math.floor(len / i); // Determine how many times it repeats

            // Check if the substring repeated `repetitions` times equals the original string
            const repeatedPattern = substring.repeat(repetitions);

            if (repeatedPattern === text) {
                result = substring; // If the entire string is made of repeated substrings, collapse it
                break; // Stop once we find the first repeating pattern
            }
        }

        return result;
    }

    // Function to process and modify chat messages
    function processChatMessage(messageSpan) {
        if (messageSpan && messageSpan.tagName === 'SPAN') {
            const originalText = messageSpan.textContent.trim();
            let filteredText = removeRepeatedText(originalText);
            filteredText = filterConsecutiveLetters(filteredText);
            filteredText = filterRepeatedMessages(filteredText);
            filteredText = filterRepeatedMessagesWithoutSpaces(filteredText);

            // Update the text content only if it was modified
            if (filteredText !== originalText) {
                messageSpan.textContent = filteredText;
            }
        }
    }

    // Function to start observing the chat messages container
    function startObserving() {
        const chatMessagesDiv = document.getElementById('chat-messages');
        if (chatMessagesDiv) {
            const observer = new MutationObserver((mutations) => {
                mutations.forEach((mutation) => {
                    mutation.addedNodes.forEach((node) => {
                        if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'SPAN') {
                            processChatMessage(node); // Process new chat message
                        } else if (node.nodeType === Node.ELEMENT_NODE) {
                            // Check for spans within added elements
                            const spans = node.querySelectorAll('span');
                            spans.forEach(span => {
                                processChatMessage(span);
                            });
                        }
                    });
                });
            });

            observer.observe(chatMessagesDiv, { childList: true, subtree: true });
        } else {
            setTimeout(startObserving, 2000); // Retry every 2 seconds if chat-messages div isn't found
        }
    }

    // Start the observation process
    startObserving();
})();