From b41b4f8e17e4e06008d1e92d6ddde2ed8e291d94 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Mon, 7 Apr 2025 21:17:01 -0400 Subject: [PATCH] read/write same sora cookie, avoid cloudflare heuristics --- .../src/commands/image_generation_command.rs | 33 +++++------- .../src/commands/open_login_command.rs | 40 +++++++++++--- crates/desktop/tauri-4o/src/lib.rs | 3 +- crates/desktop/tauri-4o/src/state/app_dir.rs | 4 ++ .../tauri-4o/src/threads/login_thread.rs | 52 ++++++++++--------- .../src/utils/clear_all_webview_cookies.rs | 7 +++ crates/desktop/tauri-4o/src/utils/mod.rs | 4 +- .../src/utils/sora_webview_cookies.rs | 26 ++++++++++ .../openai_sora_client/src/credentials.rs | 2 +- 9 files changed, 114 insertions(+), 57 deletions(-) create mode 100644 crates/desktop/tauri-4o/src/utils/clear_all_webview_cookies.rs create mode 100644 crates/desktop/tauri-4o/src/utils/sora_webview_cookies.rs diff --git a/crates/desktop/tauri-4o/src/commands/image_generation_command.rs b/crates/desktop/tauri-4o/src/commands/image_generation_command.rs index 25ebe453cb..7b33eb414a 100644 --- a/crates/desktop/tauri-4o/src/commands/image_generation_command.rs +++ b/crates/desktop/tauri-4o/src/commands/image_generation_command.rs @@ -6,39 +6,27 @@ use image::codecs::png::{CompressionType, FilterType, PngEncoder}; use image::{DynamicImage, ImageReader}; use std::io::Cursor; use log::{error, info}; -use tauri::{AppHandle, Manager}; +use tauri::{AppHandle, Manager, State}; use openai_sora_client::credentials::SoraCredentials; use openai_sora_client::image_gen::common::{ImageSize, NumImages}; use openai_sora_client::image_gen::sora_image_gen_remix::{sora_image_gen_remix, SoraImageGenRemixRequest}; use openai_sora_client::upload::upload_media_from_bytes::sora_media_upload_from_bytes; use openai_sora_client::upload::upload_media_from_file::SoraMediaUploadRequest; +use crate::state::app_dir::AppDataRoot; #[tauri::command] pub async fn image_generation_command( image: &str, prompt: &str, app: AppHandle, + app_data_root: State<'_, AppDataRoot>, ) -> Result { info!("image_generation_command called; processing image..."); - for (w, webview) in app.webviews() { - info!("sending webview: {}", w); - //let result = webview.eval("window.location.replace('https://google.com');"); // works - //let result = webview.eval("document.body.innerHTML = 'hello'"); // works - let result = webview.eval("document.body.innerHTML = document.cookie"); - if let Err(err) = result { - error!("Error with webview: {:?}", err) - } - } - - if true { - return Ok("true".to_string()); - } - let bytes = BASE64_STANDARD.decode(image) .map_err(|err| format!("Base64 decode error: {}", err))?; - generate_image(bytes, prompt) + generate_image(bytes, prompt, &app_data_root) .await .map_err(|err| { error!("error: {:?}", err); @@ -48,8 +36,8 @@ pub async fn image_generation_command( Ok("success".to_string()) } -pub async fn generate_image(file_bytes: Vec, prompt: &str) -> AnyhowResult<()> { - let sora_credentials = get_credentials()?; +pub async fn generate_image(file_bytes: Vec, prompt: &str, app_data_root: &AppDataRoot) -> AnyhowResult<()> { + let sora_credentials = get_credentials(app_data_root)?; let filename = "image.png".to_string(); @@ -79,16 +67,21 @@ pub async fn generate_image(file_bytes: Vec, prompt: &str) -> AnyhowResult<( Ok(()) } -fn get_credentials() -> AnyhowResult { +fn get_credentials(app_data_root: &AppDataRoot) -> AnyhowResult { + let cookie_file = app_data_root.get_sora_cookie_file_path(); + let bearer = read_to_string("/Users/bt/dev/storyteller/storyteller-rust/test_data/temp/bearer.txt")? .trim() .to_string(); - let cookie= read_to_string("/Users/bt/dev/storyteller/storyteller-rust/test_data/temp/cookie.txt")? + + let cookie= read_to_string(cookie_file)? .trim() .to_string(); + let sentinel = read_to_string("/Users/bt/dev/storyteller/storyteller-rust/test_data/temp/sentinel.txt")? .trim() .to_string(); + Ok(SoraCredentials { bearer_token: bearer, cookie: cookie, diff --git a/crates/desktop/tauri-4o/src/commands/open_login_command.rs b/crates/desktop/tauri-4o/src/commands/open_login_command.rs index 351ac8b24d..be136ebb2d 100644 --- a/crates/desktop/tauri-4o/src/commands/open_login_command.rs +++ b/crates/desktop/tauri-4o/src/commands/open_login_command.rs @@ -1,8 +1,23 @@ +use std::time::Duration; use log::{error, info}; use once_cell::sync::Lazy; use tauri::{AppHandle, Manager, Url, WebviewUrl, WebviewWindowBuilder, WindowBuilder}; +use tokio::time::sleep_until; use errors::AnyhowResult; use crate::threads::login_thread::{LOGIN_WINDOW_NAME, SORA_LOGIN_URL}; +use crate::utils::clear_all_webview_cookies::clear_all_webview_cookies; + +pub const START_URL_STR: &str = "https://storyteller.ai/"; + +pub static START_URL: Lazy = Lazy::new(|| { + Url::parse(START_URL_STR).expect("URL should parse") +}); + +pub const SORA_HOMEPAGE_URL_STR: &str = "https://sora.com/"; + +pub static SORA_HOMEPAGE_URL : Lazy = Lazy::new(|| { + Url::parse(SORA_HOMEPAGE_URL_STR).expect("URL should parse") +}); #[tauri::command] pub async fn open_login_command( @@ -23,9 +38,10 @@ pub async fn open_login_command( async fn do_open_login(app: &AppHandle) -> AnyhowResult<()> { info!("Building login window..."); - let url = WebviewUrl::External(SORA_LOGIN_URL.clone()); + let url = WebviewUrl::External(START_URL.clone()); - let _window = WebviewWindowBuilder::new(app, LOGIN_WINDOW_NAME, url) + let window = WebviewWindowBuilder::new(app, LOGIN_WINDOW_NAME, url) + //.user_agent(openai_sora_client::credentials::USER_AGENT) .always_on_top(true) .title("Login to OpenAI") .center() @@ -36,12 +52,20 @@ async fn do_open_login(app: &AppHandle) -> AnyhowResult<()> { .devtools(true) .build()?; - //info!("Iterating webviews..."); - //for (name, webview) in window.webviews() { - // info!("Webview URL: {:?}", webview.url()); - // webview.navigate(LOGIN_URL.clone())?; - // webview.eval(&format!("window.location.replace('{}');", LOGIN_URL_STR))?; - //} + for (name, webview) in window.webviews() { + if name != LOGIN_WINDOW_NAME { + continue; + } + + clear_all_webview_cookies(&webview)?; + + // NB: We're starting to get Cloudflare protection screens. Let's try to avoid. + webview.navigate(SORA_HOMEPAGE_URL.clone())?; + tokio::time::sleep(Duration::from_millis(100)).await; + + webview.navigate(SORA_LOGIN_URL.clone())?; + break; + } info!("Done."); Ok(()) diff --git a/crates/desktop/tauri-4o/src/lib.rs b/crates/desktop/tauri-4o/src/lib.rs index 9b98c17298..c5ff9c8fe6 100644 --- a/crates/desktop/tauri-4o/src/lib.rs +++ b/crates/desktop/tauri-4o/src/lib.rs @@ -23,7 +23,6 @@ pub fn run() { let config = AppConfig::init() .expect("config should load"); - let app_data_root = config.app_data_root.clone(); let app_data_root2 = config.app_data_root.clone(); @@ -50,7 +49,7 @@ pub fn run() { //} let app = app.handle().clone(); - tauri::async_runtime::spawn(login_thread(app)); + tauri::async_runtime::spawn(login_thread(app, app_data_root2)); Ok(()) }) diff --git a/crates/desktop/tauri-4o/src/state/app_dir.rs b/crates/desktop/tauri-4o/src/state/app_dir.rs index cbfa8777a7..f255641618 100644 --- a/crates/desktop/tauri-4o/src/state/app_dir.rs +++ b/crates/desktop/tauri-4o/src/state/app_dir.rs @@ -114,6 +114,10 @@ impl AppDataRoot { pub fn log_file_name_str(&self) -> &str { &self.log_file_name_string } + + pub fn get_sora_cookie_file_path(&self) -> PathBuf { + self.path.join("sora_cookies.txt") + } } impl AppAssetsDir { diff --git a/crates/desktop/tauri-4o/src/threads/login_thread.rs b/crates/desktop/tauri-4o/src/threads/login_thread.rs index 418dac09d1..e9368bcfd6 100644 --- a/crates/desktop/tauri-4o/src/threads/login_thread.rs +++ b/crates/desktop/tauri-4o/src/threads/login_thread.rs @@ -1,9 +1,13 @@ +use std::fs::OpenOptions; +use std::io::Write; use anyhow::anyhow; use log::{error, info}; use once_cell::sync::Lazy; use reqwest::Url; use tauri::{AppHandle, Manager, Webview}; use errors::AnyhowResult; +use crate::state::app_dir::AppDataRoot; +use crate::utils::sora_webview_cookies::get_all_sora_cookies_as_string; pub const LOGIN_WINDOW_NAME: &str = "login_window"; @@ -13,11 +17,17 @@ pub static SORA_LOGIN_URL: Lazy = Lazy::new(|| { Url::parse(SORA_LOGIN_URL_STR).expect("URL should parse") }); -pub async fn login_thread(app: AppHandle) -> ! { +pub const SORA_ROOT_URL_STR: &str = "https://sora.com/"; + +pub static SORA_ROOT_URL: Lazy = Lazy::new(|| { + Url::parse(SORA_ROOT_URL_STR).expect("URL should parse") +}); + +pub async fn login_thread(app: AppHandle, app_data_root: AppDataRoot) -> ! { loop { for (window_name, webview) in app.webviews() { if window_name == LOGIN_WINDOW_NAME { - let result = check_login_window(&webview).await; + let result = check_login_window(&webview, &app_data_root).await; if let Err(err) = result { error!("Error checking login window: {:?}", err); } @@ -28,9 +38,10 @@ pub async fn login_thread(app: AppHandle) -> ! { } } -async fn check_login_window(webview: &Webview) -> AnyhowResult<()> { +async fn check_login_window(webview: &Webview, app_data_root: &AppDataRoot) -> AnyhowResult<()> { clear_browsing_data_on_test_domain(webview)?; - keep_on_task(webview)?; + //keep_on_task(webview)?; + extract_cookies_to_file(webview, app_data_root)?; // TODO: Other maintenance tasks ... Ok(()) } @@ -73,29 +84,20 @@ fn clear_browsing_data_on_test_domain(webview: &Webview) -> AnyhowResult<()> { Ok(()) } -pub fn random_behavior(webview: &Webview) -> AnyhowResult<()> { - let url = webview.url(); - //webview.navigate(url)?; - //webview.cookies_for_url(url)?; +// TODO(bt,2025-04-07): Heuristic to detect when logged in. Only write when logged in. +fn extract_cookies_to_file(webview: &Webview, app_data_root: &AppDataRoot) -> AnyhowResult<()> { + let cookies = get_all_sora_cookies_as_string(webview)?; - let rand = rand::random_range(0..5u8); - match rand { - 0..3 => { - redirect_google(&webview)?; - } - _ => { - write_cookies_to_body(&webview)?; - } - } - Ok(()) -} + let cookie_file = app_data_root.get_sora_cookie_file_path(); -pub fn redirect_google(webview: &Webview) -> AnyhowResult<()> { - webview.eval("window.location.replace('https://google.com');")?; - Ok(()) -} + let mut file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(cookie_file)?; + + file.write_all(cookies.as_bytes())?; + file.flush()?; -pub fn write_cookies_to_body(webview: &Webview) -> AnyhowResult<()> { - webview.eval("document.body.innerHTML = document.cookie")?; Ok(()) } diff --git a/crates/desktop/tauri-4o/src/utils/clear_all_webview_cookies.rs b/crates/desktop/tauri-4o/src/utils/clear_all_webview_cookies.rs new file mode 100644 index 0000000000..a5fefd92df --- /dev/null +++ b/crates/desktop/tauri-4o/src/utils/clear_all_webview_cookies.rs @@ -0,0 +1,7 @@ +use tauri::Webview; +use errors::AnyhowResult; + +pub fn clear_all_webview_cookies(webview: &Webview) -> AnyhowResult<()> { + webview.clear_all_browsing_data()?; + Ok(()) +} diff --git a/crates/desktop/tauri-4o/src/utils/mod.rs b/crates/desktop/tauri-4o/src/utils/mod.rs index b1b6b4f003..05b034950e 100644 --- a/crates/desktop/tauri-4o/src/utils/mod.rs +++ b/crates/desktop/tauri-4o/src/utils/mod.rs @@ -1 +1,3 @@ -pub mod image; \ No newline at end of file +pub mod clear_all_webview_cookies; +pub mod image; +pub mod sora_webview_cookies; \ No newline at end of file diff --git a/crates/desktop/tauri-4o/src/utils/sora_webview_cookies.rs b/crates/desktop/tauri-4o/src/utils/sora_webview_cookies.rs new file mode 100644 index 0000000000..e85c887a81 --- /dev/null +++ b/crates/desktop/tauri-4o/src/utils/sora_webview_cookies.rs @@ -0,0 +1,26 @@ +use once_cell::sync::Lazy; +use reqwest::Url; +use tauri::Webview; +use tauri::webview::Cookie; +use errors::AnyhowResult; + +const SORA_ROOT_COOKIE_URL_STR: &str = "https://sora.com/"; + +static SORA_ROOT_COOKIE_URL: Lazy = Lazy::new(|| { + Url::parse(SORA_ROOT_COOKIE_URL_STR).expect("URL should parse") +}); + +pub fn get_all_sora_cookies(webview: &Webview) -> AnyhowResult> { + let cookies = webview.cookies_for_url(SORA_ROOT_COOKIE_URL.clone())?; + Ok(cookies) +} + +pub fn get_all_sora_cookies_as_string(webview: &Webview) -> AnyhowResult { + let cookies = get_all_sora_cookies(webview)?; + let cookie_string = cookies + .iter() + .map(|cookie| format!("{}={}", cookie.name(), cookie.value())) + .collect::>() + .join("; "); + Ok(cookie_string) +} diff --git a/crates/lib/api_clients/openai_sora_client/src/credentials.rs b/crates/lib/api_clients/openai_sora_client/src/credentials.rs index a2609cf709..c9f4a68a31 100644 --- a/crates/lib/api_clients/openai_sora_client/src/credentials.rs +++ b/crates/lib/api_clients/openai_sora_client/src/credentials.rs @@ -4,7 +4,7 @@ use serde_derive::Deserialize; use errors::AnyhowResult; // NB: It appears that the sentinel may require a matching user agent. -pub (crate) const USER_AGENT : &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"; +pub const USER_AGENT : &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"; #[derive(Clone, Deserialize)] pub struct SoraCredentials {