capmonstercloud_client/requests/
mt_captcha.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, Serialize)]
10pub struct MtCaptchaRequest {
11 #[serde(rename = "websiteURL")]
13 pub website_url: String,
14 #[serde(rename = "websiteKey")]
16 pub website_key: String,
17 #[serde(flatten, skip_serializing_if = "Option::is_none")]
18 pub proxy: Option<ProxyInfo>,
19 #[serde(rename = "pageAction", skip_serializing_if = "Option::is_none")]
21 pub page_action: Option<String>,
22 #[serde(rename = "isInvisible", skip_serializing_if = "Option::is_none")]
24 pub is_invisible: Option<bool>,
25 #[serde(rename = "userAgent", skip_serializing_if = "Option::is_none")]
27 pub user_agent: Option<String>,
28}
29
30impl MtCaptchaRequest {
31 pub fn new(website_url: impl Into<String>, website_key: impl Into<String>) -> Self {
32 Self {
33 website_url: website_url.into(),
34 website_key: website_key.into(),
35 proxy: None,
36 page_action: None,
37 is_invisible: None,
38 user_agent: None,
39 }
40 }
41
42 pub fn with_proxy(mut self, proxy: ProxyInfo) -> Self {
43 self.proxy = Some(proxy);
44 self
45 }
46
47 pub fn with_page_action(mut self, value: impl Into<String>) -> Self {
48 self.page_action = Some(value.into());
49 self
50 }
51
52 pub fn with_is_invisible(mut self, value: bool) -> Self {
53 self.is_invisible = Some(value);
54 self
55 }
56
57 pub fn with_user_agent(mut self, value: impl Into<String>) -> Self {
58 self.user_agent = Some(value.into());
59 self
60 }
61}
62
63impl CaptchaRequest for MtCaptchaRequest {
64 const TASK_TYPE: &'static str = "MTCaptchaTask";
65
66 fn timeouts(&self) -> GetResultTimeouts {
67 GetResultTimeouts::secs(1.0, 10.0, 3.0, 180.0)
68 }
69
70 fn validate(&self) -> Result<()> {
71 require_non_empty("websiteURL", &self.website_url)?;
72 require_non_empty("websiteKey", &self.website_key)?;
73 Ok(())
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 use crate::proxy::ProxyType;
81 use serde_json::json;
82
83 #[test]
84 fn minimal_task() {
85 let task = MtCaptchaRequest::new("https://example.com", "MTPublic-x")
86 .to_task()
87 .unwrap();
88 assert_eq!(
89 task,
90 json!({"type": "MTCaptchaTask", "websiteURL": "https://example.com",
91 "websiteKey": "MTPublic-x"})
92 );
93 }
94
95 #[test]
96 fn full_task() {
97 let task = MtCaptchaRequest::new("https://example.com", "MTPublic-x")
98 .with_proxy(ProxyInfo::new(ProxyType::Socks5, "1.2.3.4", 1080))
99 .with_page_action("login")
100 .with_is_invisible(true)
101 .with_user_agent("UA")
102 .to_task()
103 .unwrap();
104 assert_eq!(
105 task,
106 json!({"type": "MTCaptchaTask", "websiteURL": "https://example.com",
107 "websiteKey": "MTPublic-x", "pageAction": "login", "isInvisible": true,
108 "userAgent": "UA", "proxyType": "socks5", "proxyAddress": "1.2.3.4",
109 "proxyPort": 1080})
110 );
111 }
112
113 #[test]
114 fn empty_required_strings_are_rejected() {
115 let err = MtCaptchaRequest::new("", "KEY").to_task().unwrap_err();
116 assert!(matches!(err, crate::error::Error::Validation(ref m) if m.contains("websiteURL")));
117 let err = MtCaptchaRequest::new("https://example.com", "")
118 .to_task()
119 .unwrap_err();
120 assert!(matches!(err, crate::error::Error::Validation(ref m) if m.contains("websiteKey")));
121 }
122}