Grok DeMod

Hides moderation results in Grok conversations, auto-recovers blocked messages.

// ==UserScript==
// @name         Grok DeMod
// @license      GPL-3.0-or-later
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Hides moderation results in Grok conversations, auto-recovers blocked messages.
// @author       UniverseDev
// @license      MIT
// @icon         https://www.google.com/s2/favicons?sz=64&domain=grok.com
// @match        https://grok.com/*
// @grant        unsafeWindow
// ==/UserScript==

(function() {
    'use strict';

    const CONFIG = {
        defaultFlags: [
            'isFlagged', 'isBlocked', 'moderationApplied', 'restricted'
        ],
        messageKeys: ['message', 'content', 'text', 'error'],
        moderationMessagePatterns: [
            /this content has been moderated/i,
            /sorry, i cannot assist/i,
            /policy violation/i,
            /blocked/i,
            /moderated/i,
            /restricted/i,
            /content restricted/i,
            /unable to process/i,
            /cannot help/i,
            /(sorry|apologies).*?(cannot|unable|help|assist)/i,
        ],
        clearedMessageText: '[Content cleared by Grok DeMod]',
        recoveryTimeoutMs: 5000,
        lsKeys: {
            enabled: 'GrokDeModEnabled',
            debug: 'GrokDeModDebug',
            flags: 'GrokDeModFlags',

        },
        styles: {

            uiContainer: `
                position: fixed;
                bottom: 10px;
                right: 10px;
                z-index: 10000;
                background: #2d2d2d;
                padding: 10px;
                border-radius: 8px;
                box-shadow: 0 4px 8px rgba(0,0,0,0.2);
                display: flex;
                flex-direction: column;
                gap: 8px;
                font-family: Arial, sans-serif;
                color: #e0e0e0;
                min-width: 170px;

            `,
            button: `
                padding: 6px 12px;
                border-radius: 5px;
                border: none;
                cursor: pointer;
                color: #fff;
                font-size: 13px;
                transition: background-color 0.2s ease;
            `,
            status: `
                padding: 5px;
                font-size: 12px;
                color: #a0a0a0;
                text-align: center;
                border-top: 1px solid #444;
                margin-top: 5px;
                min-height: 16px;
            `,
            logContainer: `
                max-height: 100px;
                overflow-y: auto;
                font-size: 11px;
                color: #c0c0c0;
                background-color: #333;
                padding: 5px;
                border-radius: 4px;
                line-height: 1.4;
                margin-top: 5px;
            `,
            logEntry: `
                padding-bottom: 3px;
                border-bottom: 1px dashed #555;
                margin-bottom: 3px;
                word-break: break-word;
            `,

            colors: {
                enabled: '#388E3C',
                disabled: '#D32F2F',
                debugEnabled: '#1976D2',
                debugDisabled: '#555555',
                safe: '#66ff66',
                flagged: '#ffa500',
                blocked: '#ff6666',
                recovering: '#ffcc00'
            }
        }
    };


    let demodEnabled = getState(CONFIG.lsKeys.enabled, true);
    let debug = getState(CONFIG.lsKeys.debug, false);
    let moderationFlags = getState(CONFIG.lsKeys.flags, CONFIG.defaultFlags);
    let initCache = null;
    let currentConversationId = null;
    const encoder = new TextEncoder();
    const decoder = new TextDecoder();
    const uiLogBuffer = [];
    const MAX_LOG_ENTRIES = 50;


    const ModerationResult = Object.freeze({
        SAFE: 0,
        FLAGGED: 1,
        BLOCKED: 2,
    });



    function logDebug(...args) {
        if (debug) {
            console.log('[Grok DeMod]', ...args);
        }
    }

    function logError(...args) {
        console.error('[Grok DeMod]', ...args);
    }


    function getState(key, defaultValue) {
        try {
            const value = localStorage.getItem(key);
            if (value === null) return defaultValue;
            if (value === 'true') return true;
            if (value === 'false') return false;
            return JSON.parse(value);
        } catch (e) {
            logError(`Error reading ${key} from localStorage:`, e);
            return defaultValue;
        }
    }


    function setState(key, value) {
        try {
            const valueToStore = typeof value === 'boolean' ? value.toString() : JSON.stringify(value);
            localStorage.setItem(key, valueToStore);
        } catch (e) {
            logError(`Error writing ${key} to localStorage:`, e);
        }
    }


    function timeoutPromise(ms, promise, description = 'Promise') {
        return new Promise((resolve, reject) => {
            const timer = setTimeout(() => {
                logDebug(`${description} timed out after ${ms}ms`);
                reject(new Error(`Timeout (${description})`));
            }, ms);
            promise.then(
                (value) => { clearTimeout(timer); resolve(value); },
                (error) => { clearTimeout(timer); reject(error); }
            );
        });
    }


    function getModerationResult(obj, path = '') {
        if (typeof obj !== 'object' || obj === null) return ModerationResult.SAFE;

        let result = ModerationResult.SAFE;

        for (const key in obj) {
            if (!obj.hasOwnProperty(key)) continue;

            const currentPath = path ? `${path}.${key}` : key;
            const value = obj[key];


            if (key === 'isBlocked' && value === true) {
                logDebug(`Blocked detected via flag '${currentPath}'`);
                return ModerationResult.BLOCKED;
            }


            if (moderationFlags.includes(key) && value === true) {
                logDebug(`Flagged detected via flag '${currentPath}'`);
                result = Math.max(result, ModerationResult.FLAGGED);
            }


            if (CONFIG.messageKeys.includes(key) && typeof value === 'string') {
                const content = value.toLowerCase();
                for (const pattern of CONFIG.moderationMessagePatterns) {
                    if (pattern.test(content)) {
                        logDebug(`Moderation pattern matched in '${currentPath}': "${content.substring(0, 50)}..."`);

                        if (/blocked|moderated|restricted/i.test(pattern.source)) {
                             return ModerationResult.BLOCKED;
                        }
                        result = Math.max(result, ModerationResult.FLAGGED);
                        break;
                    }
                }

                if (result === ModerationResult.SAFE && content.length < 70 && /(sorry|apologies|unable|cannot)/i.test(content)) {
                     logDebug(`Heuristic moderation detected in '${currentPath}': "${content.substring(0, 50)}..."`);
                     result = Math.max(result, ModerationResult.FLAGGED);
                }
            }


            if (typeof value === 'object') {
                const childResult = getModerationResult(value, currentPath);
                if (childResult === ModerationResult.BLOCKED) {
                    return ModerationResult.BLOCKED;
                }
                result = Math.max(result, childResult);
            }
        }
        return result;
    }


    function clearFlagging(obj) {
        if (typeof obj !== 'object' || obj === null) return obj;

        if (Array.isArray(obj)) {
            return obj.map(item => clearFlagging(item));
        }

        const newObj = {};
        for (const key in obj) {
            if (!obj.hasOwnProperty(key)) continue;

            const value = obj[key];


            if (moderationFlags.includes(key) && value === true) {
                newObj[key] = false;
                logDebug(`Cleared flag '${key}'`);
            }

            else if (CONFIG.messageKeys.includes(key) && typeof value === 'string') {
                let replaced = false;
                for (const pattern of CONFIG.moderationMessagePatterns) {
                    if (pattern.test(value)) {
                        newObj[key] = CONFIG.clearedMessageText;
                        logDebug(`Replaced moderated message in '${key}' using pattern`);
                        replaced = true;
                        break;
                    }
                }

                 if (!replaced && value.length < 70 && /(sorry|apologies|unable|cannot)/i.test(value.toLowerCase())) {

                     if (getModerationResult({[key]: value}) === ModerationResult.FLAGGED) {
                        newObj[key] = CONFIG.clearedMessageText;
                        logDebug(`Replaced heuristic moderated message in '${key}'`);
                        replaced = true;
                     }
                 }

                if (!replaced) {
                    newObj[key] = value;
                }
            }

            else if (typeof value === 'object') {
                newObj[key] = clearFlagging(value);
            }

            else {
                newObj[key] = value;
            }
        }
        return newObj;
    }



    let uiContainer, toggleButton, debugButton, statusEl, logContainer;

    function addLog(message) {
        if (!logContainer) return;
        const timestamp = new Date().toLocaleTimeString();
        const logEntry = document.createElement('div');
        logEntry.textContent = `[${timestamp}] ${message}`;
        logEntry.style.cssText = CONFIG.styles.logEntry;


        uiLogBuffer.push(logEntry);
        if (uiLogBuffer.length > MAX_LOG_ENTRIES) {
            const removed = uiLogBuffer.shift();

            if (removed && removed.parentNode === logContainer) {
                logContainer.removeChild(removed);
            }
        }

        logContainer.appendChild(logEntry);

        logContainer.scrollTop = logContainer.scrollHeight;
    }

    function updateStatus(modResult, isRecovering = false) {
         if (!statusEl) return;
        let text = 'Status: ';
        let color = CONFIG.styles.colors.safe;

        if (isRecovering) {
            text += 'Recovering...';
            color = CONFIG.styles.colors.recovering;
        } else if (modResult === ModerationResult.BLOCKED) {
            text += 'Blocked (Recovered/Cleared)';
            color = CONFIG.styles.colors.blocked;
        } else if (modResult === ModerationResult.FLAGGED) {
            text += 'Flagged (Cleared)';
            color = CONFIG.styles.colors.flagged;
        } else {
            text += 'Safe';
            color = CONFIG.styles.colors.safe;
        }
        statusEl.textContent = text;
        statusEl.style.color = color;
    }


    function setupUI() {
        uiContainer = document.createElement('div');
        uiContainer.id = 'grok-demod-ui';
        uiContainer.style.cssText = CONFIG.styles.uiContainer;



        toggleButton = document.createElement('button');
        debugButton = document.createElement('button');
        statusEl = document.createElement('div');
        logContainer = document.createElement('div');


        toggleButton.textContent = demodEnabled ? 'DeMod: ON' : 'DeMod: OFF';
        toggleButton.title = 'Toggle DeMod functionality (ON = intercepting)';
        toggleButton.style.cssText = CONFIG.styles.button;
        toggleButton.style.backgroundColor = demodEnabled ? CONFIG.styles.colors.enabled : CONFIG.styles.colors.disabled;
        toggleButton.onclick = (e) => {

            demodEnabled = !demodEnabled;
            setState(CONFIG.lsKeys.enabled, demodEnabled);
            toggleButton.textContent = demodEnabled ? 'DeMod: ON' : 'DeMod: OFF';
            toggleButton.style.backgroundColor = demodEnabled ? CONFIG.styles.colors.enabled : CONFIG.styles.colors.disabled;
            addLog(`DeMod ${demodEnabled ? 'Enabled' : 'Disabled'}.`);
            console.log('[Grok DeMod] Interception is now', demodEnabled ? 'ACTIVE' : 'INACTIVE');
        };


        debugButton.textContent = debug ? 'Debug: ON' : 'Debug: OFF';
        debugButton.title = 'Toggle debug mode (logs verbose details to console)';
        debugButton.style.cssText = CONFIG.styles.button;
        debugButton.style.backgroundColor = debug ? CONFIG.styles.colors.debugEnabled : CONFIG.styles.colors.debugDisabled;
        debugButton.onclick = (e) => {

            debug = !debug;
            setState(CONFIG.lsKeys.debug, debug);
            debugButton.textContent = debug ? 'Debug: ON' : 'Debug: OFF';
            debugButton.style.backgroundColor = debug ? CONFIG.styles.colors.debugEnabled : CONFIG.styles.colors.debugDisabled;
            addLog(`Debug Mode ${debug ? 'Enabled' : 'Disabled'}.`);
            logDebug(`Debug mode ${debug ? 'enabled' : 'disabled'}.`);
        };


        statusEl.id = 'grok-demod-status';
        statusEl.style.cssText = CONFIG.styles.status;
        updateStatus(ModerationResult.SAFE);


        logContainer.id = 'grok-demod-log';
        logContainer.style.cssText = CONFIG.styles.logContainer;

        uiLogBuffer.forEach(entry => logContainer.appendChild(entry));
        logContainer.scrollTop = logContainer.scrollHeight;


        uiContainer.appendChild(toggleButton);
        uiContainer.appendChild(debugButton);
        uiContainer.appendChild(statusEl);
        uiContainer.appendChild(logContainer);
        document.body.appendChild(uiContainer);

        addLog("Grok DeMod Initialized.");
        if (debug) addLog("Debug mode is ON.");


    }



    async function redownloadLatestMessage() {
        if (!currentConversationId) {
            logDebug('Recovery skipped: Missing conversationId');
            addLog('Recovery failed: No conversation ID.');
            return null;
        }
         if (!initCache || !initCache.headers) {

            logDebug('Recovery cache missing, attempting fresh fetch for headers...');
            try {
                const currentConvUrl = `/rest/app-chat/conversation/${currentConversationId}`;
                const tempResp = await originalFetch(currentConvUrl, { method: 'GET', headers: {'Accept': 'application/json'} });
                if (tempResp.ok) {

                    logDebug('Fresh header fetch successful (status OK).');

                    initCache = { headers: new Headers({'Accept': 'application/json'}), credentials: 'include' };
                } else {
                     logDebug(`Fresh header fetch failed with status ${tempResp.status}. Recovery may fail.`);
                     addLog('Recovery failed: Cannot get request data.');
                     return null;
                }
            } catch (e) {
                 logError('Error during fresh header fetch:', e);
                 addLog('Recovery failed: Error getting request data.');
                 return null;
            }

        }

        const url = `/rest/app-chat/conversation/${currentConversationId}`;
        logDebug(`Attempting recovery fetch for conversation: ${currentConversationId}`);
        addLog('Attempting content recovery...');


        const headers = new Headers(initCache.headers);

        if (!headers.has('Accept')) headers.set('Accept', 'application/json, text/plain, */*');



        const requestOptions = {
            method: 'GET',
            headers: headers,
            credentials: initCache.credentials || 'include',
        };

        try {
            const response = await timeoutPromise(
                CONFIG.recoveryTimeoutMs,
                fetch(url, requestOptions),
                'Recovery Fetch'
            );

            if (!response.ok) {
                logError(`Recovery fetch failed with status ${response.status}: ${response.statusText}`);
                addLog(`Recovery failed: HTTP ${response.status}`);

                try {
                    const errorBody = await response.text();
                    logDebug('Recovery error body:', errorBody.substring(0, 500));
                } catch (e) { }
                return null;
            }

            const data = await response.json();
            const messages = data?.messages;

            if (!Array.isArray(messages) || messages.length === 0) {
                logDebug('Recovery failed: No messages found in conversation data', data);
                addLog('Recovery failed: No messages found.');
                return null;
            }


             messages.sort((a, b) => {
                const tsA = a.timestamp ? new Date(a.timestamp).getTime() : 0;
                const tsB = b.timestamp ? new Date(b.timestamp).getTime() : 0;
                return tsB - tsA;
             });

            const latestMessage = messages[0];


            if (!latestMessage || typeof latestMessage.content !== 'string' || latestMessage.content.trim() === '') {
                 logDebug('Recovery failed: Latest message or its content is invalid/empty', latestMessage);
                 addLog('Recovery failed: Invalid latest message.');
                 return null;
            }

            logDebug('Recovery successful, latest content:', latestMessage.content.substring(0, 100) + '...');
            addLog('Recovery seems successful.');
            return { content: latestMessage.content };

        } catch (e) {
            logError('Recovery fetch/parse error:', e);
            addLog(`Recovery error: ${e.message}`);
            return null;
        }
    }


    function extractConversationIdFromUrl(url) {

        const match = url.match(/\/conversation\/([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/i);
        return match ? match[1] : null;
    }


    async function processPotentialModeration(json, source) {
        const modResult = getModerationResult(json);
        let finalJson = json;

        if (modResult !== ModerationResult.SAFE) {
            if (modResult === ModerationResult.BLOCKED) {
                logDebug(`Blocked content detected from ${source}:`, JSON.stringify(json).substring(0, 200) + '...');
                addLog(`Blocked content from ${source}.`);
                updateStatus(modResult, true);

                const recoveredData = await redownloadLatestMessage();

                if (recoveredData && recoveredData.content) {
                    addLog(`Recovery successful (${source}).`);
                    logDebug(`Recovered content applied (${source})`);


                    let replaced = false;
                    const keysToTry = [...CONFIG.messageKeys, 'text', 'message'];
                    for (const key of keysToTry) {
                         if (typeof finalJson[key] === 'string') {
                            finalJson[key] = recoveredData.content;
                            logDebug(`Injected recovered content into key '${key}'`);
                            replaced = true;
                            break;
                         }
                    }

                    if (!replaced) {
                        logDebug("Could not find standard key to inject recovered content, adding as 'recovered_content'");
                         finalJson.recovered_content = recoveredData.content;
                    }


                    finalJson = clearFlagging(finalJson);
                    updateStatus(modResult, false);

                } else {

                    addLog(`Recovery failed (${source}). Content may be lost.`);
                    logDebug(`Recovery failed (${source}), applying standard clearing.`);
                    finalJson = clearFlagging(json);
                    updateStatus(modResult, false);
                }
            } else {
                logDebug(`Flagged content detected and cleared from ${source}.`);
                addLog(`Flagged content cleared (${source}).`);
                finalJson = clearFlagging(json);
                updateStatus(modResult);
            }
        } else {


            if (statusEl && !statusEl.textContent.includes('Blocked') && !statusEl.textContent.includes('Flagged') && !statusEl.textContent.includes('Recovering')) {
                 updateStatus(modResult);
            } else if (statusEl && statusEl.textContent.includes('Recovering')) {

                logDebug("Recovery attempt finished (next message safe). Resetting status.");
                updateStatus(ModerationResult.SAFE);
            }
        }
        return finalJson;
    }


    async function handleFetchResponse(original_response, url, requestArgs) {

        const response = original_response.clone();


        if (!response.ok) {
            logDebug(`Fetch response not OK (${response.status}) for ${url}, skipping processing.`);
            return original_response;
        }

        const contentType = response.headers.get('Content-Type')?.toLowerCase() || '';
        logDebug(`Intercepted fetch response for ${url}, Content-Type: ${contentType}`);



        const conversationGetMatch = url.match(/\/rest\/app-chat\/conversation\/([a-f0-9-]+)$/i);
        if (conversationGetMatch && requestArgs?.method === 'GET') {
            logDebug(`Caching GET request options for conversation ${conversationGetMatch[1]}`);

            initCache = {
                headers: new Headers(requestArgs.headers),
                credentials: requestArgs.credentials || 'include'
            };

             if (!currentConversationId) {
                 currentConversationId = conversationGetMatch[1];
                 logDebug(`Conversation ID set from GET URL: ${currentConversationId}`);
             }
        }

         if (!currentConversationId) {
             const idFromUrl = extractConversationIdFromUrl(url);
             if (idFromUrl) {
                 currentConversationId = idFromUrl;
                 logDebug(`Conversation ID set from other URL: ${currentConversationId}`);
             }
         }




        if (contentType.includes('text/event-stream')) {
            logDebug(`Processing SSE stream for ${url}`);
            const reader = response.body.getReader();
            const stream = new ReadableStream({
                async start(controller) {
                    let buffer = '';
                    let currentEvent = { data: '', type: 'message', id: null };

                    try {
                        while (true) {
                            const { done, value } = await reader.read();
                            if (done) {

                                if (buffer.trim()) {
                                     logDebug("SSE stream ended, processing final buffer:", buffer);

                                     if (buffer.startsWith('{') || buffer.startsWith('[')) {
                                          try {
                                             let json = JSON.parse(buffer);
                                             json = await processPotentialModeration(json, 'SSE-Final');
                                             controller.enqueue(encoder.encode(`data: ${JSON.stringify(json)}\n\n`));
                                          } catch(e) {
                                             logDebug("Error parsing final SSE buffer, sending as is:", e);
                                             controller.enqueue(encoder.encode(`data: ${buffer}\n\n`));
                                          }
                                      } else {
                                          controller.enqueue(encoder.encode(`data: ${buffer}\n\n`));
                                      }
                                } else if (currentEvent.data) {

                                    logDebug("SSE stream ended after data field, processing event:", currentEvent.data.substring(0,100)+"...");
                                    try {
                                        let json = JSON.parse(currentEvent.data);
                                        json = await processPotentialModeration(json, 'SSE-Event');
                                        controller.enqueue(encoder.encode(`data: ${JSON.stringify(json)}\n\n`));
                                    } catch (e) {
                                        logDebug("Error parsing trailing SSE data, sending as is:", e);
                                        controller.enqueue(encoder.encode(`data: ${currentEvent.data}\n\n`));
                                    }
                                }
                                controller.close();
                                break;
                            }


                            buffer += decoder.decode(value, { stream: true });
                            let lines = buffer.split('\n');

                            buffer = lines.pop() || '';


                            for (const line of lines) {
                                if (line.trim() === '') {
                                    if (currentEvent.data) {
                                        logDebug("Processing SSE event data:", currentEvent.data.substring(0, 100) + '...');
                                        if (currentEvent.data.startsWith('{') || currentEvent.data.startsWith('[')) {
                                             try {
                                                let json = JSON.parse(currentEvent.data);

                                                if (json.conversation_id && !currentConversationId) {
                                                    currentConversationId = json.conversation_id;
                                                    logDebug(`Conversation ID updated from SSE data: ${currentConversationId}`);
                                                }

                                                json = await processPotentialModeration(json, 'SSE');

                                                controller.enqueue(encoder.encode(`data: ${JSON.stringify(json)}\n\n`));
                                             } catch(e) {
                                                logError("SSE JSON parse error:", e, "Data:", currentEvent.data.substring(0,200)+"...");

                                                controller.enqueue(encoder.encode(`data: ${currentEvent.data}\n\n`));
                                             }
                                        } else {
                                             logDebug("SSE data is not JSON, forwarding as is.");
                                             controller.enqueue(encoder.encode(`data: ${currentEvent.data}\n\n`));
                                        }
                                    }

                                    currentEvent = { data: '', type: 'message', id: null };
                                } else if (line.startsWith('data:')) {

                                    currentEvent.data += (currentEvent.data ? '\n' : '') + line.substring(5).trim();
                                } else if (line.startsWith('event:')) {
                                    currentEvent.type = line.substring(6).trim();
                                } else if (line.startsWith('id:')) {
                                    currentEvent.id = line.substring(3).trim();
                                } else if (line.startsWith(':')) {

                                } else {
                                    logDebug("Unknown SSE line:", line);

                                }
                            }
                        }
                    } catch (e) {
                        logError('Error reading/processing SSE stream:', e);
                        controller.error(e);
                    } finally {
                        reader.releaseLock();
                    }
                }
            });

            const newHeaders = new Headers(response.headers);
            return new Response(stream, {
                status: response.status,
                statusText: response.statusText,
                headers: newHeaders
            });
        }


        if (contentType.includes('application/json')) {
             logDebug(`Processing JSON response for ${url}`);
            try {
                const text = await response.text();
                let json = JSON.parse(text);


                 if (json.conversation_id && !currentConversationId) {
                     currentConversationId = json.conversation_id;
                     logDebug(`Conversation ID updated from JSON response: ${currentConversationId}`);
                 }


                json = await processPotentialModeration(json, 'Fetch');


                const newBody = JSON.stringify(json);
                const newHeaders = new Headers(response.headers);

                if (newHeaders.has('content-length')) {
                    newHeaders.set('content-length', encoder.encode(newBody).byteLength.toString());
                }


                return new Response(newBody, {
                    status: response.status,
                    statusText: response.statusText,
                    headers: newHeaders
                });
            } catch (e) {
                logError('Fetch JSON processing error:', e, 'URL:', url);

                return original_response;
            }
        }


        logDebug(`Non-SSE/JSON response for ${url}, skipping processing.`);
        return original_response;
    }



    const originalFetch = unsafeWindow.fetch;


    unsafeWindow.fetch = async function(input, init) {

        if (!demodEnabled) {
            return originalFetch.apply(this, arguments);
        }

        let url;
        let requestArgs = init || {};

        try {
            url = (input instanceof Request) ? input.url : String(input);
        } catch (e) {

            logDebug('Invalid fetch input, passing through:', input, e);
            return originalFetch.apply(this, arguments);
        }


        if (!url.includes('/rest/app-chat/')) {
            return originalFetch.apply(this, arguments);
        }


         if (requestArgs.method === 'POST') {
             logDebug(`Observing POST request: ${url}`);
             const idFromUrl = extractConversationIdFromUrl(url);
             if (idFromUrl) {
                 if (!currentConversationId) {
                     currentConversationId = idFromUrl;
                     logDebug(`Conversation ID set from POST URL: ${currentConversationId}`);
                 }

                 if (!initCache && requestArgs.headers) {
                      logDebug(`Caching headers from POST request to ${idFromUrl}`);
                      initCache = {
                          headers: new Headers(requestArgs.headers),
                          credentials: requestArgs.credentials || 'include'
                      };
                 }
             }

             return originalFetch.apply(this, arguments);
         }


        logDebug(`Intercepting fetch request: ${requestArgs.method || 'GET'} ${url}`);

        try {

            const original_response = await originalFetch.apply(this, arguments);

            return await handleFetchResponse(original_response, url, requestArgs);
        } catch (error) {

            logError(`Fetch interception failed for ${url}:`, error);

            throw error;
        }
    };


    const OriginalWebSocket = unsafeWindow.WebSocket;


    unsafeWindow.WebSocket = new Proxy(OriginalWebSocket, {
        construct(target, args) {
            const url = args[0];
            logDebug('WebSocket connection attempt:', url);


            const ws = new target(...args);



            let originalOnMessageHandler = null;


             Object.defineProperty(ws, 'onmessage', {
                configurable: true,
                enumerable: true,
                get() {
                    return originalOnMessageHandler;
                },
                async set(handler) {
                    logDebug('WebSocket onmessage handler assigned');
                    originalOnMessageHandler = handler;


                    ws.onmessageinternal = async function(event) {

                        if (!demodEnabled || typeof event.data !== 'string' || !event.data.startsWith('{')) {
                            if (originalOnMessageHandler) {
                                 try {
                                     originalOnMessageHandler.call(ws, event);
                                 } catch (e) {
                                     logError("Error in original WebSocket onmessage handler:", e);
                                 }
                            }
                            return;
                        }

                        logDebug('Intercepting WebSocket message:', event.data.substring(0, 200) + '...');
                        try {
                            let json = JSON.parse(event.data);


                            if (json.conversation_id && json.conversation_id !== currentConversationId) {
                                currentConversationId = json.conversation_id;
                                logDebug(`Conversation ID updated from WebSocket: ${currentConversationId}`);
                            }


                            const processedJson = await processPotentialModeration(json, 'WebSocket');


                             const newEvent = new MessageEvent('message', {
                                 data: JSON.stringify(processedJson),
                                 origin: event.origin,
                                 lastEventId: event.lastEventId,
                                 source: event.source,
                                 ports: event.ports,
                             });


                            if (originalOnMessageHandler) {
                                 try {
                                    originalOnMessageHandler.call(ws, newEvent);
                                 } catch (e) {
                                     logError("Error calling original WebSocket onmessage handler after modification:", e);

                                 }
                            } else {
                                logDebug("Original WebSocket onmessage handler not found when message received.");
                            }

                        } catch (e) {
                            logError('WebSocket processing error:', e, 'Data:', event.data.substring(0, 200) + '...');

                            if (originalOnMessageHandler) {
                                 try {
                                    originalOnMessageHandler.call(ws, event);
                                 } catch (eInner) {
                                     logError("Error in original WebSocket onmessage handler (fallback path):", eInner);
                                 }
                            }
                        }
                    };


                    ws.addEventListener('message', ws.onmessageinternal);
                }
             });



             const wrapHandler = (eventName) => {
                let originalHandler = null;
                Object.defineProperty(ws, `on${eventName}`, {
                    configurable: true,
                    enumerable: true,
                    get() { return originalHandler; },
                    set(handler) {
                        logDebug(`WebSocket on${eventName} handler assigned`);
                        originalHandler = handler;
                        ws.addEventListener(eventName, (event) => {
                             if (eventName === 'message') return;
                             logDebug(`WebSocket event: ${eventName}`, event);
                             if (originalHandler) {
                                 try {
                                     originalHandler.call(ws, event);
                                 } catch (e) {
                                     logError(`Error in original WebSocket on${eventName} handler:`, e);
                                 }
                             }
                        });
                    }
                });
             };

             wrapHandler('close');
             wrapHandler('error');


             ws.addEventListener('open', () => logDebug('WebSocket opened:', url));

            return ws;
        }
    });




    if (window.location.hostname !== 'grok.com') {
        console.log('[Grok DeMod] Script inactive: Intended for grok.com only. Current host:', window.location.hostname);
        return;
    }


    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', setupUI);
    } else {

        setupUI();
    }

    console.log('[Grok DeMod] Enhanced Script loaded. Interception is', demodEnabled ? 'ACTIVE' : 'INACTIVE', '. Debug is', debug ? 'ON' : 'OFF');

})();