use std::sync::Arc;
use cratefield_adapter_telegram::{InlineButton, OutgoingMessage, TelegramBot, TelegramError};
use cratefield_core::Database;
use cratefield_module_notifications::{Channel, ChannelError, ChannelMessage};
use crate::store;
pub struct TelegramChannel {
bot: Arc<dyn TelegramBot>,
}
impl TelegramChannel {
#[must_use]
pub fn new(bot: Arc<dyn TelegramBot>) -> Self {
Self { bot }
}
}
fn map_error(error: &TelegramError) -> ChannelError {
match error {
TelegramError::Forbidden(_) => ChannelError::Unreachable,
TelegramError::RateLimited { retry_after } => ChannelError::Transient {
retry_after: Some(*retry_after),
},
TelegramError::Transient(_) => ChannelError::Transient { retry_after: None },
TelegramError::Unauthorized | TelegramError::Invalid(_) => {
ChannelError::Permanent(error.to_string())
}
}
}
#[async_trait::async_trait]
impl Channel for TelegramChannel {
fn name(&self) -> &'static str {
crate::MODULE_NAME
}
async fn reachable(&self, db: &dyn Database, account_id: &str) -> Result<bool, ChannelError> {
store::link_of_subject(db, account_id)
.await
.map(|link| link.is_some())
.map_err(|_| ChannelError::Transient { retry_after: None })
}
async fn deliver(
&self,
db: &dyn Database,
account_id: &str,
message: &ChannelMessage,
) -> Result<(), ChannelError> {
let Some((_, chat_id)) = store::link_of_subject(db, account_id)
.await
.map_err(|_| ChannelError::Transient { retry_after: None })?
else {
return Err(ChannelError::Unreachable);
};
let mut text = message.title.clone();
if !message.body.is_empty() {
text.push_str("\n\n");
text.push_str(&message.body);
}
let mut outgoing = OutgoingMessage::new(chat_id, text);
if let Some(url) = &message.url {
let button = InlineButton::url("Open", url.clone());
outgoing = outgoing.row(vec![button]);
}
self.bot
.send_message(&outgoing)
.await
.map_err(|error| map_error(&error))?;
Ok(())
}
}