Skip to main content

active_call/useragent/
invitation.rs

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