Skip to main content

capmonstercloud_client/requests/
mt_captcha.rs

1use serde::Serialize;
2
3use super::{require_non_empty, CaptchaRequest};
4use crate::error::Result;
5use crate::proxy::ProxyInfo;
6use crate::timeouts::GetResultTimeouts;
7
8/// MTCaptcha (`MTCaptchaTask`).
9#[derive(Debug, Clone, Serialize)]
10pub struct MtCaptchaRequest {
11    /// URL of the page where the captcha is solved.
12    #[serde(rename = "websiteURL")]
13    pub website_url: String,
14    /// MTCaptcha key, passed on the page as `sk`; typically `MTPublic-abCDEFJAB`.
15    #[serde(rename = "websiteKey")]
16    pub website_key: String,
17    #[serde(flatten, skip_serializing_if = "Option::is_none")]
18    pub proxy: Option<ProxyInfo>,
19    /// The `act` parameter. Send it only if it differs from the default `%24`.
20    #[serde(rename = "pageAction", skip_serializing_if = "Option::is_none")]
21    pub page_action: Option<String>,
22    /// `true` if the captcha is invisible (has a hidden confirmation field).
23    #[serde(rename = "isInvisible", skip_serializing_if = "Option::is_none")]
24    pub is_invisible: Option<bool>,
25    /// Browser User-Agent (use a current Windows one).
26    #[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}