whatsapp_cloud_api/
whatsapp_client.rs1use crate::{
2 models::{
3 CodeMethod, CodeRequestParams, CodeVerifyParams, MediaResponse, Message, MessageResponse,
4 MessageStatus, MessageStatusResponse, PhoneNumberResponse,
5 },
6 WhatsappError,
7};
8
9const FACEBOOK_GRAPH_API_BASE_URL: &str = "https://graph.facebook.com";
10
11pub struct WhatsappClient {
12 version: String,
13 access_token: String,
14 phone_number_id: String,
15}
16
17impl WhatsappClient {
18 pub fn new(access_token: &str, phone_number_id: &str) -> Self {
19 Self {
20 version: "v20.0".into(),
21 access_token: access_token.into(),
22 phone_number_id: phone_number_id.into(),
23 }
24 }
25
26 pub fn version(&mut self) -> &str {
27 &self.version
28 }
29
30 pub fn set_version(&mut self, version: &str) {
31 self.version = version.into();
32 }
33
34 pub fn set_access_token(&mut self, access_token: &str) {
35 self.access_token = access_token.into();
36 }
37
38 pub fn set_phone_number_id(&mut self, phone_number_id: &str) {
39 self.access_token = phone_number_id.into();
40 }
41
42 pub async fn send_message(&self, message: &Message) -> Result<MessageResponse, WhatsappError> {
43 http_client::post(&self.messages_api_url(), &self.access_token, message).await
44 }
45
46 pub async fn request_code(
47 &self,
48 code_method: CodeMethod,
49 language: &str,
50 ) -> Result<PhoneNumberResponse, WhatsappError> {
51 let params = CodeRequestParams {
52 code_method,
53 language: language.into(),
54 };
55 http_client::post(&self.request_code_api_url(), &self.access_token, ¶ms).await
56 }
57
58 pub async fn verify_code(&self, code: &str) -> Result<PhoneNumberResponse, WhatsappError> {
59 let params = CodeVerifyParams { code: code.into() };
60 http_client::post(&self.verify_code_api_url(), &self.access_token, ¶ms).await
61 }
62
63 pub async fn mark_message_as_read(
64 &self,
65 message_id: &str,
66 ) -> Result<MessageStatusResponse, WhatsappError> {
67 let message_status = MessageStatus::for_read(message_id);
68 http_client::post(
69 &self.messages_api_url(),
70 &self.access_token,
71 &message_status,
72 )
73 .await
74 }
75
76 pub async fn get_media(&self, media_id: &str) -> Result<MediaResponse, WhatsappError> {
77 http_client::get(&self.media_api_url(media_id), &self.access_token).await
78 }
79
80 fn facebook_api_version_url(&self) -> String {
81 format!("{FACEBOOK_GRAPH_API_BASE_URL}/{}", self.version)
82 }
83
84 fn messages_api_url(&self) -> String {
85 format!(
86 "{}/{}/messages",
87 self.facebook_api_version_url(),
88 self.phone_number_id
89 )
90 }
91
92 fn media_api_url(&self, media_id: &str) -> String {
93 format!("{}/{media_id}", self.facebook_api_version_url())
94 }
95
96 fn request_code_api_url(&self) -> String {
97 format!(
98 "{}/{}/request_code",
99 self.facebook_api_version_url(),
100 self.phone_number_id
101 )
102 }
103
104 fn verify_code_api_url(&self) -> String {
105 format!(
106 "{}/{}/verify_code",
107 self.facebook_api_version_url(),
108 self.phone_number_id
109 )
110 }
111}
112
113mod http_client {
114 use reqwest::StatusCode;
115 use serde::{de::DeserializeOwned, Serialize};
116
117 use crate::WhatsappError;
118
119 pub async fn get<U>(url: &str, bearer_token: &str) -> Result<U, WhatsappError>
120 where
121 U: DeserializeOwned,
122 {
123 let client = reqwest::Client::new();
124 let resp = client.get(url).bearer_auth(bearer_token).send().await?;
125
126 match resp.status() {
127 StatusCode::OK => {
128 let json = resp.json::<U>().await?;
129 Ok(json)
130 }
131 _ => {
132 log::warn!("{:?}", &resp);
133 let error_text = &resp.text().await?;
134 log::warn!("{:?}", &error_text);
135 Err(WhatsappError::UnexpectedError(error_text.to_string()))
136 }
137 }
138 }
139
140 pub async fn post<T, U>(url: &str, bearer_token: &str, data: &T) -> Result<U, WhatsappError>
141 where
142 T: Serialize + ?Sized,
143 U: DeserializeOwned,
144 {
145 let client = reqwest::Client::new();
146 let resp = client
147 .post(url)
148 .bearer_auth(bearer_token)
149 .json(&data)
150 .send()
151 .await?;
152
153 match resp.status() {
154 StatusCode::OK | StatusCode::CREATED => {
155 let json = resp.json::<U>().await?;
156 Ok(json)
157 }
158 _ => {
159 log::warn!("{:?}", &resp);
160 let error_text = &resp.text().await?;
161 log::warn!("{:?}", &error_text);
162 Err(WhatsappError::UnexpectedError(error_text.to_string()))
163 }
164 }
165 }
166}