use reqwest::Method;
use serde::{Deserialize, Serialize};
use crate::client::Ghl;
use crate::error::Result;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[allow(missing_docs)] pub struct Conversation {
pub id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub location_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub contact_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub full_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_message_body: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_message_date: Option<serde_json::Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_message_type: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub unread_count: Option<i64>,
#[serde(flatten)]
pub extra: serde_json::Map<String, serde_json::Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[allow(missing_docs)] pub struct Message {
pub id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub body: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message_type: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub direction: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub date_added: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub conversation_id: Option<String>,
#[serde(flatten)]
pub extra: serde_json::Map<String, serde_json::Value>,
}
#[derive(Debug, Clone, Default, Serialize)]
#[serde(rename_all = "camelCase")]
#[allow(missing_docs)] pub struct SendMessage {
#[serde(rename = "type")]
pub message_type: String,
pub contact_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subject: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub html: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub attachments: Vec<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
#[allow(missing_docs)]
pub struct SendMessageResult {
#[serde(default)]
pub conversation_id: Option<String>,
#[serde(default)]
pub message_id: Option<String>,
#[serde(flatten)]
pub extra: serde_json::Map<String, serde_json::Value>,
}
#[derive(Deserialize)]
struct ConversationSearch {
#[serde(default)]
conversations: Vec<Conversation>,
#[serde(default)]
total: Option<i64>,
}
#[derive(Deserialize)]
struct MessagesEnvelope {
messages: MessagesInner,
}
#[derive(Deserialize)]
struct MessagesInner {
#[serde(default)]
messages: Vec<Message>,
#[serde(default, rename = "nextPage")]
next_page: Option<bool>,
}
#[derive(Debug, Clone)]
pub struct ConversationPage {
pub conversations: Vec<Conversation>,
pub total: Option<i64>,
}
#[derive(Debug, Clone)]
pub struct MessagePage {
pub messages: Vec<Message>,
pub next_page: Option<bool>,
}
pub struct ConversationsService {
pub(crate) client: Ghl,
}
impl ConversationsService {
pub(crate) fn new(client: Ghl) -> Self {
Self { client }
}
pub async fn search(
&self,
location_id: &str,
query: Option<&str>,
limit: u32,
) -> Result<ConversationPage> {
let mut params: Vec<(String, String)> = vec![
("locationId".into(), location_id.to_owned()),
("limit".into(), limit.clamp(1, 100).to_string()),
];
if let Some(q) = query {
params.push(("query".into(), q.to_owned()));
}
let result: ConversationSearch = self
.client
.send(Method::GET, "/conversations/search", ¶ms, None::<&()>)
.await?;
Ok(ConversationPage {
conversations: result.conversations,
total: result.total,
})
}
pub async fn messages(&self, conversation_id: &str, limit: u32) -> Result<MessagePage> {
let envelope: MessagesEnvelope = self
.client
.send(
Method::GET,
&format!("/conversations/{conversation_id}/messages"),
&[("limit".into(), limit.clamp(1, 100).to_string())],
None::<&()>,
)
.await?;
Ok(MessagePage {
messages: envelope.messages.messages,
next_page: envelope.messages.next_page,
})
}
pub async fn send_message(&self, message: SendMessage) -> Result<SendMessageResult> {
self.client
.send(Method::POST, "/conversations/messages", &[], Some(&message))
.await
}
}