1use reqwest::Client;
21use serde::{Deserialize, Serialize};
22use url::Url;
23
24pub struct MCaptcha {
26 client: Client,
27 sitekey: String,
28 account_secret: String,
29 verify_path: Url,
30}
31
32impl MCaptcha {
33 pub fn new<T: Into<String>>(sitekey: T, account_secret: T, instance_url: Url) -> Self {
42 let mut verify_path = instance_url.clone();
43 verify_path.set_path("/api/v1/pow/siteverify");
44 Self {
45 sitekey: sitekey.into(),
46 account_secret: account_secret.into(),
47 verify_path,
48 client: Client::new(),
49 }
50 }
51
52 pub async fn verify(&self, token: &str) -> Result<bool, reqwest::Error> {
54 let payload = CaptchaVerfiyPayload::from_ctx(self, token);
55 let res: CaptchaVerifyResp = self
56 .client
57 .post(self.verify_path.clone())
58 .json(&payload)
59 .send()
60 .await?
61 .json()
62 .await?;
63
64 Ok(res.valid)
65 }
66}
67
68#[derive(Default, Debug, PartialEq, Clone, Serialize, Deserialize)]
69struct CaptchaVerifyResp {
70 valid: bool,
71}
72
73#[derive(Default, Debug, PartialEq, Clone, Serialize, Deserialize)]
74struct CaptchaVerfiyPayload<'a> {
75 token: &'a str,
76 key: &'a str,
77 secret: &'a str,
78}
79
80impl<'a> CaptchaVerfiyPayload<'a> {
81 fn from_ctx(m: &'a MCaptcha, token: &'a str) -> Self {
82 Self {
83 key: &m.sitekey,
84 token,
85 secret: &m.account_secret,
86 }
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use std::env;
93
94 use super::*;
95
96 #[actix_rt::test]
97 async fn verify_works() {
98 dotenv::from_filename(".env.local").ok();
99
100 let instance_url = env::var("INSTANCE_URL").expect("instance url not set");
101 let sitekey = env::var("SITEKEY").expect("sitekey not set");
102 let secret = env::var("SECRET").expect("secret not set");
103 let token = env::var("TOKEN").expect("token not set");
104
105 println!("token={token}");
106 println!("sitekey={sitekey}");
107 println!("secret={secret}");
108 println!("instance_url={instance_url}");
109
110 let mcaptcha = MCaptcha::new(
111 sitekey,
112 secret,
113 Url::parse(&instance_url).expect("instance_url is not URL"),
114 );
115 assert!(mcaptcha.verify(&token).await.unwrap());
116 assert!(!mcaptcha.verify("foo").await.unwrap());
117 }
118}