Tenor Multi-User Blocker - Enhanced

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

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==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();
    }
})();