use livekit_protocol as proto;
use std::collections::HashMap;
use std::time::Duration;
use crate::access_token::SIPGrants;
use crate::get_env_keys;
use crate::services::twirp_client::TwirpClient;
use crate::services::{ServiceBase, ServiceResult, LIVEKIT_PACKAGE};
use pbjson_types::Duration as ProtoDuration;
const SVC: &str = "SIP";
#[derive(Debug)]
pub struct SIPClient {
base: ServiceBase,
client: TwirpClient,
}
#[deprecated]
#[derive(Default, Clone, Debug)]
pub struct CreateSIPTrunkOptions {
pub name: String,
pub metadata: String,
pub inbound_addresses: Vec<String>,
pub inbound_numbers: Vec<String>,
pub inbound_username: String,
pub inbound_password: String,
pub outbound_address: String,
pub outbound_username: String,
pub outbound_password: String,
}
#[derive(Default, Clone, Debug)]
pub struct CreateSIPInboundTrunkOptions {
pub metadata: Option<String>,
pub allowed_addresses: Option<Vec<String>>,
pub allowed_numbers: Option<Vec<String>>,
pub auth_username: Option<String>,
pub auth_password: Option<String>,
pub headers: Option<HashMap<String, String>>,
pub headers_to_attributes: Option<HashMap<String, String>>,
pub attributes_to_headers: Option<HashMap<String, String>>,
pub max_call_duration: Option<Duration>,
pub ringing_timeout: Option<Duration>,
pub krisp_enabled: Option<bool>,
}
#[derive(Default, Clone, Debug)]
pub struct CreateSIPOutboundTrunkOptions {
pub transport: proto::SipTransport,
pub metadata: String,
pub auth_username: String,
pub auth_password: String,
pub headers: Option<HashMap<String, String>>,
pub headers_to_attributes: Option<HashMap<String, String>>,
pub attributes_to_headers: Option<HashMap<String, String>>,
}
#[deprecated]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ListSIPTrunkFilter {
All,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ListSIPInboundTrunkFilter {
All,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ListSIPOutboundTrunkFilter {
All,
}
#[derive(Default, Clone, Debug)]
pub struct CreateSIPDispatchRuleOptions {
pub name: String,
pub metadata: String,
pub attributes: HashMap<String, String>,
pub trunk_ids: Vec<String>,
pub allowed_numbers: Vec<String>,
pub hide_phone_number: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ListSIPDispatchRuleFilter {
All,
}
#[derive(Default, Clone, Debug)]
pub struct CreateSIPParticipantOptions {
pub participant_identity: String,
pub participant_name: Option<String>,
pub participant_metadata: Option<String>,
pub participant_attributes: Option<HashMap<String, String>>,
pub sip_number: Option<String>,
pub dtmf: Option<String>,
pub wait_until_answered: Option<bool>,
pub play_dialtone: Option<bool>,
pub hide_phone_number: Option<bool>,
pub ringing_timeout: Option<Duration>,
pub max_call_duration: Option<Duration>,
pub enable_krisp: Option<bool>,
}
impl SIPClient {
pub fn with_api_key(host: &str, api_key: &str, api_secret: &str) -> Self {
Self {
base: ServiceBase::with_api_key(api_key, api_secret),
client: TwirpClient::new(host, LIVEKIT_PACKAGE, None),
}
}
pub fn new(host: &str) -> ServiceResult<Self> {
let (api_key, api_secret) = get_env_keys()?;
Ok(Self::with_api_key(host, &api_key, &api_secret))
}
fn duration_to_proto(d: Option<Duration>) -> Option<ProtoDuration> {
d.map(|d| ProtoDuration { seconds: d.as_secs() as i64, nanos: d.subsec_nanos() as i32 })
}
pub async fn create_sip_inbound_trunk(
&self,
name: String,
numbers: Vec<String>,
options: CreateSIPInboundTrunkOptions,
) -> ServiceResult<proto::SipInboundTrunkInfo> {
self.client
.request(
SVC,
"CreateSIPInboundTrunk",
proto::CreateSipInboundTrunkRequest {
trunk: Some(proto::SipInboundTrunkInfo {
sip_trunk_id: Default::default(),
name,
numbers,
metadata: options.metadata.unwrap_or_default(),
allowed_numbers: options.allowed_numbers.unwrap_or_default(),
allowed_addresses: options.allowed_addresses.unwrap_or_default(),
auth_username: options.auth_username.unwrap_or_default(),
auth_password: options.auth_password.unwrap_or_default(),
headers: options.headers.unwrap_or_default(),
headers_to_attributes: options.headers_to_attributes.unwrap_or_default(),
attributes_to_headers: options.attributes_to_headers.unwrap_or_default(),
krisp_enabled: options.krisp_enabled.unwrap_or(false),
max_call_duration: Self::duration_to_proto(options.max_call_duration),
ringing_timeout: Self::duration_to_proto(options.ringing_timeout),
include_headers: Default::default(),
media_encryption: Default::default(),
created_at: Default::default(),
updated_at: Default::default(),
}),
},
self.base.auth_header(
Default::default(),
Some(SIPGrants { admin: true, ..Default::default() }),
)?,
)
.await
.map_err(Into::into)
}
pub async fn create_sip_outbound_trunk(
&self,
name: String,
address: String,
numbers: Vec<String>,
options: CreateSIPOutboundTrunkOptions,
) -> ServiceResult<proto::SipOutboundTrunkInfo> {
self.client
.request(
SVC,
"CreateSIPOutboundTrunk",
proto::CreateSipOutboundTrunkRequest {
trunk: Some(proto::SipOutboundTrunkInfo {
sip_trunk_id: Default::default(),
name,
address,
numbers,
transport: options.transport as i32,
metadata: options.metadata,
auth_username: options.auth_username.to_owned(),
auth_password: options.auth_password.to_owned(),
headers: options.headers.unwrap_or_default(),
headers_to_attributes: options.headers_to_attributes.unwrap_or_default(),
attributes_to_headers: options.attributes_to_headers.unwrap_or_default(),
include_headers: Default::default(),
media_encryption: Default::default(),
destination_country: Default::default(),
created_at: Default::default(),
updated_at: Default::default(),
from_host: Default::default(),
}),
},
self.base.auth_header(
Default::default(),
Some(SIPGrants { admin: true, ..Default::default() }),
)?,
)
.await
.map_err(Into::into)
}
#[deprecated]
pub async fn list_sip_trunk(
&self,
filter: ListSIPTrunkFilter,
) -> ServiceResult<Vec<proto::SipTrunkInfo>> {
let resp: proto::ListSipTrunkResponse = self
.client
.request(
SVC,
"ListSIPTrunk",
proto::ListSipTrunkRequest {
page: Default::default(),
},
self.base.auth_header(
Default::default(),
Some(SIPGrants { admin: true, ..Default::default() }),
)?,
)
.await?;
Ok(resp.items)
}
pub async fn list_sip_inbound_trunk(
&self,
filter: ListSIPInboundTrunkFilter,
) -> ServiceResult<Vec<proto::SipInboundTrunkInfo>> {
let resp: proto::ListSipInboundTrunkResponse = self
.client
.request(
SVC,
"ListSIPInboundTrunk",
proto::ListSipInboundTrunkRequest {
page: Default::default(),
trunk_ids: Default::default(),
numbers: Default::default(),
},
self.base.auth_header(
Default::default(),
Some(SIPGrants { admin: true, ..Default::default() }),
)?,
)
.await?;
Ok(resp.items)
}
pub async fn list_sip_outbound_trunk(
&self,
filter: ListSIPOutboundTrunkFilter,
) -> ServiceResult<Vec<proto::SipOutboundTrunkInfo>> {
let resp: proto::ListSipOutboundTrunkResponse = self
.client
.request(
SVC,
"ListSIPOutboundTrunk",
proto::ListSipOutboundTrunkRequest {
page: Default::default(),
trunk_ids: Default::default(),
numbers: Default::default(),
},
self.base.auth_header(
Default::default(),
Some(SIPGrants { admin: true, ..Default::default() }),
)?,
)
.await?;
Ok(resp.items)
}
pub async fn delete_sip_trunk(&self, sip_trunk_id: &str) -> ServiceResult<proto::SipTrunkInfo> {
self.client
.request(
SVC,
"DeleteSIPTrunk",
proto::DeleteSipTrunkRequest { sip_trunk_id: sip_trunk_id.to_owned() },
self.base.auth_header(
Default::default(),
Some(SIPGrants { admin: true, ..Default::default() }),
)?,
)
.await
.map_err(Into::into)
}
pub async fn create_sip_dispatch_rule(
&self,
rule: proto::sip_dispatch_rule::Rule,
options: CreateSIPDispatchRuleOptions,
) -> ServiceResult<proto::SipDispatchRuleInfo> {
self.client
.request(
SVC,
"CreateSIPDispatchRule",
proto::CreateSipDispatchRuleRequest {
name: options.name,
metadata: options.metadata,
attributes: options.attributes,
trunk_ids: options.trunk_ids.to_owned(),
inbound_numbers: options.allowed_numbers.to_owned(),
hide_phone_number: options.hide_phone_number,
rule: Some(proto::SipDispatchRule { rule: Some(rule.to_owned()) }),
..Default::default()
},
self.base.auth_header(
Default::default(),
Some(SIPGrants { admin: true, ..Default::default() }),
)?,
)
.await
.map_err(Into::into)
}
pub async fn list_sip_dispatch_rule(
&self,
filter: ListSIPDispatchRuleFilter,
) -> ServiceResult<Vec<proto::SipDispatchRuleInfo>> {
let resp: proto::ListSipDispatchRuleResponse = self
.client
.request(
SVC,
"ListSIPDispatchRule",
proto::ListSipDispatchRuleRequest {
page: Default::default(),
dispatch_rule_ids: Default::default(),
trunk_ids: Default::default(),
},
self.base.auth_header(
Default::default(),
Some(SIPGrants { admin: true, ..Default::default() }),
)?,
)
.await?;
Ok(resp.items)
}
pub async fn delete_sip_dispatch_rule(
&self,
sip_dispatch_rule_id: &str,
) -> ServiceResult<proto::SipDispatchRuleInfo> {
self.client
.request(
SVC,
"DeleteSIPDispatchRule",
proto::DeleteSipDispatchRuleRequest {
sip_dispatch_rule_id: sip_dispatch_rule_id.to_owned(),
},
self.base.auth_header(
Default::default(),
Some(SIPGrants { admin: true, ..Default::default() }),
)?,
)
.await
.map_err(Into::into)
}
pub async fn create_sip_participant(
&self,
sip_trunk_id: String,
call_to: String,
room_name: String,
options: CreateSIPParticipantOptions,
outbound_trunk_config: Option<proto::SipOutboundConfig>,
) -> ServiceResult<proto::SipParticipantInfo> {
self.client
.request(
SVC,
"CreateSIPParticipant",
proto::CreateSipParticipantRequest {
sip_trunk_id: sip_trunk_id.to_owned(),
trunk: outbound_trunk_config,
sip_call_to: call_to.to_owned(),
sip_number: options.sip_number.to_owned().unwrap_or_default(),
room_name: room_name.to_owned(),
participant_identity: options.participant_identity.to_owned(),
participant_name: options.participant_name.to_owned().unwrap_or_default(),
participant_metadata: options
.participant_metadata
.to_owned()
.unwrap_or_default(),
participant_attributes: options
.participant_attributes
.to_owned()
.unwrap_or_default(),
dtmf: options.dtmf.to_owned().unwrap_or_default(),
wait_until_answered: options.wait_until_answered.unwrap_or(false),
play_ringtone: options.play_dialtone.unwrap_or(false),
play_dialtone: options.play_dialtone.unwrap_or(false),
hide_phone_number: options.hide_phone_number.unwrap_or(false),
max_call_duration: Self::duration_to_proto(options.max_call_duration),
ringing_timeout: Self::duration_to_proto(options.ringing_timeout),
krisp_enabled: options.enable_krisp.unwrap_or(false),
headers: Default::default(),
include_headers: Default::default(),
media_encryption: Default::default(),
..Default::default()
},
self.base.auth_header(
Default::default(),
Some(SIPGrants { call: true, ..Default::default() }),
)?,
)
.await
.map_err(Into::into)
}
}