antibot_rs/lib.rs
1//! Auto-managed Byparr/FlareSolverr client for bypassing bot detection.
2//!
3//! Provides a unified client for the FlareSolverr-compatible API used by both
4//! [Byparr](https://github.com/thephaseless/byparr) and
5//! [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr).
6//!
7//! # Features
8//! - Automatic Docker container lifecycle (pull, start, health-wait, restart, drop)
9//! - Provider-agnostic: Byparr, FlareSolverr, or any compatible image
10//! - Builder-style [`SolveRequest`] with GET/POST, headers, cookies, proxy, sessions, fingerprint
11//! - Per-domain session/cookie cache so repeat solves are free
12//! - Concurrent-solve coalescer that dedupes parallel solves
13//! - Retry policy with exponential backoff
14//! - Lock-free [`MetricsSnapshot`] for observability
15//! - Optional disk-replay sink for debugging
16//! - Round-robin across multiple instances
17//! - `solve_stream` for batch use with bounded concurrency
18//! - Standalone [`detect`] helpers for cheap challenge fingerprinting
19//!
20//! # Quick start
21//! ```no_run
22//! use antibot_rs::{Antibot, Provider};
23//!
24//! # async fn example() -> Result<(), antibot_rs::AntibotError> {
25//! let client = Antibot::builder()
26//! .provider(Provider::Byparr)
27//! .auto_start(true)
28//! .enable_session_cache()
29//! .build()
30//! .await?;
31//!
32//! let solution = client.solve("https://example.com").await?;
33//! println!("Got {} bytes of HTML", solution.html().len());
34//! # Ok(())
35//! # }
36//! ```
37//!
38//! # Full-featured example
39//! ```no_run
40//! use antibot_rs::{
41//! Antibot, CoalesceKey, Cookie, DebugConfig, DockerLimits, ProxyConfig,
42//! Provider, RetryPolicy, SolveRequest,
43//! };
44//! use std::time::Duration;
45//!
46//! # async fn example() -> Result<(), antibot_rs::AntibotError> {
47//! let client = Antibot::builder()
48//! .provider(Provider::Byparr)
49//! .auto_start(true)
50//! .docker_limits(DockerLimits::default().memory("2g").cpus("1.5").shm_size("1g"))
51//! .enable_session_cache()
52//! .coalesce_solves(CoalesceKey::Domain)
53//! .retry(RetryPolicy::default())
54//! .default_proxy(ProxyConfig::http("http://proxy.example:8080"))
55//! .debug(DebugConfig::new("./antibot-replay"))
56//! .health_watch(Duration::from_secs(30))
57//! .manage_lifecycle(true)
58//! .build()
59//! .await?;
60//!
61//! let solution = client.execute(
62//! SolveRequest::post("https://site.com/api/login")
63//! .json(serde_json::json!({"user": "alice"}))
64//! .with_header("X-Custom", "value")
65//! .with_cookie(Cookie::new("session", "abc123"))
66//! ).await?;
67//! # let _ = solution;
68//! # Ok(())
69//! # }
70//! ```
71
72mod client;
73mod coalesce;
74mod cookie;
75mod debug_replay;
76pub mod detect;
77mod docker;
78mod error;
79mod fingerprint;
80mod metrics;
81mod proxy;
82mod request;
83mod retry;
84mod session_cache;
85mod stream;
86mod types;
87mod wire;
88
89pub use client::{merge_cookies, Antibot, AntibotBuilder, SessionHandle};
90pub use coalesce::CoalesceKey;
91pub use cookie::{Cookie, SameSite};
92pub use debug_replay::DebugConfig;
93pub use detect::{detect_challenge, ChallengeKind, DetectionInput};
94pub use docker::DockerLimits;
95pub use error::AntibotError;
96pub use fingerprint::{BrowserFingerprint, Viewport};
97pub use metrics::MetricsSnapshot;
98pub use proxy::ProxyConfig;
99pub use request::{PostBody, SolveMethod, SolveRequest};
100pub use retry::RetryPolicy;
101pub use session_cache::{CachedSession, SessionCacheConfig};
102pub use stream::SolveStream;
103pub use types::{Solution, SolutionSource};
104
105/// Re-export of `futures::StreamExt` so callers don't need a direct dep
106/// just to consume [`Antibot::solve_stream`].
107pub use futures::StreamExt;
108
109/// Docker image provider for the challenge-solving proxy.
110#[derive(Debug, Clone, Default)]
111pub enum Provider {
112 /// Byparr — recommended, actively maintained.
113 #[default]
114 Byparr,
115 /// FlareSolverr — the original implementation.
116 FlareSolverr,
117 /// Custom Docker image with a FlareSolverr-compatible `/v1` endpoint.
118 Custom(String),
119}
120
121impl Provider {
122 pub fn image(&self) -> &str {
123 match self {
124 Provider::Byparr => "ghcr.io/thephaseless/byparr:latest",
125 Provider::FlareSolverr => "ghcr.io/flaresolverr/flaresolverr:latest",
126 Provider::Custom(image) => image,
127 }
128 }
129
130 pub fn label(&self) -> &str {
131 match self {
132 Provider::Byparr => "Byparr",
133 Provider::FlareSolverr => "FlareSolverr",
134 Provider::Custom(_) => "Custom",
135 }
136 }
137}
138
139impl std::fmt::Display for Provider {
140 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141 write!(f, "{} ({})", self.label(), self.image())
142 }
143}