Skip to main content

bobby_browser_client/
challenges.rs

1//! Challenge detection and solving types for captchas, MFA, and verification flows.
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
7#[serde(rename_all = "camelCase")]
8pub enum ChallengeType {
9    RecaptchaV2Checkbox,
10    RecaptchaV3,
11    TextCaptcha,
12    ImageGridCaptcha,
13    MfaCodeEntry,
14}
15
16#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
17#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
18pub struct ChallengeDetection {
19    pub challenge_type: ChallengeType,
20    pub confidence: f32,
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub region: Option<ChallengeRegion>,
23    #[serde(default)]
24    pub blocking: bool,
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub hints: Option<ChallengeDetectionHints>,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
30#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
31pub struct ChallengeRegion {
32    pub x: f32,
33    pub y: f32,
34    pub width: f32,
35    pub height: f32,
36}
37
38#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
39#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
40pub struct ChallengeDetectionHints {
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub target_field_purpose: Option<String>,
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub instruction_text: Option<String>,
45}
46
47#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
48#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
49pub struct SolveChallengeIntent {
50    pub purpose: String,
51    #[serde(default)]
52    pub hints: SolveChallengeHints,
53}
54
55#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
56#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
57#[serde(rename_all = "camelCase")]
58pub struct SolveChallengeHints {
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub region: Option<ChallengeRegion>,
61    #[serde(default = "default_solve_timeout_ms")]
62    pub timeout_ms: u64,
63}
64
65impl Default for SolveChallengeHints {
66    fn default() -> Self {
67        Self {
68            region: None,
69            timeout_ms: default_solve_timeout_ms(),
70        }
71    }
72}
73
74fn default_solve_timeout_ms() -> u64 {
75    30_000
76}
77
78#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
79#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
80#[serde(tag = "kind", rename_all = "camelCase")]
81pub enum SolveStep {
82    Click {
83        x: f32,
84        y: f32,
85    },
86    TypeText {
87        text: String,
88        target_field_purpose: String,
89    },
90    WaitAndReassess {
91        ms: u64,
92    },
93}