use serde::{Deserialize, Serialize};
use crate::{TimingJitter, TlsRotationPolicy};
use crate::{HeaderPolicyConfig, TlsRotationConfig};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StealthProfileConfig {
pub jitter_ms_min: u64,
pub jitter_ms_max: u64,
pub header_budget: usize,
pub seed: Option<u64>,
pub rotate_tls: bool,
pub headers: HeaderPolicyConfig,
pub tls: TlsRotationConfig,
}
impl Default for StealthProfileConfig {
fn default() -> Self {
Self {
jitter_ms_min: 80,
jitter_ms_max: 350,
header_budget: 4,
seed: None,
rotate_tls: true,
headers: HeaderPolicyConfig::default(),
tls: TlsRotationConfig::default(),
}
}
}
impl StealthProfileConfig {
pub fn from_toml(toml: &str) -> Result<Self, crate::StealthError> {
let cfg: Self = toml::from_str(toml).map_err(|err| crate::StealthError::Config(err.to_string()))?;
cfg.validate()?;
Ok(cfg)
}
pub fn build(self) -> crate::StealthPolicy {
let jitter = TimingJitter::new(self.jitter_ms_min, self.jitter_ms_max);
let headers = self.headers.into_profile();
let tls = TlsRotationPolicy::from_config(self.tls);
crate::StealthPolicy::default()
.with_seed(self.seed)
.with_timing(jitter)
.with_header_budget(self.header_budget)
.with_headers(headers)
.with_tls_rotation(tls)
.with_rotate_tls(self.rotate_tls)
}
fn validate(&self) -> crate::Result<()> {
if self.jitter_ms_min > self.jitter_ms_max {
return Err(crate::StealthError::Config("jitter_ms_min cannot exceed jitter_ms_max".to_string()));
}
if self.header_budget == 0 {
return Err(crate::StealthError::Config("header_budget must be >= 1".to_string()));
}
Ok(())
}
}