Skip to main content

active_call/
event.rs

1use crate::media::PcmBuf;
2use serde::{Deserialize, Serialize};
3use serde_with::skip_serializing_none;
4use std::{collections::HashMap, fmt::Display};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7#[serde(rename_all = "camelCase")]
8pub struct Attendee {
9    pub username: String,
10    pub realm: String,
11    pub source: String,
12}
13
14impl From<&String> for Attendee {
15    fn from(source: &String) -> Self {
16        let uri = rsipstack::rsip::Uri::try_from(source.as_str()).unwrap_or_default();
17        Self {
18            username: uri.user().unwrap_or_default().to_string(),
19            realm: uri.host().to_string(),
20            source: source.to_string(),
21        }
22    }
23}
24
25#[skip_serializing_none]
26#[derive(Debug, Clone, Serialize, Deserialize)]
27#[serde(
28    tag = "event",
29    rename_all = "camelCase",
30    rename_all_fields = "camelCase"
31)]
32pub enum SessionEvent {
33    Incoming {
34        track_id: String,
35        timestamp: u64,
36        caller: String,
37        callee: String,
38        sdp: String,
39    },
40    Answer {
41        track_id: String,
42        timestamp: u64,
43        sdp: String,
44        refer: Option<bool>,
45    },
46    MediaReady {
47        track_id: String,
48        timestamp: u64,
49    },
50    Reject {
51        track_id: String,
52        timestamp: u64,
53        reason: String,
54        refer: Option<bool>,
55        code: Option<u32>,
56    },
57    Ringing {
58        track_id: String,
59        timestamp: u64,
60        early_media: bool,
61        refer: Option<bool>,
62    },
63    Hangup {
64        track_id: String,
65        timestamp: u64,
66        reason: Option<String>,
67        initiator: Option<String>,
68        start_time: String,
69        hangup_time: String,
70        answer_time: Option<String>,
71        ringing_time: Option<String>,
72        from: Option<Attendee>,
73        to: Option<Attendee>,
74        extra: Option<HashMap<String, serde_json::Value>>,
75        refer: Option<bool>,
76    },
77    AnswerMachineDetection {
78        // Answer machine detection
79        track_id: String,
80        timestamp: u64,
81        start_time: u64,
82        end_time: u64,
83        text: String,
84    },
85    Interrupt {
86        receiver: Option<String>,
87    },
88    FunctionCall {
89        track_id: String,
90        call_id: String,
91        name: String,
92        arguments: String,
93        timestamp: u64,
94    },
95    Speaking {
96        track_id: String,
97        timestamp: u64,
98        start_time: u64,
99        is_filler: Option<bool>,
100        confidence: Option<f32>,
101        refer: Option<bool>,
102    },
103    Silence {
104        track_id: String,
105        timestamp: u64,
106        start_time: u64,
107        duration: u64,
108        refer: Option<bool>,
109        #[serde(skip)]
110        samples: Option<PcmBuf>,
111    },
112    ///End of Utterance
113    Eou {
114        track_id: String,
115        timestamp: u64,
116        completed: bool,
117        interrupt_point: Option<String>,
118        text: Option<String>,
119        refer: Option<bool>,
120    },
121    ///Inactivity timeout
122    Inactivity {
123        track_id: String,
124        timestamp: u64,
125    },
126    Dtmf {
127        track_id: String,
128        timestamp: u64,
129        digit: String,
130        refer: Option<bool>,
131    },
132    Hold {
133        track_id: String,
134        timestamp: u64,
135        on_hold: bool,
136        refer: Option<bool>,
137    },
138    TransferRequest {
139        track_id: String,
140        timestamp: u64,
141        refer_to: String,
142        referred_by: Option<String>,
143        refer: Option<bool>,
144    },
145    TrackStart {
146        track_id: String,
147        timestamp: u64,
148        play_id: Option<String>,
149    },
150    TrackEnd {
151        track_id: String,
152        timestamp: u64,
153        duration: u64,
154        ssrc: u32,
155        play_id: Option<String>,
156    },
157    Interruption {
158        track_id: String,
159        timestamp: u64,
160        play_id: Option<String>,
161        subtitle: Option<String>, // current tts text
162        position: Option<u32>,    // word index in subtitle
163        total_duration: u32,      // whole tts duration
164        current: u32,             // elapsed time since start of tts
165    },
166    AsrFinal {
167        track_id: String,
168        timestamp: u64,
169        index: u32,
170        start_time: Option<u64>,
171        end_time: Option<u64>,
172        text: String,
173        is_filler: Option<bool>,
174        confidence: Option<f32>,
175        task_id: Option<String>,
176        refer: Option<bool>,
177    },
178    AsrDelta {
179        track_id: String,
180        index: u32,
181        timestamp: u64,
182        start_time: Option<u64>,
183        end_time: Option<u64>,
184        text: String,
185        is_filler: Option<bool>,
186        confidence: Option<f32>,
187        task_id: Option<String>,
188        refer: Option<bool>,
189    },
190    Metrics {
191        timestamp: u64,
192        key: String,
193        duration: u32,
194        data: serde_json::Value,
195    },
196    Error {
197        track_id: String,
198        timestamp: u64,
199        sender: String,
200        error: String,
201        code: Option<u32>,
202    },
203    AddHistory {
204        sender: Option<String>,
205        timestamp: u64,
206        speaker: String,
207        text: String,
208    },
209    Other {
210        track_id: String,
211        timestamp: u64,
212        sender: String,
213        extra: Option<HashMap<String, String>>,
214    },
215    Binary {
216        track_id: String,
217        timestamp: u64,
218        data: Vec<u8>,
219    },
220    Ping {
221        timestamp: u64,
222        payload: Option<String>,
223    },
224    Custom {
225        timestamp: u64,
226        sender: Option<String>,
227        data: serde_json::Value,
228    },
229    RingbackState {
230        track_id: String,
231        timestamp: u64,
232        /// Current line state: "ringing", "human_voice", "busy_tone", "silence", etc.
233        state: String,
234        /// Class index from the TelcoClassifier (3=ringing, 9=human_voice, ...)
235        state_index: u32,
236        /// Confidence of the prediction
237        confidence: f32,
238        /// Previous state (None on first detection)
239        prev_state: Option<String>,
240        prev_confidence: Option<f32>,
241        refer: Option<bool>,
242    },
243}
244
245impl Display for SessionEvent {
246    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
247        match self {
248            SessionEvent::Silence {
249                track_id, duration, ..
250            } => {
251                write!(f, "Silence(track_id={}, duration={})", track_id, duration)
252            }
253            SessionEvent::Binary { track_id, data, .. } => {
254                write!(f, "Silence(track_id={}, data_len={})", track_id, data.len())
255            }
256            _ => {
257                write!(f, "{:?}", self)
258            }
259        }
260    }
261}
262
263pub type EventSender = tokio::sync::broadcast::Sender<SessionEvent>;
264pub type EventReceiver = tokio::sync::broadcast::Receiver<SessionEvent>;
265
266pub fn create_event_sender() -> EventSender {
267    EventSender::new(128)
268}