1use livekit_protocol as proto;
16use std::collections::HashMap;
17use std::time::Duration;
18
19use crate::access_token::{SIPGrants, VideoGrants};
20use crate::get_env_keys;
21use crate::services::dial_timeout::{dial_timeout, DEFAULT_RINGING_TIMEOUT};
22use crate::services::twirp_client::TwirpClient;
23use crate::services::{ServiceBase, ServiceResult, LIVEKIT_PACKAGE};
24use pbjson_types::Duration as ProtoDuration;
25
26const SVC: &str = "SIP";
27
28#[derive(Debug)]
29pub struct SIPClient {
30 base: ServiceBase,
31 client: TwirpClient,
32}
33
34#[deprecated]
35#[derive(Default, Clone, Debug)]
36pub struct CreateSIPTrunkOptions {
37 pub name: String,
39 pub metadata: String,
41 pub inbound_addresses: Vec<String>,
44 pub inbound_numbers: Vec<String>,
48 pub inbound_username: String,
51 pub inbound_password: String,
52
53 pub outbound_address: String,
55 pub outbound_username: String,
58 pub outbound_password: String,
59}
60
61#[derive(Default, Clone, Debug)]
62pub struct CreateSIPInboundTrunkOptions {
63 pub metadata: Option<String>,
65 pub allowed_addresses: Option<Vec<String>>,
68 pub allowed_numbers: Option<Vec<String>>,
72 pub auth_username: Option<String>,
75 pub auth_password: Option<String>,
76 pub headers: Option<HashMap<String, String>>,
77 pub headers_to_attributes: Option<HashMap<String, String>>,
78 pub attributes_to_headers: Option<HashMap<String, String>>,
79 pub max_call_duration: Option<Duration>,
80 pub ringing_timeout: Option<Duration>,
81 pub krisp_enabled: Option<bool>,
82 pub auth_realm: Option<String>,
84}
85
86#[derive(Default, Clone, Debug)]
87pub struct CreateSIPOutboundTrunkOptions {
88 pub transport: proto::SipTransport,
89 pub metadata: String,
91 pub auth_username: String,
94 pub auth_password: String,
95
96 pub headers: Option<HashMap<String, String>>,
97 pub headers_to_attributes: Option<HashMap<String, String>>,
98 pub attributes_to_headers: Option<HashMap<String, String>>,
99}
100
101#[deprecated]
102#[derive(Debug, Clone, PartialEq, Eq)]
103pub enum ListSIPTrunkFilter {
104 All,
105}
106#[derive(Debug, Clone, PartialEq, Eq)]
107pub enum ListSIPInboundTrunkFilter {
108 All,
109}
110#[derive(Debug, Clone, PartialEq, Eq)]
111pub enum ListSIPOutboundTrunkFilter {
112 All,
113}
114
115#[derive(Default, Clone, Debug)]
116pub struct CreateSIPDispatchRuleOptions {
117 pub name: String,
118 pub metadata: String,
119 pub attributes: HashMap<String, String>,
120 pub trunk_ids: Vec<String>,
123 pub allowed_numbers: Vec<String>,
124 pub hide_phone_number: bool,
125 pub room_config: Option<proto::RoomConfiguration>,
128}
129
130#[derive(Debug, Clone, PartialEq, Eq)]
131pub enum ListSIPDispatchRuleFilter {
132 All,
133}
134
135#[derive(Default, Clone, Debug)]
136pub struct CreateSIPParticipantOptions {
137 pub participant_identity: String,
139 pub participant_name: Option<String>,
141 pub participant_metadata: Option<String>,
143 pub participant_attributes: Option<HashMap<String, String>>,
144 pub display_name: Option<String>,
148 pub sip_number: Option<String>,
150 pub dtmf: Option<String>,
153 pub wait_until_answered: Option<bool>,
159 pub play_dialtone: Option<bool>,
161 pub hide_phone_number: Option<bool>,
162 pub ringing_timeout: Option<Duration>,
163 pub max_call_duration: Option<Duration>,
164 pub enable_krisp: Option<bool>,
165 pub headers: Option<HashMap<String, String>>,
168 pub include_headers: Option<proto::SipHeaderOptions>,
170 pub media_encryption: Option<proto::SipMediaEncryption>,
172 pub timeout: Option<Duration>,
176}
177
178#[derive(Default, Clone, Debug)]
179pub struct TransferSIPParticipantOptions {
180 pub play_dialtone: Option<bool>,
183 pub ringing_timeout: Option<Duration>,
185 pub headers: Option<HashMap<String, String>>,
187 pub timeout: Option<Duration>,
191}
192
193impl SIPClient {
194 pub fn with_api_key(host: &str, api_key: &str, api_secret: &str) -> Self {
196 Self::build(
197 host,
198 ServiceBase::with_api_key(api_key, api_secret),
199 crate::http_client::Client::new(),
200 )
201 }
202
203 pub fn with_token(host: &str, token: &str) -> Self {
205 Self::build(host, ServiceBase::with_token(token), crate::http_client::Client::new())
206 }
207
208 pub(crate) fn build(host: &str, base: ServiceBase, client: crate::http_client::Client) -> Self {
211 Self { base, client: TwirpClient::with_client(host, LIVEKIT_PACKAGE, None, client) }
212 }
213
214 #[cfg(test)]
215 pub(crate) fn with_default_headers(mut self, headers: http::HeaderMap) -> Self {
216 self.client = self.client.with_default_headers(headers);
217 self
218 }
219
220 pub fn new(host: &str) -> ServiceResult<Self> {
223 let (api_key, api_secret) = get_env_keys()?;
224 Ok(Self::with_api_key(host, &api_key, &api_secret))
225 }
226
227 pub fn with_failover(mut self, enabled: bool) -> Self {
230 self.client = self.client.with_failover(enabled);
231 self
232 }
233
234 pub fn with_request_timeout(mut self, timeout: Duration) -> Self {
238 self.client = self.client.with_request_timeout(timeout);
239 self
240 }
241
242 fn duration_to_proto(d: Option<Duration>) -> Option<ProtoDuration> {
243 d.map(|d| ProtoDuration { seconds: d.as_secs() as i64, nanos: d.subsec_nanos() as i32 })
244 }
245
246 pub async fn create_sip_inbound_trunk(
247 &self,
248 name: String,
249 numbers: Vec<String>,
250 options: CreateSIPInboundTrunkOptions,
251 ) -> ServiceResult<proto::SipInboundTrunkInfo> {
252 self.client
253 .request(
254 SVC,
255 "CreateSIPInboundTrunk",
256 proto::CreateSipInboundTrunkRequest {
257 trunk: Some(proto::SipInboundTrunkInfo {
258 sip_trunk_id: Default::default(),
259 name,
260 numbers,
261 metadata: options.metadata.unwrap_or_default(),
262 allowed_numbers: options.allowed_numbers.unwrap_or_default(),
263 allowed_addresses: options.allowed_addresses.unwrap_or_default(),
264 auth_username: options.auth_username.unwrap_or_default(),
265 auth_password: options.auth_password.unwrap_or_default(),
266 auth_realm: options.auth_realm.unwrap_or_default(),
267 headers: options.headers.unwrap_or_default(),
268 headers_to_attributes: options.headers_to_attributes.unwrap_or_default(),
269 attributes_to_headers: options.attributes_to_headers.unwrap_or_default(),
270 krisp_enabled: options.krisp_enabled.unwrap_or(false),
271 max_call_duration: Self::duration_to_proto(options.max_call_duration),
272 ringing_timeout: Self::duration_to_proto(options.ringing_timeout),
273
274 include_headers: Default::default(),
276 media_encryption: Default::default(),
277 created_at: Default::default(),
278 updated_at: Default::default(),
279 media: Default::default(),
280 }),
281 },
282 self.base.auth_header(
283 Default::default(),
284 Some(SIPGrants { admin: true, ..Default::default() }),
285 )?,
286 )
287 .await
288 .map_err(Into::into)
289 }
290
291 pub async fn create_sip_outbound_trunk(
292 &self,
293 name: String,
294 address: String,
295 numbers: Vec<String>,
296 options: CreateSIPOutboundTrunkOptions,
297 ) -> ServiceResult<proto::SipOutboundTrunkInfo> {
298 self.client
299 .request(
300 SVC,
301 "CreateSIPOutboundTrunk",
302 proto::CreateSipOutboundTrunkRequest {
303 trunk: Some(proto::SipOutboundTrunkInfo {
304 sip_trunk_id: Default::default(),
305 name,
306 address,
307 numbers,
308 transport: options.transport as i32,
309 metadata: options.metadata,
310
311 auth_username: options.auth_username.to_owned(),
312 auth_password: options.auth_password.to_owned(),
313
314 headers: options.headers.unwrap_or_default(),
315 headers_to_attributes: options.headers_to_attributes.unwrap_or_default(),
316 attributes_to_headers: options.attributes_to_headers.unwrap_or_default(),
317
318 include_headers: Default::default(),
320 media_encryption: Default::default(),
321 destination_country: Default::default(),
322 created_at: Default::default(),
323 updated_at: Default::default(),
324 from_host: Default::default(),
325 media: Default::default(),
326 }),
327 },
328 self.base.auth_header(
329 Default::default(),
330 Some(SIPGrants { admin: true, ..Default::default() }),
331 )?,
332 )
333 .await
334 .map_err(Into::into)
335 }
336
337 pub async fn update_sip_inbound_trunk(
342 &self,
343 trunk_id: String,
344 update: proto::SipInboundTrunkUpdate,
345 ) -> ServiceResult<proto::SipInboundTrunkInfo> {
346 self.client
347 .request(
348 SVC,
349 "UpdateSIPInboundTrunk",
350 proto::UpdateSipInboundTrunkRequest {
351 sip_trunk_id: trunk_id,
352 action: Some(proto::update_sip_inbound_trunk_request::Action::Update(update)),
353 },
354 self.base.auth_header(
355 Default::default(),
356 Some(SIPGrants { admin: true, ..Default::default() }),
357 )?,
358 )
359 .await
360 .map_err(Into::into)
361 }
362
363 pub async fn update_sip_inbound_trunk_replace(
367 &self,
368 trunk_id: String,
369 trunk: proto::SipInboundTrunkInfo,
370 ) -> ServiceResult<proto::SipInboundTrunkInfo> {
371 self.client
372 .request(
373 SVC,
374 "UpdateSIPInboundTrunk",
375 proto::UpdateSipInboundTrunkRequest {
376 sip_trunk_id: trunk_id,
377 action: Some(proto::update_sip_inbound_trunk_request::Action::Replace(trunk)),
378 },
379 self.base.auth_header(
380 Default::default(),
381 Some(SIPGrants { admin: true, ..Default::default() }),
382 )?,
383 )
384 .await
385 .map_err(Into::into)
386 }
387
388 pub async fn update_sip_outbound_trunk(
391 &self,
392 trunk_id: String,
393 update: proto::SipOutboundTrunkUpdate,
394 ) -> ServiceResult<proto::SipOutboundTrunkInfo> {
395 self.client
396 .request(
397 SVC,
398 "UpdateSIPOutboundTrunk",
399 proto::UpdateSipOutboundTrunkRequest {
400 sip_trunk_id: trunk_id,
401 action: Some(proto::update_sip_outbound_trunk_request::Action::Update(update)),
402 },
403 self.base.auth_header(
404 Default::default(),
405 Some(SIPGrants { admin: true, ..Default::default() }),
406 )?,
407 )
408 .await
409 .map_err(Into::into)
410 }
411
412 pub async fn update_sip_outbound_trunk_replace(
415 &self,
416 trunk_id: String,
417 trunk: proto::SipOutboundTrunkInfo,
418 ) -> ServiceResult<proto::SipOutboundTrunkInfo> {
419 self.client
420 .request(
421 SVC,
422 "UpdateSIPOutboundTrunk",
423 proto::UpdateSipOutboundTrunkRequest {
424 sip_trunk_id: trunk_id,
425 action: Some(proto::update_sip_outbound_trunk_request::Action::Replace(trunk)),
426 },
427 self.base.auth_header(
428 Default::default(),
429 Some(SIPGrants { admin: true, ..Default::default() }),
430 )?,
431 )
432 .await
433 .map_err(Into::into)
434 }
435
436 #[deprecated]
437 pub async fn list_sip_trunk(
438 &self,
439 filter: ListSIPTrunkFilter,
440 ) -> ServiceResult<Vec<proto::SipTrunkInfo>> {
441 let resp: proto::ListSipTrunkResponse = self
442 .client
443 .request(
444 SVC,
445 "ListSIPTrunk",
446 proto::ListSipTrunkRequest {
447 page: Default::default(),
449 },
450 self.base.auth_header(
451 Default::default(),
452 Some(SIPGrants { admin: true, ..Default::default() }),
453 )?,
454 )
455 .await?;
456
457 Ok(resp.items)
458 }
459
460 pub async fn list_sip_inbound_trunk(
461 &self,
462 filter: ListSIPInboundTrunkFilter,
463 ) -> ServiceResult<Vec<proto::SipInboundTrunkInfo>> {
464 let resp: proto::ListSipInboundTrunkResponse = self
465 .client
466 .request(
467 SVC,
468 "ListSIPInboundTrunk",
469 proto::ListSipInboundTrunkRequest {
470 page: Default::default(),
472 trunk_ids: Default::default(),
473 numbers: Default::default(),
474 },
475 self.base.auth_header(
476 Default::default(),
477 Some(SIPGrants { admin: true, ..Default::default() }),
478 )?,
479 )
480 .await?;
481
482 Ok(resp.items)
483 }
484
485 pub async fn list_sip_outbound_trunk(
486 &self,
487 filter: ListSIPOutboundTrunkFilter,
488 ) -> ServiceResult<Vec<proto::SipOutboundTrunkInfo>> {
489 let resp: proto::ListSipOutboundTrunkResponse = self
490 .client
491 .request(
492 SVC,
493 "ListSIPOutboundTrunk",
494 proto::ListSipOutboundTrunkRequest {
495 page: Default::default(),
497 trunk_ids: Default::default(),
498 numbers: Default::default(),
499 },
500 self.base.auth_header(
501 Default::default(),
502 Some(SIPGrants { admin: true, ..Default::default() }),
503 )?,
504 )
505 .await?;
506
507 Ok(resp.items)
508 }
509
510 pub async fn delete_sip_trunk(&self, sip_trunk_id: &str) -> ServiceResult<proto::SipTrunkInfo> {
511 self.client
512 .request(
513 SVC,
514 "DeleteSIPTrunk",
515 proto::DeleteSipTrunkRequest { sip_trunk_id: sip_trunk_id.to_owned() },
516 self.base.auth_header(
517 Default::default(),
518 Some(SIPGrants { admin: true, ..Default::default() }),
519 )?,
520 )
521 .await
522 .map_err(Into::into)
523 }
524
525 pub async fn create_sip_dispatch_rule(
526 &self,
527 rule: proto::sip_dispatch_rule::Rule,
528 options: CreateSIPDispatchRuleOptions,
529 ) -> ServiceResult<proto::SipDispatchRuleInfo> {
530 self.client
531 .request(
532 SVC,
533 "CreateSIPDispatchRule",
534 proto::CreateSipDispatchRuleRequest {
535 dispatch_rule: Some(proto::SipDispatchRuleInfo {
536 rule: Some(proto::SipDispatchRule { rule: Some(rule) }),
537 name: options.name,
538 metadata: options.metadata,
539 attributes: options.attributes,
540 trunk_ids: options.trunk_ids,
541 inbound_numbers: options.allowed_numbers,
542 hide_phone_number: options.hide_phone_number,
543 room_config: options.room_config,
544 ..Default::default()
545 }),
546 ..Default::default()
547 },
548 self.base.auth_header(
549 Default::default(),
550 Some(SIPGrants { admin: true, ..Default::default() }),
551 )?,
552 )
553 .await
554 .map_err(Into::into)
555 }
556
557 pub async fn update_sip_dispatch_rule(
560 &self,
561 dispatch_rule_id: String,
562 update: proto::SipDispatchRuleUpdate,
563 ) -> ServiceResult<proto::SipDispatchRuleInfo> {
564 self.client
565 .request(
566 SVC,
567 "UpdateSIPDispatchRule",
568 proto::UpdateSipDispatchRuleRequest {
569 sip_dispatch_rule_id: dispatch_rule_id,
570 action: Some(proto::update_sip_dispatch_rule_request::Action::Update(update)),
571 },
572 self.base.auth_header(
573 Default::default(),
574 Some(SIPGrants { admin: true, ..Default::default() }),
575 )?,
576 )
577 .await
578 .map_err(Into::into)
579 }
580
581 pub async fn update_sip_dispatch_rule_replace(
584 &self,
585 dispatch_rule_id: String,
586 rule: proto::SipDispatchRuleInfo,
587 ) -> ServiceResult<proto::SipDispatchRuleInfo> {
588 self.client
589 .request(
590 SVC,
591 "UpdateSIPDispatchRule",
592 proto::UpdateSipDispatchRuleRequest {
593 sip_dispatch_rule_id: dispatch_rule_id,
594 action: Some(proto::update_sip_dispatch_rule_request::Action::Replace(rule)),
595 },
596 self.base.auth_header(
597 Default::default(),
598 Some(SIPGrants { admin: true, ..Default::default() }),
599 )?,
600 )
601 .await
602 .map_err(Into::into)
603 }
604
605 pub async fn list_sip_dispatch_rule(
606 &self,
607 filter: ListSIPDispatchRuleFilter,
608 ) -> ServiceResult<Vec<proto::SipDispatchRuleInfo>> {
609 let resp: proto::ListSipDispatchRuleResponse = self
610 .client
611 .request(
612 SVC,
613 "ListSIPDispatchRule",
614 proto::ListSipDispatchRuleRequest {
615 page: Default::default(),
617 dispatch_rule_ids: Default::default(),
618 trunk_ids: Default::default(),
619 },
620 self.base.auth_header(
621 Default::default(),
622 Some(SIPGrants { admin: true, ..Default::default() }),
623 )?,
624 )
625 .await?;
626
627 Ok(resp.items)
628 }
629
630 pub async fn delete_sip_dispatch_rule(
631 &self,
632 sip_dispatch_rule_id: &str,
633 ) -> ServiceResult<proto::SipDispatchRuleInfo> {
634 self.client
635 .request(
636 SVC,
637 "DeleteSIPDispatchRule",
638 proto::DeleteSipDispatchRuleRequest {
639 sip_dispatch_rule_id: sip_dispatch_rule_id.to_owned(),
640 },
641 self.base.auth_header(
642 Default::default(),
643 Some(SIPGrants { admin: true, ..Default::default() }),
644 )?,
645 )
646 .await
647 .map_err(Into::into)
648 }
649
650 pub async fn create_sip_participant(
651 &self,
652 sip_trunk_id: String,
653 call_to: String,
654 room_name: String,
655 options: CreateSIPParticipantOptions,
656 outbound_trunk_config: Option<proto::SipOutboundConfig>,
657 ) -> ServiceResult<proto::SipParticipantInfo> {
658 let wait_until_answered = options.wait_until_answered.unwrap_or(false);
659 let user_timeout = options.timeout;
660 let ringing_timeout =
663 options.ringing_timeout.or(wait_until_answered.then_some(DEFAULT_RINGING_TIMEOUT));
664 let request = proto::CreateSipParticipantRequest {
665 sip_trunk_id: sip_trunk_id.to_owned(),
666 trunk: outbound_trunk_config,
667 sip_call_to: call_to.to_owned(),
668 sip_number: options.sip_number.to_owned().unwrap_or_default(),
669 room_name: room_name.to_owned(),
670 participant_identity: options.participant_identity.to_owned(),
671 participant_name: options.participant_name.to_owned().unwrap_or_default(),
672 participant_metadata: options.participant_metadata.to_owned().unwrap_or_default(),
673 participant_attributes: options.participant_attributes.to_owned().unwrap_or_default(),
674 display_name: options.display_name.to_owned(),
675 dtmf: options.dtmf.to_owned().unwrap_or_default(),
676 wait_until_answered,
677 play_ringtone: options.play_dialtone.unwrap_or(false),
678 play_dialtone: options.play_dialtone.unwrap_or(false),
679 hide_phone_number: options.hide_phone_number.unwrap_or(false),
680 max_call_duration: Self::duration_to_proto(options.max_call_duration),
681 ringing_timeout: Self::duration_to_proto(ringing_timeout),
682 krisp_enabled: options.enable_krisp.unwrap_or(false),
683 headers: options.headers.unwrap_or_default(),
684 include_headers: options.include_headers.map(|h| h as i32).unwrap_or_default(),
685 media_encryption: options.media_encryption.map(|e| e as i32).unwrap_or_default(),
686 ..Default::default()
687 };
688 let headers = self.base.auth_header(
689 Default::default(),
690 Some(SIPGrants { call: true, ..Default::default() }),
691 )?;
692
693 if wait_until_answered {
697 self.client
698 .request_with_timeout(
699 SVC,
700 "CreateSIPParticipant",
701 request,
702 headers,
703 dial_timeout(user_timeout, ringing_timeout),
704 )
705 .await
706 .map_err(Into::into)
707 } else if let Some(timeout) = user_timeout {
708 self.client
709 .request_with_timeout(SVC, "CreateSIPParticipant", request, headers, timeout)
710 .await
711 .map_err(Into::into)
712 } else {
713 self.client
714 .request(SVC, "CreateSIPParticipant", request, headers)
715 .await
716 .map_err(Into::into)
717 }
718 }
719
720 pub async fn transfer_sip_participant(
724 &self,
725 room_name: String,
726 participant_identity: String,
727 transfer_to: String,
728 options: TransferSIPParticipantOptions,
729 ) -> ServiceResult<()> {
730 let ringing_timeout = options.ringing_timeout.or(Some(DEFAULT_RINGING_TIMEOUT));
733 let request = proto::TransferSipParticipantRequest {
734 participant_identity,
735 room_name: room_name.to_owned(),
736 transfer_to,
737 play_dialtone: options.play_dialtone.unwrap_or(false),
738 headers: options.headers.unwrap_or_default(),
739 ringing_timeout: Self::duration_to_proto(ringing_timeout),
740 };
741 let headers = self.base.auth_header(
742 VideoGrants { room_admin: true, room: room_name, ..Default::default() },
743 Some(SIPGrants { call: true, ..Default::default() }),
744 )?;
745
746 self.client
747 .request_with_timeout(
748 SVC,
749 "TransferSIPParticipant",
750 request,
751 headers,
752 dial_timeout(options.timeout, ringing_timeout),
753 )
754 .await
755 .map_err(Into::into)
756 }
757}