Skip to main content

whatsapp_cloud_api/
whatsapp_client.rs

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