Compare commits
2 commits
bcb7703ea6
...
e15da79204
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e15da79204 | ||
![]() |
a3118dcf0a |
7 changed files with 209 additions and 129 deletions
|
@ -2,22 +2,22 @@ let items = FIND "gb.rob", "ACTION=EMUL1"
|
||||||
FOR EACH item IN items
|
FOR EACH item IN items
|
||||||
|
|
||||||
PRINT item.company
|
PRINT item.company
|
||||||
let website = GET WEBSITE item.company "website"
|
let website = WEBSITE OF item.company
|
||||||
PRINT website
|
PRINT website
|
||||||
|
|
||||||
WAIT 10
|
WAIT 10
|
||||||
let page = GET website
|
let page = GET website
|
||||||
|
|
||||||
let prompt = "Create a website for " + item.company + " with the following details: " + page
|
let prompt = "Create a website for " + item.company + " with the following details: " + page
|
||||||
|
|
||||||
let alias = LLM "Return a single word for {item.company} like a token, no spaces, no special characters, no numbers, no uppercase letters."
|
|
||||||
|
|
||||||
CREATE SITE item.company + "bot", website, "site", prompt
|
let alias = LLM "Return a single word for " + item.company + " like a token, no spaces, no special characters, no numbers, no uppercase letters."
|
||||||
|
|
||||||
|
CREATE SITE item.company + "bot", item.company, website, "site", prompt
|
||||||
|
|
||||||
let to = item.emailcto
|
let to = item.emailcto
|
||||||
let subject = "Simulador criado " + item.company
|
let subject = "Simulador criado " + item.company
|
||||||
let body = "O simulador " + item.company + " foi criado com sucesso. Acesse o site: " + item.company + "bot"
|
let body = "O simulador " + item.company + " foi criado com sucesso. Acesse o site: " + item.company + "bot"
|
||||||
|
|
||||||
CREATE DRAFT to, subject, body
|
CREATE_DRAFT to, subject, body
|
||||||
|
|
||||||
NEXT item
|
NEXT item
|
||||||
|
|
|
@ -9,7 +9,7 @@ pub fn create_draft_keyword(state: &AppState, engine: &mut Engine) {
|
||||||
|
|
||||||
engine
|
engine
|
||||||
.register_custom_syntax(
|
.register_custom_syntax(
|
||||||
&["CREATE", "DRAFT", "$expr$", ",", "$expr$", ",", "$expr$"],
|
&["CREATE_DRAFT", "$expr$", ",", "$expr$", ",", "$expr$"],
|
||||||
true, // Statement
|
true, // Statement
|
||||||
move |context, inputs| {
|
move |context, inputs| {
|
||||||
// Extract arguments
|
// Extract arguments
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
use rhai::Dynamic;
|
use rhai::Dynamic;
|
||||||
use rhai::Engine;
|
use rhai::Engine;
|
||||||
|
use std::error::Error;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::services::state::AppState;
|
use crate::services::state::AppState;
|
||||||
|
use crate::services::utils;
|
||||||
|
|
||||||
pub fn create_site_keyword(_state: &AppState, engine: &mut Engine) {
|
pub fn create_site_keyword(state: &AppState, engine: &mut Engine) {
|
||||||
|
let state_clone = state.clone();
|
||||||
engine
|
engine
|
||||||
.register_custom_syntax(
|
.register_custom_syntax(
|
||||||
&[
|
&[
|
||||||
|
@ -19,29 +22,44 @@ pub fn create_site_keyword(_state: &AppState, engine: &mut Engine) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let _name = context.eval_expression_tree(&inputs[0])?;
|
let _name = context.eval_expression_tree(&inputs[0])?;
|
||||||
let company = context.eval_expression_tree(&inputs[1])?;
|
|
||||||
let _website = context.eval_expression_tree(&inputs[2])?;
|
let _website = context.eval_expression_tree(&inputs[2])?;
|
||||||
let _template = context.eval_expression_tree(&inputs[3])?;
|
let _template = context.eval_expression_tree(&inputs[3])?;
|
||||||
let prompt = context.eval_expression_tree(&inputs[4])?;
|
let prompt = context.eval_expression_tree(&inputs[4])?;
|
||||||
|
let ai_config = state_clone.config.as_ref().expect("Config must be initialized").ai.clone();
|
||||||
|
// Use the same pattern as find_keyword
|
||||||
|
let fut = create_site(&ai_config, _name, prompt);
|
||||||
|
let result =
|
||||||
|
tokio::task::block_in_place(|| tokio::runtime::Handle::current().block_on(fut))
|
||||||
|
.map_err(|e| format!("HTTP request failed: {}", e))?;
|
||||||
|
|
||||||
// Call the LLM to generate the HTML content
|
Ok(Dynamic::from(result))
|
||||||
let llm_result = context.call_fn::<String>("chat", (prompt.to_string(),))?;
|
|
||||||
|
|
||||||
// Create the directory structure
|
|
||||||
let base_path = "/opt/gbo/tenants/pragmatismo/proxy/data/websites/sites.pragmatismo.com.br";
|
|
||||||
let site_name = format!("{}bot", company.to_string());
|
|
||||||
let full_path = format!("{}/{}", base_path, site_name);
|
|
||||||
|
|
||||||
// Create directory if it doesn't exist
|
|
||||||
fs::create_dir_all(&full_path).map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
// Write the HTML file
|
|
||||||
let index_path = Path::new(&full_path).join("index.html");
|
|
||||||
fs::write(index_path, llm_result).map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
println!("Site created at: {}", full_path);
|
|
||||||
Ok(Dynamic::UNIT)
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn create_site(
|
||||||
|
ai_config: &crate::services::config::AIConfig,
|
||||||
|
_name: Dynamic,
|
||||||
|
prompt: Dynamic,
|
||||||
|
) -> Result<String, Box<dyn Error + Send + Sync>> {
|
||||||
|
|
||||||
|
// Call the LLM to generate the HTML contents
|
||||||
|
let llm_result = utils::call_llm(&prompt.to_string(), &ai_config).await?;
|
||||||
|
|
||||||
|
// Create the directory structure
|
||||||
|
let base_path = "/opt/gbo/tenants/pragmatismo/proxy/data/websites/sites.pragmatismo.com.br";
|
||||||
|
let site_name = format!("{}", _name.to_string());
|
||||||
|
let full_path = format!("{}/{}", base_path, site_name);
|
||||||
|
|
||||||
|
// Create directory if it doesn't exist
|
||||||
|
fs::create_dir_all(&full_path).map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
// Write the HTML file
|
||||||
|
let index_path = Path::new(&full_path).join("index.html");
|
||||||
|
fs::write(index_path, llm_result).map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
println!("Site created at: {}", full_path);
|
||||||
|
Ok(full_path)
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use rhai::{Dynamic, Engine};
|
use rhai::{Dynamic, Engine};
|
||||||
use reqwest;
|
use reqwest;
|
||||||
use crate::services::state::AppState;
|
use crate::services::state::AppState;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
|
@ -11,22 +11,21 @@ pub fn get_website_keyword(state: &AppState, engine: &mut Engine) {
|
||||||
|
|
||||||
engine
|
engine
|
||||||
.register_custom_syntax(
|
.register_custom_syntax(
|
||||||
&["GET", "WEBSITE", "$expr$", "$expr$"],
|
&["WEBSITE", "OF", "$expr$"],
|
||||||
false,
|
false,
|
||||||
move |context, inputs| {
|
move |context, inputs| {
|
||||||
let search_term = context.eval_expression_tree(&inputs[0])?.to_string();
|
let search_term = context.eval_expression_tree(&inputs[0])?.to_string();
|
||||||
let website_hint = context.eval_expression_tree(&inputs[1])?.to_string();
|
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"GET WEBSITE executed - Search: '{}', Hint: '{}'",
|
"GET WEBSITE executed - Search: '{}'",
|
||||||
search_term, website_hint
|
search_term
|
||||||
);
|
);
|
||||||
|
|
||||||
let browser_pool_clone = browser_pool.clone();
|
let browser_pool_clone = browser_pool.clone();
|
||||||
let fut = execute_headless_browser_search(
|
let fut = execute_headless_browser_search(
|
||||||
browser_pool_clone,
|
browser_pool_clone,
|
||||||
&search_term,
|
&search_term
|
||||||
&website_hint,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let result =
|
let result =
|
||||||
|
@ -41,60 +40,45 @@ pub fn get_website_keyword(state: &AppState, engine: &mut Engine) {
|
||||||
|
|
||||||
pub async fn execute_headless_browser_search(
|
pub async fn execute_headless_browser_search(
|
||||||
browser_pool: Arc<BrowserPool>, // Adjust path as needed
|
browser_pool: Arc<BrowserPool>, // Adjust path as needed
|
||||||
search_term: &str,
|
search_term: &str) -> Result<String, Box<dyn Error + Send + Sync>> {
|
||||||
website_hint: &str,
|
|
||||||
) -> Result<String, Box<dyn Error + Send + Sync>> {
|
|
||||||
println!(
|
println!(
|
||||||
"Starting headless browser search: '{}' targeting '{}'",
|
"Starting headless browser search: '{}' ",
|
||||||
search_term, website_hint
|
search_term
|
||||||
);
|
);
|
||||||
|
|
||||||
let search_term = search_term.to_string();
|
let search_term = search_term.to_string();
|
||||||
let website_hint = website_hint.to_string();
|
|
||||||
|
|
||||||
let result = browser_pool
|
let result = browser_pool
|
||||||
.with_browser(|driver| {
|
.with_browser(|driver| {
|
||||||
Box::pin(async move { perform_search(driver, &search_term, &website_hint).await })
|
Box::pin(async move { perform_search(driver, &search_term).await })
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn perform_search(
|
async fn perform_search(
|
||||||
driver: WebDriver,
|
driver: WebDriver,
|
||||||
search_term: &str,
|
search_term: &str) -> Result<String, Box<dyn Error + Send + Sync>> {
|
||||||
website_hint: &str,
|
|
||||||
) -> Result<String, Box<dyn Error + Send + Sync>> {
|
|
||||||
// Configure the search query
|
|
||||||
let query = if website_hint.trim().is_empty() {
|
|
||||||
search_term.to_string()
|
|
||||||
} else {
|
|
||||||
format!("{} site:{}", search_term, website_hint)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Navigate to DuckDuckGo
|
// Navigate to DuckDuckGo
|
||||||
println!("Navigating to DuckDuckGo...");
|
|
||||||
driver.goto("https://duckduckgo.com").await?;
|
driver.goto("https://duckduckgo.com").await?;
|
||||||
|
|
||||||
// Wait for search box and type query
|
// Wait for search box and type query
|
||||||
println!("Searching for: {}", query);
|
let search_input = driver.find(By::Id("searchbox_input")).await?;
|
||||||
let search_input = driver.find(By::Name("q")).await?;
|
|
||||||
search_input.click().await?;
|
search_input.click().await?;
|
||||||
search_input.send_keys(&query).await?;
|
search_input.send_keys(search_term).await?;
|
||||||
|
|
||||||
// Submit search by pressing Enter
|
// Submit search by pressing Enter
|
||||||
search_input.send_keys("\n").await?;
|
search_input.send_keys("\n").await?;
|
||||||
|
|
||||||
// Wait for results to load
|
// Wait for results to load - using a modern result selector
|
||||||
driver.find(By::Css(".result")).await?;
|
driver.find(By::Css("[data-testid='result']")).await?;
|
||||||
sleep(Duration::from_millis(2000)).await; // Give extra time for JS
|
sleep(Duration::from_millis(2000)).await;
|
||||||
|
|
||||||
// Extract first result link
|
// Extract results
|
||||||
let results = extract_search_results(&driver).await?;
|
let results = extract_search_results(&driver).await?;
|
||||||
|
|
||||||
if !results.is_empty() {
|
if !results.is_empty() {
|
||||||
println!("Found {} results", results.len());
|
|
||||||
Ok(results[0].clone())
|
Ok(results[0].clone())
|
||||||
} else {
|
} else {
|
||||||
Ok("No results found".to_string())
|
Ok("No results found".to_string())
|
||||||
|
@ -106,20 +90,42 @@ async fn extract_search_results(
|
||||||
) -> Result<Vec<String>, Box<dyn Error + Send + Sync>> {
|
) -> Result<Vec<String>, Box<dyn Error + Send + Sync>> {
|
||||||
let mut results = Vec::new();
|
let mut results = Vec::new();
|
||||||
|
|
||||||
// Try different selectors for search results
|
// Try different selectors for search results, ordered by most specific to most general
|
||||||
let selectors = [
|
let selectors = [
|
||||||
"a[data-testid='result-title-a']", // Modern DuckDuckGo
|
// Modern DuckDuckGo (as seen in the HTML)
|
||||||
".result__a", // Classic DuckDuckGo
|
"a[data-testid='result-title-a']", // Primary result links
|
||||||
"a.result-link", // Alternative
|
"a[data-testid='result-extras-url-link']", // URL links in results
|
||||||
".result a[href]", // Generic result links
|
"a.eVNpHGjtxRBq_gLOfGDr", // Class-based selector for result titles
|
||||||
|
"a.Rn_JXVtoPVAFyGkcaXyK", // Class-based selector for URL links
|
||||||
|
".ikg2IXiCD14iVX7AdZo1 a", // Heading container links
|
||||||
|
".OQ_6vPwNhCeusNiEDcGp a", // URL container links
|
||||||
|
// Fallback selectors
|
||||||
|
".result__a", // Classic DuckDuckGo
|
||||||
|
"a.result-link", // Alternative
|
||||||
|
".result a[href]", // Generic result links
|
||||||
];
|
];
|
||||||
|
|
||||||
for selector in &selectors {
|
for selector in &selectors {
|
||||||
if let Ok(elements) = driver.find_all(By::Css(selector)).await {
|
if let Ok(elements) = driver.find_all(By::Css(selector)).await {
|
||||||
for element in elements {
|
for element in elements {
|
||||||
if let Ok(Some(href)) = element.attr("href").await {
|
if let Ok(Some(href)) = element.attr("href").await {
|
||||||
if href.starts_with("http") && !href.contains("duckduckgo.com") {
|
// Filter out internal and non-http links
|
||||||
results.push(href);
|
if href.starts_with("http")
|
||||||
|
&& !href.contains("duckduckgo.com")
|
||||||
|
&& !href.contains("duck.co")
|
||||||
|
&& !results.contains(&href) {
|
||||||
|
|
||||||
|
// Get the display URL for verification
|
||||||
|
let display_url = if let Ok(text) = element.text().await {
|
||||||
|
text.trim().to_string()
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only add if it looks like a real result (not an ad or internal link)
|
||||||
|
if !display_url.is_empty() && !display_url.contains("Ad") {
|
||||||
|
results.push(href);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,5 +135,8 @@ async fn extract_search_results(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deduplicate results
|
||||||
|
results.dedup();
|
||||||
|
|
||||||
Ok(results)
|
Ok(results)
|
||||||
}
|
}
|
|
@ -28,8 +28,8 @@ impl ScriptService {
|
||||||
find_keyword(state, &mut engine);
|
find_keyword(state, &mut engine);
|
||||||
for_keyword(state, &mut engine);
|
for_keyword(state, &mut engine);
|
||||||
llm_keyword(state, &mut engine);
|
llm_keyword(state, &mut engine);
|
||||||
get_keyword(state, &mut engine);
|
|
||||||
get_website_keyword(state, &mut engine);
|
get_website_keyword(state, &mut engine);
|
||||||
|
get_keyword(state, &mut engine);
|
||||||
set_keyword(state, &mut engine);
|
set_keyword(state, &mut engine);
|
||||||
wait_keyword(state, &mut engine);
|
wait_keyword(state, &mut engine);
|
||||||
print_keyword(state, &mut engine);
|
print_keyword(state, &mut engine);
|
||||||
|
@ -127,6 +127,7 @@ impl ScriptService {
|
||||||
/// Preprocesses BASIC-style script to handle semicolon-free syntax
|
/// Preprocesses BASIC-style script to handle semicolon-free syntax
|
||||||
pub fn compile(&self, script: &str) -> Result<rhai::AST, Box<EvalAltResult>> {
|
pub fn compile(&self, script: &str) -> Result<rhai::AST, Box<EvalAltResult>> {
|
||||||
let processed_script = self.preprocess_basic_script(script);
|
let processed_script = self.preprocess_basic_script(script);
|
||||||
|
println!("Processed Script:\n{}", processed_script);
|
||||||
match self.engine.compile(&processed_script) {
|
match self.engine.compile(&processed_script) {
|
||||||
Ok(ast) => Ok(ast),
|
Ok(ast) => Ok(ast),
|
||||||
Err(parse_error) => Err(Box::new(EvalAltResult::from(parse_error))),
|
Err(parse_error) => Err(Box::new(EvalAltResult::from(parse_error))),
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
// wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||||
|
// sudo dpkg -i google-chrome-stable_current_amd64.deb
|
||||||
|
|
||||||
use crate::services::utils;
|
use crate::services::utils;
|
||||||
use log::debug;
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::env::temp_dir;
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
@ -45,7 +47,7 @@ impl BrowserPool {
|
||||||
|
|
||||||
let mut caps = DesiredCapabilities::chrome();
|
let mut caps = DesiredCapabilities::chrome();
|
||||||
caps.set_binary(&self.brave_path)?;
|
caps.set_binary(&self.brave_path)?;
|
||||||
caps.add_chrome_arg("--headless=new")?;
|
//caps.add_chrome_arg("--headless=new")?;
|
||||||
caps.add_chrome_arg("--disable-gpu")?;
|
caps.add_chrome_arg("--disable-gpu")?;
|
||||||
caps.add_chrome_arg("--no-sandbox")?;
|
caps.add_chrome_arg("--no-sandbox")?;
|
||||||
|
|
||||||
|
@ -91,64 +93,114 @@ impl BrowserSetup {
|
||||||
|
|
||||||
Err("Brave browser not found. Please install Brave first.".into())
|
Err("Brave browser not found. Please install Brave first.".into())
|
||||||
}
|
}
|
||||||
|
async fn setup_chromedriver() -> Result<String, Box<dyn std::error::Error>> {
|
||||||
async fn setup_chromedriver() -> Result<String, Box<dyn std::error::Error>> {
|
// Create chromedriver directory in executable's parent directory
|
||||||
let mut chromedriver_path = env::current_exe()?.parent().unwrap().to_path_buf();
|
let mut chromedriver_dir = env::current_exe()?
|
||||||
chromedriver_path.push("chromedriver");
|
.parent()
|
||||||
|
.unwrap()
|
||||||
// Check if chromedriver exists
|
.to_path_buf();
|
||||||
if fs::metadata(&chromedriver_path).await.is_err() {
|
chromedriver_dir.push("chromedriver");
|
||||||
println!("Downloading chromedriver...");
|
|
||||||
|
// Ensure the directory exists
|
||||||
// Note: This URL structure is outdated. Consider using Chrome for Testing endpoints
|
if !chromedriver_dir.exists() {
|
||||||
let (base_url, platform) =
|
fs::create_dir(&chromedriver_dir).await?;
|
||||||
match (cfg!(target_os = "windows"), cfg!(target_arch = "x86_64")) {
|
|
||||||
(true, true) => (
|
|
||||||
"https://chromedriver.storage.googleapis.com/114.0.5735.90",
|
|
||||||
"win32",
|
|
||||||
),
|
|
||||||
(false, true) if cfg!(target_os = "macos") => (
|
|
||||||
"https://chromedriver.storage.googleapis.com/114.0.5735.90",
|
|
||||||
"mac64",
|
|
||||||
),
|
|
||||||
(false, true) => (
|
|
||||||
"https://chromedriver.storage.googleapis.com/114.0.5735.90",
|
|
||||||
"linux64",
|
|
||||||
),
|
|
||||||
_ => return Err("Unsupported platform".into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let download_url = format!("{}/chromedriver_{}.zip", base_url, platform);
|
|
||||||
|
|
||||||
let mut zip_path = temp_dir();
|
|
||||||
zip_path.push("chromedriver.zip");
|
|
||||||
|
|
||||||
utils::download_file(&download_url, &zip_path.to_str().unwrap()).await?;
|
|
||||||
|
|
||||||
let extract_result = utils::extract_zip_recursive(&zip_path, &chromedriver_path);
|
|
||||||
if let Err(e) = extract_result {
|
|
||||||
debug!("Error extracting ZIP: {}", e);
|
|
||||||
}
|
|
||||||
// Clean up zip file
|
|
||||||
let _ = fs::remove_file(&zip_path).await;
|
|
||||||
|
|
||||||
if cfg!(target_os = "windows") {
|
|
||||||
chromedriver_path.push("chromedriver.exe");
|
|
||||||
} else {
|
|
||||||
chromedriver_path.push("chromedriver");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
{
|
|
||||||
use std::os::unix::fs::PermissionsExt;
|
|
||||||
let mut perms = fs::metadata(&chromedriver_path).await?.permissions();
|
|
||||||
perms.set_mode(0o755); // Make executable
|
|
||||||
fs::set_permissions(&chromedriver_path, perms).await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(chromedriver_path.to_string_lossy().to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine the final chromedriver path
|
||||||
|
let chromedriver_path = if cfg!(target_os = "windows") {
|
||||||
|
chromedriver_dir.join("chromedriver.exe")
|
||||||
|
} else {
|
||||||
|
chromedriver_dir.join("chromedriver")
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if chromedriver exists
|
||||||
|
if fs::metadata(&chromedriver_path).await.is_err() {
|
||||||
|
let (download_url, platform) = match (cfg!(target_os = "windows"), cfg!(target_arch = "x86_64")) {
|
||||||
|
(true, true) => (
|
||||||
|
"https://storage.googleapis.com/chrome-for-testing-public/138.0.7204.183/win64/chromedriver-win64.zip",
|
||||||
|
"win64",
|
||||||
|
),
|
||||||
|
(true, false) => (
|
||||||
|
"https://storage.googleapis.com/chrome-for-testing-public/138.0.7204.183/win32/chromedriver-win32.zip",
|
||||||
|
"win32",
|
||||||
|
),
|
||||||
|
(false, true) if cfg!(target_os = "macos") && cfg!(target_arch = "aarch64") => (
|
||||||
|
"https://storage.googleapis.com/chrome-for-testing-public/138.0.7204.183/mac-arm64/chromedriver-mac-arm64.zip",
|
||||||
|
"mac-arm64",
|
||||||
|
),
|
||||||
|
(false, true) if cfg!(target_os = "macos") => (
|
||||||
|
"https://storage.googleapis.com/chrome-for-testing-public/138.0.7204.183/mac-x64/chromedriver-mac-x64.zip",
|
||||||
|
"mac-x64",
|
||||||
|
),
|
||||||
|
(false, true) => (
|
||||||
|
"https://storage.googleapis.com/chrome-for-testing-public/138.0.7204.183/linux64/chromedriver-linux64.zip",
|
||||||
|
"linux64",
|
||||||
|
),
|
||||||
|
_ => return Err("Unsupported platform".into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut zip_path = std::env::temp_dir();
|
||||||
|
zip_path.push("chromedriver.zip");
|
||||||
|
println!("Downloading chromedriver for {}...", platform);
|
||||||
|
|
||||||
|
// Download the zip file
|
||||||
|
utils::download_file(download_url, &zip_path.to_str().unwrap()).await?;
|
||||||
|
|
||||||
|
// Extract the zip to a temporary directory first
|
||||||
|
let mut temp_extract_dir = std::env::temp_dir();
|
||||||
|
temp_extract_dir.push("chromedriver_extract");
|
||||||
|
let temp_extract_dir1 = temp_extract_dir.clone();
|
||||||
|
|
||||||
|
// Clean up any previous extraction
|
||||||
|
let _ = fs::remove_dir_all(&temp_extract_dir).await;
|
||||||
|
fs::create_dir(&temp_extract_dir).await?;
|
||||||
|
|
||||||
|
utils::extract_zip_recursive(&zip_path, &temp_extract_dir)?;
|
||||||
|
|
||||||
|
// Chrome for Testing zips contain a platform-specific directory
|
||||||
|
// Find the chromedriver binary in the extracted structure
|
||||||
|
let mut extracted_binary_path = temp_extract_dir;
|
||||||
|
extracted_binary_path.push(format!("chromedriver-{}", platform));
|
||||||
|
extracted_binary_path.push(if cfg!(target_os = "windows") {
|
||||||
|
"chromedriver.exe"
|
||||||
|
} else {
|
||||||
|
"chromedriver"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Try to move the file, fall back to copy if cross-device
|
||||||
|
match fs::rename(&extracted_binary_path, &chromedriver_path).await {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) if e.kind() == std::io::ErrorKind::CrossesDevices => {
|
||||||
|
// Cross-device move failed, use copy instead
|
||||||
|
fs::copy(&extracted_binary_path, &chromedriver_path).await?;
|
||||||
|
// Set permissions on the copied file
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
let mut perms = fs::metadata(&chromedriver_path).await?.permissions();
|
||||||
|
perms.set_mode(0o755);
|
||||||
|
fs::set_permissions(&chromedriver_path, perms).await?;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => return Err(e.into()),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
let _ = fs::remove_file(&zip_path).await;
|
||||||
|
let _ = fs::remove_dir_all(temp_extract_dir1).await;
|
||||||
|
|
||||||
|
// Set executable permissions (if not already set during copy)
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
let mut perms = fs::metadata(&chromedriver_path).await?.permissions();
|
||||||
|
perms.set_mode(0o755);
|
||||||
|
fs::set_permissions(&chromedriver_path, perms).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(chromedriver_path.to_string_lossy().to_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modified BrowserPool initialization
|
// Modified BrowserPool initialization
|
||||||
|
|
Loading…
Add table
Reference in a new issue