use crate::callrecord::CallRecordHangupReason;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::time::Duration;
mod app_context;
mod controller;
mod event_loop;
pub mod agent_registry;
pub mod ivr;
pub mod ivr_config;
pub mod queue;
pub mod voicemail;
#[cfg(test)]
pub mod testing;
#[cfg(test)]
mod app_test;
#[cfg(test)]
mod ivr_test;
#[cfg(test)]
mod queue_test;
pub use app_context::{AppSharedState, ApplicationContext, CallInfo, extract_sip_headers};
pub use controller::{
CallController, ControllerEvent, DtmfCollectConfig, HangupDuringCollection, PlaybackHandle,
RecordingHandle, RecordingInfo,
};
pub use event_loop::AppEventLoop;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CallAppType {
Voicemail,
Ivr,
Conference,
Queue,
Fax,
Custom,
}
impl fmt::Display for CallAppType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CallAppType::Voicemail => write!(f, "voicemail"),
CallAppType::Ivr => write!(f, "ivr"),
CallAppType::Conference => write!(f, "conference"),
CallAppType::Queue => write!(f, "queue"),
CallAppType::Fax => write!(f, "fax"),
CallAppType::Custom => write!(f, "custom"),
}
}
}
#[derive(Debug)]
pub enum AppAction {
Continue,
Exit,
Transfer(String),
Chain(Box<dyn CallApp>),
Hangup {
reason: Option<CallRecordHangupReason>,
code: Option<u16>,
},
Sleep(Duration),
}
#[derive(Debug, Clone)]
pub enum ExitReason {
Normal,
Hangup,
RemoteHangup(Option<CallRecordHangupReason>),
Transferred,
Chained,
Cancelled,
Error(String),
}
impl fmt::Display for ExitReason {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ExitReason::Normal => write!(f, "normal"),
ExitReason::Hangup => write!(f, "hangup"),
ExitReason::RemoteHangup(r) => write!(f, "remote_hangup({:?})", r),
ExitReason::Transferred => write!(f, "transferred"),
ExitReason::Chained => write!(f, "chained"),
ExitReason::Cancelled => write!(f, "cancelled"),
ExitReason::Error(e) => write!(f, "error: {}", e),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum AppEvent {
HttpResponse { body: String },
ParticipantJoined { room_id: String, count: usize },
ParticipantLeft { room_id: String, count: usize },
ConferenceEnded { room_id: String },
Custom {
name: String,
data: serde_json::Value,
},
}
#[async_trait]
pub trait CallApp: Send + Sync {
fn app_type(&self) -> CallAppType;
fn name(&self) -> &str;
async fn on_enter(
&mut self,
controller: &mut CallController,
context: &ApplicationContext,
) -> anyhow::Result<AppAction>;
async fn on_dtmf(
&mut self,
digit: String,
controller: &mut CallController,
context: &ApplicationContext,
) -> anyhow::Result<AppAction> {
let _ = (digit, controller, context);
Ok(AppAction::Continue)
}
async fn on_audio_complete(
&mut self,
track_id: String,
controller: &mut CallController,
context: &ApplicationContext,
) -> anyhow::Result<AppAction> {
let _ = (track_id, controller, context);
Ok(AppAction::Continue)
}
async fn on_record_complete(
&mut self,
info: RecordingInfo,
controller: &mut CallController,
context: &ApplicationContext,
) -> anyhow::Result<AppAction> {
let _ = (info, controller, context);
Ok(AppAction::Continue)
}
async fn on_external_event(
&mut self,
event: AppEvent,
controller: &mut CallController,
context: &ApplicationContext,
) -> anyhow::Result<AppAction> {
let _ = (event, controller, context);
Ok(AppAction::Continue)
}
async fn on_timeout(
&mut self,
timeout_id: String,
controller: &mut CallController,
context: &ApplicationContext,
) -> anyhow::Result<AppAction> {
let _ = (timeout_id, controller, context);
Ok(AppAction::Continue)
}
async fn on_exit(&mut self, reason: ExitReason) -> anyhow::Result<()> {
let _ = reason;
Ok(())
}
}
impl fmt::Debug for dyn CallApp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CallApp")
.field("type", &self.app_type())
.field("name", &self.name())
.finish()
}
}