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