use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use tokio::sync::mpsc;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IncomingMessage {
pub id: String,
pub sender_id: String,
pub sender_name: Option<String>,
pub chat_id: String,
pub text: String,
pub is_group: bool,
pub reply_to: Option<String>,
pub timestamp: chrono::DateTime<chrono::Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OutgoingMessage {
pub chat_id: String,
pub text: String,
pub reply_to: Option<String>,
}
#[async_trait]
pub trait Channel: Send + Sync {
fn name(&self) -> &str;
async fn start(&mut self) -> anyhow::Result<mpsc::Receiver<IncomingMessage>>;
async fn send(&self, message: OutgoingMessage) -> anyhow::Result<Option<String>>;
async fn send_typing(&self, _chat_id: &str) -> anyhow::Result<()> {
Ok(())
}
async fn edit(&self, _chat_id: &str, _message_id: &str, _new_text: &str) -> anyhow::Result<()> {
Ok(())
}
fn as_draft(&self) -> Option<&dyn DraftChannel> {
None
}
async fn stop(&mut self) -> anyhow::Result<()>;
}
#[async_trait]
pub trait DraftChannel: Send + Sync {
async fn send_draft(&self, chat_id: &str, text: &str) -> anyhow::Result<Option<String>>;
async fn update_draft_progress(
&self,
chat_id: &str,
message_id: &str,
text: &str,
) -> anyhow::Result<()>;
async fn finalize_draft(
&self,
chat_id: &str,
message_id: &str,
text: &str,
) -> anyhow::Result<()>;
}
pub enum Delivery<'a> {
Draft {
channel: &'a dyn DraftChannel,
message_id: String,
},
Return,
}
impl<'a> Delivery<'a> {
pub async fn open(channel: &'a dyn Channel, chat_id: &str, placeholder: &str) -> Self {
let Some(draft) = channel.as_draft() else {
return Delivery::Return;
};
match draft.send_draft(chat_id, placeholder).await {
Ok(Some(message_id)) => Delivery::Draft {
channel: draft,
message_id,
},
Ok(None) => Delivery::Return,
Err(e) => {
tracing::warn!("send_draft failed, falling back to returned reply: {e}");
Delivery::Return
}
}
}
pub fn draft(&self) -> Option<(&dyn DraftChannel, &str)> {
match self {
Delivery::Draft {
channel,
message_id,
} => Some((*channel, message_id.as_str())),
Delivery::Return => None,
}
}
pub async fn deliver(&self, chat_id: &str, text: &str) -> anyhow::Result<String> {
match self {
Delivery::Draft {
channel,
message_id,
} => {
channel.finalize_draft(chat_id, message_id, text).await?;
Ok(String::new())
}
Delivery::Return => Ok(text.to_string()),
}
}
}