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(),
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())
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 ringing_timeout_status: Default::default(),
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}