1use chrono::Local;
2use json::{JsonValue, object};
3use log::{error, info};
4use crate::{WechatMethod};
5
6#[derive(Clone)]
7pub struct Applet {
8    pub appid: String,
9    pub secret: String,
10    pub access_token: String,
11    pub expires_in: i64,
12}
13
14impl Applet {
15    fn access_token(&mut self) -> Result<String, String> {
16        let dt = Local::now();
17        let timestamp = dt.timestamp_millis();
18        if self.expires_in > timestamp {
19            return Ok(self.access_token.clone());
20        }
21        let url = format!("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}", self.appid, self.secret);
22        let mut http = br_reqwest::Client::new();
23        http.get(url.as_str());
24        let res = http.send()?.json()?;
25        if res["errcode"].is_empty() {
26            self.access_token = res["access_token"].to_string();
27            self.expires_in = timestamp + res["expires_in"].as_i64().unwrap();
28            Ok(self.access_token.clone())
29        } else {
30            Err(res["errmsg"].to_string())
31        }
32    }
33}
34
35impl WechatMethod for Applet {
36    fn access_token(&mut self) -> Result<JsonValue, String> {
37        let dt = Local::now();
38        let timestamp = dt.timestamp_millis();
39        if self.expires_in > timestamp {
40            return Ok(object! {
41                appid: self.appid.clone(),
42                access_token: self.access_token.clone(),
43                expires_in: self.expires_in
44            });
45        }
46        let url = format!("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}", self.appid, self.secret);
47        let mut http = br_reqwest::Client::new();
48        http.get(url.as_str());
49        let res = http.send()?.json()?;
50        if res["errcode"].is_empty() {
51            self.access_token = res["access_token"].to_string();
52            self.expires_in = timestamp + res["expires_in"].as_i64().unwrap();
53            Ok(object! {
54            appid: self.appid.clone(),
55            access_token: self.access_token.clone(),
56            expires_in: self.expires_in,
57        })
58        } else {
59            Err(res["errmsg"].to_string())
60        }
61    }
62
63
64    fn check(&mut self) -> Result<bool, String> {
65        match self.access_token() {
66            Ok(_) => Ok(true),
67            Err(e) => Err(e),
68        }
69    }
70    fn code2_session(&mut self, code: &str) -> Result<JsonValue, String> {
72        let url = "https://api.weixin.qq.com/sns/jscode2session".to_string();
73        let mut http = br_reqwest::Client::new();
74        http.get(url.as_str()).raw_json(object! {
75            appid:self.appid.clone(),
76            secret:self.secret.clone(),
77            js_code:code,
78            grant_type:"authorization_code"
79        });
80        let res = http.send()?;
81        let res = res.json()?;
82        let mut user_info = object! {};
83        let res = json::parse(res.as_str().unwrap()).unwrap();
84        if res["errcode"].is_empty() {
85            user_info["session_key"] = res["session_key"].clone();
86            user_info["unionid"] = res["unionid"].as_str().unwrap_or("").into();
87            user_info["openid"] = res["openid"].clone();
88        } else {
89            info!("code2_session: {}", res["errmsg"].as_str().unwrap());
90        }
91        Ok(user_info)
92    }
93    fn login(&mut self, code: &str) -> Result<JsonValue, String> {
94        let mut http = br_reqwest::Client::new();
95        http.get("https://api.weixin.qq.com/sns/jscode2session".to_string().as_str());
96        http.header("Accept", "application/json");
97        http.header("User-Agent", "api");
98        http.header("Content-Type", "application/json");
99        http.query(object! {
100                appid: self.appid.as_str(),
101                secret: self.secret.as_str(),
102                js_code:code,
103                grant_type:"authorization_code",
104        });
105        match http.send()?.json() {
106            Ok(e) => Ok(e),
107            Err(e) => Err(e),
108        }
109    }
110    fn get_phone_number(&mut self, iv: &str, encrypted_data: &str, session_key: &str) -> Result<JsonValue, String> {
112        let data = br_crypto::aes::aes_128_cbc(iv, encrypted_data, session_key)?;
113        let mut data = json::parse(&data).unwrap();
114        data["phone"] = data["purePhoneNumber"].clone();
115        Ok(data)
116    }
117
118    fn getuserinfo(&mut self, code: &str) -> Result<JsonValue, String> {
120        self.access_token()?;
121        let url = format!("https://api.weixin.qq.com/wxa/getpluginopenpid?access_token={}", self.access_token);
122        let mut http = br_reqwest::Client::new();
123        http.post(url.as_str());
124        http.raw_json(object! {
125            code:code
126        });
127        let res = http.send()?;
128        let res = res.json()?;
129        let mut user_info = object! {};
130        if res["errcode"].as_i32().unwrap() == 0 {
131            user_info["userid"] = res["userid"].clone();
132            user_info["user_ticket"] = res["user_ticket"].clone();
133            Ok(user_info)
134        } else {
135            error!("getuserinfo: {}", res["errmsg"].as_str().unwrap());
136            Err(res["errmsg"].to_string())
137        }
138    }
139    fn send_message(&mut self, template_id: &str, page: &str, touser: &str, data: JsonValue, miniprogram_state: &str, lang: &str) -> Result<JsonValue, String> {
140        self.access_token()?;
141        let lang = if lang.is_empty() { "zh_CN" } else { lang };
142        let miniprogram_state = if miniprogram_state.is_empty() { "formal" } else { miniprogram_state };
143        let url = format!("https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token={}", self.access_token);
144        let mut http = br_reqwest::Client::new();
145        http.post(url.as_str());
146        http.raw_json(object! {
147            access_token:self.access_token()?,
148            template_id:template_id,
149            page:page,
150            touser:touser,
151            data:data,
152            miniprogram_state:miniprogram_state,
153            lang:lang
154        });
155        let res = http.send()?;
156        let res = res.json()?;
157        if res["errcode"].as_i32().unwrap() == 0 {
158            Ok(res["errmsg"].clone())
159        } else {
160            Err(res["errmsg"].to_string())
161        }
162    }
163}