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