active_call/useragent/
invitation.rs1use std::sync::Arc;
2
3use crate::{
4 call::{RoutingState, sip::Invitation},
5 config::InviteHandlerConfig,
6 useragent::{playbook_handler::PlaybookInvitationHandler, webhook::WebhookInvitationHandler},
7};
8use anyhow::{Result, anyhow};
9use async_trait::async_trait;
10use rsipstack::dialog::{
11 dialog::{Dialog, DialogStateReceiver},
12 server_dialog::ServerInviteDialog,
13};
14use tokio_util::sync::CancellationToken;
15use tracing::info;
16
17pub struct PendingDialog {
18 pub token: CancellationToken,
19 pub dialog: ServerInviteDialog,
20 pub state_receiver: DialogStateReceiver,
21}
22pub struct PendingDialogGuard {
23 pub id: String,
24 pub invitation: Invitation,
25}
26
27impl PendingDialogGuard {
28 pub fn new(invitation: Invitation, id: String, pending_dialog: PendingDialog) -> Self {
29 invitation.add_pending(id.clone(), pending_dialog);
30 info!(%id, "added pending dialog");
31 Self { id, invitation }
32 }
33
34 fn take_dialog(&self) -> Option<Dialog> {
35 if let Some(pending) = self.invitation.get_pending_call(&self.id) {
36 let dialog_id = pending.dialog.id();
37 match self.invitation.dialog_layer.get_dialog(&dialog_id) {
38 Some(dialog) => {
39 self.invitation.dialog_layer.remove_dialog(&dialog_id);
40 return Some(dialog);
41 }
42 None => {}
43 }
44 }
45 None
46 }
47 pub async fn drop_async(&self) {
48 if let Some(dialog) = self.take_dialog() {
49 dialog.hangup().await.ok();
50 }
51 }
52}
53
54impl Drop for PendingDialogGuard {
55 fn drop(&mut self) {
56 if let Some(dialog) = self.take_dialog() {
57 info!(%self.id, "removing pending dialog on drop");
58
59 crate::spawn(async move {
60 dialog.hangup().await.ok();
61 });
62 }
63 }
64}
65
66#[async_trait]
67pub trait InvitationHandler: Send + Sync {
68 async fn on_invite(
69 &self,
70 _session_id: String,
71 _cancel_token: CancellationToken,
72 _dialog: ServerInviteDialog,
73 _routing_state: Arc<RoutingState>,
74 ) -> Result<()> {
75 return Err(anyhow!("invite not handled"));
76 }
77}
78
79pub fn default_create_invite_handler(
80 config: Option<&InviteHandlerConfig>,
81 app_state: Option<crate::app::AppState>,
82) -> Option<Box<dyn InvitationHandler>> {
83 match config {
84 Some(InviteHandlerConfig::Webhook {
85 url,
86 urls,
87 method,
88 headers,
89 }) => {
90 let all_urls = if let Some(urls) = urls {
91 urls.clone()
92 } else if let Some(url) = url {
93 vec![url.clone()]
94 } else {
95 vec![]
96 };
97 Some(Box::new(WebhookInvitationHandler::new(
98 all_urls,
99 method.clone(),
100 headers.clone(),
101 )))
102 }
103 Some(InviteHandlerConfig::Playbook { rules, default }) => {
104 let app_state = match app_state {
105 Some(s) => s,
106 None => {
107 tracing::error!("app_state required for playbook invitation handler");
108 return None;
109 }
110 };
111 match PlaybookInvitationHandler::new(rules.clone(), default.clone(), app_state) {
112 Ok(handler) => Some(Box::new(handler)),
113 Err(e) => {
114 tracing::error!("failed to create playbook invitation handler: {}", e);
115 None
116 }
117 }
118 }
119 _ => None,
120 }
121}
122
123pub type FnCreateInvitationHandler =
124 fn(config: Option<&InviteHandlerConfig>) -> Result<Box<dyn InvitationHandler>>;