1use crate::{
2 client::common::model::Response,
3 error::{new_api_error, Result},
4 utils::http::{do_http, PostParameters},
5};
6use common::model::{Responser, Token};
7use reqwest::Method;
8use serde::{de::DeserializeOwned, ser::Serialize};
9use serde_json::{json, Value};
10use std::env;
11use tracing::debug;
12
13const BASE_URL: &str = "https://qyapi.weixin.qq.com/cgi-bin";
14
15#[derive(Debug)]
16pub struct Client {
17 pub(crate) corp_id: String,
18 pub(crate) corp_secret: String,
19 pub(crate) custom_contact_secret: Option<String>,
21}
22
23impl Client {
24 pub fn new(
25 corp_id: String,
26 corp_secret: String,
27 custom_contact_secret: Option<String>,
28 ) -> Client {
29 Self {
30 corp_id,
31 corp_secret,
32 custom_contact_secret,
33 }
34 }
35
36 pub fn new_from_env() -> Result<Self> {
37 let custom_contact_secret = if let Ok(ccs) = env::var("CUSTOM_CONTACT_SECRET") {
38 Some(ccs)
39 } else {
40 None
41 };
42 Ok(Self {
43 corp_id: env::var("CORP_ID")?,
44 corp_secret: env::var("CORP_SECRET")?,
45 custom_contact_secret,
46 })
47 }
48
49 async fn access_token(&self) -> Result<String> {
50 let query_body = json!({
51 "corpid": self.corp_id,
52 "corpsecret": self.corp_secret,
53 });
54
55 let resp = do_http(
56 Method::GET,
57 &format!("{}/gettoken", BASE_URL),
58 None,
59 Some(query_body),
60 None,
61 )
62 .await?;
63
64 let data = resp.json::<Response<Token>>().await?;
65 if data.err_code != 0 {
66 return Err(new_api_error(data.err_code, data.err_msg));
67 }
68 let token = data.data.unwrap();
69 Ok(token.access_token)
70
71 }
76
77 async fn custom_contact_access_token(&self) -> Result<String> {
78 let query_body = json!({
79 "corpid": self.corp_id,
80 "corpsecret": self.custom_contact_secret,
81 });
82
83 let resp = do_http(
84 Method::GET,
85 &format!("{}/gettoken", BASE_URL),
86 None,
87 Some(query_body),
88 None,
89 )
90 .await?;
91
92 let data = resp.json::<Response<Token>>().await?;
93 if data.err_code != 0 {
94 return Err(new_api_error(data.err_code, data.err_msg));
95 }
96 let token = data.data.unwrap();
97 Ok(token.access_token)
98 }
99
100 async fn request<R: Responser + DeserializeOwned + Serialize + Default>(
102 &self,
103 method: Method,
104 url: &str,
105 body: Option<Value>,
106 ) -> Result<R> {
107 let body = if let Some(data) = body {
108 Some(PostParameters::json(data))
109 } else {
110 None
111 };
112
113 let resp = do_http(method, url, None, None, body)
114 .await?
115 .json::<R>()
116 .await?;
117
118 debug!("\nurl:{url} \nresponse: {}", serde_json::to_string(&resp)?);
119 if resp.error_code() != 0 {
120 return Err(new_api_error(resp.error_code(), resp.error_message()));
121 }
122 Ok(resp)
123 }
124}
125
126mod contact;
128pub use contact::*;
129
130mod external_contact;
132pub use external_contact::*;
133mod wechat_custom_service;
135pub use wechat_custom_service::*;
136mod auth;
138pub use auth::*;
139mod app;
141pub use app::*;
142mod message_push;
144pub use message_push::*;
145pub mod event;
147pub mod common;