use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _, ser::Error as _};
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(tag = "type")]
#[non_exhaustive]
pub enum AriEvent {
StasisStart {
channel: Channel,
#[serde(default)]
args: Vec<String>,
#[serde(default)]
replace_channel: Option<Channel>,
},
StasisEnd { channel: Channel },
ChannelCreated { channel: Channel },
ChannelDestroyed {
channel: Channel,
cause: i32,
cause_txt: String,
},
ChannelStateChange { channel: Channel },
ChannelDtmfReceived {
channel: Channel,
digit: String,
duration_ms: u32,
},
ChannelHangupRequest { channel: Channel },
ChannelVarset {
channel: Option<Channel>,
variable: String,
value: String,
},
BridgeCreated { bridge: Bridge },
BridgeDestroyed { bridge: Bridge },
ChannelEnteredBridge { bridge: Bridge, channel: Channel },
ChannelLeftBridge { bridge: Bridge, channel: Channel },
PlaybackStarted { playback: Playback },
PlaybackFinished { playback: Playback },
RecordingStarted { recording: LiveRecording },
RecordingFinished { recording: LiveRecording },
ChannelCallerId {
channel: Channel,
caller_presentation: i32,
caller_presentation_txt: String,
},
ChannelConnectedLine { channel: Channel },
ChannelDialplan {
channel: Channel,
dialplan_app: String,
dialplan_app_data: String,
},
ChannelHold {
channel: Channel,
#[serde(default)]
musicclass: Option<String>,
},
ChannelUnhold { channel: Channel },
ChannelTalkingStarted { channel: Channel },
ChannelTalkingFinished { channel: Channel, duration: i32 },
ChannelToneDetected { channel: Channel },
ChannelTransfer {
channel: Channel,
#[serde(default)]
refer_to: Option<Box<ReferTo>>,
#[serde(default)]
referred_by: Option<Box<ReferredBy>>,
#[serde(default)]
state: Option<String>,
},
ChannelUserevent {
#[serde(default)]
channel: Option<Channel>,
#[serde(default)]
bridge: Option<Bridge>,
#[serde(default)]
endpoint: Option<Endpoint>,
eventname: String,
#[serde(default)]
userevent: serde_json::Value,
},
Dial {
peer: Channel,
#[serde(default)]
caller: Option<Channel>,
#[serde(default)]
forwarded: Option<Channel>,
dialstatus: String,
#[serde(default)]
dialstring: Option<String>,
#[serde(default)]
forward: Option<String>,
},
BridgeAttendedTransfer {
transferer_first_leg: Channel,
transferer_second_leg: Channel,
result: String,
destination_type: String,
is_external: bool,
#[serde(default)]
transferee: Option<Box<Channel>>,
#[serde(default)]
transfer_target: Option<Box<Channel>>,
#[serde(default)]
replace_channel: Option<Box<Channel>>,
#[serde(default)]
transferer_first_leg_bridge: Option<Bridge>,
#[serde(default)]
transferer_second_leg_bridge: Option<Bridge>,
#[serde(default)]
destination_bridge: Option<String>,
#[serde(default)]
destination_application: Option<String>,
#[serde(default)]
destination_link_first_leg: Option<Box<Channel>>,
#[serde(default)]
destination_link_second_leg: Option<Box<Channel>>,
#[serde(default)]
destination_threeway_channel: Option<Box<Channel>>,
#[serde(default)]
destination_threeway_bridge: Option<Bridge>,
},
BridgeBlindTransfer {
channel: Channel,
exten: String,
context: String,
result: String,
is_external: bool,
#[serde(default)]
bridge: Option<Bridge>,
#[serde(default)]
transferee: Option<Channel>,
#[serde(default)]
replace_channel: Option<Channel>,
},
BridgeMerged { bridge: Bridge, bridge_from: Bridge },
BridgeVideoSourceChanged {
bridge: Bridge,
#[serde(default)]
old_video_source_id: Option<String>,
},
ContactStatusChange {
contact_info: ContactInfo,
endpoint: Endpoint,
},
DeviceStateChanged { device_state: DeviceState },
EndpointStateChange { endpoint: Endpoint },
PeerStatusChange { endpoint: Endpoint, peer: Peer },
PlaybackContinuing { playback: Playback },
RecordingFailed { recording: LiveRecording },
ApplicationMoveFailed {
channel: Channel,
destination: String,
#[serde(default)]
args: Vec<String>,
},
ApplicationRegistered {},
ApplicationReplaced {},
ApplicationUnregistered {},
TextMessageReceived {
message: TextMessage,
#[serde(default)]
endpoint: Option<Endpoint>,
},
RESTResponse {
status_code: i32,
reason_phrase: String,
uri: String,
request_id: String,
transaction_id: String,
#[serde(default)]
content_type: Option<String>,
#[serde(default)]
message_body: Option<String>,
},
#[serde(other)]
Unknown,
}
impl AriEvent {
fn known_event_type(&self) -> Option<&'static str> {
Some(match self {
Self::StasisStart { .. } => "StasisStart",
Self::StasisEnd { .. } => "StasisEnd",
Self::ChannelCreated { .. } => "ChannelCreated",
Self::ChannelDestroyed { .. } => "ChannelDestroyed",
Self::ChannelStateChange { .. } => "ChannelStateChange",
Self::ChannelDtmfReceived { .. } => "ChannelDtmfReceived",
Self::ChannelHangupRequest { .. } => "ChannelHangupRequest",
Self::ChannelVarset { .. } => "ChannelVarset",
Self::BridgeCreated { .. } => "BridgeCreated",
Self::BridgeDestroyed { .. } => "BridgeDestroyed",
Self::ChannelEnteredBridge { .. } => "ChannelEnteredBridge",
Self::ChannelLeftBridge { .. } => "ChannelLeftBridge",
Self::PlaybackStarted { .. } => "PlaybackStarted",
Self::PlaybackFinished { .. } => "PlaybackFinished",
Self::RecordingStarted { .. } => "RecordingStarted",
Self::RecordingFinished { .. } => "RecordingFinished",
Self::ChannelCallerId { .. } => "ChannelCallerId",
Self::ChannelConnectedLine { .. } => "ChannelConnectedLine",
Self::ChannelDialplan { .. } => "ChannelDialplan",
Self::ChannelHold { .. } => "ChannelHold",
Self::ChannelUnhold { .. } => "ChannelUnhold",
Self::ChannelTalkingStarted { .. } => "ChannelTalkingStarted",
Self::ChannelTalkingFinished { .. } => "ChannelTalkingFinished",
Self::ChannelToneDetected { .. } => "ChannelToneDetected",
Self::ChannelTransfer { .. } => "ChannelTransfer",
Self::ChannelUserevent { .. } => "ChannelUserevent",
Self::Dial { .. } => "Dial",
Self::BridgeAttendedTransfer { .. } => "BridgeAttendedTransfer",
Self::BridgeBlindTransfer { .. } => "BridgeBlindTransfer",
Self::BridgeMerged { .. } => "BridgeMerged",
Self::BridgeVideoSourceChanged { .. } => "BridgeVideoSourceChanged",
Self::ContactStatusChange { .. } => "ContactStatusChange",
Self::DeviceStateChanged { .. } => "DeviceStateChanged",
Self::EndpointStateChange { .. } => "EndpointStateChange",
Self::PeerStatusChange { .. } => "PeerStatusChange",
Self::PlaybackContinuing { .. } => "PlaybackContinuing",
Self::RecordingFailed { .. } => "RecordingFailed",
Self::ApplicationMoveFailed { .. } => "ApplicationMoveFailed",
Self::ApplicationRegistered { .. } => "ApplicationRegistered",
Self::ApplicationReplaced { .. } => "ApplicationReplaced",
Self::ApplicationUnregistered { .. } => "ApplicationUnregistered",
Self::TextMessageReceived { .. } => "TextMessageReceived",
Self::RESTResponse { .. } => "RESTResponse",
Self::Unknown => return None,
})
}
pub fn channel_ids(&self) -> Vec<&str> {
fn push_channel<'a>(ids: &mut Vec<&'a str>, channel: &'a Channel) {
ids.push(channel.id.as_str());
}
fn push_optional_channel<'a>(ids: &mut Vec<&'a str>, channel: &'a Option<Channel>) {
if let Some(channel) = channel {
push_channel(ids, channel);
}
}
let mut ids = Vec::new();
match self {
Self::StasisStart {
channel: primary,
replace_channel,
..
} => {
push_channel(&mut ids, primary);
push_optional_channel(&mut ids, replace_channel);
}
Self::StasisEnd { channel: value }
| Self::ChannelCreated { channel: value }
| Self::ChannelDestroyed { channel: value, .. }
| Self::ChannelStateChange { channel: value }
| Self::ChannelDtmfReceived { channel: value, .. }
| Self::ChannelHangupRequest { channel: value }
| Self::ChannelCallerId { channel: value, .. }
| Self::ChannelConnectedLine { channel: value }
| Self::ChannelDialplan { channel: value, .. }
| Self::ChannelHold { channel: value, .. }
| Self::ChannelUnhold { channel: value }
| Self::ChannelTalkingStarted { channel: value }
| Self::ChannelTalkingFinished { channel: value, .. }
| Self::ChannelToneDetected { channel: value }
| Self::ChannelEnteredBridge { channel: value, .. }
| Self::ChannelLeftBridge { channel: value, .. }
| Self::ApplicationMoveFailed { channel: value, .. } => {
push_channel(&mut ids, value);
}
Self::ChannelVarset { channel: value, .. }
| Self::ChannelUserevent { channel: value, .. } => {
push_optional_channel(&mut ids, value);
}
Self::ChannelTransfer {
channel: primary,
refer_to,
referred_by,
..
} => {
push_channel(&mut ids, primary);
if let Some(refer_to) = refer_to {
push_optional_channel(&mut ids, &refer_to.destination_channel);
push_optional_channel(&mut ids, &refer_to.connected_channel);
}
if let Some(referred_by) = referred_by {
push_channel(&mut ids, &referred_by.source_channel);
push_optional_channel(&mut ids, &referred_by.connected_channel);
}
}
Self::Dial {
peer,
caller,
forwarded,
..
} => {
push_channel(&mut ids, peer);
push_optional_channel(&mut ids, caller);
push_optional_channel(&mut ids, forwarded);
}
Self::BridgeAttendedTransfer {
transferer_first_leg,
transferer_second_leg,
transferee,
transfer_target,
replace_channel,
destination_link_first_leg,
destination_link_second_leg,
destination_threeway_channel,
..
} => {
push_channel(&mut ids, transferer_first_leg);
push_channel(&mut ids, transferer_second_leg);
for value in [
transferee,
transfer_target,
replace_channel,
destination_link_first_leg,
destination_link_second_leg,
destination_threeway_channel,
]
.into_iter()
.flatten()
{
push_channel(&mut ids, value);
}
}
Self::BridgeBlindTransfer {
channel: primary,
transferee,
replace_channel,
..
} => {
push_channel(&mut ids, primary);
push_optional_channel(&mut ids, transferee);
push_optional_channel(&mut ids, replace_channel);
}
Self::ContactStatusChange { endpoint, .. }
| Self::EndpointStateChange { endpoint }
| Self::PeerStatusChange { endpoint, .. } => {
ids.extend(endpoint.channel_ids.iter().map(String::as_str));
}
Self::BridgeCreated { .. }
| Self::BridgeDestroyed { .. }
| Self::PlaybackStarted { .. }
| Self::PlaybackFinished { .. }
| Self::RecordingStarted { .. }
| Self::RecordingFinished { .. }
| Self::BridgeMerged { .. }
| Self::BridgeVideoSourceChanged { .. }
| Self::DeviceStateChanged { .. }
| Self::PlaybackContinuing { .. }
| Self::RecordingFailed { .. }
| Self::ApplicationRegistered {}
| Self::ApplicationReplaced {}
| Self::ApplicationUnregistered {}
| Self::TextMessageReceived { .. }
| Self::RESTResponse { .. }
| Self::Unknown => {}
}
ids
}
pub fn bridge_ids(&self) -> Vec<&str> {
fn push_bridge<'a>(ids: &mut Vec<&'a str>, bridge: &'a Bridge) {
ids.push(bridge.id.as_str());
}
fn push_optional_bridge<'a>(ids: &mut Vec<&'a str>, bridge: &'a Option<Bridge>) {
if let Some(bridge) = bridge {
push_bridge(ids, bridge);
}
}
let mut ids = Vec::new();
match self {
Self::BridgeCreated { bridge: value }
| Self::BridgeDestroyed { bridge: value }
| Self::ChannelEnteredBridge { bridge: value, .. }
| Self::ChannelLeftBridge { bridge: value, .. }
| Self::BridgeVideoSourceChanged { bridge: value, .. } => {
push_bridge(&mut ids, value);
}
Self::ChannelUserevent { bridge: value, .. }
| Self::BridgeBlindTransfer { bridge: value, .. } => {
push_optional_bridge(&mut ids, value);
}
Self::ChannelTransfer {
refer_to,
referred_by,
..
} => {
if let Some(refer_to) = refer_to {
push_optional_bridge(&mut ids, &refer_to.bridge);
}
if let Some(referred_by) = referred_by {
push_optional_bridge(&mut ids, &referred_by.bridge);
}
}
Self::BridgeAttendedTransfer {
transferer_first_leg_bridge,
transferer_second_leg_bridge,
destination_bridge,
destination_threeway_bridge,
..
} => {
push_optional_bridge(&mut ids, transferer_first_leg_bridge);
push_optional_bridge(&mut ids, transferer_second_leg_bridge);
if let Some(value) = destination_bridge {
ids.push(value.as_str());
}
push_optional_bridge(&mut ids, destination_threeway_bridge);
}
Self::BridgeMerged {
bridge: value,
bridge_from,
} => {
push_bridge(&mut ids, value);
push_bridge(&mut ids, bridge_from);
}
Self::StasisStart { .. }
| Self::StasisEnd { .. }
| Self::ChannelCreated { .. }
| Self::ChannelDestroyed { .. }
| Self::ChannelStateChange { .. }
| Self::ChannelDtmfReceived { .. }
| Self::ChannelHangupRequest { .. }
| Self::ChannelVarset { .. }
| Self::PlaybackStarted { .. }
| Self::PlaybackFinished { .. }
| Self::RecordingStarted { .. }
| Self::RecordingFinished { .. }
| Self::ChannelCallerId { .. }
| Self::ChannelConnectedLine { .. }
| Self::ChannelDialplan { .. }
| Self::ChannelHold { .. }
| Self::ChannelUnhold { .. }
| Self::ChannelTalkingStarted { .. }
| Self::ChannelTalkingFinished { .. }
| Self::ChannelToneDetected { .. }
| Self::Dial { .. }
| Self::ContactStatusChange { .. }
| Self::DeviceStateChanged { .. }
| Self::EndpointStateChange { .. }
| Self::PeerStatusChange { .. }
| Self::PlaybackContinuing { .. }
| Self::RecordingFailed { .. }
| Self::ApplicationMoveFailed { .. }
| Self::ApplicationRegistered {}
| Self::ApplicationReplaced {}
| Self::ApplicationUnregistered {}
| Self::TextMessageReceived { .. }
| Self::RESTResponse { .. }
| Self::Unknown => {}
}
ids
}
pub fn playback_ids(&self) -> Vec<&str> {
match self {
Self::PlaybackStarted { playback }
| Self::PlaybackFinished { playback }
| Self::PlaybackContinuing { playback } => vec![playback.id.as_str()],
Self::StasisStart { .. }
| Self::StasisEnd { .. }
| Self::ChannelCreated { .. }
| Self::ChannelDestroyed { .. }
| Self::ChannelStateChange { .. }
| Self::ChannelDtmfReceived { .. }
| Self::ChannelHangupRequest { .. }
| Self::ChannelVarset { .. }
| Self::BridgeCreated { .. }
| Self::BridgeDestroyed { .. }
| Self::ChannelEnteredBridge { .. }
| Self::ChannelLeftBridge { .. }
| Self::RecordingStarted { .. }
| Self::RecordingFinished { .. }
| Self::ChannelCallerId { .. }
| Self::ChannelConnectedLine { .. }
| Self::ChannelDialplan { .. }
| Self::ChannelHold { .. }
| Self::ChannelUnhold { .. }
| Self::ChannelTalkingStarted { .. }
| Self::ChannelTalkingFinished { .. }
| Self::ChannelToneDetected { .. }
| Self::ChannelTransfer { .. }
| Self::ChannelUserevent { .. }
| Self::Dial { .. }
| Self::BridgeAttendedTransfer { .. }
| Self::BridgeBlindTransfer { .. }
| Self::BridgeMerged { .. }
| Self::BridgeVideoSourceChanged { .. }
| Self::ContactStatusChange { .. }
| Self::DeviceStateChanged { .. }
| Self::EndpointStateChange { .. }
| Self::PeerStatusChange { .. }
| Self::RecordingFailed { .. }
| Self::ApplicationMoveFailed { .. }
| Self::ApplicationRegistered {}
| Self::ApplicationReplaced {}
| Self::ApplicationUnregistered {}
| Self::TextMessageReceived { .. }
| Self::RESTResponse { .. }
| Self::Unknown => Vec::new(),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize)]
#[non_exhaustive]
pub struct UnknownAriEvent {
pub event_type: String,
pub payload: serde_json::Map<String, serde_json::Value>,
}
impl UnknownAriEvent {
pub fn new(
event_type: impl Into<String>,
payload: serde_json::Map<String, serde_json::Value>,
) -> Self {
Self {
event_type: event_type.into(),
payload,
}
}
}
#[derive(Debug, Clone)]
pub struct AriMessage {
pub application: String,
pub timestamp: String,
pub asterisk_id: Option<String>,
pub event: AriEvent,
pub unknown: Option<UnknownAriEvent>,
}
impl AriMessage {
pub fn event_type(&self) -> &'static str {
self.event.known_event_type().unwrap_or("Unknown")
}
}
impl Serialize for AriMessage {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut object = match (&self.event, &self.unknown) {
(AriEvent::Unknown, Some(unknown)) => {
if unknown.payload.contains_key("type") {
return Err(S::Error::custom(format_args!(
"unknown ARI event payload contains reserved field `type`"
)));
}
let metadata_matches =
|key: &str, expected: Option<&str>, null_matches: bool| match unknown
.payload
.get(key)
{
None => true,
Some(serde_json::Value::Null) => null_matches,
Some(serde_json::Value::String(value)) => expected == Some(value),
Some(_) => false,
};
for (key, expected, null_matches) in [
(
"application",
Some(self.application.as_str()),
self.application.is_empty(),
),
(
"timestamp",
Some(self.timestamp.as_str()),
self.timestamp.is_empty(),
),
(
"asterisk_id",
self.asterisk_id.as_deref(),
self.asterisk_id.is_none(),
),
] {
if !metadata_matches(key, expected, null_matches) {
return Err(S::Error::custom(format_args!(
"unknown ARI event payload contains contradictory reserved field `{key}`"
)));
}
}
let mut payload = unknown.payload.clone();
payload.insert(
"type".to_owned(),
serde_json::Value::String(unknown.event_type.clone()),
);
payload
}
(AriEvent::Unknown, None) => {
return Err(S::Error::custom(
"unknown ARI event is missing its retained type and payload",
));
}
(_, Some(_)) => {
return Err(S::Error::custom(
"known ARI event cannot carry an unknown event payload",
));
}
(_, None) => serde_json::to_value(&self.event)
.map_err(S::Error::custom)?
.as_object()
.cloned()
.ok_or_else(|| S::Error::custom("ARI event did not serialize as an object"))?,
};
let is_unknown = matches!(self.event, AriEvent::Unknown);
if !is_unknown || !self.application.is_empty() {
object.insert(
"application".to_owned(),
serde_json::Value::String(self.application.clone()),
);
}
if !is_unknown || !self.timestamp.is_empty() {
object.insert(
"timestamp".to_owned(),
serde_json::Value::String(self.timestamp.clone()),
);
}
match &self.asterisk_id {
Some(asterisk_id) => {
object.insert(
"asterisk_id".to_owned(),
serde_json::Value::String(asterisk_id.clone()),
);
}
None if !is_unknown => {
object.insert("asterisk_id".to_owned(), serde_json::Value::Null);
}
None => {}
}
object.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for AriMessage {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = serde_json::Value::deserialize(deserializer)?;
let object = value
.as_object()
.ok_or_else(|| D::Error::custom("ARI event must be a JSON object"))?;
let event_type = object
.get("type")
.and_then(serde_json::Value::as_str)
.ok_or_else(|| D::Error::missing_field("type"))?
.to_owned();
let event: AriEvent = serde_json::from_value(value.clone()).map_err(D::Error::custom)?;
let is_unknown = matches!(event, AriEvent::Unknown);
let application = string_field(object, "application", is_unknown)?;
let timestamp = string_field(object, "timestamp", is_unknown)?;
let asterisk_id = optional_string_field(object, "asterisk_id")?;
let unknown = if is_unknown {
let mut payload = object.clone();
payload.remove("type");
Some(UnknownAriEvent::new(event_type, payload))
} else {
None
};
Ok(Self {
application,
timestamp,
asterisk_id,
event,
unknown,
})
}
}
fn string_field<E: serde::de::Error>(
object: &serde_json::Map<String, serde_json::Value>,
field: &'static str,
allow_null: bool,
) -> Result<String, E> {
match object.get(field) {
None => Ok(String::new()),
Some(serde_json::Value::Null) if allow_null => Ok(String::new()),
Some(serde_json::Value::String(value)) => Ok(value.clone()),
Some(_) => Err(E::custom(format_args!(
"ARI field `{field}` must be a string"
))),
}
}
fn optional_string_field<E: serde::de::Error>(
object: &serde_json::Map<String, serde_json::Value>,
field: &'static str,
) -> Result<Option<String>, E> {
match object.get(field) {
None | Some(serde_json::Value::Null) => Ok(None),
Some(serde_json::Value::String(value)) => Ok(Some(value.clone())),
Some(_) => Err(E::custom(format_args!(
"ARI field `{field}` must be a string"
))),
}
}
impl asterisk_rs_core::event::Event for AriMessage {}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ContactInfo {
pub uri: String,
pub contact_status: String,
pub aor: String,
#[serde(default)]
pub roundtrip_usec: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Peer {
pub peer_status: String,
#[serde(default)]
pub address: Option<String>,
#[serde(default)]
pub port: Option<String>,
#[serde(default)]
pub cause: Option<String>,
#[serde(default)]
pub time: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Endpoint {
pub technology: String,
pub resource: String,
#[serde(default)]
pub state: Option<String>,
#[serde(default)]
pub channel_ids: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct DeviceState {
pub name: String,
pub state: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TextMessage {
pub from: String,
pub to: String,
pub body: String,
#[serde(default)]
pub variables: serde_json::Map<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ReferTo {
#[serde(default)]
pub requested_destination: serde_json::Value,
#[serde(default)]
pub destination_channel: Option<Channel>,
#[serde(default)]
pub connected_channel: Option<Channel>,
#[serde(default)]
pub bridge: Option<Bridge>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ReferredBy {
pub source_channel: Channel,
#[serde(default)]
pub connected_channel: Option<Channel>,
#[serde(default)]
pub bridge: Option<Bridge>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Channel {
pub id: String,
#[serde(default)]
pub protocol_id: String,
pub name: String,
pub state: String,
#[serde(default)]
pub caller: CallerId,
#[serde(default)]
pub connected: CallerId,
#[serde(default)]
pub accountcode: String,
#[serde(default)]
pub dialplan: DialplanCep,
#[serde(default)]
pub creationtime: String,
#[serde(default)]
pub language: String,
#[serde(default)]
pub channelvars: serde_json::Map<String, serde_json::Value>,
#[serde(default)]
pub caller_rdnis: String,
#[serde(default)]
pub tenantid: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CallerId {
#[serde(default)]
pub name: String,
#[serde(default)]
pub number: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[non_exhaustive]
pub struct DialplanCep {
#[serde(default)]
pub context: String,
#[serde(default)]
pub exten: String,
#[serde(default)]
pub priority: i64,
#[serde(default)]
pub app_name: String,
#[serde(default)]
pub app_data: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Bridge {
pub id: String,
pub technology: String,
pub bridge_type: String,
#[serde(default)]
pub bridge_class: String,
#[serde(default)]
pub creator: String,
#[serde(default)]
pub name: String,
#[serde(default)]
pub channels: Vec<String>,
#[serde(default)]
pub video_mode: Option<String>,
#[serde(default)]
pub video_source_id: Option<String>,
#[serde(default)]
pub creationtime: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Playback {
pub id: String,
pub media_uri: String,
#[serde(default)]
pub next_media_uri: Option<String>,
pub state: String,
pub target_uri: String,
#[serde(default)]
pub language: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LiveRecording {
pub name: String,
pub format: String,
pub state: String,
pub target_uri: String,
#[serde(default)]
pub duration: Option<i32>,
#[serde(default)]
pub talking_duration: Option<i32>,
#[serde(default)]
pub silence_duration: Option<i32>,
#[serde(default)]
pub cause: Option<String>,
}