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}