capmonstercloud_client/requests/
ten_di.rs1use serde::Serialize;
2
3use super::{require_non_empty, CaptchaRequest};
4use crate::error::Result;
5use crate::proxy::ProxyInfo;
6use crate::timeouts::GetResultTimeouts;
7
8#[derive(Debug, Clone, Default, Serialize)]
10pub struct TenDiMetadata {
11 #[serde(rename = "captchaUrl", skip_serializing_if = "Option::is_none")]
14 pub captcha_url: Option<String>,
15 #[serde(rename = "aidEncrypted", skip_serializing_if = "Option::is_none")]
18 pub aid_encrypted: Option<String>,
19}
20
21impl TenDiMetadata {
22 pub fn new() -> Self {
23 Self::default()
24 }
25
26 pub fn with_captcha_url(mut self, value: impl Into<String>) -> Self {
27 self.captcha_url = Some(value.into());
28 self
29 }
30
31 pub fn with_aid_encrypted(mut self, value: impl Into<String>) -> Self {
32 self.aid_encrypted = Some(value.into());
33 self
34 }
35}
36
37#[derive(Debug, Clone, Serialize)]
39pub struct TenDiCustomTaskRequest {
40 #[serde(rename = "websiteURL")]
42 pub website_url: String,
43 #[serde(rename = "websiteKey")]
45 pub website_key: String,
46 #[serde(skip_serializing_if = "Option::is_none")]
47 pub metadata: Option<TenDiMetadata>,
48 #[serde(flatten, skip_serializing_if = "Option::is_none")]
49 pub proxy: Option<ProxyInfo>,
50 #[serde(rename = "userAgent", skip_serializing_if = "Option::is_none")]
52 pub user_agent: Option<String>,
53}
54
55impl TenDiCustomTaskRequest {
56 pub fn new(website_url: impl Into<String>, website_key: impl Into<String>) -> Self {
57 Self {
58 website_url: website_url.into(),
59 website_key: website_key.into(),
60 metadata: None,
61 proxy: None,
62 user_agent: None,
63 }
64 }
65
66 pub fn with_metadata(mut self, value: TenDiMetadata) -> Self {
67 self.metadata = Some(value);
68 self
69 }
70
71 pub fn with_proxy(mut self, proxy: ProxyInfo) -> Self {
72 self.proxy = Some(proxy);
73 self
74 }
75
76 pub fn with_user_agent(mut self, value: impl Into<String>) -> Self {
77 self.user_agent = Some(value.into());
78 self
79 }
80}
81
82impl CaptchaRequest for TenDiCustomTaskRequest {
83 const TASK_TYPE: &'static str = "CustomTask";
84 const CLASS: Option<&'static str> = Some("TenDI");
85
86 fn timeouts(&self) -> GetResultTimeouts {
87 GetResultTimeouts::secs(1.0, 10.0, 3.0, 180.0)
88 }
89
90 fn validate(&self) -> Result<()> {
91 require_non_empty("websiteURL", &self.website_url)?;
92 require_non_empty("websiteKey", &self.website_key)?;
93 Ok(())
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100 use crate::proxy::ProxyType;
101 use serde_json::json;
102
103 #[test]
104 fn minimal_task() {
105 let task = TenDiCustomTaskRequest::new("https://example.com", "189123456")
106 .to_task()
107 .unwrap();
108 assert_eq!(
109 task,
110 json!({"type": "CustomTask", "class": "TenDI", "websiteURL": "https://example.com", "websiteKey": "189123456"})
111 );
112 }
113
114 #[test]
115 fn full_task() {
116 let task = TenDiCustomTaskRequest::new("https://example.com", "189123456")
117 .with_metadata(
118 TenDiMetadata::new()
119 .with_captcha_url("https://x/TCaptcha.js")
120 .with_aid_encrypted("AID"),
121 )
122 .with_proxy(ProxyInfo::new(ProxyType::Http, "1.2.3.4", 80))
123 .with_user_agent("UA")
124 .to_task()
125 .unwrap();
126 assert_eq!(
127 task,
128 json!({
129 "type": "CustomTask", "class": "TenDI", "websiteURL": "https://example.com", "websiteKey": "189123456",
130 "metadata": {"captchaUrl": "https://x/TCaptcha.js", "aidEncrypted": "AID"},
131 "proxyType": "http", "proxyAddress": "1.2.3.4", "proxyPort": 80, "userAgent": "UA"
132 })
133 );
134 }
135
136 #[test]
137 fn empty_required_strings_are_rejected() {
138 let err = TenDiCustomTaskRequest::new("", "KEY")
139 .to_task()
140 .unwrap_err();
141 assert!(matches!(err, crate::error::Error::Validation(ref m) if m.contains("websiteURL")));
142 let err = TenDiCustomTaskRequest::new("https://example.com", "")
143 .to_task()
144 .unwrap_err();
145 assert!(matches!(err, crate::error::Error::Validation(ref m) if m.contains("websiteKey")));
146 }
147}