Skip to content

Instantly share code, notes, and snippets.

@Kreijstal
Last active June 21, 2025 07:02
Show Gist options
  • Save Kreijstal/3b68d94c54e98a22b733e0fe0c739afd to your computer and use it in GitHub Desktop.
Save Kreijstal/3b68d94c54e98a22b733e0fe0c739afd to your computer and use it in GitHub Desktop.
function get_content(data) {
let content_parts = [];
// This recursive helper function will walk through any nested structure.
function find_strings_recursively(item) {
// Base Case: If the item is a string, process it.
if (typeof item === 'string') {
// This is our filter. We want the content, not the metadata.
// We exclude the "model" identifier and the "thought process" monologues.
if (item !== 'model' && !item.startsWith('**')) {
content_parts.push(item);
}
return;
}
// Recursive Step: If the item is an array, go deeper into each element.
if (Array.isArray(item)) {
for (const element of item) {
find_strings_recursively(element);
}
}
}
// Start the recursive search from the top-level data object.
find_strings_recursively(data);
// Join all the pieces of content that were found.
return content_parts.join('');
}
window.interceptorControl.onXhrResponse = (xhr) => {
try {
const requestHostname = xhr._requestURL;
if (/GenerateContent/.test(requestHostname)) {
const responseData = xhr.responseText ? JSON.parse(xhr.responseText) : 'No response body';
console.log(`%c[XHR Log] ${xhr._requestURL}`, 'color: lightgreen', get_content(responseData));
}
} catch (e) { /* Error parsing URL or JSON */ }
};
// ==UserScript==
// @name Dynamic Network Interceptor (Full Stream Support)
// @namespace http://violentmonkey.net/
// @version 1.1
// @description Intercepts XHR, Fetch, etc., handles both Fetch and XHR streaming, and allows hooks to be changed at runtime.
// @author You
// @match https://aistudio.google.com/*
// @match https://chat.mistral.ai/*
// @match https://www.meta.ai/*
// @match https://chat.deepseek.com/*
// @match https://grok.com/*
// @match https://chat.minimax.io/*
// @grant none
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
if (window.interceptorLoaded) {
console.warn("Interceptor is already loaded. Aborting to prevent duplicates.");
return;
}
window.interceptorLoaded = true;
// ===================================================================
// 1. THE CONTROL OBJECT - This is exposed to the browser console
// ===================================================================
window.interceptorControl = {
// --- Hooks for different event types ---
onXhrResponse: null, // Fires when an XHR request is fully loaded.
onXhrStreamChunk: null, // Fires for each chunk of data received in a streaming XHR.
onFetchResponse: null, // Fires when a Fetch request's headers are received.
onFetchStreamChunk: null, // Fires for each chunk of data received in a streaming Fetch.
onWebSocketEvent: null, // Fires for incoming WebSocket messages ('message', 'close', etc.).
onWebSocketSend: null, // Fires for outgoing WebSocket messages.
onWebRTCEvent: null, // Fires for WebRTC events.
onEventSourceEvent: null, // Fires for Server-Sent Events (SSE).
onBeaconSend: null, // Fires when navigator.sendBeacon is called.
onResourceLoad: null, // Fires for general resource loads (images, scripts).
perfObserver: null,
// --- Helper to enable verbose default logging for all hooks ---
enableDefaultLogging: function() {
console.log("Enabling default logging for all hooks...");
this.onXhrResponse = (xhr) => {
console.log(`[XHR Response] Status: ${xhr.status} URL: ${xhr._requestURL}`);
try { console.log("Body:", JSON.parse(xhr.responseText)); } catch (e) { console.log("Body:", xhr.responseText); }
};
this.onXhrStreamChunk = (chunk, xhr) => {
console.log(`[XHR Stream Chunk] from ${xhr._requestURL}:`, chunk);
};
this.onFetchResponse = (url, response, responseData) => {
console.log(`[Fetch Response] Status: ${response.status} URL: ${url}`);
console.log(responseData);
};
this.onFetchStreamChunk = (chunkText, url, isDone) => {
if (isDone) console.log(`%c[Fetch Stream] Finished for: ${url}`, 'color: green');
else console.log(`[Fetch Stream Chunk] from ${url}:`, chunkText);
};
this.onWebSocketEvent = (event, url) => {
if (event.type === 'message') console.log(`[WebSocket Msg In] from ${url}:`, event.data);
else if (event.type === 'close') console.log(`[WebSocket Closed] Connection to ${url} closed.`);
};
this.onWebSocketSend = (data, url) => {
console.log(`[WebSocket Msg Out] to ${url}:`, data);
};
this.onWebRTCEvent = (pc, eventType, event) => {
if (eventType === 'icecandidate' && event.candidate) console.log(`[WebRTC ICE] Candidate:`, event.candidate.candidate);
else if (eventType === 'connectionstatechange') console.log(`[WebRTC State] Connection state: ${pc.connectionState}`);
};
this.onEventSourceEvent = (event, url) => {
console.log(`[SSE Event] from ${url} (type: ${event.type}):`, event.data || "[No data]");
};
this.onBeaconSend = (url, data) => {
console.log(`[Beacon Sent] To: ${url}`, data);
};
this.onResourceLoad = (resource) => {
if (!resource.name.startsWith('data:')) {
console.log(`[Resource Loaded] Type: ${resource.initiatorType}, URL: ${resource.name}`);
}
};
if (this.perfObserver) this.perfObserver.observe({ type: "resource", buffered: true });
},
// --- Helper to disable all logging hooks (the default state) ---
disable: function() {
this.onXhrResponse = () => {};
this.onXhrStreamChunk = () => {};
this.onFetchResponse = () => {};
this.onFetchStreamChunk = () => {};
this.onWebSocketEvent = () => {};
this.onWebSocketSend = () => {};
this.onWebRTCEvent = () => {};
this.onEventSourceEvent = () => {};
this.onBeaconSend = () => {};
this.onResourceLoad = () => {};
if (this.perfObserver) this.perfObserver.disconnect();
console.log("All interceptor logging hooks have been disabled.");
}
};
// ===================================================================
// 2. THE INTERCEPTION LOGIC
// ===================================================================
// --- Intercepting XMLHttpRequest (with Stream support) ---
const originalXhrOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url) {
this._requestURL = url;
originalXhrOpen.apply(this, arguments);
};
const originalXhrSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function(body) {
this._lastResponseLength = 0; // Initialize for stream tracking
// Listener for streaming chunks
this.addEventListener('progress', () => {
const currentResponseText = this.responseText;
const newChunk = currentResponseText.substring(this._lastResponseLength);
this._lastResponseLength = currentResponseText.length;
if (newChunk) { // Only fire if there's new data
window.interceptorControl.onXhrStreamChunk?.(newChunk, this);
}
});
// Listener for the final, complete response
this.addEventListener('load', () => {
// It's possible a final chunk arrived with the 'load' event.
// Check for it before calling the final response hook.
const finalChunk = this.responseText.substring(this._lastResponseLength);
if (finalChunk) {
window.interceptorControl.onXhrStreamChunk?.(finalChunk, this);
}
window.interceptorControl.onXhrResponse?.(this);
});
originalXhrSend.apply(this, arguments);
};
// --- Intercepting Fetch (with Stream support) ---
const originalFetch = window.fetch;
window.fetch = async function(url, config) {
const response = await originalFetch.apply(this, arguments);
const clonedResponse = response.clone();
if (response.body && typeof response.body.getReader === 'function') {
const reader = clonedResponse.body.getReader();
const decoder = new TextDecoder();
const readStream = async () => {
try {
while (true) {
const { done, value } = await reader.read();
if (done) {
window.interceptorControl.onFetchStreamChunk?.(null, url, true);
break;
}
const chunkText = decoder.decode(value, { stream: true });
window.interceptorControl.onFetchStreamChunk?.(chunkText, url, false);
}
} catch (e) { console.error('[Interceptor] Error reading fetch stream:', e); }
};
readStream();
window.interceptorControl.onFetchResponse?.(url, response, "[Streaming Body... see onFetchStreamChunk or onXhrStreamChunk]");
} else {
let responseData;
try { responseData = await clonedResponse.json(); }
catch (e) { responseData = await clonedResponse.text().catch(() => "[Could not read body]"); }
window.interceptorControl.onFetchResponse?.(url, response, responseData);
}
return response;
};
// --- Intercepting WebSockets ---
const originalWebSocket = window.WebSocket;
window.WebSocket = function(url, protocols) {
const wsInstance = protocols ? new originalWebSocket(url, protocols) : new originalWebSocket(url);
wsInstance.addEventListener('message', (e) => window.interceptorControl.onWebSocketEvent?.(e, url));
wsInstance.addEventListener('close', (e) => window.interceptorControl.onWebSocketEvent?.(e, url));
wsInstance.addEventListener('error', (e) => window.interceptorControl.onWebSocketEvent?.(e, url));
const originalSend = wsInstance.send;
wsInstance.send = function(data) {
window.interceptorControl.onWebSocketSend?.(data, url);
return originalSend.apply(this, arguments);
};
return wsInstance;
};
window.WebSocket.prototype = originalWebSocket.prototype;
// --- Intercepting WebRTC ---
if (window.RTCPeerConnection) {
const originalRTCPeerConnection = window.RTCPeerConnection;
window.RTCPeerConnection = function(config) {
const pc = new originalRTCPeerConnection(config);
const handler = (type, event) => window.interceptorControl.onWebRTCEvent?.(pc, type, event);
pc.addEventListener('icecandidate', (e) => handler('icecandidate', e));
pc.addEventListener('connectionstatechange', (e) => handler('connectionstatechange', e));
return pc;
};
window.RTCPeerConnection.prototype = originalRTCPeerConnection.prototype;
}
// --- Intercepting EventSource (Server-Sent Events) ---
if (window.EventSource) {
const originalEventSource = window.EventSource;
window.EventSource = function(url, config) {
const es = new originalEventSource(url, config);
const handler = (e) => window.interceptorControl.onEventSourceEvent?.(e, url);
es.addEventListener('message', handler);
es.addEventListener('error', handler);
es.addEventListener('open', handler);
return es;
}
window.EventSource.prototype = originalEventSource.prototype;
}
// --- Intercepting navigator.sendBeacon ---
if (navigator.sendBeacon) {
const originalSendBeacon = navigator.sendBeacon.bind(navigator);
navigator.sendBeacon = function(url, data) {
window.interceptorControl.onBeaconSend?.(url, data);
return originalSendBeacon(url, data);
};
}
// --- Intercepting Resource Loads via PerformanceObserver ---
if (window.PerformanceObserver) {
window.interceptorControl.perfObserver = new PerformanceObserver((list) => {
list.getEntries().forEach((resource) => {
window.interceptorControl.onResourceLoad?.(resource);
});
});
}
// ===================================================================
// 3. INITIALIZE
// ===================================================================
window.interceptorControl.disable(); // Start silently
console.log("%cDYNAMIC Interceptor loaded. Hooks are OFF by default.", "color: blue; font-weight: bold;");
console.log("Use `window.interceptorControl.enableDefaultLogging()` to see all network activity.");
console.log("Or set a specific hook, e.g., `window.interceptorControl.onXhrStreamChunk = (chunk, xhr) => console.log(chunk);`");
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment