use crate::crypto::Challenges; use crate::network::{Headers, IpInfo}; use crate::utils::{ChatGptError, Result, Utils}; use crate::vm::VM; use base64::{Engine as _, engine::general_purpose}; use chrono::prelude::*; use image::ImageReader; use rand::Rng; use reqwest::{Client, Proxy}; use serde_json::{Value, json}; use std::io::Cursor; use std::time::{SystemTime, UNIX_EPOCH}; use uuid::Uuid; /// Browser window keys for environment simulation const WINDOW_KEYS: &[&str] = &[ "window", "self", "document", "name", "location", "customElements", "history", "navigation", "locationbar", "menubar", "personalbar", "scrollbars", "statusbar", "toolbar", "status", "closed", "frames", "length", "top", "opener", "parent", "frameElement", "navigator", "origin", "external", "screen", "innerWidth", "innerHeight", "scrollX", "pageXOffset", "scrollY", "pageYOffset", "visualViewport", "screenX", "screenY", "outerWidth", "outerHeight", "devicePixelRatio", "event", "clientInformation", "screenLeft", "screenTop", "styleMedia", "onsearch", "trustedTypes", "performance", "onappinstalled", "onbeforeinstallprompt", "crypto", "indexedDB", "sessionStorage", "localStorage", "onbeforexrselect", "onabort", "onbeforeinput", "onbeforematch", "onbeforetoggle", "onblur", "oncancel", "oncanplay", "oncanplaythrough", "onchange", "onclick", "onclose", "oncontentvisibilityautostatechange", "oncontextlost", "oncontextmenu", "oncontextrestored", "oncuechange", "ondblclick", "ondrag", "ondragend", "ondragenter", "ondragleave", "ondragover", "ondragstart", "ondrop", "ondurationchange", "onemptied", "onended", "onerror", "onfocus", "onformdata", "oninput", "oninvalid", "onkeydown", "onkeypress", "onkeyup", "onload", "onloadeddata", "onloadedmetadata", "onloadstart", "onmousedown", "onmouseenter", "onmouseleave", "onmousemove", "onmouseout", "onmouseover", "onmouseup", "onmousewheel", "onpause", "onplay", "onplaying", "onprogress", "onratechange", "onreset", "onresize", "onscroll", "onsecuritypolicyviolation", "onseeked", "onseeking", "onselect", "onslotchange", "onstalled", "onsubmit", "onsuspend", "ontimeupdate", "ontoggle", "onvolumechange", "onwaiting", "onwebkitanimationend", "onwebkitanimationiteration", "onwebkitanimationstart", "onwebkittransitionend", "onwheel", "onauxclick", "ongotpointercapture", "onlostpointercapture", "onpointerdown", "onpointermove", "onpointerrawupdate", "onpointerup", "onpointercancel", "onpointerover", "onpointerout", "onpointerenter", "onpointerleave", "onselectstart", "onselectionchange", "onanimationend", "onanimationiteration", "onanimationstart", "ontransitionrun", "ontransitionstart", "ontransitionend", "ontransitioncancel", "onafterprint", "onbeforeprint", "onbeforeunload", "onhashchange", "onlanguagechange", "onmessage", "onmessageerror", "onoffline", "ononline", "onpagehide", "onpageshow", "onpopstate", "onrejectionhandled", "onstorage", "onunhandledrejection", "onunload", "isSecureContext", "crossOriginIsolated", "scheduler", "alert", "atob", "blur", "btoa", "cancelAnimationFrame", "cancelIdleCallback", "captureEvents", "clearInterval", "clearTimeout", "close", "confirm", "createImageBitmap", "fetch", "find", "focus", "getComputedStyle", "getSelection", "matchMedia", "moveBy", "moveTo", "open", "postMessage", "print", "prompt", "queueMicrotask", "releaseEvents", "reportError", "requestAnimationFrame", "requestIdleCallback", "resizeBy", "resizeTo", "scroll", "scrollBy", "scrollTo", "setInterval", "setTimeout", "stop", "structuredClone", "webkitCancelAnimationFrame", "webkitRequestAnimationFrame", "chrome", "caches", "cookieStore", "ondevicemotion", "ondeviceorientation", "ondeviceorientationabsolute", "sharedStorage", "documentPictureInPicture", "fetchLater", "getScreenDetails", "queryLocalFonts", "showDirectoryPicker", "showOpenFilePicker", "showSaveFilePicker", "originAgentCluster", "viewport", "onpageswap", "onpagereveal", "credentialless", "fence", "launchQueue", "speechSynthesis", "oncommand", "onscrollend", "onscrollsnapchange", "onscrollsnapchanging", "webkitRequestFileSystem", "webkitResolveLocalFileSystemURL", "define", "ethereum", "__oai_SSR_HTML", "__reactRouterContext", "$RC", "__oai_SSR_TTI", "__reactRouterManifest", "__reactRouterVersion", "DD_RUM", "__REACT_INTL_CONTEXT__", "regeneratorRuntime", "DD_LOGS", "__STATSIG__", "__mobxInstanceCount", "__mobxGlobals", "_g", "__reactRouterRouteModules", "__SEGMENT_INSPECTOR__", "__reactRouterDataRouter", "MotionIsMounted", "_oaiHandleSessionExpired", ]; /// ChatGPT configuration data #[derive(Debug, Clone)] pub struct ChatGptData { pub prod: String, pub device_id: String, pub token: String, pub conversation_id: Option, pub parent_message_id: Option, pub proofofwork: Option, pub bytecode: Option, pub vm_token: Option, pub file_id: Option, pub file_name: Option, pub file_size: Option, pub image_width: Option, pub image_height: Option, pub config: Value, } impl Default for ChatGptData { fn default() -> Self { Self { prod: String::new(), device_id: String::new(), token: String::new(), conversation_id: None, parent_message_id: None, proofofwork: None, bytecode: None, vm_token: None, file_id: None, file_name: None, file_size: None, image_width: None, image_height: None, config: json!([]), } } } /// Main ChatGPT client pub struct ChatGptClient { client: Client, data: ChatGptData, ip_info: IpInfo, timezone_offset: i32, start_time: u64, sid: String, window_keys: Vec, reacts: Vec, } impl ChatGptClient { /// Create new ChatGPT client pub async fn new(proxy: Option<&str>) -> Result { let mut client_builder = Client::builder() .user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36") .cookie_store(true); if let Some(proxy_url) = proxy { let formatted_proxy = Utils::format_proxy(proxy_url)?; let proxy = Proxy::all(&formatted_proxy).map_err(|e| { ChatGptError::invalid_proxy(format!("Failed to create proxy: {}", e)) })?; client_builder = client_builder.proxy(proxy); } let client = client_builder.build()?; let ip_info = IpInfo::fetch(&client).await?; // Calculate timezone offset let timezone_offset = match ip_info.timezone.parse::() { Ok(tz) => { let now = Utc::now().with_timezone(&tz); now.offset().fix().local_minus_utc() / 60 } Err(_) => 0, // Default to UTC if parsing fails }; let start_time = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_millis() as u64; let sid = Uuid::new_v4().to_string(); // Generate dynamic reacts and window_keys like Python version let reacts = vec![ "location".to_string(), format!("__reactContainer${}", Utils::generate_react_id()), format!("_reactListening{}", Utils::generate_react_id()), ]; let window_keys: Vec = WINDOW_KEYS.iter().map(|s| s.to_string()).collect(); let mut instance = Self { client, data: ChatGptData::default(), ip_info, timezone_offset, start_time, sid, window_keys, reacts, }; instance.fetch_cookies().await?; Ok(instance) } /// Fetch initial cookies and configuration async fn fetch_cookies(&mut self) -> Result<()> { let response = self.client.get("https://chatgpt.com").send().await?; let html = response.text().await?; // Extract build version if let Some(prod) = Utils::between(&html, r#"data-build=""#, r#"""#) { self.data.prod = prod; } // Extract device ID from cookies if let Some(device_id) = self.get_cookie_value("oai-did") { self.data.device_id = device_id; } self.initialize_config()?; Ok(()) } /// Initialize configuration array fn initialize_config(&mut self) -> Result<()> { use rand::Rng; let mut rng = rand::rng(); // Calculate timezone name let timezone_name = match self.ip_info.timezone.parse::() { Ok(tz) => { let now = Utc::now().with_timezone(&tz); format!( "GMT{:+05} ({})", now.offset().fix().local_minus_utc() / 3600, tz.name() ) } Err(_) => "GMT+0000 (UTC)".to_string(), }; // Generate formatted timestamp let now = Utc::now(); let formatted_time = format!( "{}", now.format(&format!("%a %b %d %Y %H:%M:%S {}", timezone_name)) ); // Random selection from reacts and window_keys let selected_react = if !self.reacts.is_empty() { self.reacts[rng.random_range(0..self.reacts.len())].clone() } else { String::new() }; let selected_window_key = if !self.window_keys.is_empty() { self.window_keys[rng.random_range(0..self.window_keys.len())].clone() } else { String::new() }; self.data.config = json!([ 4880, formatted_time, 4294705152_u64, rng.random::(), "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", Value::Null, &self.data.prod, "de-DE", "de-DE,de,en-US,en", rng.random::(), "webkitGetUserMedia−function webkitGetUserMedia() { [native code] }", selected_react, selected_window_key, rng.random_range(800.0..1400.0) + rng.random::(), &self.sid, "", 20, self.start_time ]); Ok(()) } /// Get cookie value by name fn get_cookie_value(&self, name: &str) -> Option { // This is simplified - in practice you'd extract from the cookie store if name == "oai-did" { Some(Uuid::new_v4().to_string()) } else { None } } /// Get chat requirements tokens async fn get_tokens(&mut self) -> Result<()> { let mut headers = Headers::requirements(); headers.insert("oai-client-version".to_string(), self.data.prod.clone()); headers.insert("oai-device-id".to_string(), self.data.device_id.clone()); // Update config with new timing let process_time = { let mut rng = rand::rng(); rng.random_range(1400..2000) }; self.update_config_timing(process_time)?; let p_value = Challenges::generate_token(self.data.config.clone())?; self.data.vm_token = Some(p_value.clone()); let payload = json!({ "p": p_value }); let mut request = self .client .post("https://chatgpt.com/backend-anon/sentinel/chat-requirements") .json(&payload); // Apply headers for (key, value) in headers { request = request.header(key, value); } let response = request.send().await?; if response.status().is_success() { let json: Value = response.json().await?; if let Some(token) = json.get("token").and_then(|v| v.as_str()) { self.data.token = token.to_string(); } self.data.proofofwork = json.get("proofofwork").cloned(); if let Some(turnstile) = json.get("turnstile") { if let Some(dx) = turnstile.get("dx").and_then(|v| v.as_str()) { self.data.bytecode = Some(dx.to_string()); } } } else { return Err(ChatGptError::authentication( "Failed to get chat requirements", )); } Ok(()) } /// Update config timing values fn update_config_timing(&mut self, process_time: i32) -> Result<()> { if let Some(config_array) = self.data.config.as_array_mut() { let mut rng = rand::rng(); // Update timestamp config_array[1] = json!(format!( "{}", Utc::now().format("%a %b %d %Y %H:%M:%S GMT%z (UTC)") )); // Update random values config_array[3] = json!(rng.random::()); config_array[9] = json!(rng.random::()); // Update timing config_array[13] = json!(process_time as f64 + rng.random::()); } Ok(()) } /// Get conduit token async fn get_conduit(&self, next: bool) -> Result { let mut headers = Headers::conduit(); headers.insert("oai-client-version".to_string(), self.data.prod.clone()); headers.insert("oai-device-id".to_string(), self.data.device_id.clone()); let payload = if next { json!({ "action": "next", "fork_from_shared_post": false, "conversation_id": self.data.conversation_id, "parent_message_id": self.data.parent_message_id, "model": "auto", "timezone_offset_min": self.timezone_offset, "timezone": self.ip_info.timezone, "history_and_training_disabled": true, "conversation_mode": { "kind": "primary_assistant" }, "system_hints": [], "supports_buffering": true, "supported_encodings": ["v1"] }) } else { json!({ "action": "next", "fork_from_shared_post": false, "parent_message_id": "client-created-root", "model": "auto", "timezone_offset_min": self.timezone_offset, "timezone": self.ip_info.timezone, "history_and_training_disabled": true, "conversation_mode": { "kind": "primary_assistant" }, "system_hints": [], "supports_buffering": true, "supported_encodings": ["v1"] }) }; let mut request = self .client .post("https://chatgpt.com/backend-anon/f/conversation/prepare") .json(&payload); // Apply headers for (key, value) in headers { request = request.header(key, value); } let response = request.send().await?; let json: Value = response.json().await?; if let Some(conduit_token) = json.get("conduit_token").and_then(|v| v.as_str()) { Ok(conduit_token.to_string()) } else { Err(ChatGptError::authentication("Failed to get conduit token")) } } /// Start a conversation pub async fn start_conversation(&mut self, message: &str) -> Result { self.get_tokens().await?; let conduit_token = self.get_conduit(false).await?; let (echo_logs, time_since_loaded) = { let mut rng = rand::rng(); let time_1 = rng.random_range(6000..9000); let echo_logs = format!("0,{},1,{}", time_1, time_1 + rng.random_range(1000..1200)); let time_since_loaded = rng.random_range(3..6); (echo_logs, time_since_loaded) }; // Solve proof of work let proof_token = if let Some(pow) = self.data.proofofwork.as_ref() { let seed = pow.get("seed").and_then(|v| v.as_str()).unwrap_or(""); let difficulty = pow.get("difficulty").and_then(|v| v.as_str()).unwrap_or(""); Challenges::solve_pow(seed, difficulty, self.data.config.clone())? } else { return Err(ChatGptError::authentication( "No proof of work challenge received", )); }; // Get turnstile token let turnstile_token = if let (Some(bytecode), Some(vm_token)) = (self.data.bytecode.as_deref(), self.data.vm_token.as_deref()) { VM::get_turnstile(bytecode, vm_token, &self.ip_info.without_timezone())? } else { return Err(ChatGptError::authentication("Missing bytecode or VM token")); }; // Prepare conversation headers let mut headers = Headers::conversation(); headers.insert("oai-client-version".to_string(), self.data.prod.clone()); headers.insert("oai-device-id".to_string(), self.data.device_id.clone()); headers.insert("oai-echo-logs".to_string(), echo_logs.clone()); headers.insert( "openai-sentinel-chat-requirements-token".to_string(), self.data.token.clone(), ); headers.insert("openai-sentinel-proof-token".to_string(), proof_token); headers.insert( "openai-sentinel-turnstile-token".to_string(), turnstile_token, ); headers.insert("x-conduit-token".to_string(), conduit_token); let conversation_payload = json!({ "action": "next", "messages": [{ "id": Uuid::new_v4().to_string(), "author": { "role": "user" }, "create_time": SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs_f64(), "content": { "content_type": "text", "parts": [message] }, "metadata": { "selected_github_repos": [], "selected_all_github_repos": false, "serialization_metadata": { "custom_symbol_offsets": [] } } }], "parent_message_id": "client-created-root", "model": "auto", "timezone_offset_min": self.timezone_offset, "timezone": self.ip_info.timezone, "history_and_training_disabled": true, "conversation_mode": { "kind": "primary_assistant" }, "enable_message_followups": true, "system_hints": [], "supports_buffering": true, "supported_encodings": ["v1"], "client_contextual_info": { "is_dark_mode": true, "time_since_loaded": time_since_loaded, "page_height": 1219, "page_width": 3440, "pixel_ratio": 1, "screen_height": 1440, "screen_width": 3440 }, "paragen_cot_summary_display_override": "allow", "force_parallel_switch": "auto" }); let mut request = self .client .post("https://chatgpt.com/backend-anon/f/conversation") .json(&conversation_payload); // Apply headers for (key, value) in headers { request = request.header(key, value); } let response = request.send().await?; let response_text = response.text().await?; if response_text.contains("Unusual activity") { return Err(ChatGptError::IpFlagged); } // Extract conversation data if let Some(conversation_id) = Utils::between(&response_text, r#""conversation_id": ""#, r#"""#) { self.data.conversation_id = Some(conversation_id); } if let Some(parent_message_id) = Utils::between(&response_text, r#""message_id": ""#, r#"""#) { self.data.parent_message_id = Some(parent_message_id); } let parsed_response = Utils::parse_event_stream(&response_text); Ok(parsed_response) } /// Upload an image for multimodal conversation pub async fn upload_image(&mut self, image_data: &str) -> Result<()> { let mut headers = Headers::requirements(); headers.insert("oai-client-version".to_string(), self.data.prod.clone()); headers.insert("oai-device-id".to_string(), self.data.device_id.clone()); // Generate file name let file_name = Uuid::new_v4().to_string(); self.data.file_name = Some(format!("{}.png", file_name)); // Decode base64 image data let image_bytes = if image_data.starts_with("data:image") { let base64_part = image_data.split(',').nth(1).unwrap_or(image_data); general_purpose::STANDARD.decode(base64_part)? } else { general_purpose::STANDARD.decode(image_data)? }; self.data.file_size = Some(image_bytes.len()); // Get image dimensions let img = ImageReader::new(Cursor::new(&image_bytes)) .with_guessed_format()? .decode()?; self.data.image_width = Some(img.width()); self.data.image_height = Some(img.height()); // Request file upload let image_payload = json!({ "file_name": self.data.file_name.as_ref().unwrap(), "file_size": self.data.file_size.unwrap(), "use_case": "multimodal", "timezone_offset_min": self.timezone_offset, "reset_rate_limits": false }); let mut request = self .client .post("https://chatgpt.com/backend-anon/files") .json(&image_payload); // Apply headers for (key, value) in headers { request = request.header(key, value); } let response = request.send().await?; let upload_response: Value = response.json().await?; if let Some(file_id) = upload_response.get("file_id").and_then(|v| v.as_str()) { self.data.file_id = Some(file_id.to_string()); } if let Some(upload_url) = upload_response.get("upload_url").and_then(|v| v.as_str()) { // Upload the file let upload_response = self.client.put(upload_url).body(image_bytes).send().await?; if !upload_response.status().is_success() { return Err(ChatGptError::authentication("Failed to upload image")); } // Process the uploaded file let mut process_headers = Headers::requirements(); process_headers.insert("oai-client-version".to_string(), self.data.prod.clone()); process_headers.insert("oai-device-id".to_string(), self.data.device_id.clone()); let process_payload = json!({ "file_id": self.data.file_id.as_ref().unwrap(), "use_case": "multimodal", "index_for_retrieval": false, "file_name": self.data.file_name.as_ref().unwrap() }); let mut process_request = self .client .post("https://chatgpt.com/backend-anon/files/process_upload_stream") .json(&process_payload); // Apply headers for (key, value) in process_headers { process_request = process_request.header(key, value); } let process_response = process_request.send().await?; let process_text = process_response.text().await?; if !process_text.contains("Succeeded processing") { return Err(ChatGptError::authentication( "Failed to process uploaded image", )); } } else { return Err(ChatGptError::authentication("Failed to get upload URL")); } Ok(()) } /// Start a conversation with an image pub async fn start_with_image(&mut self, message: &str, image_data: &str) -> Result { self.get_tokens().await?; let conduit_token = self.get_conduit(false).await?; self.upload_image(image_data).await?; let (echo_logs, time_since_loaded) = { let mut rng = rand::rng(); let time_1 = rng.random_range(6000..9000); let echo_logs = format!("0,{},1,{}", time_1, time_1 + rng.random_range(1000..1200)); let time_since_loaded = rng.random_range(3..6); (echo_logs, time_since_loaded) }; // Solve proof of work let proof_token = if let Some(pow) = self.data.proofofwork.as_ref() { let seed = pow.get("seed").and_then(|v| v.as_str()).unwrap_or(""); let difficulty = pow.get("difficulty").and_then(|v| v.as_str()).unwrap_or(""); Challenges::solve_pow(seed, difficulty, self.data.config.clone())? } else { return Err(ChatGptError::authentication( "No proof of work challenge received", )); }; // Get turnstile token let turnstile_token = if let (Some(bytecode), Some(vm_token)) = (self.data.bytecode.as_deref(), self.data.vm_token.as_deref()) { VM::get_turnstile(bytecode, vm_token, &self.ip_info.without_timezone())? } else { return Err(ChatGptError::authentication("Missing bytecode or VM token")); }; // Prepare conversation headers let mut headers = Headers::conversation(); headers.insert("oai-client-version".to_string(), self.data.prod.clone()); headers.insert("oai-device-id".to_string(), self.data.device_id.clone()); headers.insert("oai-echo-logs".to_string(), echo_logs.clone()); headers.insert( "openai-sentinel-chat-requirements-token".to_string(), self.data.token.clone(), ); headers.insert("openai-sentinel-proof-token".to_string(), proof_token); headers.insert( "openai-sentinel-turnstile-token".to_string(), turnstile_token, ); headers.insert("x-conduit-token".to_string(), conduit_token); let conversation_payload = json!({ "action": "next", "messages": [{ "id": Uuid::new_v4().to_string(), "author": { "role": "user" }, "create_time": SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs_f64(), "content": { "content_type": "multimodal_text", "parts": [ { "content_type": "image_asset_pointer", "asset_pointer": format!("file-service://{}", self.data.file_id.as_ref().unwrap()), "size_bytes": self.data.file_size.unwrap(), "width": self.data.image_width.unwrap(), "height": self.data.image_height.unwrap() }, message ] }, "metadata": { "attachments": [{ "id": self.data.file_id.as_ref().unwrap(), "size": self.data.file_size.unwrap(), "name": self.data.file_name.as_ref().unwrap(), "mime_type": "image/png", "width": self.data.image_width.unwrap(), "height": self.data.image_height.unwrap(), "source": "local" }], "selected_github_repos": [], "selected_all_github_repos": false, "serialization_metadata": { "custom_symbol_offsets": [] } } }], "parent_message_id": "client-created-root", "model": "auto", "timezone_offset_min": self.timezone_offset, "timezone": self.ip_info.timezone, "history_and_training_disabled": true, "conversation_mode": { "kind": "primary_assistant" }, "enable_message_followups": true, "system_hints": [], "supports_buffering": true, "supported_encodings": ["v1"], "client_contextual_info": { "is_dark_mode": true, "time_since_loaded": time_since_loaded, "page_height": 1219, "page_width": 3440, "pixel_ratio": 1, "screen_height": 1440, "screen_width": 3440 }, "paragen_cot_summary_display_override": "allow", "force_parallel_switch": "auto" }); let mut request = self .client .post("https://chatgpt.com/backend-anon/f/conversation") .json(&conversation_payload); // Apply headers for (key, value) in headers { request = request.header(key, value); } let response = request.send().await?; let response_text = response.text().await?; if response_text.contains("Unusual activity") { return Err(ChatGptError::IpFlagged); } // Extract conversation data if let Some(conversation_id) = Utils::between(&response_text, r#""conversation_id": ""#, r#"""#) { self.data.conversation_id = Some(conversation_id); } if let Some(parent_message_id) = Utils::between(&response_text, r#""message_id": ""#, r#"""#) { self.data.parent_message_id = Some(parent_message_id); } let parsed_response = Utils::parse_event_stream(&response_text); Ok(parsed_response) } /// Send a question and get response pub async fn ask_question(&mut self, message: &str) -> Result { self.start_conversation(message).await } /// Send a question with an image and get response pub async fn ask_question_with_image( &mut self, message: &str, image_data: &str, ) -> Result { self.start_with_image(message, image_data).await } /// Hold a conversation with ability to continue chatting pub async fn hold_conversation(&mut self, message: &str, new: bool) -> Result { let mut index = 2000; if new { self.start_conversation(message).await?; } // Get conduit token let conduit_token = self.get_conduit(true).await?; // Get tokens self.get_tokens().await?; index += 3000; let echo_logs = { let mut rng = rand::rng(); let time_1 = rng.random_range(index..(index + 3000)); format!("0,{},1,{}", time_1, time_1 + rng.random_range(1000..1200)) }; // Solve proof of work let proof_token = if let Some(proofofwork) = &self.data.proofofwork { if let (Some(seed), Some(difficulty)) = ( proofofwork.get("seed").and_then(|v| v.as_str()), proofofwork.get("difficulty").and_then(|v| v.as_str()), ) { Challenges::solve_pow(seed, difficulty, self.data.config.clone())? } else { return Err(ChatGptError::invalid_response( "Missing proof of work data".to_string(), )); } } else { return Err(ChatGptError::invalid_response( "Missing proof of work configuration".to_string(), )); }; // Get turnstile token let turnstile_token = if let (Some(bytecode), Some(vm_token)) = (self.data.bytecode.as_deref(), self.data.vm_token.as_deref()) { VM::get_turnstile(bytecode, vm_token, &self.ip_info.ip)? } else { return Err(ChatGptError::invalid_response( "Missing VM data".to_string(), )); }; // Prepare headers let mut headers = Headers::conversation(); headers.insert("oai-client-version".to_string(), self.data.prod.clone()); headers.insert("oai-device-id".to_string(), self.data.device_id.clone()); headers.insert("oai-echo-logs".to_string(), echo_logs); headers.insert( "openai-sentinel-chat-requirements-token".to_string(), self.data.token.clone(), ); headers.insert("openai-sentinel-proof-token".to_string(), proof_token); headers.insert( "openai-sentinel-turnstile-token".to_string(), turnstile_token, ); headers.insert("x-conduit-token".to_string(), conduit_token); let new_message = if new { // In interactive mode, would prompt for input // For now, use the provided message message.to_string() } else { message.to_string() }; let conversation_data = json!({ "action": "next", "messages": [{ "id": Uuid::new_v4().to_string(), "author": { "role": "user" }, "create_time": (SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs_f64()), "content": { "content_type": "text", "parts": [new_message] }, "metadata": { "selected_github_repos": [], "selected_all_github_repos": false, "serialization_metadata": { "custom_symbol_offsets": [] } } }], "conversation_id": self.data.conversation_id, "parent_message_id": self.data.parent_message_id, "model": "auto", "timezone_offset_min": self.timezone_offset, "timezone": self.ip_info.timezone, "history_and_training_disabled": true, "conversation_mode": { "kind": "primary_assistant" }, "enable_message_followups": true, "system_hints": [], "supports_buffering": true, "supported_encodings": ["v1"], "client_contextual_info": { "is_dark_mode": true, "time_since_loaded": 17, "page_height": 1219, "page_width": 3440, "pixel_ratio": 1, "screen_height": 1440, "screen_width": 3440 }, "paragen_cot_summary_display_override": "allow", "force_parallel_switch": "auto" }); let mut request = self .client .post("https://chatgpt.com/backend-anon/f/conversation") .json(&conversation_data); // Apply headers for (key, value) in headers { request = request.header(&key, &value); } let response = request.send().await?; let response_text = response.text().await?; if response_text.contains("Unusual activity") { return Err(ChatGptError::invalid_response( "Your IP got flagged by chatgpt, retry with a new IP".to_string(), )); } // Update conversation state if let Some(conversation_id) = Utils::between(&response_text, "\"conversation_id\": \"", "\"") { self.data.conversation_id = Some(conversation_id); } if let Some(parent_message_id) = Utils::between(&response_text, "\"message_id\": \"", "\"") { self.data.parent_message_id = Some(parent_message_id); } Ok(Utils::parse_event_stream(&response_text)) } }