#![cfg(feature = "datadome")]
use std::sync::Arc;
use std::time::Duration;
use rmcp::ErrorData;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use tokio::sync::Mutex;
use zendriver::{DataDomeClearanceOutcome, ZendriverError};
use crate::errors::{McpServerError, map_error};
use crate::state::SessionState;
use crate::tools::common::current_tab;
#[derive(Debug, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SolveDataDomeInput {
#[serde(default = "default_timeout")]
pub timeout_ms: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub poll_interval_ms: Option<u64>,
#[serde(default)]
pub with_interception: bool,
}
fn default_timeout() -> u64 {
30_000
}
#[derive(Debug, Serialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum Outcome {
Cleared,
ChallengeGone,
AlreadyClear,
Blocked,
Timeout,
}
#[derive(Debug, Serialize, JsonSchema)]
pub struct SolveDataDomeOutput {
pub outcome: Outcome,
#[serde(skip_serializing_if = "Option::is_none")]
pub datadome: Option<String>,
}
pub async fn solve_datadome(
state: Arc<Mutex<SessionState>>,
input: SolveDataDomeInput,
) -> Result<SolveDataDomeOutput, ErrorData> {
let s = state.lock().await;
let tab = current_tab(&s).await?;
let mut bypass = tab
.datadome()
.timeout(Duration::from_millis(input.timeout_ms));
if let Some(p) = input.poll_interval_ms {
bypass = bypass.poll_interval(Duration::from_millis(p));
}
if input.with_interception {
bypass = bypass.with_interception();
}
match bypass.wait_for_clearance().await {
Ok(DataDomeClearanceOutcome::Cleared { datadome }) => Ok(SolveDataDomeOutput {
outcome: Outcome::Cleared,
datadome: Some(datadome),
}),
Ok(DataDomeClearanceOutcome::ChallengeGone) => Ok(SolveDataDomeOutput {
outcome: Outcome::ChallengeGone,
datadome: None,
}),
Ok(DataDomeClearanceOutcome::AlreadyClear) => Ok(SolveDataDomeOutput {
outcome: Outcome::AlreadyClear,
datadome: None,
}),
Ok(DataDomeClearanceOutcome::Blocked) => Ok(SolveDataDomeOutput {
outcome: Outcome::Blocked,
datadome: None,
}),
Ok(DataDomeClearanceOutcome::TimedOut { .. }) => Ok(SolveDataDomeOutput {
outcome: Outcome::Timeout,
datadome: None,
}),
Err(other) => Err(map_error(McpServerError::from(ZendriverError::from(other)))),
}
}
#[cfg(test)]
#[allow(clippy::panic, clippy::unwrap_used)]
mod tests {
use super::*;
#[tokio::test]
async fn solve_datadome_with_no_browser_errors() {
let state = Arc::new(Mutex::new(SessionState::new()));
let err = solve_datadome(
state,
SolveDataDomeInput {
timeout_ms: 100,
poll_interval_ms: None,
with_interception: false,
},
)
.await
.expect_err("expected BrowserNotOpen");
assert!(err.message.contains("Browser not open"));
}
}