use std::collections::HashMap;
use std::sync::Mutex;
use chrono::{Local, Utc};
use json::{object, JsonValue};
use lazy_static::lazy_static;
use rand::distr::Alphanumeric;
use rand::Rng;
use crate::{WechatMethod};
lazy_static! {
pub static ref TICKET: Mutex<HashMap<String,JsonValue>> =Mutex::new(HashMap::new());
}
#[derive(Clone)]
pub struct Subscribe {
pub appid: String,
pub secret: String,
pub access_token: String,
pub expires_in: i64,
pub ticket_expires_in: i64,
pub ticket: String,
}
impl Subscribe {
fn access_token(&mut self) -> Result<String, String> {
let dt = Local::now();
let timestamp = dt.timestamp_millis();
if self.expires_in > timestamp {
return Ok(self.access_token.clone());
}
let url = "https://api.weixin.qq.com/cgi-bin/stable_token".to_string();
let mut http = br_reqwest::Client::new();
http.post(url.as_str());
http.raw_json(object! {
"grant_type" =>"client_credential",
"appid" => self.appid.clone(),
"secret" => self.secret.clone(),
});
let res = http.send()?.json()?;
if res["errcode"].is_empty() {
self.access_token = res["access_token"].to_string();
self.expires_in = timestamp + res["expires_in"].as_i64().unwrap();
Ok(self.access_token.clone())
} else {
Err(res["errmsg"].to_string())
}
}
}
impl WechatMethod for Subscribe {
fn access_token(&mut self) -> Result<JsonValue, String> {
let dt = Local::now();
let timestamp = dt.timestamp_millis();
if self.expires_in > timestamp {
return Ok(object! {
appid: self.appid.clone(),
access_token: self.access_token.clone(),
expires_in: self.expires_in,
});
}
let url = "https://api.weixin.qq.com/cgi-bin/stable_token".to_string();
let mut http = br_reqwest::Client::new();
http.post(url.as_str());
http.raw_json(object! {
"grant_type" =>"client_credential",
"appid" => self.appid.clone(),
"secret" => self.secret.clone(),
});
let res = http.send()?.json()?;
if res["errcode"].is_empty() {
self.access_token = res["access_token"].to_string();
self.expires_in = timestamp + res["expires_in"].as_i64().unwrap();
Ok(object! {
appid: self.appid.clone(),
access_token: self.access_token.clone(),
expires_in: self.expires_in,
})
} else {
Err(res["errmsg"].to_string())
}
}
fn check(&mut self) -> Result<bool, String> {
match self.access_token() {
Ok(_) => Ok(true),
Err(e) => Err(e),
}
}
fn login(&mut self, code: &str) -> Result<JsonValue, String> {
let mut http = br_reqwest::Client::new();
let get_url = format!("https://api.weixin.qq.com/sns/oauth2/access_token?appid={}&secret={}&code={code}&grant_type=authorization_code", self.appid, self.secret);
let res = http.get(get_url.as_str()).send()?.json()?;
let data = object! {
appid:self.appid.clone(),
openid:res["openid"].as_str().unwrap_or(""),
unionid:res["unionid"].as_str().unwrap_or(""),
};
Ok(data)
}
fn jsapi_ticket(&mut self, url: &str) -> Result<JsonValue, String> {
self.access_token()?;
let dt = Local::now();
let timestamp = dt.timestamp_millis();
if self.ticket_expires_in <= timestamp {
let mut http = br_reqwest::Client::new();
let get_url = format!("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={}&type=jsapi", self.access_token);
let res = http.get(get_url.as_str()).send()?.json()?;
if res["errcode"].as_i32().unwrap() != 0 {
return Err(res["errmsg"].to_string());
}
self.ticket = res["ticket"].to_string();
self.ticket_expires_in = res["expires_in"].as_i64().unwrap();
TICKET.lock().unwrap().insert(self.appid.clone(), object! {
ticket: self.ticket.clone(),
ticket_expires_in:self.ticket_expires_in
});
}
let nonce_str: String = rand::rng().sample_iter(&Alphanumeric).take(16).map(char::from).collect();
let timestamp = format!("{}", Utc::now().timestamp());
let raw_string = format!("jsapi_ticket={}&noncestr={nonce_str}×tamp={timestamp}&url={url}", self.ticket);
let signature = br_crypto::sha1::encrypt_hex(raw_string.as_bytes());
let data = object! {
appId:self.appid.clone(),
timestamp:timestamp,
nonceStr:nonce_str,
signature:signature,
};
Ok(data)
}
fn getuserinfo(&mut self, code: &str) -> Result<JsonValue, String> {
let mut http = br_reqwest::Client::new();
let access_token_url = format!(
"https://api.weixin.qq.com/sns/oauth2/access_token?appid={}&secret={}&code={}&grant_type=authorization_code",
self.appid, self.secret, code
);
let res = http.get(access_token_url.as_str()).send()?.json()?;
let (web_access_token, openid) = if res["errcode"].is_empty() {
(res["access_token"].to_string(), res["openid"].to_string())
} else {
return Err(res["errmsg"].to_string());
};
let info_url = format!(
"https://api.weixin.qq.com/sns/userinfo?access_token={}&openid={}&lang=zh_CN",
web_access_token, openid
);
let mut http = br_reqwest::Client::new();
let res = http.get(info_url.as_str()).send()?;
let res = res.json()?;
if res["errcode"].is_empty() {
Ok(res)
} else {
Err(res["errmsg"].to_string())
}
}
}