mirror of
https://github.com/teloxide/teloxide.git
synced 2025-01-09 11:43:57 +01:00
Refactor
This commit is contained in:
parent
d7d97ef136
commit
61d002b8d4
10 changed files with 206 additions and 171 deletions
|
@ -12,4 +12,7 @@ log = "0.4.8"
|
||||||
tokio = "0.2.9"
|
tokio = "0.2.9"
|
||||||
strum = "0.17.1"
|
strum = "0.17.1"
|
||||||
strum_macros = "0.17.1"
|
strum_macros = "0.17.1"
|
||||||
teloxide = { path = "../../" }
|
teloxide = { path = "../../" }
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
|
@ -99,13 +99,13 @@ type Res = Result<SessionState<User>, RequestError>;
|
||||||
|
|
||||||
async fn start(ctx: Ctx) -> Res {
|
async fn start(ctx: Ctx) -> Res {
|
||||||
reply!(ctx, "Let's start! First, what's your full name?");
|
reply!(ctx, "Let's start! First, what's your full name?");
|
||||||
Ok(SessionState::Continue(ctx.session))
|
Ok(SessionState::Next(ctx.session))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn full_name(mut ctx: Ctx) -> Res {
|
async fn full_name(mut ctx: Ctx) -> Res {
|
||||||
reply!(ctx, "What a wonderful name! Your age?");
|
reply!(ctx, "What a wonderful name! Your age?");
|
||||||
ctx.session.full_name = Some(ctx.update.text().unwrap().to_owned());
|
ctx.session.full_name = Some(ctx.update.text().unwrap().to_owned());
|
||||||
Ok(SessionState::Continue(ctx.session))
|
Ok(SessionState::Next(ctx.session))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn age(mut ctx: Ctx) -> Res {
|
async fn age(mut ctx: Ctx) -> Res {
|
||||||
|
@ -117,7 +117,7 @@ async fn age(mut ctx: Ctx) -> Res {
|
||||||
Err(_) => reply!(ctx, "Oh, please, enter a number!"),
|
Err(_) => reply!(ctx, "Oh, please, enter a number!"),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(SessionState::Continue(ctx.session))
|
Ok(SessionState::Next(ctx.session))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn favourite_music(mut ctx: Ctx) -> Res {
|
async fn favourite_music(mut ctx: Ctx) -> Res {
|
||||||
|
@ -125,11 +125,11 @@ async fn favourite_music(mut ctx: Ctx) -> Res {
|
||||||
Ok(ok) => {
|
Ok(ok) => {
|
||||||
ctx.session.favourite_music = Some(ok);
|
ctx.session.favourite_music = Some(ok);
|
||||||
reply!(ctx, format!("Fine. {}", ctx.session));
|
reply!(ctx, format!("Fine. {}", ctx.session));
|
||||||
Ok(SessionState::Terminate)
|
Ok(SessionState::Exit)
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
reply!(ctx, "Oh, please, enter from the keyboard!");
|
reply!(ctx, "Oh, please, enter from the keyboard!");
|
||||||
Ok(SessionState::Continue(ctx.session))
|
Ok(SessionState::Next(ctx.session))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ async fn handle_message(ctx: Ctx) -> Res {
|
||||||
return favourite_music(ctx).await;
|
return favourite_music(ctx).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(SessionState::Terminate)
|
Ok(SessionState::Exit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
dispatching::{
|
dispatching::{
|
||||||
error_handlers, update_listeners, update_listeners::UpdateListener,
|
error_handlers, update_listeners, update_listeners::UpdateListener,
|
||||||
AsyncHandler,
|
AsyncHandler, HandlerCtx,
|
||||||
},
|
},
|
||||||
requests::{Request, ResponseResult},
|
|
||||||
types::{
|
types::{
|
||||||
CallbackQuery, ChosenInlineResult, InlineQuery, Message, Poll,
|
CallbackQuery, ChosenInlineResult, InlineQuery, Message, Poll,
|
||||||
PreCheckoutQuery, ShippingQuery, UpdateKind,
|
PreCheckoutQuery, ShippingQuery, UpdateKind,
|
||||||
|
@ -13,32 +12,6 @@ use crate::{
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use std::{fmt::Debug, sync::Arc};
|
use std::{fmt::Debug, sync::Arc};
|
||||||
|
|
||||||
/// A dispatcher's handler's context of a bot and an update.
|
|
||||||
///
|
|
||||||
/// See [the module-level documentation for the design
|
|
||||||
/// overview](teloxide::dispatching).
|
|
||||||
pub struct HandlerCtx<Upd> {
|
|
||||||
pub bot: Arc<Bot>,
|
|
||||||
pub update: Upd,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HandlerCtx<Message> {
|
|
||||||
pub fn chat_id(&self) -> i64 {
|
|
||||||
self.update.chat_id()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn reply<T>(self, text: T) -> ResponseResult<()>
|
|
||||||
where
|
|
||||||
T: Into<String>,
|
|
||||||
{
|
|
||||||
self.bot
|
|
||||||
.send_message(self.chat_id(), text)
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.map(|_| ())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type H<'a, Upd, HandlerE> =
|
type H<'a, Upd, HandlerE> =
|
||||||
Option<Box<dyn AsyncHandler<HandlerCtx<Upd>, Result<(), HandlerE>> + 'a>>;
|
Option<Box<dyn AsyncHandler<HandlerCtx<Upd>, Result<(), HandlerE>> + 'a>>;
|
||||||
|
|
||||||
|
|
38
src/dispatching/handler_ctx.rs
Normal file
38
src/dispatching/handler_ctx.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use crate::{
|
||||||
|
dispatching::session::GetChatId,
|
||||||
|
requests::{Request, ResponseResult},
|
||||||
|
types::Message,
|
||||||
|
Bot,
|
||||||
|
};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
/// A dispatcher's handler's context of a bot and an update.
|
||||||
|
///
|
||||||
|
/// See [the module-level documentation for the design
|
||||||
|
/// overview](teloxide::dispatching).
|
||||||
|
pub struct HandlerCtx<Upd> {
|
||||||
|
pub bot: Arc<Bot>,
|
||||||
|
pub update: Upd,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Upd> GetChatId for HandlerCtx<Upd>
|
||||||
|
where
|
||||||
|
Upd: GetChatId,
|
||||||
|
{
|
||||||
|
fn chat_id(&self) -> i64 {
|
||||||
|
self.update.chat_id()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HandlerCtx<Message> {
|
||||||
|
pub async fn reply<T>(&self, text: T) -> ResponseResult<()>
|
||||||
|
where
|
||||||
|
T: Into<String>,
|
||||||
|
{
|
||||||
|
self.bot
|
||||||
|
.send_message(self.chat_id(), text)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
//! Update dispatching.
|
//! Update dispatching.
|
||||||
//!
|
//!
|
||||||
//!
|
|
||||||
|
|
||||||
mod async_handler;
|
mod async_handler;
|
||||||
mod dispatcher;
|
mod dispatcher;
|
||||||
pub mod error_handlers;
|
pub mod error_handlers;
|
||||||
|
mod handler_ctx;
|
||||||
pub mod session;
|
pub mod session;
|
||||||
pub mod update_listeners;
|
pub mod update_listeners;
|
||||||
|
|
||||||
pub use async_handler::*;
|
pub use async_handler::AsyncHandler;
|
||||||
pub use dispatcher::*;
|
pub use dispatcher::Dispatcher;
|
||||||
|
pub use handler_ctx::HandlerCtx;
|
||||||
|
|
|
@ -30,136 +30,13 @@
|
||||||
// TODO: examples
|
// TODO: examples
|
||||||
|
|
||||||
mod get_chat_id;
|
mod get_chat_id;
|
||||||
|
mod session_dispatcher;
|
||||||
|
mod session_handler_ctx;
|
||||||
|
mod session_state;
|
||||||
mod storage;
|
mod storage;
|
||||||
|
|
||||||
use crate::{
|
pub use get_chat_id::GetChatId;
|
||||||
dispatching::{AsyncHandler, HandlerCtx},
|
pub use session_dispatcher::SessionDispatcher;
|
||||||
requests::{Request, ResponseResult},
|
pub use session_handler_ctx::SessionHandlerCtx;
|
||||||
types::Message,
|
pub use session_state::SessionState;
|
||||||
Bot,
|
pub use storage::{InMemStorage, Storage};
|
||||||
};
|
|
||||||
pub use get_chat_id::*;
|
|
||||||
use std::{future::Future, pin::Pin, sync::Arc};
|
|
||||||
pub use storage::*;
|
|
||||||
|
|
||||||
/// A context of a private message handler.
|
|
||||||
pub struct SessionHandlerCtx<Upd, Session> {
|
|
||||||
pub bot: Arc<Bot>,
|
|
||||||
pub update: Upd,
|
|
||||||
pub session: Session,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Session> SessionHandlerCtx<Message, Session> {
|
|
||||||
pub fn chat_id(&self) -> i64 {
|
|
||||||
self.update.chat_id()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn reply<T>(&self, text: T) -> ResponseResult<()>
|
|
||||||
where
|
|
||||||
T: Into<String>,
|
|
||||||
{
|
|
||||||
self.bot
|
|
||||||
.send_message(self.chat_id(), text)
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.map(|_| ())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Continue or terminate a user session.
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
|
|
||||||
pub enum SessionState<Session> {
|
|
||||||
Continue(Session),
|
|
||||||
Terminate,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A dispatcher of user sessions.
|
|
||||||
///
|
|
||||||
/// Note that `SessionDispatcher` implements `AsyncHandler`, so you can just put
|
|
||||||
/// an instance of this dispatcher into the [`Dispatcher`]'s methods.
|
|
||||||
///
|
|
||||||
/// [`Dispatcher`]: crate::dispatching::Dispatcher
|
|
||||||
pub struct SessionDispatcher<'a, Session, H> {
|
|
||||||
storage: Box<dyn Storage<Session> + 'a>,
|
|
||||||
handler: H,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Session, H> SessionDispatcher<'a, Session, H>
|
|
||||||
where
|
|
||||||
Session: Default + 'a,
|
|
||||||
{
|
|
||||||
/// Creates a dispatcher with the specified `handler` and [`InMemStorage`]
|
|
||||||
/// (a default storage).
|
|
||||||
///
|
|
||||||
/// [`InMemStorage`]: crate::dispatching::session::InMemStorage
|
|
||||||
#[must_use]
|
|
||||||
pub fn new(handler: H) -> Self {
|
|
||||||
Self {
|
|
||||||
storage: Box::new(InMemStorage::default()),
|
|
||||||
handler,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a dispatcher with the specified `handler` and `storage`.
|
|
||||||
#[must_use]
|
|
||||||
pub fn with_storage<Stg>(handler: H, storage: Stg) -> Self
|
|
||||||
where
|
|
||||||
Stg: Storage<Session> + 'a,
|
|
||||||
{
|
|
||||||
Self {
|
|
||||||
storage: Box::new(storage),
|
|
||||||
handler,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Session, H, Upd> AsyncHandler<HandlerCtx<Upd>, Result<(), ()>>
|
|
||||||
for SessionDispatcher<'a, Session, H>
|
|
||||||
where
|
|
||||||
H: AsyncHandler<SessionHandlerCtx<Upd, Session>, SessionState<Session>>,
|
|
||||||
Upd: GetChatId,
|
|
||||||
Session: Default,
|
|
||||||
{
|
|
||||||
/// Dispatches a single `message` from a private chat.
|
|
||||||
fn handle<'b>(
|
|
||||||
&'b self,
|
|
||||||
ctx: HandlerCtx<Upd>,
|
|
||||||
) -> Pin<Box<dyn Future<Output = Result<(), ()>> + 'b>>
|
|
||||||
where
|
|
||||||
Upd: 'b,
|
|
||||||
{
|
|
||||||
Box::pin(async move {
|
|
||||||
let chat_id = ctx.update.chat_id();
|
|
||||||
|
|
||||||
let session = self
|
|
||||||
.storage
|
|
||||||
.remove_session(chat_id)
|
|
||||||
.await
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
if let SessionState::Continue(new_session) = self
|
|
||||||
.handler
|
|
||||||
.handle(SessionHandlerCtx {
|
|
||||||
bot: ctx.bot,
|
|
||||||
update: ctx.update,
|
|
||||||
session,
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
if self
|
|
||||||
.storage
|
|
||||||
.update_session(chat_id, new_session)
|
|
||||||
.await
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
panic!(
|
|
||||||
"We previously storage.remove_session() so \
|
|
||||||
storage.update_session() must return None"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
98
src/dispatching/session/session_dispatcher.rs
Normal file
98
src/dispatching/session/session_dispatcher.rs
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
use crate::dispatching::{
|
||||||
|
session::{
|
||||||
|
GetChatId, InMemStorage, SessionHandlerCtx, SessionState, Storage,
|
||||||
|
},
|
||||||
|
AsyncHandler, HandlerCtx,
|
||||||
|
};
|
||||||
|
use std::{future::Future, pin::Pin};
|
||||||
|
|
||||||
|
/// A dispatcher of user sessions.
|
||||||
|
///
|
||||||
|
/// Note that `SessionDispatcher` implements `AsyncHandler`, so you can just put
|
||||||
|
/// an instance of this dispatcher into the [`Dispatcher`]'s methods.
|
||||||
|
///
|
||||||
|
/// [`Dispatcher`]: crate::dispatching::Dispatcher
|
||||||
|
pub struct SessionDispatcher<'a, Session, H> {
|
||||||
|
storage: Box<dyn Storage<Session> + 'a>,
|
||||||
|
handler: H,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Session, H> SessionDispatcher<'a, Session, H>
|
||||||
|
where
|
||||||
|
Session: Default + 'a,
|
||||||
|
{
|
||||||
|
/// Creates a dispatcher with the specified `handler` and [`InMemStorage`]
|
||||||
|
/// (a default storage).
|
||||||
|
///
|
||||||
|
/// [`InMemStorage`]: crate::dispatching::session::InMemStorage
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(handler: H) -> Self {
|
||||||
|
Self {
|
||||||
|
storage: Box::new(InMemStorage::default()),
|
||||||
|
handler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a dispatcher with the specified `handler` and `storage`.
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_storage<Stg>(handler: H, storage: Stg) -> Self
|
||||||
|
where
|
||||||
|
Stg: Storage<Session> + 'a,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
storage: Box::new(storage),
|
||||||
|
handler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Session, H, Upd> AsyncHandler<HandlerCtx<Upd>, Result<(), ()>>
|
||||||
|
for SessionDispatcher<'a, Session, H>
|
||||||
|
where
|
||||||
|
H: AsyncHandler<SessionHandlerCtx<Upd, Session>, SessionState<Session>>,
|
||||||
|
Upd: GetChatId,
|
||||||
|
Session: Default,
|
||||||
|
{
|
||||||
|
/// Dispatches a single `message` from a private chat.
|
||||||
|
fn handle<'b>(
|
||||||
|
&'b self,
|
||||||
|
ctx: HandlerCtx<Upd>,
|
||||||
|
) -> Pin<Box<dyn Future<Output = Result<(), ()>> + 'b>>
|
||||||
|
where
|
||||||
|
Upd: 'b,
|
||||||
|
{
|
||||||
|
Box::pin(async move {
|
||||||
|
let chat_id = ctx.update.chat_id();
|
||||||
|
|
||||||
|
let session = self
|
||||||
|
.storage
|
||||||
|
.remove_session(chat_id)
|
||||||
|
.await
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
if let SessionState::Next(new_session) = self
|
||||||
|
.handler
|
||||||
|
.handle(SessionHandlerCtx {
|
||||||
|
bot: ctx.bot,
|
||||||
|
update: ctx.update,
|
||||||
|
session,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
if self
|
||||||
|
.storage
|
||||||
|
.update_session(chat_id, new_session)
|
||||||
|
.await
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
panic!(
|
||||||
|
"We previously storage.remove_session() so \
|
||||||
|
storage.update_session() must return None"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
38
src/dispatching/session/session_handler_ctx.rs
Normal file
38
src/dispatching/session/session_handler_ctx.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use crate::{
|
||||||
|
dispatching::session::GetChatId,
|
||||||
|
requests::{Request, ResponseResult},
|
||||||
|
types::Message,
|
||||||
|
Bot,
|
||||||
|
};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
/// A context of a [`SessionDispatcher`]'s message handler.
|
||||||
|
///
|
||||||
|
/// [`SessionDispatcher`]: crate::dispatching::session::SessionDispatcher
|
||||||
|
pub struct SessionHandlerCtx<Upd, Session> {
|
||||||
|
pub bot: Arc<Bot>,
|
||||||
|
pub update: Upd,
|
||||||
|
pub session: Session,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Upd, Session> GetChatId for SessionHandlerCtx<Upd, Session>
|
||||||
|
where
|
||||||
|
Upd: GetChatId,
|
||||||
|
{
|
||||||
|
fn chat_id(&self) -> i64 {
|
||||||
|
self.update.chat_id()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Session> SessionHandlerCtx<Message, Session> {
|
||||||
|
pub async fn reply<T>(&self, text: T) -> ResponseResult<()>
|
||||||
|
where
|
||||||
|
T: Into<String>,
|
||||||
|
{
|
||||||
|
self.bot
|
||||||
|
.send_message(self.chat_id(), text)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
}
|
6
src/dispatching/session/session_state.rs
Normal file
6
src/dispatching/session/session_state.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
/// Continue or terminate a user session.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
|
||||||
|
pub enum SessionState<Session> {
|
||||||
|
Next(Session),
|
||||||
|
Exit,
|
||||||
|
}
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
dispatching::{
|
dispatching::{
|
||||||
session::{SessionDispatcher, SessionHandlerCtx, SessionState},
|
session::{
|
||||||
|
GetChatId, SessionDispatcher, SessionHandlerCtx, SessionState,
|
||||||
|
},
|
||||||
Dispatcher, HandlerCtx,
|
Dispatcher, HandlerCtx,
|
||||||
},
|
},
|
||||||
requests::{Request, ResponseResult},
|
requests::{Request, ResponseResult},
|
||||||
|
|
Loading…
Reference in a new issue