capmonstercloud_client/requests/
altcha.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 AltchaMetadata {
11 pub challenge: String,
12 pub iterations: String,
13 pub salt: String,
14 pub signature: String,
15}
16
17#[derive(Debug, Clone, Serialize)]
19pub struct AltchaCustomTaskRequest {
20 #[serde(rename = "websiteURL")]
21 pub website_url: String,
22 #[serde(rename = "websiteKey")]
24 pub website_key: String,
25 pub metadata: AltchaMetadata,
26 #[serde(flatten, skip_serializing_if = "Option::is_none")]
27 pub proxy: Option<ProxyInfo>,
28 #[serde(rename = "userAgent", skip_serializing_if = "Option::is_none")]
29 pub user_agent: Option<String>,
30}
31
32impl AltchaCustomTaskRequest {
33 pub fn new(
34 website_url: impl Into<String>,
35 website_key: impl Into<String>,
36 metadata: AltchaMetadata,
37 ) -> Self {
38 Self {
39 website_url: website_url.into(),
40 website_key: website_key.into(),
41 metadata,
42 proxy: None,
43 user_agent: None,
44 }
45 }
46
47 pub fn with_proxy(mut self, proxy: ProxyInfo) -> Self {
48 self.proxy = Some(proxy);
49 self
50 }
51
52 pub fn with_user_agent(mut self, value: impl Into<String>) -> Self {
53 self.user_agent = Some(value.into());
54 self
55 }
56}
57
58impl CaptchaRequest for AltchaCustomTaskRequest {
59 const TASK_TYPE: &'static str = "CustomTask";
60 const CLASS: Option<&'static str> = Some("altcha");
61
62 fn timeouts(&self) -> GetResultTimeouts {
63 GetResultTimeouts::secs(1.0, 0.0, 1.0, 50.0)
64 }
65
66 fn validate(&self) -> Result<()> {
67 require_non_empty("websiteURL", &self.website_url)?;
68 require_non_empty("metadata.challenge", &self.metadata.challenge)?;
69 require_non_empty("metadata.iterations", &self.metadata.iterations)?;
70 require_non_empty("metadata.salt", &self.metadata.salt)?;
71 require_non_empty("metadata.signature", &self.metadata.signature)?;
72 Ok(())
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79 use serde_json::json;
80
81 #[test]
82 fn task_has_class_and_metadata() {
83 let meta = AltchaMetadata {
84 challenge: "c".into(),
85 iterations: "1".into(),
86 salt: "s".into(),
87 signature: "g".into(),
88 };
89 let task = AltchaCustomTaskRequest::new("https://example.com", "", meta)
90 .to_task()
91 .unwrap();
92 assert_eq!(
93 task,
94 json!({
95 "type": "CustomTask", "class": "altcha",
96 "websiteURL": "https://example.com", "websiteKey": "",
97 "metadata": {"challenge": "c", "iterations": "1", "salt": "s", "signature": "g"}
98 })
99 );
100 }
101
102 #[test]
103 fn empty_required_strings_are_rejected() {
104 let err = AltchaCustomTaskRequest::new(
105 "",
106 "",
107 AltchaMetadata {
108 challenge: "c".into(),
109 iterations: "1".into(),
110 salt: "s".into(),
111 signature: "g".into(),
112 },
113 )
114 .to_task()
115 .unwrap_err();
116 assert!(matches!(err, crate::error::Error::Validation(ref m) if m.contains("websiteURL")));
117 let err = AltchaCustomTaskRequest::new(
118 "https://example.com",
119 "",
120 AltchaMetadata {
121 challenge: "".into(),
122 iterations: "1".into(),
123 salt: "s".into(),
124 signature: "g".into(),
125 },
126 )
127 .to_task()
128 .unwrap_err();
129 assert!(
130 matches!(err, crate::error::Error::Validation(ref m) if m.contains("metadata.challenge"))
131 );
132 let err = AltchaCustomTaskRequest::new(
133 "https://example.com",
134 "",
135 AltchaMetadata {
136 challenge: "c".into(),
137 iterations: "".into(),
138 salt: "s".into(),
139 signature: "g".into(),
140 },
141 )
142 .to_task()
143 .unwrap_err();
144 assert!(
145 matches!(err, crate::error::Error::Validation(ref m) if m.contains("metadata.iterations"))
146 );
147 let err = AltchaCustomTaskRequest::new(
148 "https://example.com",
149 "",
150 AltchaMetadata {
151 challenge: "c".into(),
152 iterations: "1".into(),
153 salt: "".into(),
154 signature: "g".into(),
155 },
156 )
157 .to_task()
158 .unwrap_err();
159 assert!(
160 matches!(err, crate::error::Error::Validation(ref m) if m.contains("metadata.salt"))
161 );
162 let err = AltchaCustomTaskRequest::new(
163 "https://example.com",
164 "",
165 AltchaMetadata {
166 challenge: "c".into(),
167 iterations: "1".into(),
168 salt: "s".into(),
169 signature: "".into(),
170 },
171 )
172 .to_task()
173 .unwrap_err();
174 assert!(
175 matches!(err, crate::error::Error::Validation(ref m) if m.contains("metadata.signature"))
176 );
177 }
178}