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    Message {
146        track_id: String,
147        timestamp: u64,
148        body: String,
149        content_type: Option<String>,
150        refer: Option<bool>,
151    },
152    TrackStart {
153        track_id: String,
154        timestamp: u64,
155        play_id: Option<String>,
156    },
157    TrackEnd {
158        track_id: String,
159        timestamp: u64,
160        duration: u64,
161        ssrc: u32,
162        play_id: Option<String>,
163    },
164    Interruption {
165        track_id: String,
166        timestamp: u64,
167        play_id: Option<String>,
168        subtitle: Option<String>, // current tts text
169        position: Option<u32>,    // word index in subtitle
170        total_duration: u32,      // whole tts duration
171        current: u32,             // elapsed time since start of tts
172    },
173    AsrFinal {
174        track_id: String,
175        timestamp: u64,
176        index: u32,
177        start_time: Option<u64>,
178        end_time: Option<u64>,
179        text: String,
180        is_filler: Option<bool>,
181        confidence: Option<f32>,
182        task_id: Option<String>,
183        refer: Option<bool>,
184    },
185    AsrDelta {
186        track_id: String,
187        index: u32,
188        timestamp: u64,
189        start_time: Option<u64>,
190        end_time: Option<u64>,
191        text: String,
192        is_filler: Option<bool>,
193        confidence: Option<f32>,
194        task_id: Option<String>,
195        refer: Option<bool>,
196    },
197    Metrics {
198        timestamp: u64,
199        key: String,
200        duration: u32,
201        data: serde_json::Value,
202    },
203    Error {
204        track_id: String,
205        timestamp: u64,
206        sender: String,
207        error: String,
208        code: Option<u32>,
209    },
210    AddHistory {
211        sender: Option<String>,
212        timestamp: u64,
213        speaker: String,
214        text: String,
215    },
216    Other {
217        track_id: String,
218        timestamp: u64,
219        sender: String,
220        extra: Option<HashMap<String, String>>,
221    },
222    Binary {
223        track_id: String,
224        timestamp: u64,
225        data: Vec<u8>,
226    },
227    Ping {
228        timestamp: u64,
229        payload: Option<String>,
230    },
231    Custom {
232        timestamp: u64,
233        sender: Option<String>,
234        data: serde_json::Value,
235    },
236    RingbackState {
237        track_id: String,
238        timestamp: u64,
239        /// Current line state: "ringing", "human_voice", "busy_tone", "silence", etc.
240        state: String,
241        /// Class index from the TelcoClassifier (3=ringing, 9=human_voice, ...)
242        state_index: u32,
243        /// Confidence of the prediction
244        confidence: f32,
245        /// Previous state (None on first detection)
246        prev_state: Option<String>,
247        prev_confidence: Option<f32>,
248        refer: Option<bool>,
249        /// Whether this is the final determination
250        #[serde(default)]
251        is_final: bool,
252    },
253}
254
255impl Display for SessionEvent {
256    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
257        match self {
258            SessionEvent::Silence {
259                track_id, duration, ..
260            } => {
261                write!(f, "Silence(track_id={}, duration={})", track_id, duration)
262            }
263            SessionEvent::Binary { track_id, data, .. } => {
264                write!(f, "Silence(track_id={}, data_len={})", track_id, data.len())
265            }
266            _ => {
267                write!(f, "{:?}", self)
268            }
269        }
270    }
271}
272
273pub type EventSender = tokio::sync::broadcast::Sender<SessionEvent>;
274pub type EventReceiver = tokio::sync::broadcast::Receiver<SessionEvent>;
275
276pub fn create_event_sender() -> EventSender {
277    EventSender::new(128)
278}