#[cfg(feature = "redis-storage")]
pub use self::{RedisStorage, RedisStorageError};
#[cfg(any(feature = "sqlite-storage-nativetls", feature = "sqlite-storage-rustls"))]
pub use self::{SqliteStorage, SqliteStorageError};
#[cfg(feature = "postgres-storage-nativetls")]
pub use self::{PostgresStorage, PostgresStorageError};
pub use get_chat_id::GetChatId;
pub use storage::*;
use dptree::Handler;
use teloxide_core::types::ChatId;
use std::{fmt::Debug, marker::PhantomData, sync::Arc};
use super::DpHandlerDescription;
mod get_chat_id;
mod storage;
const TELOXIDE_DIALOGUE_BEHAVIOUR: &str = "TELOXIDE_DIALOGUE_BEHAVIOUR";
#[derive(Debug)]
pub struct Dialogue<D, S>
where
S: ?Sized,
{
storage: Arc<S>,
chat_id: ChatId,
_phantom: PhantomData<D>,
}
impl<D, S> Clone for Dialogue<D, S>
where
S: ?Sized,
{
fn clone(&self) -> Self {
Dialogue { storage: self.storage.clone(), chat_id: self.chat_id, _phantom: PhantomData }
}
}
impl<D, S> Dialogue<D, S>
where
D: Send + 'static,
S: Storage<D> + ?Sized,
{
#[must_use]
pub fn new(storage: Arc<S>, chat_id: ChatId) -> Self {
Self { storage, chat_id, _phantom: PhantomData }
}
#[must_use]
pub fn chat_id(&self) -> ChatId {
self.chat_id
}
pub async fn get(&self) -> Result<Option<D>, S::Error> {
self.storage.clone().get_dialogue(self.chat_id).await
}
pub async fn get_or_default(&self) -> Result<D, S::Error>
where
D: Default,
{
match self.get().await? {
Some(d) => Ok(d),
None => {
self.storage.clone().update_dialogue(self.chat_id, D::default()).await?;
Ok(D::default())
}
}
}
pub async fn update<State>(&self, state: State) -> Result<(), S::Error>
where
D: From<State>,
{
let new_dialogue = state.into();
self.storage.clone().update_dialogue(self.chat_id, new_dialogue).await?;
Ok(())
}
pub async fn reset(&self) -> Result<(), S::Error>
where
D: Default,
{
self.update(D::default()).await
}
pub async fn exit(&self) -> Result<(), S::Error> {
self.storage.clone().remove_dialogue(self.chat_id).await
}
}
#[must_use]
pub fn enter<Upd, S, D, Output>() -> Handler<'static, Output, DpHandlerDescription>
where
S: Storage<D> + ?Sized + Send + Sync + 'static,
<S as Storage<D>>::Error: Debug + Send,
D: Default + Clone + Send + Sync + 'static,
Upd: GetChatId + Clone + Send + Sync + 'static,
Output: Send + Sync + 'static,
{
dptree::filter_map(|storage: Arc<S>, upd: Upd| {
let chat_id = upd.chat_id()?;
Some(Dialogue::new(storage, chat_id))
})
.filter_map_async(|dialogue: Dialogue<D, S>| async move {
match dialogue.get_or_default().await {
Ok(dialogue) => Some(dialogue),
Err(err) => match std::env::var(TELOXIDE_DIALOGUE_BEHAVIOUR).as_deref() {
Ok("default") => {
let default = D::default();
dialogue.update(default.clone()).await.ok()?;
Some(default)
}
Ok("panic") | Err(_) => {
log::error!("dialogue.get_or_default() failed: {err:?}");
None
}
Ok(_) => {
panic!(
"`TELOXIDE_DIALOGUE_BEHAVIOUR` env variable should be one of: \
default/panic"
)
}
},
}
})
}