Tenor Multi-User Blocker - Enhanced

Blocks GIFs from multiple users in Tenor search results with debug mode

// ==UserScript==
// @name         Tenor Multi-User Blocker - Enhanced
// @namespace    http://tampermonkey.net/
// @version      3.0
// @description  Blocks GIFs from multiple users in Tenor search results with debug mode
// @author       Ruben Van den Broeck
// @match        https://tenor.com/*
// @license MIT
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // Configuration
    const DEBUG_MODE = true; // Set to false for production

    // Add usernames separated by commas
    const BLOCKED_USERNAMES = "Blazzord, Lastfridayart, TobiasDavidson";

    // Parse blocked usernames into an array and trim whitespace
    const blockedUsersArray = BLOCKED_USERNAMES.split(',').map(name => name.trim()).filter(name => name.length > 0);

    const API_PATTERNS = [
        /\/gifapi\//,
        /\/v1\//,
        /\/v2\//,
        /\/search\//,
        /\/trending\//,
        /\/categories\//,
        /\/suggestions\//
    ];

    // Logging function
    function debugLog(...args) {
        if (DEBUG_MODE) {
            console.log('[Tenor Blocker]', ...args);
        }
    }

    // Initialize
    debugLog('Blocked users:', blockedUsersArray);

    // Filter function to remove unwanted GIFs
    function filterTenorResponse(data) {
        if (!data || !data.results || !Array.isArray(data.results)) {
            debugLog('Invalid API response structure');
            return data;
        }

        const originalCount = data.results.length;
        const blockedCounts = {};

        data.results = data.results.filter(item => {
            const hasUser = item.user && item.user.username;
            if (!hasUser) return true;

            const isBlocked = blockedUsersArray.includes(item.user.username);

            if (isBlocked) {
                blockedCounts[item.user.username] = (blockedCounts[item.user.username] || 0) + 1;
                debugLog(`Blocked GIF from ${item.user.username}:`, item.id, item.title);
            }

            return !isBlocked;
        });

        const totalBlocked = originalCount - data.results.length;
        if (totalBlocked > 0) {
            debugLog(`Filtered ${totalBlocked} total items:`, blockedCounts);
        }

        return data;
    }

    // Main blocking function
    function startBlocking() {
        debugLog('Starting multi-user blocker...');

        // Intercept Fetch API
        if (window.fetch) {
            const originalFetch = window.fetch;
            window.fetch = async function(...args) {
                const requestUrl = args[0] instanceof Request ? args[0].url : args[0];

                if (API_PATTERNS.some(pattern => pattern.test(requestUrl))) {
                    debugLog('Intercepted fetch request:', requestUrl);

                    const response = await originalFetch.apply(this, args);
                    try {
                        const clonedResponse = response.clone();
                        const json = await clonedResponse.json();
                        const filtered = filterTenorResponse(json);

                        return new Response(JSON.stringify(filtered), {
                            status: response.status,
                            statusText: response.statusText,
                            headers: response.headers
                        });
                    } catch (e) {
                        debugLog('Error processing fetch response:', e);
                        return response;
                    }
                }
                return originalFetch.apply(this, args);
            };
        }

        // Intercept XHR requests
        if (window.XMLHttpRequest) {
            const originalOpen = XMLHttpRequest.prototype.open;
            XMLHttpRequest.prototype.open = function(method, url) {
                this._requestUrl = url;
                return originalOpen.apply(this, arguments);
            };

            const originalSend = XMLHttpRequest.prototype.send;
            XMLHttpRequest.prototype.send = function(body) {
                if (this._requestUrl && API_PATTERNS.some(pattern => pattern.test(this._requestUrl))) {
                    debugLog('Intercepted XHR request:', this._requestUrl);

                    const originalOnload = this.onload;
                    this.onload = function(e) {
                        if (this.responseText) {
                            try {
                                const json = JSON.parse(this.responseText);
                                const filtered = filterTenorResponse(json);
                                Object.defineProperty(this, 'responseText', {
                                    value: JSON.stringify(filtered)
                                });
                            } catch (error) {
                                debugLog('Error processing XHR response:', error);
                            }
                        }
                        if (originalOnload) return originalOnload.call(this, e);
                    };
                }
                return originalSend.apply(this, arguments);
            };
        }

        // Fallback: Clean DOM periodically
        setInterval(() => {
            const gifElements = document.querySelectorAll('div[data-username], div.gif');
            gifElements.forEach(el => {
                const username = el.getAttribute('data-username') ||
                                el.querySelector('.gif-user')?.textContent;

                if (username && blockedUsersArray.some(blocked => username.includes(blocked))) {
                    debugLog('Removing DOM element:', el);
                    el.remove();
                }
            });
        }, 3000);
    }

    // Start the blocking process
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', startBlocking);
    } else {
        startBlocking();
    }
})();