Skip to main content

capmonstercloud_client/requests/
binance.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/// Binance custom captcha (`BinanceTask`). Use only to log in with your own account.
9///
10/// `validate_id` is dynamic: extract it right before creating the task.
11#[derive(Debug, Clone, Serialize)]
12pub struct BinanceTaskRequest {
13    /// Address of the main page where the captcha is solved.
14    #[serde(rename = "websiteURL")]
15    pub website_url: String,
16    /// Identifies the section of the site: value of `bizId`, `bizType` or `bizCode` taken from
17    /// the traffic, e.g. `login`.
18    #[serde(rename = "websiteKey")]
19    pub website_key: String,
20    /// Dynamic key: value of `validateId`, `securityId` or `securityCheckResponseValidateId`
21    /// taken from the traffic.
22    #[serde(rename = "validateId")]
23    pub validate_id: String,
24    #[serde(flatten, skip_serializing_if = "Option::is_none")]
25    pub proxy: Option<ProxyInfo>,
26    /// Browser User-Agent (use a current Windows one).
27    #[serde(rename = "userAgent", skip_serializing_if = "Option::is_none")]
28    pub user_agent: Option<String>,
29}
30
31impl BinanceTaskRequest {
32    pub fn new(
33        website_url: impl Into<String>,
34        website_key: impl Into<String>,
35        validate_id: impl Into<String>,
36    ) -> Self {
37        Self {
38            website_url: website_url.into(),
39            website_key: website_key.into(),
40            validate_id: validate_id.into(),
41            proxy: None,
42            user_agent: None,
43        }
44    }
45
46    pub fn with_proxy(mut self, proxy: ProxyInfo) -> Self {
47        self.proxy = Some(proxy);
48        self
49    }
50
51    pub fn with_user_agent(mut self, value: impl Into<String>) -> Self {
52        self.user_agent = Some(value.into());
53        self
54    }
55}
56
57impl CaptchaRequest for BinanceTaskRequest {
58    const TASK_TYPE: &'static str = "BinanceTask";
59
60    fn timeouts(&self) -> GetResultTimeouts {
61        GetResultTimeouts::secs(1.0, 0.0, 1.0, 20.0)
62    }
63
64    fn validate(&self) -> Result<()> {
65        require_non_empty("websiteURL", &self.website_url)?;
66        require_non_empty("websiteKey", &self.website_key)?;
67        require_non_empty("validateId", &self.validate_id)?;
68        Ok(())
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75    use crate::proxy::ProxyType;
76    use serde_json::json;
77
78    #[test]
79    fn minimal_task() {
80        let task = BinanceTaskRequest::new("https://example.com", "login", "vid")
81            .to_task()
82            .unwrap();
83        assert_eq!(
84            task,
85            json!({"type": "BinanceTask", "websiteURL": "https://example.com",
86                   "websiteKey": "login", "validateId": "vid"})
87        );
88    }
89
90    #[test]
91    fn full_task() {
92        let task = BinanceTaskRequest::new("https://example.com", "login", "vid")
93            .with_proxy(ProxyInfo::new(ProxyType::Http, "1.2.3.4", 8080).with_auth("u", "p"))
94            .with_user_agent("UA")
95            .to_task()
96            .unwrap();
97        assert_eq!(
98            task,
99            json!({"type": "BinanceTask", "websiteURL": "https://example.com",
100                   "websiteKey": "login", "validateId": "vid", "userAgent": "UA",
101                   "proxyType": "http", "proxyAddress": "1.2.3.4", "proxyPort": 8080,
102                   "proxyLogin": "u", "proxyPassword": "p"})
103        );
104    }
105
106    #[test]
107    fn empty_required_strings_are_rejected() {
108        let err = BinanceTaskRequest::new("", "k", "v").to_task().unwrap_err();
109        assert!(matches!(err, crate::error::Error::Validation(ref m) if m.contains("websiteURL")));
110        let err = BinanceTaskRequest::new("https://example.com", "", "v")
111            .to_task()
112            .unwrap_err();
113        assert!(matches!(err, crate::error::Error::Validation(ref m) if m.contains("websiteKey")));
114        let err = BinanceTaskRequest::new("https://example.com", "k", "")
115            .to_task()
116            .unwrap_err();
117        assert!(matches!(err, crate::error::Error::Validation(ref m) if m.contains("validateId")));
118    }
119}