Last active
June 21, 2025 07:02
-
-
Save Kreijstal/3b68d94c54e98a22b733e0fe0c739afd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 */ } | |
}; | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==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