use anyhow::{Context, Result};
use captchaforge::{solve_url, StealthProfile};
use chromiumoxide::{Browser, BrowserConfig};
use futures_util::stream::StreamExt;
#[tokio::main]
async fn main() -> Result<()> {
let mut args = std::env::args().skip(1);
let url = args
.next()
.context("usage: solve_url_simple <url> [--profile <name>]")?;
let profile = parse_profile_arg(args)?;
let (mut browser, mut handler) = Browser::launch(
BrowserConfig::builder()
.build()
.map_err(|e| anyhow::anyhow!("BrowserConfig::build: {e}"))?,
)
.await
.context("launching chromium")?;
let handler_task = tokio::spawn(async move {
while let Some(_event) = handler.next().await {
}
});
let page = browser.new_page("about:blank").await.context("new_page")?;
let outcome = solve_url(&page, &url, profile).await?;
match outcome {
None => println!("no captcha detected at {url}"),
Some(r) => println!(
"captcha {} via {:?} in {}ms (confidence={:.2}, verified={:?})",
if r.success { "SOLVED" } else { "FAILED" },
r.method,
r.time_ms,
r.confidence,
r.verified_outcome,
),
}
browser.close().await.ok();
handler_task.abort();
Ok(())
}
fn parse_profile_arg(mut args: impl Iterator<Item = String>) -> Result<Option<StealthProfile>> {
while let Some(a) = args.next() {
if a == "--profile" {
let name = args.next().context("--profile requires a value")?;
return Ok(Some(profile_from_name(&name)?));
}
}
Ok(None)
}
fn profile_from_name(name: &str) -> Result<StealthProfile> {
Ok(match name.to_ascii_lowercase().as_str() {
"chrome-windows" | "chrome-win" => StealthProfile::ChromeWindowsStable,
"chrome-mac" | "chrome-osx" => StealthProfile::ChromeMacStable,
"chrome-linux" => StealthProfile::ChromeLinux,
"chrome-android" | "android" => StealthProfile::ChromeAndroid,
"edge-windows" | "edge" => StealthProfile::EdgeWindowsStable,
"firefox-linux" | "firefox" => StealthProfile::FirefoxLinux,
"safari-iphone" | "iphone" => StealthProfile::SafariIphone,
"safari-ipad" | "ipad" => StealthProfile::SafariIpad,
"safari-mac" | "safari" => StealthProfile::SafariMacStable,
"brave-windows" | "brave" => StealthProfile::BraveWindows,
"opera-windows" | "opera" => StealthProfile::OperaWindows,
"samsung-internet" | "samsung" => StealthProfile::SamsungInternetAndroid,
other => anyhow::bail!(
"unknown stealth profile {other:?}; try `captchaforge profiles` for the full list"
),
})
}