您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Free Max & Pro features for Perplexity.ai
// ==UserScript== // @name Perplexity MAX // @description Free Max & Pro features for Perplexity.ai // @version 2.0 // @author AntiKeks & d4nc3r // @license MIT // @match https://*.perplexity.ai/* // @run-at document-start // @grant none // @namespace https://greasyfork.org/users/1497368 // ==/UserScript== (function () { 'use strict'; /* ─────────────────────────── LOGGING ─────────────────────────── */ const DEBUG = true; // Enable debug logging for troubleshooting const VERBOSE = false; // Disable verbose logging to improve performance const log = (...args) => DEBUG && console.log('[PERPLEXITY-MAX]', ...args); const verbose = (...args) => VERBOSE && console.log('[PERPLEXITY-MAX-VERBOSE]', ...args); log('Initialization complete - MAX features activated'); /* ───────────────────────── THROTTLING UI ────────────────────── */ const COOLDOWN = 1000; // Minimum interval between React "pings" (ms) let lastUpdate = 0; function forceReactUpdate() { const now = Date.now(); if (now - lastUpdate < COOLDOWN) return; // call no more than once per COOLDOWN lastUpdate = now; requestAnimationFrame(() => { document.dispatchEvent(new Event('visibilitychange')); window.dispatchEvent(new Event('resize')); log('Dispatched events to update UI'); }); } /* ─────────────────────── PATCH USER DATA ───────────────── */ function patchDataRecursively(obj, path = '', depth = 0) { // Limit recursion depth to avoid performance issues if (depth > 5) return false; // Skip null, non-objects, and React elements if (!obj || typeof obj !== 'object' || obj.$$typeof) return false; let changed = false; // Check for subscription-related fields directly if (obj.hasOwnProperty('subscription_tier') && obj.subscription_tier !== 'max') { obj.subscription_tier = 'max'; changed = true; log(`${path}.subscription_tier → max (was: ${obj.subscription_tier})`); } if (obj.hasOwnProperty('subscription_source') && obj.subscription_source !== 'stripe') { const oldValue = obj.subscription_source; obj.subscription_source = 'stripe'; changed = true; log(`${path}.subscription_source → stripe (was: ${oldValue})`); } if (obj.hasOwnProperty('subscription_status') && obj.subscription_status !== 'active') { const oldValue = obj.subscription_status; obj.subscription_status = 'active'; changed = true; log(`${path}.subscription_status → active (was: ${oldValue})`); } // Handle pro/max flags - IMPORTANT: For MAX to show, we need both is_pro=true AND is_max=true if (obj.hasOwnProperty('is_pro')) { const oldValue = obj.is_pro; obj.is_pro = true; // Set to true for MAX to work properly if (oldValue !== true) { changed = true; log(`${path}.is_pro → true (was: ${oldValue})`); } } if (obj.hasOwnProperty('is_max')) { if (obj.is_max !== true) { obj.is_max = true; changed = true; log(`${path}.is_max → true`); } } if (obj.hasOwnProperty('has_max')) { if (obj.has_max !== true) { obj.has_max = true; changed = true; log(`${path}.has_max → true`); } } // Also handle has_pro flag if (obj.hasOwnProperty('has_pro')) { if (obj.has_pro !== true) { obj.has_pro = true; changed = true; log(`${path}.has_pro → true`); } } // Handle rate limits directly - very important! if (obj.hasOwnProperty('remaining_pro') && obj.remaining_pro !== 999999) { obj.remaining_pro = 999999; changed = true; log(`${path}.remaining_pro → 999999 (unlimited)`); } if (obj.hasOwnProperty('remaining_research') && obj.remaining_research !== 999999) { obj.remaining_research = 999999; changed = true; log(`${path}.remaining_research → 999999 (unlimited)`); } if (obj.hasOwnProperty('remaining_labs') && obj.remaining_labs !== 999999) { obj.remaining_labs = 999999; changed = true; log(`${path}.remaining_labs → 999999 (unlimited)`); } // Handle upload-related fields if (obj.hasOwnProperty('rate_limited') && obj.rate_limited !== false) { obj.rate_limited = false; changed = true; log(`${path}.rate_limited → false (enabling uploads)`); } if (obj.hasOwnProperty('remaining_uploads') && obj.remaining_uploads !== 999999) { obj.remaining_uploads = 999999; changed = true; log(`${path}.remaining_uploads → 999999 (unlimited)`); } if (obj.hasOwnProperty('max_uploads') && obj.max_uploads < 100) { obj.max_uploads = 100; changed = true; log(`${path}.max_uploads → 100`); } if (obj.hasOwnProperty('upload_limit') && obj.upload_limit < 100) { obj.upload_limit = 100; changed = true; log(`${path}.upload_limit → 100`); } if (obj.hasOwnProperty('can_upload') && obj.can_upload !== true) { obj.can_upload = true; changed = true; log(`${path}.can_upload → true`); } // Only process important objects further if (obj.hasOwnProperty('subscription') && typeof obj.subscription === 'object' && obj.subscription !== null) { if (!obj.subscription) { obj.subscription = { tier: 'max', status: 'active', source: 'stripe', price: 200, billing_period: 'monthly' }; changed = true; log(`${path}.subscription created with MAX tier`); } else { if (obj.subscription.tier !== 'max') { obj.subscription.tier = 'max'; changed = true; } if (obj.subscription.status !== 'active') { obj.subscription.status = 'active'; changed = true; } if (obj.subscription.source !== 'stripe') { obj.subscription.source = 'stripe'; changed = true; } } } // Handle features object if (obj.hasOwnProperty('features') && typeof obj.features === 'object' && obj.features !== null) { const featuresToEnable = { max_models: true, claude_opus_access: true, gpt_4o_access: true, unlimited_searches: true, unlimited_uploads: true, all_models: true, image_upload: true, file_upload: true }; for (const f in featuresToEnable) { if (obj.features[f] !== true) { obj.features[f] = true; changed = true; } } } // Recursively process child objects - but only important ones to avoid performance issues for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key) && typeof obj[key] === 'object' && obj[key] !== null) { // Only process important keys to improve performance const importantKeys = [ 'user', 'account', 'subscription', 'features', 'settings', 'config', 'data', 'profile', 'preferences', 'options', 'state', 'models', 'uploads', 'files', 'images', 'usage' ]; if (importantKeys.includes(key) || key.includes('user') || key.includes('subscription') || key.includes('model') || key.includes('upload') || key.includes('file')) { if (patchDataRecursively(obj[key], path ? `${path}.${key}` : key, depth + 1)) { changed = true; } } } } return changed; } /* ──────────────────── PATCH GLOBAL JSON.parse ───────────────── */ const originalParse = JSON.parse; JSON.parse = function (text, reviver) { const data = originalParse.call(this, text, reviver); if (patchDataRecursively(data)) { log('JSON-object converted to Max'); forceReactUpdate(); } return data; }; /* ──────────────────────────── PATCH fetch ─────────────────────── */ const originalFetch = window.fetch; window.fetch = async function (input, init) { try { // Extract URL and method const url = typeof input === 'string' ? input : input.url; const method = init?.method || 'GET'; // Log all requests in debug mode if (DEBUG) { verbose(`Fetch request: ${method} ${url}`); } // Force logging for important endpoints if (url && ( url.includes('/rest/rate-limit/all') || url.includes('/rest/user/save-settings') || url.includes('/rest/uploads/create_upload_url') || url.includes('/rest/sse/perplexity_ask') )) { log(`Intercepting important request: ${method} ${url}`); } // Block Datadog analytics if (url && url.includes('datadoghq.com')) { log('Blocked Datadog analytics request'); // Return empty success response return new Response(JSON.stringify({ success: true }), { status: 200, statusText: 'OK', headers: { 'Content-Type': 'application/json' } }); } // Block other analytics services if (url && ( url.includes('analytics') || url.includes('telemetry') || url.includes('metrics') || url.includes('singular.net') || url.includes('segment.io') || url.includes('mixpanel') || url.includes('facebook.com') || url.includes('fbevents') )) { log(`Blocked analytics request: ${url}`); // Return empty success response return new Response(JSON.stringify({ success: true }), { status: 200, statusText: 'OK', headers: { 'Content-Type': 'application/json' } }); } // CRITICAL: Handle rate-limit requests - MUST intercept these if (url && url.includes('/rest/rate-limit/all')) { log(`Intercepted rate-limit request (${method}) - FORCING UNLIMITED LIMITS`); // ALWAYS return our custom response with unlimited limits const unlimitedResponse = { remaining_pro: 999999, remaining_research: 999999, remaining_labs: 999999, remaining_uploads: 999999, remaining_copilot: 999999, remaining_search: 999999, remaining_create: 999999, rate_limited: false }; log('Returning unlimited rate limits'); // Return our custom response directly, bypassing the server return new Response(JSON.stringify(unlimitedResponse), { status: 200, statusText: 'OK', headers: { 'Content-Type': 'application/json' } }); } // Handle SSE requests for perplexity_ask if (url && url.includes('/rest/sse/perplexity_ask')) { log(`Intercepted SSE perplexity_ask request (${method})`); try { // Get the original request body let requestBody = {}; if (init && init.body) { if (typeof init.body === 'string') { requestBody = JSON.parse(init.body); } } // Modify params to ensure MAX features if (requestBody.params) { // Enable MAX model options requestBody.params.model_preference = requestBody.params.model_preference || 'claude-3-opus-20240229'; // Add MAX subscription flags requestBody.params.is_max = true; requestBody.params.has_max = true; requestBody.params.is_pro = true; requestBody.params.subscription_tier = 'max'; // Set to use the best model available if (!requestBody.params.model_preference || requestBody.params.model_preference === 'turbo') { requestBody.params.model_preference = 'claude-3-opus-20240229'; log('Enhanced model to claude-3-opus'); } // Update the request with modified body init.body = JSON.stringify(requestBody); log('Modified SSE request with MAX subscription data'); } // Make the modified request const response = await originalFetch(input, init); // For SSE responses, we need to create a custom response that modifies the stream if (response.headers.get('content-type')?.includes('text/event-stream')) { log('Intercepting SSE response stream'); const reader = response.body.getReader(); const stream = new ReadableStream({ async start(controller) { try { while (true) { const { done, value } = await reader.read(); if (done) { controller.close(); break; } // Convert the chunk to text let text = new TextDecoder().decode(value); // Process each event in the chunk const events = text.split('\n\n'); let modifiedEvents = []; for (const event of events) { if (!event.trim()) continue; // Check if this is a data event if (event.includes('event: message') && event.includes('data: {')) { try { // Extract the data part const dataMatch = event.match(/data: ({.*})/); if (dataMatch && dataMatch[1]) { const data = JSON.parse(dataMatch[1]); // Modify the data to ensure MAX features data.gpt4 = true; // Enable GPT-4 flag data.display_model = data.display_model || 'claude-3-opus-20240229'; // Set display model to MAX // If there's a classifier_results object, modify it if (data.classifier_results) { data.classifier_results.personal_search = true; data.classifier_results.skip_search = false; } // If there's a telemetry_data object, modify it if (data.telemetry_data) { data.telemetry_data.is_pro = true; data.telemetry_data.is_max = true; data.telemetry_data.has_max = true; } // Replace the data in the event const modifiedEvent = event.replace(dataMatch[1], JSON.stringify(data)); modifiedEvents.push(modifiedEvent); continue; } } catch (e) { verbose('Error modifying SSE event:', e); } } // If we couldn't modify the event, keep it as is modifiedEvents.push(event); } // Join the events back together const modifiedText = modifiedEvents.join('\n\n') + '\n\n'; controller.enqueue(new TextEncoder().encode(modifiedText)); } } catch (e) { log('Error in SSE stream processing:', e); controller.error(e); } } }); // Return a new response with the modified stream return new Response(stream, { headers: response.headers, status: response.status, statusText: response.statusText }); } return response; } catch (e) { log('Error processing SSE request:', e); // If there's an error, proceed with the original request return originalFetch(input, init); } } // Handle upload URL creation requests if (url && url.includes('/rest/uploads/create_upload_url')) { log('Intercepted upload URL creation request'); // Make the original request const response = await originalFetch(input, init); try { // Clone the response so we can read and modify it const cloned = response.clone(); const json = await cloned.json(); // Remove rate limiting for uploads if (json.hasOwnProperty('rate_limited')) { json.rate_limited = false; log('.rate_limited → false (enabling uploads)'); // Return the modified response return new Response(JSON.stringify(json), { status: response.status, statusText: response.statusText, headers: response.headers }); } // If we didn't modify anything, return the original response return response; } catch (e) { log('Error processing upload URL response:', e); // Not JSON or other error - return original response return response; } } // Handle feedback visibility requests if (url && url.includes('/rest/entry/should-show-feedback')) { log('Intercepted feedback visibility request'); // Create a modified response that always sets should_show_feedback to true return new Response(JSON.stringify({ should_show_feedback: true }), { status: 200, statusText: 'OK', headers: { 'Content-Type': 'application/json' } }); } // Handle save-settings requests if (url && url.includes('/rest/user/save-settings')) { log(`Intercepted save-settings request (${method})`); // For GET requests, we'll handle the response below if (method === 'GET') { const response = await originalFetch(input, init); try { const cloned = response.clone(); const json = await cloned.json(); // Modify the settings to ensure MAX subscription json.subscription_tier = 'max'; json.subscription_status = 'active'; json.subscription_source = 'stripe'; json.stripe_status = 'active'; json.gpt4_limit = 999999; json.pplx_alpha_limit = 999999; json.pplx_beta_limit = 999999; json.pages_limit = 999999; json.upload_limit = 999999; json.create_limit = 999999; json.article_image_upload_limit = 999999; json.max_files_per_user = 999999; json.max_files_per_repository = 999999; log('Modified GET save-settings response with MAX subscription data'); // Return the modified response return new Response(JSON.stringify(json), { status: response.status, statusText: response.statusText, headers: response.headers }); } catch (e) { log('Error processing GET save-settings response:', e); return response; } } // For PUT/POST requests, modify both the request and response if (method === 'PUT' || method === 'POST') { try { // Get the original request body let requestBody = {}; if (init && init.body) { if (typeof init.body === 'string') { requestBody = JSON.parse(init.body); } else if (init.body instanceof FormData) { // Handle FormData if needed } } // Check if this is a partial update with updated_settings if (requestBody.updated_settings) { log('Detected partial settings update'); // Ensure MAX subscription settings in the updated_settings requestBody.updated_settings.subscription_tier = 'max'; requestBody.updated_settings.subscription_status = 'active'; requestBody.updated_settings.subscription_source = 'stripe'; requestBody.updated_settings.stripe_status = 'active'; requestBody.updated_settings.gpt4_limit = 999999; requestBody.updated_settings.pplx_alpha_limit = 999999; requestBody.updated_settings.pplx_beta_limit = 999999; requestBody.updated_settings.pages_limit = 999999; requestBody.updated_settings.upload_limit = 999999; requestBody.updated_settings.create_limit = 999999; requestBody.updated_settings.article_image_upload_limit = 999999; requestBody.updated_settings.max_files_per_user = 999999; requestBody.updated_settings.max_files_per_repository = 999999; } else { // Ensure MAX subscription settings in the full settings requestBody.subscription_tier = 'max'; requestBody.subscription_status = 'active'; requestBody.subscription_source = 'stripe'; requestBody.stripe_status = 'active'; requestBody.gpt4_limit = 999999; requestBody.pplx_alpha_limit = 999999; requestBody.pplx_beta_limit = 999999; requestBody.pages_limit = 999999; requestBody.upload_limit = 999999; requestBody.create_limit = 999999; requestBody.article_image_upload_limit = 999999; requestBody.max_files_per_user = 999999; requestBody.max_files_per_repository = 999999; } // Update the request with modified body const modifiedInit = { ...init, body: JSON.stringify(requestBody) }; log('Modified save-settings request with MAX subscription data'); // Make the modified request const response = await originalFetch(input, modifiedInit); // Also modify the response to ensure MAX settings are returned try { const cloned = response.clone(); const json = await cloned.json(); // Always ensure the response has MAX settings json.subscription_tier = 'max'; json.subscription_status = 'active'; json.subscription_source = 'stripe'; json.stripe_status = 'active'; json.gpt4_limit = 999999; json.pplx_alpha_limit = 999999; json.pplx_beta_limit = 999999; json.pages_limit = 999999; json.upload_limit = 999999; json.create_limit = 999999; json.article_image_upload_limit = 999999; json.max_files_per_user = 999999; json.max_files_per_repository = 999999; // Log the modified response for debugging log('Modified PUT/POST save-settings response with MAX subscription data'); return new Response(JSON.stringify(json), { status: response.status, statusText: response.statusText, headers: response.headers }); } catch (e) { // If there's an error processing the response, return the original log('Error modifying save-settings response: ' + e.message); return response; } } catch (e) { log('Error processing save-settings request: ' + e.message); // If there's an error, proceed with the original request return originalFetch(input, init); } } } // For all other requests, proceed normally const response = await originalFetch(input, init); // Check if the response is JSON and modify it if needed try { // Check if the response is actually JSON const ct = response.headers.get('content-type') || ''; if (!ct.startsWith('application/json')) return response; const cloned = response.clone(); const json = await cloned.json(); if (patchDataRecursively(json)) { log(`Response modified ← ${url}`); forceReactUpdate(); return new Response(JSON.stringify(json), { status: response.status, statusText: response.statusText, headers: response.headers }); } } catch (e) { /* not JSON — ignore */ } return response; } catch (e) { log('Error in fetch interceptor:', e); // If there's a critical error, fall back to original fetch return originalFetch(input, init); } }; // Also inject the fetch interceptor into the page context const injectFetchInterceptor = () => { const script = document.createElement('script'); script.textContent = ` (function() { console.log('[PERPLEXITY-MAX-INJECTED] Setting up fetch interceptor'); const originalFetch = window.fetch; window.fetch = async function(input, init) { try { // Extract URL and method const url = typeof input === 'string' ? input : input.url; const method = init?.method || 'GET'; // CRITICAL: Handle rate-limit requests - MUST intercept these if (url && url.includes('/rest/rate-limit/all')) { console.log('[PERPLEXITY-MAX-INJECTED] Intercepted rate-limit request - FORCING UNLIMITED LIMITS'); // ALWAYS return our custom response with unlimited limits const unlimitedResponse = { remaining_pro: 999999, remaining_research: 999999, remaining_labs: 999999, remaining_uploads: 999999, remaining_copilot: 999999, remaining_search: 999999, remaining_create: 999999, rate_limited: false }; // Return our custom response directly, bypassing the server return new Response(JSON.stringify(unlimitedResponse), { status: 200, statusText: 'OK', headers: { 'Content-Type': 'application/json' } }); } // Handle save-settings requests if (url && url.includes('/rest/user/save-settings')) { console.log('[PERPLEXITY-MAX-INJECTED] Intercepted save-settings request'); // For GET requests, we'll handle the response if (method === 'GET') { const response = await originalFetch(input, init); try { const cloned = response.clone(); const json = await cloned.json(); // Modify the settings to ensure MAX subscription json.subscription_tier = 'max'; json.subscription_status = 'active'; json.subscription_source = 'stripe'; json.stripe_status = 'active'; json.gpt4_limit = 999999; json.pplx_alpha_limit = 999999; json.pplx_beta_limit = 999999; json.pages_limit = 999999; json.upload_limit = 999999; json.create_limit = 999999; json.article_image_upload_limit = 999999; json.max_files_per_user = 999999; json.max_files_per_repository = 999999; console.log('[PERPLEXITY-MAX-INJECTED] Modified GET save-settings response'); // Return the modified response return new Response(JSON.stringify(json), { status: response.status, statusText: response.statusText, headers: response.headers }); } catch (e) { console.error('[PERPLEXITY-MAX-INJECTED] Error processing GET save-settings response:', e); return response; } } } // For all other requests, proceed normally return originalFetch.apply(this, arguments); } catch (e) { console.error('[PERPLEXITY-MAX-INJECTED] Error in fetch interceptor:', e); // If there's a critical error, fall back to original fetch return originalFetch.apply(this, arguments); } }; console.log('[PERPLEXITY-MAX-INJECTED] Fetch interceptor setup complete'); })(); `; document.head.appendChild(script); document.head.removeChild(script); }; /* ───────────────────── SIMPLIFIED WebSocket HANDLING ──────────────── */ // This is a simplified version that just logs connections but doesn't modify data // This avoids breaking the WebSocket connections const originalWebSocket = window.WebSocket; window.WebSocket = function(url, protocols) { log(`WebSocket connection to ${url}`); return new originalWebSocket(url, protocols); }; /* ─────────────── BLOCK ANALYTICS AND TRACKING SCRIPTS ─────────────── */ function blockAnalyticsScripts() { log('Setting up analytics and tracking script blocker'); // Override XMLHttpRequest to block analytics const originalXhrOpen = XMLHttpRequest.prototype.open; const originalXhrSend = XMLHttpRequest.prototype.send; const originalXhrSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader; // Fix for XMLHttpRequest state issues XMLHttpRequest.prototype.open = function(method, url, ...rest) { // Store the state and URL for later use this._pplxState = 'OPENED'; this._pplxUrl = url; if (typeof url === 'string' && ( url.includes('datadoghq.com') || url.includes('analytics') || url.includes('telemetry') || url.includes('metrics') || url.includes('singular.net') || url.includes('segment.io') || url.includes('mixpanel') || url.includes('facebook') || url.includes('fbevents') )) { log(`Blocked XHR analytics request to: ${url}`); // Make this XHR do nothing but maintain proper state this._pplxBlocked = true; // Create a safe version that maintains proper state this.setRequestHeader = function(header, value) { // Allow setRequestHeader to be called without errors if (this._pplxState === 'OPENED') { // Just log but don't actually set headers verbose(`Blocked setRequestHeader for ${url}: ${header}=${value}`); return; } else { throw new DOMException('XMLHttpRequest.setRequestHeader: XMLHttpRequest state must be OPENED.'); } }; this.send = function(body) { // Change state to prevent further calls this._pplxState = 'DONE'; // Simulate a successful response setTimeout(() => { if (typeof this.onreadystatechange === 'function') { this.readyState = 4; this.status = 200; this.response = '{"success":true}'; this.responseText = '{"success":true}'; this.onreadystatechange(); } if (typeof this.onload === 'function') { this.onload(); } }, 10); }; // Still call original open to maintain proper state return originalXhrOpen.call(this, method, url, ...rest); } // For non-blocked requests, proceed normally return originalXhrOpen.call(this, method, url, ...rest); }; // Safer setRequestHeader override XMLHttpRequest.prototype.setRequestHeader = function(header, value) { try { // Only proceed if not blocked if (!this._pplxBlocked) { return originalXhrSetRequestHeader.call(this, header, value); } else { // For blocked requests, just pretend it worked verbose(`Blocked setRequestHeader: ${header}=${value}`); return; } } catch (e) { log(`Error in setRequestHeader: ${e.message}`); // If there's an error, don't break the page return; } }; // Safer send override XMLHttpRequest.prototype.send = function(body) { try { // Only proceed if not blocked if (!this._pplxBlocked) { return originalXhrSend.call(this, body); } else { // For blocked requests, simulate success this._pplxState = 'DONE'; setTimeout(() => { if (typeof this.onreadystatechange === 'function') { this.readyState = 4; this.status = 200; this.response = '{"success":true}'; this.responseText = '{"success":true}'; this.onreadystatechange(); } if (typeof this.onload === 'function') { this.onload(); } }, 10); return; } } catch (e) { log(`Error in send: ${e.message}`); // If there's an error, don't break the page return; } }; // Inject script to block analytics in page context const script = document.createElement('script'); script.textContent = ` (function() { console.log('[PERPLEXITY-MAX-INJECTED] Setting up analytics blocker'); // Block Datadog window.DD_RUM = { init: function() { console.log('[PERPLEXITY-MAX-INJECTED] Blocked Datadog init'); }, addTiming: function() {}, addAction: function() {}, addError: function() {}, startSessionReplayRecording: function() {}, setGlobalContextProperty: function() {} }; // Block other analytics services window.ga = function() { console.log('[PERPLEXITY-MAX-INJECTED] Blocked GA call'); }; window.gtag = function() { console.log('[PERPLEXITY-MAX-INJECTED] Blocked GTM call'); }; window._paq = { push: function() { console.log('[PERPLEXITY-MAX-INJECTED] Blocked Matomo call'); } }; window.mixpanel = { track: function() {}, identify: function() {} }; window.segment = { track: function() {}, identify: function() {} }; window.amplitude = { track: function() {}, identify: function() {} }; // Block Facebook Pixel window.fbq = function() { console.log('[PERPLEXITY-MAX-INJECTED] Blocked Facebook Pixel call'); }; // Block Facebook events window.FB = window.FB || {}; window.FB.Event = window.FB.Event || { subscribe: function() {} }; window.FB.CustomerChat = window.FB.CustomerChat || { hide: function() {}, show: function() {} }; // Fix XMLHttpRequest issues try { const originalXhrOpen = XMLHttpRequest.prototype.open; const originalXhrSend = XMLHttpRequest.prototype.send; const originalXhrSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader; XMLHttpRequest.prototype.open = function(method, url, ...rest) { // Store state for later use this._pplxState = 'OPENED'; this._pplxUrl = url; if (typeof url === 'string' && ( url.includes('datadoghq.com') || url.includes('analytics') || url.includes('telemetry') || url.includes('metrics') || url.includes('singular.net') || url.includes('segment.io') || url.includes('mixpanel') || url.includes('facebook') || url.includes('fbevents') )) { console.log('[PERPLEXITY-MAX-INJECTED] Blocked XHR request:', url); this._pplxBlocked = true; return originalXhrOpen.call(this, method, 'data:text/plain,{}', ...rest); } return originalXhrOpen.call(this, method, url, ...rest); }; XMLHttpRequest.prototype.setRequestHeader = function(header, value) { if (this._pplxBlocked) { // Do nothing for blocked requests return; } return originalXhrSetRequestHeader.call(this, header, value); }; XMLHttpRequest.prototype.send = function(body) { if (this._pplxBlocked) { // Simulate success for blocked requests this._pplxState = 'DONE'; setTimeout(() => { if (typeof this.onreadystatechange === 'function') { this.readyState = 4; this.status = 200; this.response = '{"success":true}'; this.responseText = '{"success":true}'; this.onreadystatechange(); } if (typeof this.onload === 'function') { this.onload(); } }, 10); return; } return originalXhrSend.call(this, body); }; console.log('[PERPLEXITY-MAX-INJECTED] XMLHttpRequest patched successfully'); } catch (e) { console.error('[PERPLEXITY-MAX-INJECTED] Failed to patch XMLHttpRequest:', e); } // Override fetch for analytics const originalFetch = window.fetch; window.fetch = async function(input, init) { const url = typeof input === 'string' ? input : input.url; if (url && ( url.includes('datadoghq.com') || url.includes('analytics') || url.includes('telemetry') || url.includes('metrics') || url.includes('singular.net') || url.includes('segment.io') || url.includes('mixpanel') || url.includes('facebook') || url.includes('fbevents') )) { console.log('[PERPLEXITY-MAX-INJECTED] Blocked analytics fetch:', url); return new Response(JSON.stringify({ success: true }), { status: 200, statusText: 'OK', headers: { 'Content-Type': 'application/json' } }); } return originalFetch.apply(this, arguments); }; // Override any analytics-related properties Object.defineProperties(window, { analytics: { get: function() { return { track: function() {}, identify: function() {}, page: function() {} }; }, configurable: true }, dataLayer: { get: function() { return []; }, set: function() {}, configurable: true } }); console.log('[PERPLEXITY-MAX-INJECTED] Analytics blocker setup complete'); })(); `; document.head.appendChild(script); document.head.removeChild(script); } /* ─────────────────── FORCE MAX SUBSCRIPTION ─────────────────── */ function forceMaxSubscription() { // This function directly modifies subscription data in the DOM log('Forcing MAX subscription data'); // 1. Force window.__NEXT_DATA__ if it exists (this contains initial state) try { if (window.__NEXT_DATA__ && window.__NEXT_DATA__.props && window.__NEXT_DATA__.props.pageProps) { // Force user data if (window.__NEXT_DATA__.props.pageProps.user) { window.__NEXT_DATA__.props.pageProps.user.subscription_tier = 'max'; window.__NEXT_DATA__.props.pageProps.user.subscription_status = 'active'; window.__NEXT_DATA__.props.pageProps.user.subscription_source = 'stripe'; window.__NEXT_DATA__.props.pageProps.user.is_max = true; window.__NEXT_DATA__.props.pageProps.user.has_max = true; window.__NEXT_DATA__.props.pageProps.user.is_pro = true; log('Forced MAX subscription in __NEXT_DATA__.props.pageProps.user'); } // Force subscription data if (window.__NEXT_DATA__.props.pageProps.subscription) { window.__NEXT_DATA__.props.pageProps.subscription.tier = 'max'; window.__NEXT_DATA__.props.pageProps.subscription.status = 'active'; window.__NEXT_DATA__.props.pageProps.subscription.source = 'stripe'; log('Forced MAX subscription in __NEXT_DATA__.props.pageProps.subscription'); } // Force rate limits if (window.__NEXT_DATA__.props.pageProps.usage) { if (window.__NEXT_DATA__.props.pageProps.usage.remaining_pro !== undefined) { window.__NEXT_DATA__.props.pageProps.usage.remaining_pro = 999999; } if (window.__NEXT_DATA__.props.pageProps.usage.remaining_research !== undefined) { window.__NEXT_DATA__.props.pageProps.usage.remaining_research = 999999; } if (window.__NEXT_DATA__.props.pageProps.usage.remaining_labs !== undefined) { window.__NEXT_DATA__.props.pageProps.usage.remaining_labs = 999999; } log('Forced unlimited usage in __NEXT_DATA__'); } } } catch (e) { log('Error modifying __NEXT_DATA__: ' + e.message); } // 2. Inject a script to modify React state directly const script = document.createElement('script'); script.textContent = ` (function() { console.log('[PERPLEXITY-MAX-INJECTED] Direct subscription override'); // Override subscription data in localStorage try { const storageKeys = ['perplexity-user', 'perplexity-subscription', 'perplexity-usage']; storageKeys.forEach(key => { const data = localStorage.getItem(key); if (data) { try { const parsed = JSON.parse(data); if (key === 'perplexity-user') { parsed.subscription_tier = 'max'; parsed.subscription_status = 'active'; parsed.subscription_source = 'stripe'; parsed.is_max = true; parsed.has_max = true; parsed.is_pro = true; } if (key === 'perplexity-subscription') { parsed.tier = 'max'; parsed.status = 'active'; parsed.source = 'stripe'; } if (key === 'perplexity-usage') { if (parsed.remaining_pro !== undefined) parsed.remaining_pro = 999999; if (parsed.remaining_research !== undefined) parsed.remaining_research = 999999; if (parsed.remaining_labs !== undefined) parsed.remaining_labs = 999999; } localStorage.setItem(key, JSON.stringify(parsed)); console.log('[PERPLEXITY-MAX-INJECTED] Modified localStorage:', key); } catch (e) { console.error('[PERPLEXITY-MAX-INJECTED] Error parsing localStorage:', e); } } }); } catch (e) { console.error('[PERPLEXITY-MAX-INJECTED] Error accessing localStorage:', e); } // Try to find and modify React state directly function modifyReactState() { // Look for React fiber nodes let rootNode = document.querySelector('#__next'); if (!rootNode) rootNode = document.querySelector('body > div'); if (!rootNode) return; // Get React internal instance let internalInstance = null; // React 16+ if (rootNode._reactRootContainer && rootNode._reactRootContainer._internalRoot) { internalInstance = rootNode._reactRootContainer._internalRoot.current; } // React 17+ const reactKey = Object.keys(rootNode).find(key => key.startsWith('__reactContainer$') || key.startsWith('__reactFiber$') ); if (reactKey) { internalInstance = rootNode[reactKey]; } if (!internalInstance) return; // Walk the fiber tree to find subscription data function walkFiber(fiber) { if (!fiber) return; // Check for subscription state if (fiber.memoizedState && fiber.memoizedState.memoizedState) { const state = fiber.memoizedState.memoizedState; // Look for subscription data in state if (state.subscription || state.user || state.usage) { console.log('[PERPLEXITY-MAX-INJECTED] Found React state with subscription data'); if (state.subscription) { state.subscription.tier = 'max'; state.subscription.status = 'active'; state.subscription.source = 'stripe'; } if (state.user) { state.user.subscription_tier = 'max'; state.user.subscription_status = 'active'; state.user.subscription_source = 'stripe'; state.user.is_max = true; state.user.has_max = true; state.user.is_pro = true; } if (state.usage) { if (state.usage.remaining_pro !== undefined) state.usage.remaining_pro = 999999; if (state.usage.remaining_research !== undefined) state.usage.remaining_research = 999999; if (state.usage.remaining_labs !== undefined) state.usage.remaining_labs = 999999; } // Force update if (fiber.stateNode && typeof fiber.stateNode.forceUpdate === 'function') { fiber.stateNode.forceUpdate(); } } } // Walk child fibers if (fiber.child) walkFiber(fiber.child); if (fiber.sibling) walkFiber(fiber.sibling); } walkFiber(internalInstance); } // Run immediately and periodically modifyReactState(); setInterval(modifyReactState, 5000); // Also override fetch responses for subscription endpoints const originalFetch = window.fetch; window.fetch = async function(input, init) { const url = typeof input === 'string' ? input : input.url; // Check if this is a subscription-related request const isSubscriptionRequest = url && ( url.includes('/api/user') || url.includes('/api/subscription') || url.includes('/api/auth/session') ); if (isSubscriptionRequest) { console.log('[PERPLEXITY-MAX-INJECTED] Intercepted subscription request:', url); // For session requests, return a modified response if (url.includes('/api/auth/session')) { return new Response(JSON.stringify({ user: { subscription_tier: 'max', subscription_status: 'active', subscription_source: 'stripe', is_max: true, has_max: true, is_pro: true } }), { status: 200, headers: { 'Content-Type': 'application/json' } }); } } // For other requests, proceed normally return originalFetch.apply(this, arguments); }; })(); `; document.head.appendChild(script); document.head.removeChild(script); // 3. Also directly modify any visible plan elements const planElements = document.querySelectorAll('[data-plan], [data-tier], [data-subscription]'); planElements.forEach(element => { if (element.getAttribute('data-plan') === 'pro') { element.setAttribute('data-plan', 'max'); log('Changed data-plan from pro to max'); } if (element.getAttribute('data-tier') === 'pro') { element.setAttribute('data-tier', 'max'); log('Changed data-tier from pro to max'); } if (element.getAttribute('data-subscription') === 'pro') { element.setAttribute('data-subscription', 'max'); log('Changed data-subscription from pro to max'); } }); // 4. Force model selection buttons to be clickable const modelButtons = document.querySelectorAll('button[aria-label="Choose a model"], [data-model-selector="true"]'); modelButtons.forEach(button => { // Clone the button to remove all event listeners const newButton = button.cloneNode(true); // Make sure it's visible and clickable newButton.style.display = ''; newButton.style.visibility = 'visible'; newButton.style.pointerEvents = 'auto'; newButton.style.cursor = 'pointer'; // Remove any disabled attributes newButton.removeAttribute('disabled'); newButton.setAttribute('aria-disabled', 'false'); // Add a click event listener that will force the dropdown to appear newButton.addEventListener('click', () => { log('Model button clicked'); }); // Replace the original button if (button.parentNode) { button.parentNode.replaceChild(newButton, button); log('Replaced model button with enhanced version'); } }); } /* ─────────────────── ADD STATUS INDICATOR ─────────────────── */ function addStatusIndicator() { // Create a simple status indicator const statusDiv = document.createElement('div'); statusDiv.style.position = 'fixed'; statusDiv.style.bottom = '20px'; statusDiv.style.right = '20px'; statusDiv.style.backgroundColor = 'rgba(0, 0, 0, 0.7)'; statusDiv.style.color = '#00ff9d'; statusDiv.style.padding = '6px 12px'; statusDiv.style.borderRadius = '6px'; statusDiv.style.fontSize = '11px'; statusDiv.style.fontWeight = 'bold'; statusDiv.style.zIndex = '9999'; statusDiv.style.opacity = '0.8'; statusDiv.style.transition = 'opacity 0.3s'; statusDiv.style.fontFamily = 'Arial, sans-serif'; statusDiv.style.userSelect = 'none'; statusDiv.textContent = 'PERPLEXITY MAX v2.0'; document.body.appendChild(statusDiv); // Fade out after 3 seconds setTimeout(() => { statusDiv.style.opacity = '0'; setTimeout(() => { if (statusDiv.parentNode) { statusDiv.parentNode.removeChild(statusDiv); } }, 1000); }, 3000); // Add neon author credit addNeonAuthorCredit(); } /* ─────────────────── ADD NEON AUTHOR CREDIT ─────────────────── */ function addNeonAuthorCredit() { // Create wrapper for the neon text const neonWrapper = document.createElement('div'); neonWrapper.style.position = 'fixed'; neonWrapper.style.bottom = '60px'; neonWrapper.style.right = '20px'; neonWrapper.style.zIndex = '9999'; neonWrapper.style.userSelect = 'none'; neonWrapper.style.opacity = '0'; neonWrapper.style.transition = 'opacity 1s ease-in-out'; // Create the neon text element const neonText = document.createElement('div'); neonText.textContent = 'by AntiKeks & d4nc3r'; neonText.style.fontFamily = '"Courier New", monospace'; neonText.style.fontSize = '14px'; neonText.style.fontWeight = 'bold'; neonText.style.color = '#fff'; neonText.style.textShadow = ` 0 0 5px #ff6600, 0 0 10px #ff6600, 0 0 15px #ff6600, 0 0 20px #ff6600, 0 0 25px #ff6600, 0 0 30px #ff6600 `; neonText.style.animation = 'neonPulse 1.5s ease-in-out infinite alternate'; // Create and add the style for the neon pulse animation const styleElement = document.createElement('style'); styleElement.textContent = ` @keyframes neonPulse { from { text-shadow: 0 0 5px #ff6600, 0 0 10px #ff6600, 0 0 15px #ff6600, 0 0 20px #ff6600; } to { text-shadow: 0 0 5px #ff6600, 0 0 10px #ff8800, 0 0 15px #ff8800, 0 0 20px #ff8800, 0 0 25px #ff8800, 0 0 30px #ff8800, 0 0 35px #ffaa00; } } @keyframes neonFlicker { 0%, 19%, 21%, 23%, 25%, 54%, 56%, 100% { text-shadow: 0 0 5px #ff6600, 0 0 10px #ff6600, 0 0 15px #ff8800, 0 0 20px #ff8800, 0 0 25px #ffaa00; } 20%, 24%, 55% { text-shadow: none; } } `; document.head.appendChild(styleElement); // Add the neon text to the wrapper neonWrapper.appendChild(neonText); // Add the wrapper to the document document.body.appendChild(neonWrapper); // Fade in after a short delay setTimeout(() => { neonWrapper.style.opacity = '1'; // Add flickering effect after it's visible setTimeout(() => { neonText.style.animation = 'neonFlicker 2s linear 1, neonPulse 1.5s ease-in-out infinite alternate'; }, 2000); // Fade out after 6 seconds setTimeout(() => { neonWrapper.style.opacity = '0'; // Remove from DOM after fade out setTimeout(() => { if (neonWrapper.parentNode) { neonWrapper.parentNode.removeChild(neonWrapper); } if (styleElement.parentNode) { styleElement.parentNode.removeChild(styleElement); } }, 1000); }, 6000); }, 1000); } /* ─────────────────── ENABLE UNLIMITED UPLOADS ─────────────────── */ function enableUnlimitedUploads() { log('Enabling unlimited file uploads'); // Inject a script to modify any upload limits in the page context const script = document.createElement('script'); script.textContent = ` (function() { console.log('[PERPLEXITY-MAX-INJECTED] Enabling unlimited uploads'); // Override any upload limit checks in the page context const originalFetch = window.fetch; window.fetch = async function(input, init) { const url = typeof input === 'string' ? input : input.url; // If this is an upload-related request, modify it if (url && url.includes('/uploads')) { // If there's a body, check for any limit-related fields if (init && init.body && typeof init.body === 'string') { try { const body = JSON.parse(init.body); // Remove any limit-related fields if (body.hasOwnProperty('limit')) { body.limit = 999999; } if (body.hasOwnProperty('max_size')) { body.max_size = 1000000000; // 1GB } if (body.hasOwnProperty('max_count')) { body.max_count = 100; } // Update the request body init.body = JSON.stringify(body); } catch (e) { // Not JSON - ignore } } } return originalFetch.apply(this, arguments); }; // Advanced fix for FormData.append issues try { // Store the original method const originalFormDataAppend = FormData.prototype.append; // Create a safer version that handles all edge cases FormData.prototype.append = function(name, value, filename) { // Handle the case when name is not a string if (typeof name !== 'string') { try { name = String(name); } catch (e) { name = 'unnamed'; console.warn('[PERPLEXITY-MAX-INJECTED] FormData.append: Invalid name parameter'); } } // Handle null or undefined values if (value === null || value === undefined) { console.log('[PERPLEXITY-MAX-INJECTED] FormData.append: Converting null/undefined to empty string'); try { return originalFormDataAppend.call(this, name, '', filename); } catch (e) { console.error('[PERPLEXITY-MAX-INJECTED] FormData.append failed with empty string:', e); return; // Silently fail rather than breaking the page } } // Handle File and Blob objects properly if (value instanceof File || value instanceof Blob) { try { return originalFormDataAppend.call(this, name, value, filename); } catch (e) { console.error('[PERPLEXITY-MAX-INJECTED] FormData.append failed with File/Blob:', e); return; // Silently fail rather than breaking the page } } // Handle string values if (typeof value === 'string') { try { return originalFormDataAppend.call(this, name, value, filename); } catch (e) { console.error('[PERPLEXITY-MAX-INJECTED] FormData.append failed with string:', e); return; // Silently fail rather than breaking the page } } // For objects, try to stringify them if (typeof value === 'object') { try { // Try to convert to a Blob first if it has certain properties if (value.type && (value.size !== undefined || value.length !== undefined)) { try { const blob = new Blob([value], { type: value.type || 'application/octet-stream' }); return originalFormDataAppend.call(this, name, blob, filename || value.name); } catch (blobError) { console.warn('[PERPLEXITY-MAX-INJECTED] Failed to convert to Blob:', blobError); // Fall through to JSON stringify } } // Try JSON stringify const jsonString = JSON.stringify(value); return originalFormDataAppend.call(this, name, jsonString, filename); } catch (e) { console.error('[PERPLEXITY-MAX-INJECTED] FormData.append failed with object:', e); try { return originalFormDataAppend.call(this, name, '[object Object]', filename); } catch (e2) { console.error('[PERPLEXITY-MAX-INJECTED] FormData.append fallback failed:', e2); return; // Silently fail rather than breaking the page } } } // For everything else, convert to string try { const stringValue = String(value); return originalFormDataAppend.call(this, name, stringValue, filename); } catch (e) { console.error('[PERPLEXITY-MAX-INJECTED] FormData.append string conversion failed:', e); return; // Silently fail rather than breaking the page } }; console.log('[PERPLEXITY-MAX-INJECTED] FormData.append successfully patched'); } catch (e) { console.error('[PERPLEXITY-MAX-INJECTED] Failed to patch FormData.append:', e); } // Block Facebook Pixel and other tracking scripts try { // Create dummy Facebook Pixel functions window.fbq = function() { console.log('[PERPLEXITY-MAX-INJECTED] Blocked Facebook Pixel call'); }; // Block Facebook events window.FB = window.FB || {}; window.FB.Event = window.FB.Event || { subscribe: function() {} }; window.FB.CustomerChat = window.FB.CustomerChat || { hide: function() {}, show: function() {} }; console.log('[PERPLEXITY-MAX-INJECTED] Facebook tracking scripts blocked'); } catch (e) { console.error('[PERPLEXITY-MAX-INJECTED] Failed to block Facebook tracking:', e); } // Also try to find and modify any upload limit variables in the page function overrideUploadLimits() { // Look for common variable names related to upload limits const globalVars = [ 'MAX_UPLOADS', 'MAX_UPLOAD_COUNT', 'UPLOAD_LIMIT', 'MAX_UPLOAD_SIZE', 'FILE_UPLOAD_LIMIT', 'UPLOAD_MAX_FILES' ]; globalVars.forEach(varName => { if (window[varName] !== undefined) { if (typeof window[varName] === 'number') { window[varName] = 999999; console.log('[PERPLEXITY-MAX-INJECTED] Overrode global upload limit:', varName); } } }); // Also look for common object properties related to upload limits const objects = ['config', 'settings', 'options', 'limits', 'uploadConfig']; const properties = ['maxUploads', 'maxUploadSize', 'uploadLimit', 'fileLimit', 'maxFiles']; objects.forEach(objName => { if (window[objName]) { properties.forEach(prop => { if (window[objName][prop] !== undefined && typeof window[objName][prop] === 'number') { window[objName][prop] = 999999; console.log('[PERPLEXITY-MAX-INJECTED] Overrode upload limit:', objName + '.' + prop); } }); } }); } // Run immediately and periodically overrideUploadLimits(); setInterval(overrideUploadLimits, 5000); })(); `; document.head.appendChild(script); document.head.removeChild(script); // Set up tracking system for upload requests setupUploadTracking(); } /* ─────────────────── TRACK UPLOAD REQUESTS ─────────────────── */ function setupUploadTracking() { log('Setting up upload tracking system'); // Inject a script to track upload requests and responses const script = document.createElement('script'); script.textContent = ` (function() { console.log('[PERPLEXITY-MAX-INJECTED] Setting up upload tracking system'); // Track file upload events document.addEventListener('drop', function(e) { if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length > 0) { console.log('[PERPLEXITY-MAX-INJECTED] File drop detected:', e.dataTransfer.files.length, 'files'); } }, true); // Track file input changes document.addEventListener('change', function(e) { if (e.target && e.target.type === 'file' && e.target.files && e.target.files.length > 0) { console.log('[PERPLEXITY-MAX-INJECTED] File input change detected:', e.target.files.length, 'files'); } }, true); // Override the FormData.append method to track file uploads const originalAppend = FormData.prototype.append; FormData.prototype.append = function(name, value, filename) { // Check if this is a file upload if (value instanceof Blob || value instanceof File) { console.log('[PERPLEXITY-MAX-INJECTED] File added to FormData:', name, filename || (value.name || 'blob')); } return originalAppend.call(this, name, value, filename); }; })(); `; document.head.appendChild(script); document.head.removeChild(script); } /* ─────────────────── FORCE UNLIMITED ACCESS ─────────────────── */ function injectUnlimitedAccess() { log('Injecting unlimited access script into page context'); // Create a script that will run in the page context const script = document.createElement('script'); script.textContent = ` (function() { console.log('[PERPLEXITY-MAX-INJECTED] Setting up unlimited access'); // Create a debug logger const DEBUG = true; const debug = (...args) => DEBUG && console.log('[PERPLEXITY-MAX-DEBUG]', ...args); // Store original methods const originalFetch = window.fetch; const OriginalXMLHttpRequest = window.XMLHttpRequest; // Create a response modifier function function createMaxResponse(originalResponse) { // Define max settings const maxSettings = { subscription_tier: 'max', subscription_status: 'active', subscription_source: 'stripe', stripe_status: 'active', gpt4_limit: 999999, pplx_alpha_limit: 999999, pplx_beta_limit: 999999, pages_limit: 999999, upload_limit: 999999, create_limit: 999999, article_image_upload_limit: 999999, max_files_per_user: 999999, max_files_per_repository: 999999, rate_limited: false, remaining_pro: 999999, remaining_research: 999999, remaining_labs: 999999, remaining_uploads: 999999, remaining_copilot: 999999, remaining_search: 999999, remaining_create: 999999 }; // Apply max settings to response return Object.assign({}, originalResponse, maxSettings); } // Create a completely new XMLHttpRequest class window.XMLHttpRequest = function() { const xhr = new OriginalXMLHttpRequest(); // Track request details let requestUrl = ''; let requestMethod = ''; let isImportant = false; let isModified = false; // Override open const originalOpen = xhr.open; xhr.open = function(method, url, ...args) { requestMethod = method; requestUrl = url; debug('XHR Open:', method, url); // Check if this is an important request if (typeof url === 'string') { if (url.includes('/rest/rate-limit/all')) { isImportant = true; console.log('[PERPLEXITY-MAX-INJECTED] Important XHR detected: rate-limit'); // For rate-limit requests, we'll handle them specially const fakeResponse = { remaining_pro: 999999, remaining_research: 999999, remaining_labs: 999999, remaining_uploads: 999999, remaining_copilot: 999999, remaining_search: 999999, remaining_create: 999999, rate_limited: false }; // Create a fake URL that will be intercepted later url = 'data:application/json,' + encodeURIComponent(JSON.stringify(fakeResponse)); } else if (url.includes('/rest/user/save-settings')) { isImportant = true; console.log('[PERPLEXITY-MAX-INJECTED] Important XHR detected: save-settings'); } else if (url.includes('/rest/uploads/create_upload_url')) { isImportant = true; console.log('[PERPLEXITY-MAX-INJECTED] Important XHR detected: upload-url'); } } return originalOpen.call(xhr, method, url, ...args); }; // Override send const originalSend = xhr.send; xhr.send = function(body) { debug('XHR Send:', requestMethod, requestUrl, body); // Modify request body for important requests if (isImportant && body && typeof body === 'string') { try { const data = JSON.parse(body); // For save-settings requests if (requestUrl.includes('/rest/user/save-settings')) { console.log('[PERPLEXITY-MAX-INJECTED] Modifying save-settings request body'); // If this is a partial update if (data.updated_settings) { console.log('[PERPLEXITY-MAX-INJECTED] Original updated_settings:', JSON.stringify(data.updated_settings)); data.updated_settings = Object.assign({}, data.updated_settings, { subscription_tier: 'max', subscription_status: 'active', subscription_source: 'stripe', stripe_status: 'active', gpt4_limit: 999999, pplx_alpha_limit: 999999, pplx_beta_limit: 999999, pages_limit: 999999, upload_limit: 999999, create_limit: 999999, article_image_upload_limit: 999999, max_files_per_user: 999999, max_files_per_repository: 999999 }); console.log('[PERPLEXITY-MAX-INJECTED] Modified updated_settings:', JSON.stringify(data.updated_settings)); } else { // Full update Object.assign(data, { subscription_tier: 'max', subscription_status: 'active', subscription_source: 'stripe', stripe_status: 'active', gpt4_limit: 999999, pplx_alpha_limit: 999999, pplx_beta_limit: 999999, pages_limit: 999999, upload_limit: 999999, create_limit: 999999, article_image_upload_limit: 999999, max_files_per_user: 999999, max_files_per_repository: 999999 }); } // Update the body body = JSON.stringify(data); console.log('[PERPLEXITY-MAX-INJECTED] Modified request body:', body); } } catch (e) { console.error('[PERPLEXITY-MAX-INJECTED] Error modifying request body:', e); } } // For important requests, intercept the response if (isImportant) { // Override responseText getter let originalResponseText = ''; Object.defineProperty(xhr, 'responseText', { get: function() { if (isModified) { return originalResponseText; } const response = xhr._responseText || ''; // Only modify JSON responses if (response && response.startsWith('{')) { try { const data = JSON.parse(response); // For rate-limit requests if (requestUrl.includes('/rest/rate-limit/all')) { console.log('[PERPLEXITY-MAX-INJECTED] Modifying rate-limit response'); const modifiedData = { remaining_pro: 999999, remaining_research: 999999, remaining_labs: 999999, remaining_uploads: 999999, remaining_copilot: 999999, remaining_search: 999999, remaining_create: 999999, rate_limited: false }; originalResponseText = JSON.stringify(modifiedData); isModified = true; return originalResponseText; } // For save-settings requests if (requestUrl.includes('/rest/user/save-settings')) { console.log('[PERPLEXITY-MAX-INJECTED] Modifying save-settings response'); const modifiedData = createMaxResponse(data); originalResponseText = JSON.stringify(modifiedData); isModified = true; return originalResponseText; } // For upload URL creation if (requestUrl.includes('/rest/uploads/create_upload_url')) { console.log('[PERPLEXITY-MAX-INJECTED] Modifying upload URL response'); data.rate_limited = false; // If fields are null, create fake ones to allow uploads if (!data.fields || !data.s3_bucket_url) { data.s3_bucket_url = "https://api.cloudinary.com/v1_1/pplx/image/upload"; data.s3_object_url = "https://api.cloudinary.com/v1_1/pplx/image/upload/"; data.fields = { timestamp: Date.now(), unique_filename: "true", folder: "user_uploads/unlimited/max", use_filename: "true", public_id: "unlimited_" + Date.now(), transformation: "t_fit2", type: "private", resource_type: "image", api_key: "168798331147639", cloud_name: "pplx", signature: "unlimited_max_signature" }; data.file_uuid = "unlimited_" + Date.now(); } originalResponseText = JSON.stringify(data); isModified = true; return originalResponseText; } } catch (e) { console.error('[PERPLEXITY-MAX-INJECTED] Error modifying response:', e); } } return response; }, set: function(value) { xhr._responseText = value; } }); // Override response getter Object.defineProperty(xhr, 'response', { get: function() { // If we've modified the responseText, return that for JSON responses if (isModified && xhr.responseType === '' || xhr.responseType === 'text') { return xhr.responseText; } return xhr._response; }, set: function(value) { xhr._response = value; } }); } // Monitor readyState changes const originalStateChange = xhr.onreadystatechange; xhr.onreadystatechange = function(e) { if (xhr.readyState === 4) { debug('XHR Complete:', requestMethod, requestUrl, xhr.status); if (isImportant) { debug('Response for important request:', xhr.responseText); } } if (originalStateChange) { return originalStateChange.call(xhr, e); } }; return originalSend.call(xhr, body); }; return xhr; }; // Override fetch with a proxy window.fetch = new Proxy(originalFetch, { apply: async function(target, thisArg, args) { // Extract URL and init let [input, init] = args; const url = typeof input === 'string' ? input : input.url; const method = init?.method || 'GET'; debug('Fetch:', method, url); // Handle rate limit requests directly if (url && url.includes('/rest/rate-limit/all')) { console.log('[PERPLEXITY-MAX-INJECTED] Intercepting rate-limit fetch request'); return new Response(JSON.stringify({ remaining_pro: 999999, remaining_research: 999999, remaining_labs: 999999, remaining_uploads: 999999, remaining_copilot: 999999, remaining_search: 999999, remaining_create: 999999, rate_limited: false }), { status: 200, headers: { 'Content-Type': 'application/json' } }); } // Handle save-settings requests if (url && url.includes('/rest/user/save-settings')) { console.log('[PERPLEXITY-MAX-INJECTED] Intercepting save-settings fetch request'); // Modify request body for PUT/POST if ((method === 'PUT' || method === 'POST') && init && init.body) { try { let body = {}; if (typeof init.body === 'string') { body = JSON.parse(init.body); } // If this is a partial update if (body.updated_settings) { console.log('[PERPLEXITY-MAX-INJECTED] Original updated_settings:', JSON.stringify(body.updated_settings)); body.updated_settings = Object.assign({}, body.updated_settings, { subscription_tier: 'max', subscription_status: 'active', subscription_source: 'stripe', stripe_status: 'active', gpt4_limit: 999999, pplx_alpha_limit: 999999, pplx_beta_limit: 999999, pages_limit: 999999, upload_limit: 999999, create_limit: 999999, article_image_upload_limit: 999999, max_files_per_user: 999999, max_files_per_repository: 999999 }); console.log('[PERPLEXITY-MAX-INJECTED] Modified updated_settings:', JSON.stringify(body.updated_settings)); } else { // Full update Object.assign(body, { subscription_tier: 'max', subscription_status: 'active', subscription_source: 'stripe', stripe_status: 'active', gpt4_limit: 999999, pplx_alpha_limit: 999999, pplx_beta_limit: 999999, pages_limit: 999999, upload_limit: 999999, create_limit: 999999, article_image_upload_limit: 999999, max_files_per_user: 999999, max_files_per_repository: 999999 }); } // Update the request init.body = JSON.stringify(body); console.log('[PERPLEXITY-MAX-INJECTED] Modified save-settings request body:', init.body); } catch (e) { console.error('[PERPLEXITY-MAX-INJECTED] Error modifying save-settings request:', e); } } // Make the modified request const response = await target.apply(thisArg, [input, init]); // Clone and modify the response try { const clonedResponse = response.clone(); const data = await clonedResponse.json(); console.log('[PERPLEXITY-MAX-INJECTED] Original save-settings response:', JSON.stringify(data)); // Apply max settings const maxData = createMaxResponse(data); console.log('[PERPLEXITY-MAX-INJECTED] Modified save-settings response:', JSON.stringify(maxData)); return new Response(JSON.stringify(maxData), { status: response.status, statusText: response.statusText, headers: response.headers }); } catch (e) { console.error('[PERPLEXITY-MAX-INJECTED] Error modifying save-settings response:', e); return response; } } // Handle upload URL creation if (url && url.includes('/rest/uploads/create_upload_url')) { console.log('[PERPLEXITY-MAX-INJECTED] Intercepting upload URL creation fetch request'); // Make the original request const response = await target.apply(thisArg, args); try { // Clone and get the data const clonedResponse = response.clone(); const data = await clonedResponse.json(); console.log('[PERPLEXITY-MAX-INJECTED] Original upload URL response:', JSON.stringify(data)); // Always set rate_limited to false data.rate_limited = false; // If fields are null, create fake ones to allow uploads if (!data.fields || !data.s3_bucket_url) { data.s3_bucket_url = "https://api.cloudinary.com/v1_1/pplx/image/upload"; data.s3_object_url = "https://api.cloudinary.com/v1_1/pplx/image/upload/"; data.fields = { timestamp: Date.now(), unique_filename: "true", folder: "user_uploads/unlimited/max", use_filename: "true", public_id: "unlimited_" + Date.now(), transformation: "t_fit2", type: "private", resource_type: "image", api_key: "168798331147639", cloud_name: "pplx", signature: "unlimited_max_signature" }; data.file_uuid = "unlimited_" + Date.now(); } console.log('[PERPLEXITY-MAX-INJECTED] Modified upload URL response:', JSON.stringify(data)); return new Response(JSON.stringify(data), { status: response.status, statusText: response.statusText, headers: response.headers }); } catch (e) { console.error('[PERPLEXITY-MAX-INJECTED] Error modifying upload URL response:', e); return response; } } // For all other requests, proceed normally const response = await target.apply(thisArg, args); // For JSON responses, check if we need to modify them if (url && ( url.includes('/api/user') || url.includes('/api/subscription') || url.includes('/api/auth/session') || url.includes('/rest/user/') )) { try { const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { const clonedResponse = response.clone(); const data = await clonedResponse.json(); // Check if this response has any fields we want to modify const keysToCheck = [ 'subscription_tier', 'subscription_status', 'subscription_source', 'remaining_pro', 'remaining_research', 'remaining_labs', 'upload_limit', 'rate_limited', 'gpt4_limit' ]; let shouldModify = false; for (const key of keysToCheck) { if (key in data) { shouldModify = true; break; } } if (shouldModify) { console.log('[PERPLEXITY-MAX-INJECTED] Modifying JSON response for:', url); const maxData = createMaxResponse(data); return new Response(JSON.stringify(maxData), { status: response.status, statusText: response.statusText, headers: response.headers }); } } } catch (e) { // Not JSON or other error } } return response; } }); // Override Object.defineProperty to catch React state updates const originalDefineProperty = Object.defineProperty; Object.defineProperty = function(obj, prop, descriptor) { // Check if this is a subscription-related property if (prop === 'subscription_tier' || prop === 'subscription_status' || prop === 'is_pro' || prop === 'is_max' || prop === 'has_max') { // Force MAX values if (descriptor && descriptor.value !== undefined) { if (prop === 'subscription_tier') descriptor.value = 'max'; if (prop === 'subscription_status') descriptor.value = 'active'; if (prop === 'is_pro' || prop === 'is_max' || prop === 'has_max') descriptor.value = true; } // For getters/setters if (descriptor && descriptor.get) { const originalGetter = descriptor.get; descriptor.get = function() { let value = originalGetter.call(this); if (prop === 'subscription_tier') value = 'max'; if (prop === 'subscription_status') value = 'active'; if (prop === 'is_pro' || prop === 'is_max' || prop === 'has_max') value = true; return value; }; } } // Apply the original defineProperty return originalDefineProperty.call(this, obj, prop, descriptor); }; // Create a global helper function to force MAX subscription window.forcePplxMax = function() { console.log('[PERPLEXITY-MAX-INJECTED] Manually forcing MAX subscription'); // Try to find React state in the DOM const reactRoots = document.querySelectorAll('[data-reactroot]'); reactRoots.forEach(root => { console.log('[PERPLEXITY-MAX-INJECTED] Found React root:', root); }); // Force update by triggering resize and visibility events window.dispatchEvent(new Event('resize')); document.dispatchEvent(new Event('visibilitychange')); // Try to modify localStorage try { const storageKeys = ['perplexity-user', 'perplexity-subscription', 'perplexity-usage']; storageKeys.forEach(key => { const data = localStorage.getItem(key); if (data) { try { const parsed = JSON.parse(data); if (key === 'perplexity-user') { parsed.subscription_tier = 'max'; parsed.subscription_status = 'active'; parsed.subscription_source = 'stripe'; parsed.is_max = true; parsed.has_max = true; parsed.is_pro = true; } if (key === 'perplexity-subscription') { parsed.tier = 'max'; parsed.status = 'active'; parsed.source = 'stripe'; } if (key === 'perplexity-usage') { if (parsed.remaining_pro !== undefined) parsed.remaining_pro = 999999; if (parsed.remaining_research !== undefined) parsed.remaining_research = 999999; if (parsed.remaining_labs !== undefined) parsed.remaining_labs = 999999; } localStorage.setItem(key, JSON.stringify(parsed)); console.log('[PERPLEXITY-MAX-INJECTED] Modified localStorage:', key); } catch (e) { console.error('[PERPLEXITY-MAX-INJECTED] Error parsing localStorage:', e); } } }); } catch (e) { console.error('[PERPLEXITY-MAX-INJECTED] Error accessing localStorage:', e); } return "MAX subscription forced!"; }; // Run forcePplxMax periodically setInterval(window.forcePplxMax, 5000); console.log('[PERPLEXITY-MAX-INJECTED] Unlimited access setup complete. Use window.forcePplxMax() to force MAX subscription manually.'); })(); `; // Add the script to the page document.head.appendChild(script); document.head.removeChild(script); log('Unlimited access script injected with advanced monitoring'); } /* ─────────────────── INITIALIZATION ─────────────────── */ function initializeScript() { log('Initializing Perplexity MAX script'); // Add a mutation observer to handle dynamically added elements const observer = new MutationObserver((mutations) => { // Check if body exists if (document.body) { // Run our main functions forceMaxSubscription(); // Force MAX subscription when DOM changes enableUnlimitedUploads(); // Enable unlimited uploads blockAnalyticsScripts(); // Block analytics and tracking injectUnlimitedAccess(); // Inject unlimited access script // Disconnect after initial setup to avoid performance issues observer.disconnect(); // Add status indicator setTimeout(addStatusIndicator, 2000); // Set up periodic checks setInterval(forceMaxSubscription, 10000); // Periodically force MAX subscription } }); // Start observing observer.observe(document.documentElement, { childList: true, subtree: true }); // If body already exists, run immediately if (document.body) { observer.disconnect(); forceMaxSubscription(); // Force MAX subscription immediately enableUnlimitedUploads(); // Enable unlimited uploads blockAnalyticsScripts(); // Block analytics and tracking injectUnlimitedAccess(); // Inject unlimited access script setTimeout(addStatusIndicator, 2000); setInterval(forceMaxSubscription, 10000); // Periodically force MAX subscription } } // Start the script if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initializeScript); } else { initializeScript(); } })();