use crate::{keyboard::Reply, platform::PlatformKind, Error, Result};
use futures::future::BoxFuture;
use std::{collections::HashMap, sync::Arc};
use tokio::sync::RwLock;
pub(crate) type SendFn =
Arc<dyn Fn(String, Reply) -> BoxFuture<'static, Result<()>> + Send + Sync + 'static>;
pub(crate) type DmSendFn =
Arc<dyn Fn(String, Reply) -> BoxFuture<'static, Result<()>> + Send + Sync + 'static>;
pub(crate) type UserLookupFn =
Arc<dyn Fn(String) -> BoxFuture<'static, Result<Option<String>>> + Send + Sync + 'static>;
pub(crate) type MenuAppFn =
Arc<dyn Fn(String, String) -> BoxFuture<'static, Result<()>> + Send + Sync + 'static>;
#[derive(Clone, Default)]
pub struct Notifier {
senders: Arc<RwLock<HashMap<PlatformKind, SendFn>>>,
dm_senders: Arc<RwLock<HashMap<PlatformKind, DmSendFn>>>,
user_lookups: Arc<RwLock<HashMap<PlatformKind, UserLookupFn>>>,
menu_app: Arc<RwLock<Option<MenuAppFn>>>,
}
impl Notifier {
pub fn new() -> Self {
Self {
senders: Arc::new(RwLock::new(HashMap::new())),
dm_senders: Arc::new(RwLock::new(HashMap::new())),
user_lookups: Arc::new(RwLock::new(HashMap::new())),
menu_app: Arc::new(RwLock::new(None)),
}
}
pub(crate) async fn register_menu_app(&self, publish: MenuAppFn) {
*self.menu_app.write().await = Some(publish);
}
pub async fn set_menu_web_app(
&self,
label: impl Into<String>,
url: impl Into<String>,
) -> Result<()> {
let publish = { self.menu_app.read().await.clone() };
match publish {
Some(f) => f(label.into(), url.into()).await,
None => Err(Error::Other(
"no live telegram adapter to publish the menu button through".to_owned(),
)),
}
}
pub(crate) async fn register(&self, platform: PlatformKind, send: SendFn) {
self.senders.write().await.insert(platform, send);
}
pub(crate) async fn register_dm(&self, platform: PlatformKind, send: DmSendFn) {
self.dm_senders.write().await.insert(platform, send);
}
pub(crate) async fn register_user_lookup(&self, platform: PlatformKind, lookup: UserLookupFn) {
self.user_lookups.write().await.insert(platform, lookup);
}
pub async fn is_ready(&self, platform: PlatformKind) -> bool {
self.senders.read().await.contains_key(&platform)
}
pub async fn is_dm_ready(&self, platform: PlatformKind) -> bool {
self.dm_senders.read().await.contains_key(&platform)
}
pub async fn send_text(
&self,
platform: PlatformKind,
chat_id: impl Into<String>,
text: impl Into<String>,
) -> Result<()> {
self.send(platform, chat_id, Reply::text(text)).await
}
pub async fn send(
&self,
platform: PlatformKind,
chat_id: impl Into<String>,
reply: impl Into<Reply>,
) -> Result<()> {
let sender = {
let map = self.senders.read().await;
map.get(&platform).cloned()
};
match sender {
Some(send) => send(chat_id.into(), reply.into()).await,
None => Err(Error::Other(format!(
"no live {platform} adapter to send through"
))),
}
}
pub async fn send_dm(
&self,
platform: PlatformKind,
user_id: impl Into<String>,
reply: impl Into<Reply>,
) -> Result<()> {
let sender = {
let map = self.dm_senders.read().await;
map.get(&platform).cloned()
};
match sender {
Some(send) => send(user_id.into(), reply.into()).await,
None => Err(Error::Other(format!(
"no live {platform} adapter to send DMs through"
))),
}
}
pub async fn user_name(&self, platform: PlatformKind, user_id: &str) -> Result<Option<String>> {
let lookup = {
let map = self.user_lookups.read().await;
map.get(&platform).cloned()
};
match lookup {
Some(lookup) => lookup(user_id.to_string()).await,
None => Err(Error::Other(format!(
"no live {platform} adapter to look up users through"
))),
}
}
}