cloudscraper_rs/external_deps/captcha/
mod.rs

1//! Captcha provider integrations.
2//!
3//! These adapters provide a unified interface for third-party captcha
4//! solvers such as AntiCaptcha, CapSolver, and TwoCaptcha. The core solver can
5//! remain agnostic of vendor-specific details while still retrieving challenge
6//! tokens when necessary.
7
8mod anticaptcha;
9mod capsolver;
10mod twocaptcha;
11
12pub use anticaptcha::AntiCaptchaProvider;
13pub use capsolver::CapSolverProvider;
14pub use twocaptcha::TwoCaptchaProvider;
15
16use std::collections::HashMap;
17use std::time::Duration;
18
19use async_trait::async_trait;
20use thiserror::Error;
21use url::Url;
22
23/// High-level configuration that controls captcha solving behaviour.
24#[derive(Debug, Clone)]
25pub struct CaptchaConfig {
26    pub timeout: Duration,
27    pub poll_interval: Duration,
28}
29
30impl Default for CaptchaConfig {
31    fn default() -> Self {
32        Self {
33            timeout: Duration::from_secs(120),
34            poll_interval: Duration::from_secs(2),
35        }
36    }
37}
38
39/// Details describing the captcha Cloudflare issued.
40#[derive(Debug, Clone)]
41pub struct CaptchaTask {
42    pub site_key: String,
43    pub page_url: Url,
44    pub action: Option<String>,
45    pub data: HashMap<String, String>,
46}
47
48impl CaptchaTask {
49    pub fn new(site_key: impl Into<String>, page_url: Url) -> Self {
50        Self {
51            site_key: site_key.into(),
52            page_url,
53            action: None,
54            data: HashMap::new(),
55        }
56    }
57
58    pub fn with_action(mut self, action: impl Into<String>) -> Self {
59        self.action = Some(action.into());
60        self
61    }
62
63    pub fn insert_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
64        self.data.insert(key.into(), value.into());
65        self
66    }
67}
68
69/// Resolved captcha token and optional metadata.
70#[derive(Debug, Clone)]
71pub struct CaptchaSolution {
72    pub token: String,
73    pub expires_in: Option<Duration>,
74    pub metadata: HashMap<String, String>,
75}
76
77impl CaptchaSolution {
78    pub fn new(token: impl Into<String>) -> Self {
79        Self {
80            token: token.into(),
81            expires_in: None,
82            metadata: HashMap::new(),
83        }
84    }
85
86    pub fn with_expiry(mut self, ttl: Duration) -> Self {
87        self.expires_in = Some(ttl);
88        self
89    }
90
91    pub fn insert_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
92        self.metadata.insert(key.into(), value.into());
93        self
94    }
95}
96
97/// Common result type returned by captcha providers.
98pub type CaptchaResult = Result<CaptchaSolution, CaptchaError>;
99
100/// Shared interface implemented by captcha vendors.
101#[async_trait]
102pub trait CaptchaProvider: Send + Sync {
103    fn name(&self) -> &'static str;
104    async fn solve(&self, task: &CaptchaTask) -> CaptchaResult;
105}
106
107/// Errors surfaced by captcha providers.
108#[derive(Debug, Error)]
109pub enum CaptchaError {
110    #[error("captcha provider misconfigured: {0}")]
111    Configuration(String),
112    #[error("captcha provider request failed: {0}")]
113    Provider(String),
114    #[error("captcha solving timed out after {0:?}")]
115    Timeout(Duration),
116    #[error("captcha provider {0} not implemented")]
117    NotImplemented(&'static str),
118    #[error("captcha error: {0}")]
119    Other(String),
120}