Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ sea-orm-migration = "1.1.14"
object_store = { version = "0.12.1", features = ["aws", "azure", "gcp"] }
humantime = "2"
ndarray = "0.16.1"
serde_with = "3.14.0"


[dev-dependencies]
Expand Down
54 changes: 10 additions & 44 deletions src/call/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use rsipstack::{
transport::SipAddr,
};
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use std::{collections::HashMap, time::Instant};
pub mod active_call;
pub mod b2bua;
Expand All @@ -36,36 +37,23 @@ pub struct SipOption {
pub headers: Option<HashMap<String, String>>,
}

#[skip_serializing_none]
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct CallOption {
#[serde(skip_serializing_if = "Option::is_none")]
pub denoise: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub offer: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub callee: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub caller: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub recorder: Option<RecorderOption>,
#[serde(skip_serializing_if = "Option::is_none")]
pub vad: Option<VADOption>,
#[serde(skip_serializing_if = "Option::is_none")]
pub asr: Option<TranscriptionOption>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tts: Option<SynthesisOption>,
#[serde(skip_serializing_if = "Option::is_none")]
pub handshake_timeout: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enable_ipv6: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sip: Option<SipOption>,
#[serde(skip_serializing_if = "Option::is_none")]
pub extra: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub codec: Option<String>, // pcmu, pcma, g722, pcm, only for websocket call
#[serde(skip_serializing_if = "Option::is_none")]
pub eou: Option<EouOption>,
}

Expand Down Expand Up @@ -130,43 +118,39 @@ impl CallOption {
}
}

#[skip_serializing_none]
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ReferOption {
#[serde(skip_serializing_if = "Option::is_none")]
pub denoise: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub moh: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub asr: Option<TranscriptionOption>,
/// hangup after the call is ended
pub auto_hangup: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sip: Option<SipOption>,
}

#[skip_serializing_none]
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EouOption {
#[serde(skip_serializing_if = "Option::is_none")]
pub r#type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub endpoint: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub secret_key: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub secret_id: Option<String>,
/// max timeout in milliseconds
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout: Option<u32>,
}

// WebSocket Commands
#[skip_serializing_none]
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(tag = "command")]
#[serde(rename_all = "camelCase")]
#[serde(
tag = "command",
rename_all = "camelCase",
rename_all_fields = "camelCase"
)]
pub enum Command {
Invite {
option: CallOption,
Expand All @@ -185,60 +169,42 @@ pub enum Command {
},
Tts {
text: String,
#[serde(skip_serializing_if = "Option::is_none")]
speaker: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "playId")]
/// If the play_id is the same, it will not interrupt the previous playback
play_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "autoHangup")]
/// If auto_hangup is true, it means the call will be hung up automatically after the TTS playback is finished
auto_hangup: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
/// If streaming is true, it means the input text is streaming text,
/// and end_of_stream needs to be used to determine if it's finished,
/// equivalent to LLM's streaming output to TTS synthesis
streaming: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
/// If end_of_stream is true, it means the input text is finished
end_of_stream: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
option: Option<SynthesisOption>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "waitInputTimeout")]
wait_input_timeout: Option<u32>,
},
Play {
url: String,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "autoHangup")]
auto_hangup: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "waitInputTimeout")]
wait_input_timeout: Option<u32>,
},
Interrupt {},
Pause {},
Resume {},
Hangup {
reason: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
initiator: Option<String>,
},
Refer {
caller: String,
/// aor of the calee, e.g., sip:[email protected]
callee: String,
#[serde(skip_serializing_if = "Option::is_none")]
options: Option<ReferOption>,
},
Mute {
#[serde(rename = "trackId")]
track_id: Option<String>,
},
Unmute {
#[serde(rename = "trackId")]
track_id: Option<String>,
},
History {
Expand Down
12 changes: 2 additions & 10 deletions src/callrecord/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use object_store::{
};
use reqwest;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use std::{
collections::HashMap, future::Future, path::Path, pin::Pin, str::FromStr, sync::Arc,
time::Instant,
Expand Down Expand Up @@ -70,35 +71,26 @@ impl<'a> CallRecordEvent<'a> {
}
}
}

#[skip_serializing_none]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CallRecord {
pub call_type: ActiveCallType,
#[serde(skip_serializing_if = "Option::is_none")]
pub option: Option<CallOption>,
pub call_id: String,
pub start_time: DateTime<Utc>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ring_time: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub answer_time: Option<DateTime<Utc>>,
pub end_time: DateTime<Utc>,
pub caller: String,
pub callee: String,
pub status_code: u16,
#[serde(skip_serializing_if = "Option::is_none")]
pub offer: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub answer: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub hangup_reason: Option<CallRecordHangupReason>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub recorder: Vec<CallRecordMedia>,
#[serde(skip_serializing_if = "Option::is_none")]
pub extras: Option<HashMap<String, serde_json::Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub dump_event_file: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub refer_callrecord: Option<Box<CallRecord>>,
}

Expand Down
49 changes: 8 additions & 41 deletions src/event.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::PcmBuf;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use std::collections::HashMap;

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand All @@ -21,141 +22,111 @@ impl From<&String> for Attendee {
}
}
}

#[skip_serializing_none]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "event")]
#[serde(rename_all = "camelCase")]
#[serde(
tag = "event",
rename_all = "camelCase",
rename_all_fields = "camelCase"
)]
pub enum SessionEvent {
Incoming {
#[serde(rename = "trackId")]
track_id: String,
timestamp: u64,
caller: String,
callee: String,
sdp: String,
},
Answer {
#[serde(rename = "trackId")]
track_id: String,
timestamp: u64,
sdp: String,
},
Reject {
#[serde(rename = "trackId")]
track_id: String,
timestamp: u64,
reason: String,
#[serde(skip_serializing_if = "Option::is_none")]
code: Option<u32>,
},
Ringing {
#[serde(rename = "trackId")]
track_id: String,
timestamp: u64,
#[serde(rename = "earlyMedia")]
early_media: bool,
},
Hangup {
timestamp: u64,
#[serde(skip_serializing_if = "Option::is_none")]
reason: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
initiator: Option<String>,
start_time: String,
hangup_time: String,
#[serde(skip_serializing_if = "Option::is_none")]
answer_time: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
ringing_time: Option<String>,
from: Option<Attendee>,
to: Option<Attendee>,
#[serde(skip_serializing_if = "Option::is_none")]
extra: Option<HashMap<String, serde_json::Value>>,
},
AnswerMachineDetection {
// Answer machine detection
timestamp: u64,
#[serde(rename = "startTime")]
start_time: u64,
#[serde(rename = "endTime")]
end_time: u64,
text: String,
},
Speaking {
#[serde(rename = "trackId")]
track_id: String,
timestamp: u64,
#[serde(rename = "startTime")]
start_time: u64,
},
Silence {
#[serde(rename = "trackId")]
track_id: String,
timestamp: u64,
#[serde(rename = "startTime")]
start_time: u64,
duration: u64,
#[serde(skip)]
samples: Option<PcmBuf>,
},
///End of Utterance
Eou {
#[serde(rename = "trackId")]
track_id: String,
timestamp: u64,
completed: bool,
},
Dtmf {
#[serde(rename = "trackId")]
track_id: String,
timestamp: u64,
digit: String,
},
TrackStart {
#[serde(rename = "trackId")]
track_id: String,
timestamp: u64,
},
TrackEnd {
#[serde(rename = "trackId")]
track_id: String,
timestamp: u64,
duration: u64,
ssrc: u32,
},
Interruption {
#[serde(rename = "trackId")]
track_id: String,
timestamp: u64,
subtitle: Option<String>, // current tts text
position: Option<u32>, // word index in subtitle
#[serde(rename = "totalDuration")]
total_duration: u32, // whole tts duration
total_duration: u32, // whole tts duration
current: u32, // elapsed time since start of tts
},
AsrFinal {
#[serde(rename = "trackId")]
track_id: String,
timestamp: u64,
index: u32,
#[serde(rename = "startTime")]
#[serde(skip_serializing_if = "Option::is_none")]
start_time: Option<u64>,
#[serde(rename = "endTime")]
#[serde(skip_serializing_if = "Option::is_none")]
end_time: Option<u64>,
text: String,
},
AsrDelta {
#[serde(rename = "trackId")]
track_id: String,
index: u32,
timestamp: u64,
#[serde(rename = "startTime")]
#[serde(skip_serializing_if = "Option::is_none")]
start_time: Option<u64>,
#[serde(rename = "endTime")]
#[serde(skip_serializing_if = "Option::is_none")]
end_time: Option<u64>,
text: String,
},
Expand All @@ -166,12 +137,10 @@ pub enum SessionEvent {
data: serde_json::Value,
},
Error {
#[serde(rename = "trackId")]
track_id: String,
timestamp: u64,
sender: String,
error: String,
#[serde(skip_serializing_if = "Option::is_none")]
code: Option<u32>,
},
AddHistory {
Expand All @@ -181,14 +150,12 @@ pub enum SessionEvent {
text: String,
},
Other {
#[serde(rename = "trackId")]
track_id: String,
timestamp: u64,
sender: String,
extra: Option<HashMap<String, String>>,
},
Binary {
#[serde(rename = "trackId")]
track_id: String,
timestamp: u64,
data: Vec<u8>,
Expand Down
Loading