android_sms_gateway/types/
webhook.rs1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(transparent)]
10pub struct WebhookEvent(pub String);
11
12impl WebhookEvent {
13 pub const SMS_RECEIVED: &'static str = "sms:received";
14 pub const SMS_DATA_RECEIVED: &'static str = "sms:data-received";
15 pub const SMS_SENT: &'static str = "sms:sent";
16 pub const SMS_DELIVERED: &'static str = "sms:delivered";
17 pub const SMS_FAILED: &'static str = "sms:failed";
18 pub const SMS_CANCELLED: &'static str = "sms:cancelled";
19 pub const SYSTEM_PING: &'static str = "system:ping";
20 pub const MMS_RECEIVED: &'static str = "mms:received";
21 pub const MMS_DOWNLOADED: &'static str = "mms:downloaded";
22 pub const APP_STARTED: &'static str = "app:started";
23 pub const SMS_BATCH_RECEIVED: &'static str = "sms:batch:received";
24 pub const SMS_BATCH_DATA_RECEIVED: &'static str = "sms:batch:data-received";
25 pub const MMS_BATCH_RECEIVED: &'static str = "mms:batch:received";
26 pub const MMS_BATCH_DOWNLOADED: &'static str = "mms:batch:downloaded";
27
28 pub fn new(s: impl Into<String>) -> Self {
30 Self(s.into())
31 }
32
33 pub fn as_str(&self) -> &str {
35 &self.0
36 }
37}
38
39pub const WEBHOOK_EVENT_TYPES: &[&str] = &[
41 WebhookEvent::SMS_RECEIVED,
42 WebhookEvent::SMS_DATA_RECEIVED,
43 WebhookEvent::SMS_SENT,
44 WebhookEvent::SMS_DELIVERED,
45 WebhookEvent::SMS_FAILED,
46 WebhookEvent::SMS_CANCELLED,
47 WebhookEvent::SYSTEM_PING,
48 WebhookEvent::MMS_RECEIVED,
49 WebhookEvent::MMS_DOWNLOADED,
50 WebhookEvent::APP_STARTED,
51 WebhookEvent::SMS_BATCH_RECEIVED,
52 WebhookEvent::SMS_BATCH_DATA_RECEIVED,
53 WebhookEvent::MMS_BATCH_RECEIVED,
54 WebhookEvent::MMS_BATCH_DOWNLOADED,
55];
56
57pub fn is_valid_webhook_event(e: &str) -> bool {
59 WEBHOOK_EVENT_TYPES.contains(&e)
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct Webhook {
65 #[serde(default, skip_serializing_if = "Option::is_none")]
67 pub id: Option<String>,
68 #[serde(default, skip_serializing_if = "Option::is_none")]
70 pub device_id: Option<String>,
71 pub url: String,
73 pub event: WebhookEvent,
75}
76
77impl Webhook {
78 pub fn validate(&self) -> Result<(), crate::Error> {
82 if !is_valid_webhook_event(self.event.as_str()) {
83 return Err(crate::Error::Validation("invalid event type".to_string()));
84 }
85
86 if !self.url.to_lowercase().starts_with("https://") {
87 return Err(crate::Error::Validation(
88 "url must start with https://".to_string(),
89 ));
90 }
91
92 let parsed = url::Url::parse(&self.url)
93 .map_err(|_| crate::Error::Validation("invalid url".to_string()))?;
94 if parsed.host_str().is_none_or(|h| h.is_empty()) {
95 return Err(crate::Error::Validation(
96 "url must have a valid host".to_string(),
97 ));
98 }
99
100 Ok(())
101 }
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize)]
106#[serde(rename_all = "camelCase")]
107pub struct SmsEventPayload {
108 pub message_id: String,
110 pub phone_number: String,
112 pub sender: String,
114 #[serde(default, skip_serializing_if = "Option::is_none")]
116 pub recipient: Option<String>,
117 #[serde(default, skip_serializing_if = "Option::is_none")]
119 pub sim_number: Option<u8>,
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize)]
124#[serde(rename_all = "camelCase")]
125pub struct SmsReceivedPayload {
126 #[serde(flatten)]
127 pub base: SmsEventPayload,
128 pub message: String,
130 pub received_at: DateTime<Utc>,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
136#[serde(rename_all = "camelCase")]
137pub struct SmsSentPayload {
138 #[serde(flatten)]
139 pub base: SmsEventPayload,
140 pub sent_at: DateTime<Utc>,
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
146#[serde(rename_all = "camelCase")]
147pub struct SmsDeliveredPayload {
148 #[serde(flatten)]
149 pub base: SmsEventPayload,
150 pub delivered_at: DateTime<Utc>,
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
156#[serde(rename_all = "camelCase")]
157pub struct SmsCancelledPayload {
158 #[serde(flatten)]
159 pub base: SmsEventPayload,
160 pub cancelled_at: DateTime<Utc>,
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize)]
166#[serde(rename_all = "camelCase")]
167pub struct SmsFailedPayload {
168 #[serde(flatten)]
169 pub base: SmsEventPayload,
170 pub failed_at: DateTime<Utc>,
172 pub reason: String,
174}
175
176#[derive(Debug, Clone, Serialize, Deserialize)]
178#[serde(rename_all = "camelCase")]
179pub struct SmsDataReceivedPayload {
180 #[serde(flatten)]
181 pub base: SmsEventPayload,
182 pub data: String,
184 pub received_at: DateTime<Utc>,
186}
187
188#[derive(Debug, Clone, Serialize, Deserialize)]
190#[serde(rename_all = "camelCase")]
191pub struct MmsReceivedPayload {
192 #[serde(flatten)]
193 pub base: SmsEventPayload,
194 pub transaction_id: String,
196 #[serde(default, skip_serializing_if = "Option::is_none")]
198 pub subject: Option<String>,
199 pub content_class: String,
201 pub size: i64,
203 pub received_at: DateTime<Utc>,
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize)]
209#[serde(rename_all = "camelCase")]
210pub struct MmsDownloadedAttachment {
211 pub part_id: i32,
213 pub content_type: String,
215 #[serde(default, skip_serializing_if = "Option::is_none")]
217 pub name: Option<String>,
218 #[serde(default, skip_serializing_if = "Option::is_none")]
220 pub data: Option<String>,
221 #[serde(default, skip_serializing_if = "Option::is_none")]
223 pub size: Option<i64>,
224}
225
226#[derive(Debug, Clone, Serialize, Deserialize)]
228#[serde(rename_all = "camelCase")]
229pub struct MmsDownloadedPayload {
230 #[serde(flatten)]
231 pub base: SmsEventPayload,
232 #[serde(default, skip_serializing_if = "Option::is_none")]
234 pub subject: Option<String>,
235 #[serde(default, skip_serializing_if = "Option::is_none")]
237 pub body: Option<String>,
238 pub attachments: Vec<MmsDownloadedAttachment>,
240 pub received_at: DateTime<Utc>,
242}
243
244#[derive(Debug, Clone, Serialize, Deserialize)]
246#[serde(rename_all = "camelCase")]
247pub struct SmsBatchReceivedPayload {
248 #[serde(default)]
250 pub messages: Vec<SmsReceivedPayload>,
251}
252
253#[derive(Debug, Clone, Serialize, Deserialize)]
255#[serde(rename_all = "camelCase")]
256pub struct SmsBatchDataReceivedPayload {
257 #[serde(default)]
259 pub messages: Vec<SmsDataReceivedPayload>,
260}
261
262#[derive(Debug, Clone, Serialize, Deserialize)]
264#[serde(rename_all = "camelCase")]
265pub struct MmsBatchReceivedPayload {
266 #[serde(default)]
268 pub messages: Vec<MmsReceivedPayload>,
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize)]
273#[serde(rename_all = "camelCase")]
274pub struct MmsBatchDownloadedPayload {
275 #[serde(default)]
277 pub messages: Vec<MmsDownloadedPayload>,
278}