mirror of
https://github.com/teloxide/teloxide.git
synced 2025-03-14 11:44:04 +01:00
Implement chat::Dispatcher
This commit is contained in:
parent
5fd6ae3630
commit
dedc861523
10 changed files with 63 additions and 44 deletions
|
@ -4,10 +4,10 @@ version = "0.0.0"
|
|||
publish = false
|
||||
edition = "2018"
|
||||
|
||||
[dev-dependencies]
|
||||
[dependencies]
|
||||
teloxide = { path = "../" }
|
||||
tokio = "0.2.6"
|
||||
pretty_env_logger = "0.3"
|
||||
pretty_env_logger = "0.3.1"
|
||||
futures = "0.3.1"
|
||||
log = "0.4.8"
|
||||
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
use futures::stream::StreamExt;
|
||||
use teloxide::{
|
||||
dispatching::{
|
||||
private::Dispatcher, update_listeners::polling_default, SessionState,
|
||||
chat::Dispatcher, update_listeners::polling_default, SessionState,
|
||||
},
|
||||
requests::Request,
|
||||
types::{Update, UpdateKind},
|
||||
Bot,
|
||||
};
|
||||
use teloxide::dispatching::chat::{ChatUpdate, ChatUpdateKind};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
pretty_env_logger::init();
|
||||
|
||||
|
||||
let bot = &Bot::new("1061598315:AAErEDodTsrqD3UxA_EvFyEfXbKA6DT25G0");
|
||||
let mut updater = Box::pin(polling_default(bot));
|
||||
let handler = |s, upd: Update| async move {
|
||||
let handler = |s, upd: ChatUpdate| async move {
|
||||
match upd.kind {
|
||||
UpdateKind::Message(m) => {
|
||||
ChatUpdateKind::Message(m) => {
|
||||
let msg = bot.send_message(m.chat.id, "pong");
|
||||
msg.send().await.unwrap();
|
||||
}
|
||||
|
|
18
src/dispatching/chat/chat_update.rs
Normal file
18
src/dispatching/chat/chat_update.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::types::{CallbackQuery, Message};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ChatUpdate {
|
||||
pub id: i32,
|
||||
|
||||
pub kind: ChatUpdateKind,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ChatUpdateKind {
|
||||
Message(Message),
|
||||
EditedMessage(Message),
|
||||
CallbackQuery(CallbackQuery),
|
||||
}
|
|
@ -3,33 +3,21 @@ use super::{
|
|||
storage::{InMemStorage, Storage},
|
||||
};
|
||||
use crate::{
|
||||
dispatching::{Handler, SessionState},
|
||||
types::{ChatKind, Update, UpdateKind},
|
||||
dispatching::{chat::ChatUpdate, Handler, SessionState},
|
||||
types::{Update, UpdateKind},
|
||||
};
|
||||
use crate::dispatching::chat::ChatUpdateKind;
|
||||
|
||||
/// A dispatcher that dispatches updates from 1-to-1 chats.
|
||||
/// A dispatcher that dispatches updates from chats.
|
||||
pub struct Dispatcher<'a, Session, H> {
|
||||
storage: Box<dyn Storage<Session> + 'a>,
|
||||
handler: H,
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
mod macros {
|
||||
#[macro_export]
|
||||
macro_rules! private_chat_id {
|
||||
($msg:expr) => {
|
||||
match &$msg.chat.kind {
|
||||
ChatKind::Private { .. } => $msg.chat.id,
|
||||
_ => return DispatchResult::Unhandled,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Session, H> Dispatcher<'a, Session, H>
|
||||
where
|
||||
Session: Default + 'a,
|
||||
H: Handler<Session>,
|
||||
H: Handler<Session, ChatUpdate>,
|
||||
{
|
||||
/// Creates a dispatcher with the specified `handler` and [`InMemStorage`]
|
||||
/// (a default storage).
|
||||
|
@ -58,18 +46,28 @@ where
|
|||
/// ## Returns
|
||||
/// Returns [`DispatchResult::Handled`] if `update` was supplied to a
|
||||
/// handler, and [`DispatchResult::Unhandled`] if it was an update not
|
||||
/// from a 1-to-1 chat.
|
||||
/// from a chat.
|
||||
///
|
||||
/// [`DispatchResult::Handled`]: crate::dispatching::DispatchResult::Handled
|
||||
/// [`DispatchResult::Unhandled`]:
|
||||
/// crate::dispatching::DispatchResult::Unhandled
|
||||
pub async fn dispatch(&mut self, update: Update) -> DispatchResult {
|
||||
let chat_id = match &update.kind {
|
||||
UpdateKind::Message(msg) => private_chat_id!(msg),
|
||||
UpdateKind::EditedMessage(msg) => private_chat_id!(msg),
|
||||
let chat_update = match update.kind {
|
||||
UpdateKind::Message(msg) => ChatUpdate { id: update.id, kind: ChatUpdateKind::Message(msg) },
|
||||
UpdateKind::EditedMessage(msg) => ChatUpdate { id: update.id, kind: ChatUpdateKind::EditedMessage(msg) },
|
||||
UpdateKind::CallbackQuery(query) => ChatUpdate { id: update.id, kind: ChatUpdateKind::CallbackQuery(query) },
|
||||
_ => return DispatchResult::Unhandled,
|
||||
};
|
||||
|
||||
let chat_id = match &chat_update.kind {
|
||||
ChatUpdateKind::Message(msg) => msg.chat.id,
|
||||
ChatUpdateKind::EditedMessage(msg) => msg.chat.id,
|
||||
ChatUpdateKind::CallbackQuery(query) => match &query.message {
|
||||
None => return DispatchResult::Unhandled,
|
||||
Some(msg) => msg.chat.id,
|
||||
},
|
||||
};
|
||||
|
||||
let session = self
|
||||
.storage
|
||||
.remove_session(chat_id)
|
||||
|
@ -77,7 +75,7 @@ where
|
|||
.unwrap_or_default();
|
||||
|
||||
if let SessionState::Continue(session) =
|
||||
self.handler.handle(session, update).await
|
||||
self.handler.handle(session, chat_update).await
|
||||
{
|
||||
if self
|
||||
.storage
|
|
@ -1,4 +1,4 @@
|
|||
//! Dispatching updates from 1-to-1 chats.
|
||||
//! Dispatching updates from chats.
|
||||
//!
|
||||
//! There are four main components:
|
||||
//!
|
||||
|
@ -14,13 +14,13 @@
|
|||
//! Every time you call [`.dispatch(update)`] on your dispatcher, the following
|
||||
//! steps are executed:
|
||||
//!
|
||||
//! 1. If a supplied update is not from a 1-to-1 chat, return
|
||||
//! 1. If a supplied update is not from a chat, return
|
||||
//! [`DispatchResult::Unhandled`].
|
||||
//! 2. If a storage doesn't contain a session from this chat, supply
|
||||
//! `Session::default()` into you handler, otherwise, supply the previous
|
||||
//! session.
|
||||
//! 3. If a handler has returned [`SessionState::Terminate`], remove the sesion
|
||||
//! from a storage, otherwise force the storage to update the session.
|
||||
//! 3. If a handler has returned [`SessionState::Terminate`], remove the
|
||||
//! session from a storage, otherwise force the storage to update the session.
|
||||
//!
|
||||
//! [`Storage`]: crate::dispatching::private::Storage
|
||||
//! [`Dispatcher`]: crate::dispatching::private::Dispatcher
|
||||
|
@ -32,8 +32,10 @@
|
|||
|
||||
// TODO: examples
|
||||
|
||||
mod chat_update;
|
||||
mod dispatcher;
|
||||
mod storage;
|
||||
|
||||
pub use chat_update::*;
|
||||
pub use dispatcher::*;
|
||||
pub use storage::*;
|
|
@ -3,7 +3,7 @@ mod in_mem_storage;
|
|||
use async_trait::async_trait;
|
||||
pub use in_mem_storage::InMemStorage;
|
||||
|
||||
/// A storage of user sessions.
|
||||
/// A storage of sessions.
|
||||
///
|
||||
/// You can implement this trait for a structure that communicates with a DB and
|
||||
/// be sure that after you restart your bot, all the sessions won't be lost.
|
|
@ -1,4 +1,3 @@
|
|||
use crate::types::Update;
|
||||
use std::{future::Future, pin::Pin};
|
||||
|
||||
/// Continue or terminate a user session.
|
||||
|
@ -17,31 +16,33 @@ pub enum SessionState<Session> {
|
|||
/// [`SessionState::Continue(session)`]:
|
||||
/// crate::dispatching::SessionState::Continue
|
||||
/// [`SessionState::Terminate`]: crate::dispatching::SessionState::Terminate
|
||||
pub trait Handler<Session> {
|
||||
pub trait Handler<Session, U> {
|
||||
#[must_use]
|
||||
fn handle<'a>(
|
||||
&'a self,
|
||||
session: Session,
|
||||
update: Update,
|
||||
update: U,
|
||||
) -> Pin<Box<dyn Future<Output = SessionState<Session>> + 'a>>
|
||||
where
|
||||
Session: 'a;
|
||||
Session: 'a,
|
||||
U: 'a;
|
||||
}
|
||||
|
||||
/// The implementation of `Handler` for `Fn(Session, Update) -> Future<Output =
|
||||
/// SessionState<Session>>`.
|
||||
impl<Session, F, Fut> Handler<Session> for F
|
||||
impl<Session, U, F, Fut> Handler<Session, U> for F
|
||||
where
|
||||
F: Fn(Session, Update) -> Fut,
|
||||
F: Fn(Session, U) -> Fut,
|
||||
Fut: Future<Output = SessionState<Session>>,
|
||||
{
|
||||
fn handle<'a>(
|
||||
&'a self,
|
||||
session: Session,
|
||||
update: Update,
|
||||
update: U,
|
||||
) -> Pin<Box<dyn Future<Output = Fut::Output> + 'a>>
|
||||
where
|
||||
Session: 'a,
|
||||
U: 'a,
|
||||
{
|
||||
Box::pin(async move { self(session, update).await })
|
||||
}
|
||||
|
|
|
@ -7,9 +7,9 @@ pub enum DispatchResult {
|
|||
Unhandled,
|
||||
}
|
||||
|
||||
pub mod chat;
|
||||
pub mod filters;
|
||||
mod handler;
|
||||
pub mod private;
|
||||
pub mod update_listeners;
|
||||
|
||||
pub use filters::Filter;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use derive_more::From;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::types::{
|
||||
ForceReply, InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove,
|
||||
|
@ -25,4 +25,4 @@ mod tests {
|
|||
let actual: ReplyMarkup = data.into();
|
||||
assert_eq!(actual, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue