use std::collections::HashMap;
use crate::client::{AriClient, url_encode};
use crate::error::Result;
use crate::event::{Channel, LiveRecording, Playback};
use crate::resources::playback::PlaybackHandle;
use crate::resources::recording::RecordingHandle;
#[derive(Debug, Clone, serde::Serialize)]
#[must_use]
#[non_exhaustive]
pub struct OriginateParams {
endpoint: String,
#[serde(skip_serializing_if = "Option::is_none")]
extension: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
context: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
priority: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
app: Option<String>,
#[serde(rename = "appArgs", skip_serializing_if = "Option::is_none")]
app_args: Option<String>,
#[serde(rename = "callerId", skip_serializing_if = "Option::is_none")]
caller_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
timeout: Option<i32>,
#[serde(rename = "channelId", skip_serializing_if = "Option::is_none")]
pub(crate) channel_id: Option<String>,
#[serde(rename = "otherChannelId", skip_serializing_if = "Option::is_none")]
other_channel_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
originator: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
formats: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
variables: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
label: Option<String>,
}
impl OriginateParams {
pub fn new(endpoint: impl Into<String>) -> Self {
Self {
endpoint: endpoint.into(),
extension: None,
context: None,
priority: None,
app: None,
app_args: None,
caller_id: None,
timeout: None,
channel_id: None,
other_channel_id: None,
originator: None,
formats: None,
variables: None,
label: None,
}
}
pub fn extension(mut self, value: impl Into<String>) -> Self {
self.extension = Some(value.into());
self
}
pub fn context(mut self, value: impl Into<String>) -> Self {
self.context = Some(value.into());
self
}
pub fn priority(mut self, value: i64) -> Self {
self.priority = Some(value);
self
}
pub fn app(mut self, value: impl Into<String>) -> Self {
self.app = Some(value.into());
self
}
pub fn app_args(mut self, value: impl Into<String>) -> Self {
self.app_args = Some(value.into());
self
}
pub fn caller_id(mut self, value: impl Into<String>) -> Self {
self.caller_id = Some(value.into());
self
}
pub fn timeout(mut self, value: i32) -> Self {
self.timeout = Some(value);
self
}
pub fn channel_id(mut self, value: impl Into<String>) -> Self {
self.channel_id = Some(value.into());
self
}
pub fn other_channel_id(mut self, value: impl Into<String>) -> Self {
self.other_channel_id = Some(value.into());
self
}
pub fn originator(mut self, value: impl Into<String>) -> Self {
self.originator = Some(value.into());
self
}
pub fn formats(mut self, value: impl Into<String>) -> Self {
self.formats = Some(value.into());
self
}
pub fn variables(mut self, value: HashMap<String, String>) -> Self {
self.variables = Some(value);
self
}
pub fn label(mut self, value: impl Into<String>) -> Self {
self.label = Some(value.into());
self
}
}
#[derive(Debug, Clone, serde::Serialize)]
#[must_use]
#[non_exhaustive]
pub struct ExternalMediaParams {
app: String,
external_host: String,
format: String,
#[serde(skip_serializing_if = "Option::is_none")]
encapsulation: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
transport: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
connection_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
direction: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
data: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
transport_data: Option<String>,
#[serde(rename = "channelId", skip_serializing_if = "Option::is_none")]
channel_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
variables: Option<HashMap<String, String>>,
}
impl ExternalMediaParams {
pub fn new(
app: impl Into<String>,
external_host: impl Into<String>,
format: impl Into<String>,
) -> Self {
Self {
app: app.into(),
external_host: external_host.into(),
format: format.into(),
encapsulation: None,
transport: None,
connection_type: None,
direction: None,
data: None,
transport_data: None,
channel_id: None,
variables: None,
}
}
pub fn websocket_json(
app: impl Into<String>,
external_host: impl Into<String>,
format: impl Into<String>,
) -> Self {
Self::new(app, external_host, format)
.encapsulation("none")
.transport("websocket")
.transport_data("f(json)")
}
pub fn encapsulation(mut self, encapsulation: impl Into<String>) -> Self {
self.encapsulation = Some(encapsulation.into());
self
}
pub fn transport(mut self, transport: impl Into<String>) -> Self {
self.transport = Some(transport.into());
self
}
pub fn connection_type(mut self, connection_type: impl Into<String>) -> Self {
self.connection_type = Some(connection_type.into());
self
}
pub fn direction(mut self, direction: impl Into<String>) -> Self {
self.direction = Some(direction.into());
self
}
pub fn data(mut self, data: impl Into<String>) -> Self {
self.data = Some(data.into());
self
}
pub fn transport_data(mut self, transport_data: impl Into<String>) -> Self {
self.transport_data = Some(transport_data.into());
self
}
pub fn channel_id(mut self, channel_id: impl Into<String>) -> Self {
self.channel_id = Some(channel_id.into());
self
}
pub fn variables(mut self, variables: HashMap<String, String>) -> Self {
self.variables = Some(variables);
self
}
}
#[derive(Debug, Clone, serde::Deserialize)]
#[non_exhaustive]
pub struct Variable {
pub value: String,
}
#[derive(Debug, Clone)]
pub struct ChannelHandle {
id: String,
client: AriClient,
}
impl ChannelHandle {
pub fn new(id: impl Into<String>, client: AriClient) -> Self {
Self {
id: id.into(),
client,
}
}
pub fn id(&self) -> &str {
&self.id
}
pub async fn answer(&self) -> Result<()> {
self.client
.post_empty(&format!("/channels/{}/answer", url_encode(&self.id)))
.await
}
pub async fn hangup(&self, reason: Option<&str>) -> Result<()> {
let path = match reason {
Some(r) => format!(
"/channels/{}?reason={}",
url_encode(&self.id),
url_encode(r)
),
None => format!("/channels/{}", url_encode(&self.id)),
};
self.client.delete(&path).await
}
pub async fn play(&self, media: &str) -> Result<Playback> {
self.client
.post(
&format!("/channels/{}/play", url_encode(&self.id)),
&serde_json::json!({"media": media}),
)
.await
}
pub async fn play_handle(&self, media: &str) -> Result<PlaybackHandle> {
let playback = self.play(media).await?;
Ok(PlaybackHandle::new(playback.id, self.client.clone()))
}
pub async fn record(&self, name: &str, format: &str) -> Result<LiveRecording> {
self.client
.post(
&format!("/channels/{}/record", url_encode(&self.id)),
&serde_json::json!({"name": name, "format": format}),
)
.await
}
pub async fn record_handle(&self, name: &str, format: &str) -> Result<RecordingHandle> {
let recording = self.record(name, format).await?;
Ok(RecordingHandle::new(recording.name, self.client.clone()))
}
pub async fn mute(&self, direction: Option<&str>) -> Result<()> {
let path = match direction {
Some(d) => format!(
"/channels/{}/mute?direction={}",
url_encode(&self.id),
url_encode(d)
),
None => format!("/channels/{}/mute", url_encode(&self.id)),
};
self.client.post_empty(&path).await
}
pub async fn unmute(&self, direction: Option<&str>) -> Result<()> {
let path = match direction {
Some(d) => format!(
"/channels/{}/mute?direction={}",
url_encode(&self.id),
url_encode(d)
),
None => format!("/channels/{}/mute", url_encode(&self.id)),
};
self.client.delete(&path).await
}
pub async fn hold(&self) -> Result<()> {
self.client
.post_empty(&format!("/channels/{}/hold", url_encode(&self.id)))
.await
}
pub async fn unhold(&self) -> Result<()> {
self.client
.delete(&format!("/channels/{}/hold", url_encode(&self.id)))
.await
}
pub async fn send_dtmf(&self, dtmf: &str) -> Result<()> {
self.client
.post_empty(&format!(
"/channels/{}/dtmf?dtmf={}",
url_encode(&self.id),
url_encode(dtmf)
))
.await
}
pub async fn get_variable(&self, name: &str) -> Result<Variable> {
self.client
.get(&format!(
"/channels/{}/variable?variable={}",
url_encode(&self.id),
url_encode(name)
))
.await
}
pub async fn set_variable(&self, name: &str, value: &str) -> Result<()> {
self.client
.post_empty(&format!(
"/channels/{}/variable?variable={}&value={}",
url_encode(&self.id),
url_encode(name),
url_encode(value)
))
.await
}
pub async fn continue_in_dialplan(
&self,
context: Option<&str>,
extension: Option<&str>,
priority: Option<i64>,
) -> Result<()> {
let mut path = format!("/channels/{}/continue", url_encode(&self.id));
let mut params = Vec::new();
if let Some(c) = context {
params.push(format!("context={}", url_encode(c)));
}
if let Some(e) = extension {
params.push(format!("extension={}", url_encode(e)));
}
if let Some(p) = priority {
params.push(format!("priority={p}"));
}
if !params.is_empty() {
path.push('?');
path.push_str(¶ms.join("&"));
}
self.client.post_empty(&path).await
}
pub async fn snoop(
&self,
spy: Option<&str>,
whisper: Option<&str>,
app: &str,
) -> Result<Channel> {
let mut params = vec![format!("app={}", url_encode(app))];
if let Some(s) = spy {
params.push(format!("spy={}", url_encode(s)));
}
if let Some(w) = whisper {
params.push(format!("whisper={}", url_encode(w)));
}
let query = params.join("&");
self.client
.post(
&format!("/channels/{}/snoop?{}", url_encode(&self.id), query),
&serde_json::json!({}),
)
.await
}
pub async fn snoop_handle(
&self,
spy: Option<&str>,
whisper: Option<&str>,
app: &str,
) -> Result<ChannelHandle> {
let channel = self.snoop(spy, whisper, app).await?;
Ok(ChannelHandle::new(channel.id, self.client.clone()))
}
pub async fn redirect(&self, endpoint: &str) -> Result<()> {
self.client
.post_empty(&format!(
"/channels/{}/redirect?endpoint={}",
url_encode(&self.id),
url_encode(endpoint),
))
.await
}
pub async fn ring(&self) -> Result<()> {
self.client
.post_empty(&format!("/channels/{}/ring", url_encode(&self.id)))
.await
}
pub async fn ring_stop(&self) -> Result<()> {
self.client
.delete(&format!("/channels/{}/ring", url_encode(&self.id)))
.await
}
pub async fn start_silence(&self) -> Result<()> {
self.client
.post_empty(&format!("/channels/{}/silence", url_encode(&self.id)))
.await
}
pub async fn stop_silence(&self) -> Result<()> {
self.client
.delete(&format!("/channels/{}/silence", url_encode(&self.id)))
.await
}
pub async fn play_with_id(&self, playback_id: &str, media: &str) -> Result<Playback> {
self.client
.post(
&format!(
"/channels/{}/play/{}",
url_encode(&self.id),
url_encode(playback_id)
),
&serde_json::json!({"media": media}),
)
.await
}
pub async fn play_with_id_handle(
&self,
playback_id: &str,
media: &str,
) -> Result<PlaybackHandle> {
self.play_with_id(playback_id, media).await?;
Ok(PlaybackHandle::new(playback_id, self.client.clone()))
}
pub async fn dial(&self, caller: Option<&str>, timeout: Option<i32>) -> Result<()> {
let mut params = Vec::new();
if let Some(c) = caller {
params.push(format!("caller={}", url_encode(c)));
}
if let Some(t) = timeout {
params.push(format!("timeout={t}"));
}
let query = if params.is_empty() {
String::new()
} else {
format!("?{}", params.join("&"))
};
self.client
.post_empty(&format!("/channels/{}/dial{}", url_encode(&self.id), query))
.await
}
pub async fn rtp_statistics(&self) -> Result<serde_json::Value> {
self.client
.get(&format!(
"/channels/{}/rtp_statistics",
url_encode(&self.id)
))
.await
}
pub async fn external_media(&self, params: &ExternalMediaParams) -> Result<Channel> {
self.client.post("/channels/externalMedia", params).await
}
pub async fn external_media_handle(
&self,
params: &ExternalMediaParams,
) -> Result<ChannelHandle> {
let channel = self.external_media(params).await?;
Ok(ChannelHandle::new(channel.id, self.client.clone()))
}
}
pub async fn list(client: &AriClient) -> Result<Vec<Channel>> {
client.get("/channels").await
}
pub async fn get(client: &AriClient, channel_id: &str) -> Result<Channel> {
client
.get(&format!("/channels/{}", url_encode(channel_id)))
.await
}
pub async fn originate(client: &AriClient, params: &OriginateParams) -> Result<Channel> {
client.post("/channels", params).await
}
pub async fn originate_handle(
client: &AriClient,
params: &OriginateParams,
) -> Result<ChannelHandle> {
let channel = originate(client, params).await?;
Ok(ChannelHandle::new(channel.id, client.clone()))
}
pub async fn create(client: &AriClient, endpoint: &str, app: &str) -> Result<Channel> {
client
.post(
"/channels/create",
&serde_json::json!({
"endpoint": endpoint,
"app": app,
}),
)
.await
}
pub async fn create_handle(client: &AriClient, endpoint: &str, app: &str) -> Result<ChannelHandle> {
let channel = create(client, endpoint, app).await?;
Ok(ChannelHandle::new(channel.id, client.clone()))
}
pub async fn external_media(client: &AriClient, params: &ExternalMediaParams) -> Result<Channel> {
client.post("/channels/externalMedia", params).await
}
pub async fn external_media_handle(
client: &AriClient,
params: &ExternalMediaParams,
) -> Result<ChannelHandle> {
let channel = external_media(client, params).await?;
Ok(ChannelHandle::new(channel.id, client.clone()))
}