interact_rs/
lib.rs

1pub mod sms {
2    use chrono::prelude::{DateTime, Utc};
3    use chrono::Duration;
4    use serde::Serialize;
5
6    #[derive(Serialize)]
7    pub struct Phonenumber {
8        phone: Vec<String>,
9    }
10
11    #[derive(Serialize)]
12    struct Payload {
13        message_body: String,
14        #[serde(rename = "from")]
15        from_field: String,
16        send_at: String,
17        valid_until: String,
18        #[serde(rename = "to")]
19        to_field: Vec<Phonenumber>,
20    }
21
22    #[derive(Debug, Clone)]
23    pub struct InteractSMS {
24        api_endpoint: String,
25        apikey: String,
26        body: String,
27        from: String,
28        to: Vec<String>,
29        schedule_time: Option<DateTime<Utc>>,
30        valid_until: Option<DateTime<Utc>>,
31    }
32
33    pub struct InteractResponse {
34        pub status: u16,
35        pub response_body: String,
36    }
37
38    #[derive(Debug)]
39    pub enum InteractError {
40        Error { message: String,data: Option <String> },
41    }
42
43    impl InteractError {
44        pub fn get_data(&self) -> Option<String> {
45            match self {
46                InteractError::Error { message:_,data } => {return data.clone();},
47            }
48        }
49    }
50
51    impl std::error::Error for InteractError {}
52
53    impl std::fmt::Display for InteractError {
54        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
55            match self {
56                InteractError::Error { message,data:_ } => write!(f, "{}.", message),
57            }
58        }
59    }
60
61
62    impl InteractSMS {
63        pub fn add_recipient(mut self, recipient: String) -> InteractSMS {
64            self.to.push(recipient.to_string());
65            self
66        }
67
68        pub fn message(mut self, message_body: String) -> InteractSMS {
69            self.body = message_body;
70            self
71        }
72
73        pub fn set_originator(mut self, sender_name: String) -> InteractSMS {
74            self.from = sender_name;
75            self
76        }
77
78        pub fn expires(mut self, expiry_timestamp: DateTime<Utc>) -> InteractSMS {
79            self.valid_until = Some(expiry_timestamp);
80            self
81        }
82
83        pub fn send_at(mut self, send_timestamp: DateTime<Utc>) -> InteractSMS {
84            self.schedule_time = Some(send_timestamp);
85            self
86        }
87
88        pub fn send_sms(&self) -> Result<InteractResponse, InteractError> {
89            let schedule_time = match self.schedule_time {
90                Some(a) => a,
91                None => Utc::now() + Duration::minutes(5),
92            };
93
94            let validity = match self.valid_until {
95                Some(a) => a,
96                None => Utc::now() + Duration::days(2),
97            };
98
99            let mut recipients: Vec<Phonenumber> = Vec::new();
100
101            recipients.push(Phonenumber {
102                phone: self.to.clone(),
103            });
104
105            let schedule_time = schedule_time.format("%Y-%m-%dT%H:%M:%SZ");
106            let validity = validity.format("%Y-%m-%dT%H:%M:%SZ");
107
108            let data = Payload {
109                message_body: self.body.clone(),
110                from_field: self.from.clone(),
111                to_field: recipients,
112                send_at: schedule_time.to_string(),
113                valid_until: validity.to_string(),
114            };
115
116            let payload = serde_json::to_string(&data).expect("Failed to serialize payload");
117            let client = reqwest::blocking::Client::new();
118
119            let res = client
120                .post(self.api_endpoint.clone())
121                .header("accept", "application/json")
122                .header("Content-Type", "application/json")
123                .header("X-auth-key", self.apikey.clone())
124                 .header("Content-Length", &payload.len().to_string())
125                 .header("User-Agent", "Rust/interact.rs".to_string())
126                .body(payload);
127               let res = res.send();
128
129            match res {
130                Ok(resp) => {
131                    let mut response = InteractResponse {
132                        status: 0,
133                        response_body: "".to_string(),
134                    };
135                    let success=resp.status().is_success();
136                    let status_code = resp.status().clone();
137                    if success {
138                        response.status = status_code.as_u16();
139                    }
140
141                    response.response_body = match resp.text() {
142                        Ok(t) => t,
143                        Err(_) => {
144                            if !success {
145                                return Err(InteractError::Error {
146                                    message: "API call was unsuccessful, and unable to parse response body"
147                                        .to_string(),
148                                    data: None
149                                });
150                            } else {
151                                return Err(InteractError::Error {
152                                    message: "API call successful, but unable to parse response body"
153                                        .to_string(),
154                                    data: None
155                                });
156                            }
157                        }
158                    };
159                    if !success {
160                        return Err(InteractError::Error {
161                            message: format!("API returned error {}", status_code),
162                            data: Some(response.response_body.to_string())
163                        });
164                    }
165
166                    return Ok(response);
167                }
168                Err(err) => {
169                    return Err(InteractError::Error {
170                        message: format!("Fatal Error: {}", err),
171                        data: None
172                    });
173                }
174            };
175        }
176    }
177
178    pub fn sms_api(api_key: String) -> InteractSMS {
179        InteractSMS {
180            api_endpoint: "https://api.webexinteract.com/v1/sms".to_string(),
181            apikey: api_key,
182            body: "".to_string(),
183            from: "".to_string(),
184            to: vec![],
185            schedule_time: None,
186            valid_until: None,
187        }
188    }
189}