asterisk_ari_client_rs/models/
events.rs

1use super::playbacks::Playback;
2#[cfg(feature = "parse-event-datetimes")]
3use crate::models::channels::ari_date_format;
4use crate::models::channels::Channel;
5use crate::models::recordings::Recording;
6#[cfg(feature = "parse-event-datetimes")]
7use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9
10// TBD: Event extends Message and all event types extend event.
11// Since rust does not support inheritance we need to figure out how
12// how composition would work with serde. For now just duplicating the stuff
13
14#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
15pub struct Message {
16    /// Indicates the type of this message.
17    #[serde(rename = "type")]
18    pub r#type: String,
19
20    /// The unique ID for the Asterisk instance that raised this event.
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub asterisk_id: Option<String>,
23}
24
25#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
26pub struct Event {
27    /// Indicates the type of this message.
28    #[serde(rename = "type")]
29    pub r#type: String,
30
31    /// The unique ID for the Asterisk instance that raised this event.
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub asterisk_id: Option<String>,
34
35    /// Name of the application receiving the event.
36    pub application: String,
37
38    /// Time at which this event was created.
39    #[cfg(feature = "parse-event-datetimes")]
40    #[serde(with = "ari_date_format")]
41    pub timestamp: DateTime<Utc>,
42
43    #[cfg(not(feature = "parse-event-datetimes"))]
44    pub timestamp: String,
45}
46
47#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
48pub struct StasisStart {
49    /// The unique ID for the Asterisk instance that raised this event.
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub asterisk_id: Option<String>,
52
53    /// Name of the application receiving the event.
54    pub application: String,
55
56    /// Time at which this event was created. E.g. 2020-11-22T20:12:51.214+0000
57    #[cfg(feature = "parse-event-datetimes")]
58    #[serde(with = "ari_date_format")]
59    pub timestamp: DateTime<Utc>,
60
61    #[cfg(not(feature = "parse-event-datetimes"))]
62    pub timestamp: String,
63
64    /// Arguments to the application.
65    pub args: Vec<String>,
66
67    /// Channel.
68    pub channel: Channel,
69
70    /// Replace_channel.
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub replace_channel: Option<Channel>,
73}
74
75#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
76pub struct ChannelDtmfReceived {
77    /// The unique ID for the Asterisk instance that raised this event.
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub asterisk_id: Option<String>,
80
81    /// Name of the application receiving the event.
82    pub application: String,
83
84    /// Time at which this event was created.
85    #[cfg(feature = "parse-event-datetimes")]
86    #[serde(with = "ari_date_format")]
87    pub timestamp: DateTime<Utc>,
88
89    #[cfg(not(feature = "parse-event-datetimes"))]
90    pub timestamp: String,
91
92    /// DTMF digit received (0-9, A-E, # or *).
93    pub digit: String,
94
95    /// Number of milliseconds DTMF was received.
96    pub duration_ms: i64,
97
98    /// The channel on which DTMF was received.
99    pub channel: Channel,
100}
101
102#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
103pub struct ChannelHangupRequest {
104    /// The unique ID for the Asterisk instance that raised this event.
105    #[serde(skip_serializing_if = "Option::is_none")]
106    pub asterisk_id: Option<String>,
107
108    /// Name of the application receiving the event.
109    pub application: String,
110
111    /// Time at which this event was created.
112    #[cfg(feature = "parse-event-datetimes")]
113    #[serde(with = "ari_date_format")]
114    pub timestamp: DateTime<Utc>,
115
116    #[cfg(not(feature = "parse-event-datetimes"))]
117    pub timestamp: String,
118
119    /// Integer representation of the cause of the hangup.
120    #[serde(skip_serializing_if = "Option::is_none")]
121    pub cause: Option<i64>,
122
123    /// Whether the hangup request was a soft hangup request.
124    #[serde(skip_serializing_if = "Option::is_none")]
125    pub soft: Option<bool>,
126
127    /// The channel on which the hangup was requested.
128    pub channel: Channel,
129}
130
131#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
132pub struct StasisEnd {
133    /// The unique ID for the Asterisk instance that raised this event.
134    #[serde(skip_serializing_if = "Option::is_none")]
135    pub asterisk_id: Option<String>,
136
137    /// Name of the application receiving the event.
138    pub application: String,
139
140    /// Time at which this event was created.
141    #[cfg(feature = "parse-event-datetimes")]
142    #[serde(with = "ari_date_format")]
143    pub timestamp: DateTime<Utc>,
144
145    #[cfg(not(feature = "parse-event-datetimes"))]
146    pub timestamp: String,
147
148    /// Channel.
149    pub channel: Channel,
150}
151
152#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
153pub struct ChannelTalkingFinished {
154    /// The unique ID for the Asterisk instance that raised this event.
155    #[serde(skip_serializing_if = "Option::is_none")]
156    pub asterisk_id: Option<String>,
157
158    /// Name of the application receiving the event.
159    pub application: String,
160
161    /// Time at which this event was created.
162    #[cfg(feature = "parse-event-datetimes")]
163    #[serde(with = "ari_date_format")]
164    pub timestamp: DateTime<Utc>,
165
166    #[cfg(not(feature = "parse-event-datetimes"))]
167    pub timestamp: String,
168
169    /// The channel on which talking completed.
170    pub channel: Channel,
171
172    /// The length of time, in milliseconds, that talking was detected on the channel.
173    pub duration: i64,
174}
175
176#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
177pub struct ChannelTalkingStarted {
178    /// The unique ID for the Asterisk instance that raised this event.
179    #[serde(skip_serializing_if = "Option::is_none")]
180    pub asterisk_id: Option<String>,
181
182    /// Name of the application receiving the event.
183    pub application: String,
184
185    /// Time at which this event was created.
186    #[cfg(feature = "parse-event-datetimes")]
187    #[serde(with = "ari_date_format")]
188    pub timestamp: DateTime<Utc>,
189
190    #[cfg(not(feature = "parse-event-datetimes"))]
191    pub timestamp: String,
192
193    /// The channel on which talking started.
194    pub channel: Channel,
195}
196
197#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
198pub struct ChannelDestroyed {
199    /// The unique ID for the Asterisk instance that raised this event.
200    #[serde(skip_serializing_if = "Option::is_none")]
201    pub asterisk_id: Option<String>,
202
203    /// Name of the application receiving the event.
204    pub application: String,
205
206    /// Time at which this event was created.
207    #[cfg(feature = "parse-event-datetimes")]
208    #[serde(with = "ari_date_format")]
209    pub timestamp: DateTime<Utc>,
210
211    #[cfg(not(feature = "parse-event-datetimes"))]
212    pub timestamp: String,
213
214    /// Integer representation of the cause of the hangup.
215    pub cause: i64,
216
217    /// Text representation of the cause of the hangup.
218    pub cause_txt: String,
219
220    /// Channel.
221    pub channel: Channel,
222}
223
224#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
225pub struct PlaybackStarted {
226    /// The unique ID for the Asterisk instance that raised this event.
227    #[serde(skip_serializing_if = "Option::is_none")]
228    pub asterisk_id: Option<String>,
229
230    /// Name of the application receiving the event.
231    pub application: String,
232
233    /// Time at which this event was created.
234    #[cfg(feature = "parse-event-datetimes")]
235    #[serde(with = "ari_date_format")]
236    pub timestamp: DateTime<Utc>,
237
238    #[cfg(not(feature = "parse-event-datetimes"))]
239    pub timestamp: String,
240
241    /// playback resource
242    pub playback: Playback,
243}
244
245#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
246pub struct PlaybackFinished {
247    /// The unique ID for the Asterisk instance that raised this event.
248    #[serde(skip_serializing_if = "Option::is_none")]
249    pub asterisk_id: Option<String>,
250
251    /// Name of the application receiving the event.
252    pub application: String,
253
254    /// Time at which this event was created.
255    #[cfg(feature = "parse-event-datetimes")]
256    #[serde(with = "ari_date_format")]
257    pub timestamp: DateTime<Utc>,
258
259    #[cfg(not(feature = "parse-event-datetimes"))]
260    pub timestamp: String,
261
262    /// playback resource
263    pub playback: Playback,
264}
265
266#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
267pub struct ChannelStateChange {
268    /// The unique ID for the Asterisk instance that raised this event.
269    #[serde(skip_serializing_if = "Option::is_none")]
270    pub asterisk_id: Option<String>,
271
272    /// Name of the application receiving the event.
273    pub application: String,
274
275    /// Time at which this event was created. E.g. 2020-11-22T20:12:51.214+0000
276    #[cfg(feature = "parse-event-datetimes")]
277    #[serde(with = "ari_date_format")]
278    pub timestamp: DateTime<Utc>,
279
280    #[cfg(not(feature = "parse-event-datetimes"))]
281    pub timestamp: String,
282
283    /// Channel.
284    pub channel: Channel,
285}
286
287#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
288pub struct ChannelVarset {
289    /// The unique ID for the Asterisk instance that raised this event.
290    #[serde(skip_serializing_if = "Option::is_none")]
291    pub asterisk_id: Option<String>,
292
293    /// Name of the application receiving the event.
294    pub application: String,
295
296    /// Time at which this event was created. E.g. 2020-11-22T20:12:51.214+0000
297    #[cfg(feature = "parse-event-datetimes")]
298    #[serde(with = "ari_date_format")]
299    pub timestamp: DateTime<Utc>,
300
301    #[cfg(not(feature = "parse-event-datetimes"))]
302    pub timestamp: String,
303
304    /// Channel.
305    pub channel: Channel,
306
307    pub variable: String,
308
309    pub value: String,
310}
311
312#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
313pub struct RecordingStarted {
314    /// The unique ID for the Asterisk instance that raised this event.
315    #[serde(skip_serializing_if = "Option::is_none")]
316    pub asterisk_id: Option<String>,
317
318    /// Name of the application receiving the event.
319    pub application: String,
320
321    /// Time at which this event was created. E.g. 2020-11-22T20:12:51.214+0000
322    #[cfg(feature = "parse-event-datetimes")]
323    #[serde(with = "ari_date_format")]
324    pub timestamp: DateTime<Utc>,
325
326    #[cfg(not(feature = "parse-event-datetimes"))]
327    pub timestamp: String,
328
329    /// Recording.
330    pub recording: Recording,
331}
332
333#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
334pub struct RecordingFinished {
335    /// The unique ID for the Asterisk instance that raised this event.
336    #[serde(skip_serializing_if = "Option::is_none")]
337    pub asterisk_id: Option<String>,
338
339    /// Name of the application receiving the event.
340    pub application: String,
341
342    /// Time at which this event was created. E.g. 2020-11-22T20:12:51.214+0000
343    #[cfg(feature = "parse-event-datetimes")]
344    #[serde(with = "ari_date_format")]
345    pub timestamp: DateTime<Utc>,
346
347    #[cfg(not(feature = "parse-event-datetimes"))]
348    pub timestamp: String,
349
350    /// Recording.
351    pub recording: Recording,
352}
353
354#[derive(Debug, Serialize, Deserialize)]
355#[serde(tag = "type")]
356pub enum AriEvent {
357    StasisStart(StasisStart),
358    ChannelDtmfReceived(ChannelDtmfReceived),
359    ChannelHangupRequest(ChannelHangupRequest),
360    StasisEnd(StasisEnd),
361    ChannelTalkingFinished(ChannelTalkingFinished),
362    ChannelTalkingStarted(ChannelTalkingStarted),
363    ChannelDestroyed(ChannelDestroyed),
364    PlaybackStarted(PlaybackStarted),
365    PlaybackFinished(PlaybackFinished),
366    ChannelStateChange(ChannelStateChange),
367    ChannelVarset(ChannelVarset),
368    RecordingStarted(RecordingStarted),
369    RecordingFinished(RecordingFinished),
370}
371
372#[cfg(test)]
373mod tests {
374    use super::*;
375
376    const STR_JSON: &str = "{\n  \"type\": \"StasisStart\",\n  \"timestamp\": \"2020-11-22T20:17:06.150+0000\",\n  \"args\": [\n    \"its-va-demo-app\",\n    \"en-US\"\n  ],\n  \"channel\": {\n    \"id\": \"1606076223.3\",\n    \"name\": \"PJSIP/6001-00000003\",\n    \"state\": \"Up\",\n    \"caller\": {\n      \"name\": \"\",\n      \"number\": \"6001\"\n    },\n    \"connected\": {\n      \"name\": \"\",\n      \"number\": \"\"\n    },\n    \"accountcode\": \"\",\n    \"dialplan\": {\n      \"context\": \"from-internal\",\n      \"exten\": \"101\",\n      \"priority\": 6,\n      \"app_name\": \"Stasis\",\n      \"app_data\": \"va-voicegw,its-va-demo-app,en-US\"\n    },\n    \"creationtime\": \"2020-11-22T20:17:03.741+0000\",\n    \"language\": \"en\"\n  },\n  \"asterisk_id\": \"00:15:5d:01:65:04\",\n  \"application\": \"va-voicegw\"\n}";
377    const STR_JSON2: &str = "{\n  \"type\": \"StasisStart\",\n  \"timestamp\": \"2021-01-07T21:12:57.268+0100\",\n  \"args\": [\n    \"freight-cs-voice\",\n    \"en-US\"\n  ],\n  \"channel\": {\n    \"id\": \"1610050377.0\",\n    \"name\": \"SIP/1004-00000000\",\n    \"state\": \"Ring\",\n    \"caller\": {\n      \"name\": \"Adam\",\n      \"number\": \"1004\"\n    },\n    \"connected\": {\n      \"name\": \"\",\n      \"number\": \"\"\n    },\n    \"accountcode\": \"\",\n    \"dialplan\": {\n      \"context\": \"internal\",\n      \"exten\": \"158\",\n      \"priority\": 10,\n      \"app_name\": \"Stasis\",\n      \"app_data\": \"va-voicegw-rs,freight-cs-voice,en-US\"\n    },\n    \"creationtime\": \"2021-01-07T21:12:57.267+0100\",\n    \"language\": \"en\"\n  },\n  \"asterisk_id\": \"00:50:56:98:74:21\",\n  \"application\": \"va-voicegw-rs\"\n}";
378
379    const STR_JSON_CHNL_STATE_CHANGED: &str = "{\n  \"type\": \"ChannelStateChange\",\n  \"timestamp\": \"2021-01-07T22:12:29.571+0100\",\n  \"channel\": {\n    \"id\": \"1610053949.0\",\n    \"name\": \"SIP/1004-00000000\",\n    \"state\": \"Up\",\n    \"caller\": {\n      \"name\": \"Adam\",\n      \"number\": \"1004\"\n    },\n    \"connected\": {\n      \"name\": \"\",\n      \"number\": \"\"\n    },\n    \"accountcode\": \"\",\n    \"dialplan\": {\n      \"context\": \"internal\",\n      \"exten\": \"158\",\n      \"priority\": 10,\n      \"app_name\": \"Stasis\",\n      \"app_data\": \"va-voicegw-rs,freight-cs-voice,en-US\"\n    },\n    \"creationtime\": \"2021-01-07T22:12:29.369+0100\",\n    \"language\": \"en\"\n  },\n  \"asterisk_id\": \"00:50:56:98:74:21\",\n  \"application\": \"va-voicegw-rs\"\n}";
380
381    // cargo test --package asterisk-ari-client -- --show-output test_parse_stasis_start
382    #[test]
383    fn test_parse_stasis_start() {
384        let ari_event: StasisStart = serde_json::from_str(STR_JSON).unwrap();
385        println!("{:#?}", ari_event);
386    }
387
388    // cargo test --package asterisk-ari-client -- --show-output test_2_parse_stasis_start
389    // throws chrono lib error: no possible date and time matching input
390    // if: "2021-01-07T21:12:57.268+0100 -> "2021-01-07T21:12:57.268+0000 (i.e. TZ 0100 -> 00) it will pass
391    // to be investigated
392    // for now this resulted in feature parse-event-datetimes
393    // i.e. this will pass:
394    //      cargo test --package asterisk-ari-client -- --show-output test_2_parse_stasis_start
395    // but this will not:
396    //      cargo test --features parse-event-datetimes --package asterisk-ari-client -- --show-output test_2_parse_stasis_start
397    #[test]
398    fn test_2_parse_stasis_start() {
399        let ari_event: StasisStart = serde_json::from_str(STR_JSON2).unwrap();
400        println!("{:#?}", ari_event);
401    }
402
403    // cargo test --package asterisk-ari-client -- --show-output test_parse_channel_state_change
404    #[test]
405    fn test_parse_channel_state_change() {
406        let ari_event: ChannelStateChange =
407            serde_json::from_str(STR_JSON_CHNL_STATE_CHANGED).unwrap();
408        println!("{:#?}", ari_event);
409    }
410
411    // cargo test -- --show-output test_parse_ari_event_stasis_start
412    // serialize into enum AriEvent. Test that 'Internally tagged enum representation' serde feature works fine
413    #[test]
414    fn test_parse_ari_event_stasis_start() {
415        let ari_event: AriEvent = serde_json::from_str(STR_JSON).unwrap();
416        println!("{:#?}", ari_event);
417    }
418}