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