mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-23 06:51:01 +01:00
Merge branch 'dev' of https://github.com/teloxide/teloxide into useful_functions
This commit is contained in:
commit
50c29227ce
16 changed files with 439 additions and 609 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,4 +1,6 @@
|
||||||
/target
|
/target
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
.idea/
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
examples/target
|
||||||
|
|
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),
|
||||||
|
}
|
106
src/dispatching/chat/dispatcher.rs
Normal file
106
src/dispatching/chat/dispatcher.rs
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
use super::{
|
||||||
|
super::DispatchResult,
|
||||||
|
storage::{InMemStorage, Storage},
|
||||||
|
};
|
||||||
|
use crate::{
|
||||||
|
dispatching::{
|
||||||
|
chat::{ChatUpdate, ChatUpdateKind},
|
||||||
|
Handler, SessionState,
|
||||||
|
},
|
||||||
|
types::{Update, UpdateKind},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A dispatcher that dispatches updates from chats.
|
||||||
|
pub struct Dispatcher<'a, Session, H> {
|
||||||
|
storage: Box<dyn Storage<Session> + 'a>,
|
||||||
|
handler: H,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Session, H> Dispatcher<'a, Session, H>
|
||||||
|
where
|
||||||
|
Session: Default + 'a,
|
||||||
|
H: Handler<Session, ChatUpdate>,
|
||||||
|
{
|
||||||
|
/// Creates a dispatcher with the specified `handler` and [`InMemStorage`]
|
||||||
|
/// (a default storage).
|
||||||
|
///
|
||||||
|
/// [`InMemStorage`]: crate::dispatching::private::InMemStorage
|
||||||
|
pub fn new(handler: H) -> Self {
|
||||||
|
Self {
|
||||||
|
storage: Box::new(InMemStorage::default()),
|
||||||
|
handler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a dispatcher with the specified `handler` and `storage`.
|
||||||
|
pub fn with_storage<Stg>(handler: H, storage: Stg) -> Self
|
||||||
|
where
|
||||||
|
Stg: Storage<Session> + 'a,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
storage: Box::new(storage),
|
||||||
|
handler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dispatches a single `update`.
|
||||||
|
///
|
||||||
|
/// ## Returns
|
||||||
|
/// Returns [`DispatchResult::Handled`] if `update` was supplied to a
|
||||||
|
/// handler, and [`DispatchResult::Unhandled`] if it was an update not
|
||||||
|
/// 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_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)
|
||||||
|
.await
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
if let SessionState::Continue(session) =
|
||||||
|
self.handler.handle(session, chat_update).await
|
||||||
|
{
|
||||||
|
if self
|
||||||
|
.storage
|
||||||
|
.update_session(chat_id, session)
|
||||||
|
.await
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
panic!(
|
||||||
|
"We previously storage.remove_session() so \
|
||||||
|
storage.update_session() must return None"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchResult::Handled
|
||||||
|
}
|
||||||
|
}
|
41
src/dispatching/chat/mod.rs
Normal file
41
src/dispatching/chat/mod.rs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
//! Dispatching updates from chats.
|
||||||
|
//!
|
||||||
|
//! There are four main components:
|
||||||
|
//!
|
||||||
|
//! 1. Your session type `Session`, which designates a dialogue state at the
|
||||||
|
//! current moment.
|
||||||
|
//! 2. [`Storage`] that encapsulates all the sessions.
|
||||||
|
//! 3. Your handler of type `H: async Fn(Session, Update) ->
|
||||||
|
//! HandleResult<Session>` that receives an update and turns your session into
|
||||||
|
//! the next state.
|
||||||
|
//! 4. [`Dispatcher`], which encapsulates your handler and [`Storage`], and has
|
||||||
|
//! the [`dispatch(Update) -> DispatchResult`] function.
|
||||||
|
//!
|
||||||
|
//! Every time you call [`.dispatch(update)`] on your dispatcher, the following
|
||||||
|
//! steps are executed:
|
||||||
|
//!
|
||||||
|
//! 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
|
||||||
|
//! session from a storage, otherwise force the storage to update the session.
|
||||||
|
//!
|
||||||
|
//! [`Storage`]: crate::dispatching::private::Storage
|
||||||
|
//! [`Dispatcher`]: crate::dispatching::private::Dispatcher
|
||||||
|
//! [`dispatch(Update) -> DispatchResult`]:
|
||||||
|
//! crate::dispatching::private::Dispatcher::dispatch
|
||||||
|
//! [`.dispatch(update)`]: crate::dispatching::private::Dispatcher::dispatch
|
||||||
|
//! [`DispatchResult::Unhandled`]: crate::dispatching::DispatchResult::Unhandled
|
||||||
|
//! [`SessionState::Terminate`]: crate::dispatching::SessionState::Terminate
|
||||||
|
|
||||||
|
// TODO: examples
|
||||||
|
|
||||||
|
mod chat_update;
|
||||||
|
mod dispatcher;
|
||||||
|
mod storage;
|
||||||
|
|
||||||
|
pub use chat_update::*;
|
||||||
|
pub use dispatcher::*;
|
||||||
|
pub use storage::*;
|
32
src/dispatching/chat/storage/in_mem_storage.rs
Normal file
32
src/dispatching/chat/storage/in_mem_storage.rs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
use super::Storage;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
/// A memory storage based on a hash map. Stores all the sessions directly in
|
||||||
|
/// RAM.
|
||||||
|
///
|
||||||
|
/// ## Note
|
||||||
|
/// All the sessions will be lost after you restart your bot. If you need to
|
||||||
|
/// store them somewhere on a drive, you need to implement a storage
|
||||||
|
/// communicating with a DB.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Default)]
|
||||||
|
pub struct InMemStorage<Session> {
|
||||||
|
map: HashMap<i64, Session>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
#[async_trait]
|
||||||
|
impl<Session> Storage<Session> for InMemStorage<Session> {
|
||||||
|
async fn remove_session(&mut self, chat_id: i64) -> Option<Session> {
|
||||||
|
self.map.remove(&chat_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_session(
|
||||||
|
&mut self,
|
||||||
|
chat_id: i64,
|
||||||
|
state: Session,
|
||||||
|
) -> Option<Session> {
|
||||||
|
self.map.insert(chat_id, state)
|
||||||
|
}
|
||||||
|
}
|
32
src/dispatching/chat/storage/mod.rs
Normal file
32
src/dispatching/chat/storage/mod.rs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
mod in_mem_storage;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
pub use in_mem_storage::InMemStorage;
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// For a storage based on a simple hash map, see [`InMemStorage`].
|
||||||
|
///
|
||||||
|
/// [`InMemStorage`]: crate::dispatching::private::InMemStorage
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
#[async_trait]
|
||||||
|
pub trait Storage<Session> {
|
||||||
|
/// Removes a session with the specified `chat_id`.
|
||||||
|
///
|
||||||
|
/// Returns `None` if there wasn't such a session, `Some(session)` if a
|
||||||
|
/// `session` was deleted.
|
||||||
|
async fn remove_session(&mut self, chat_id: i64) -> Option<Session>;
|
||||||
|
|
||||||
|
/// Updates a session with the specified `chat_id`.
|
||||||
|
///
|
||||||
|
/// Returns `None` if there wasn't such a session, `Some(session)` if a
|
||||||
|
/// `session` was updated.
|
||||||
|
async fn update_session(
|
||||||
|
&mut self,
|
||||||
|
chat_id: i64,
|
||||||
|
session: Session,
|
||||||
|
) -> Option<Session>;
|
||||||
|
}
|
|
@ -1,348 +0,0 @@
|
||||||
//! A dispatcher based on filters.
|
|
||||||
|
|
||||||
use futures::StreamExt;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
dispatching::{filters::Filter, ErrorHandler, Handler, Updater},
|
|
||||||
types::{
|
|
||||||
CallbackQuery, ChosenInlineResult, InlineQuery, Message, Update,
|
|
||||||
UpdateKind,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use either::Either;
|
|
||||||
|
|
||||||
type FilterWithHandler<'a, T, E> =
|
|
||||||
(Box<dyn Filter<T> + 'a>, Box<dyn Handler<T, E> + 'a>);
|
|
||||||
type FiltersWithHandlers<'a, T, E> = Vec<FilterWithHandler<'a, T, E>>;
|
|
||||||
|
|
||||||
/// A dispatcher based on filters.
|
|
||||||
///
|
|
||||||
/// It consists of:
|
|
||||||
/// 1. [`ErrorHandler`] than handles errors both from [`Updater`] and
|
|
||||||
/// [`Handler`].
|
|
||||||
/// 2. Filters and handlers.
|
|
||||||
///
|
|
||||||
/// First you call [`FilterDispatcher::new(eh)`] to create this dispatcher. `eh`
|
|
||||||
/// stands for **E**rror **H**andler, you can simply provide a closure that
|
|
||||||
/// takes [`Either<UpdaterE, HandlerE>`]:
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use either::Either;
|
|
||||||
/// use std::convert::Infallible;
|
|
||||||
/// use teloxide::{dispatching::FilterDispatcher, RequestError};
|
|
||||||
///
|
|
||||||
/// let _ = FilterDispatcher::new(|err: Either<RequestError, Infallible>| {
|
|
||||||
/// async {
|
|
||||||
/// dbg!(err);
|
|
||||||
/// }
|
|
||||||
/// });
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Or you can do it even simpler by providing the built-in error handler
|
|
||||||
/// [`Print`]:
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use std::convert::Infallible;
|
|
||||||
/// use teloxide::{
|
|
||||||
/// dispatching::{error_handlers::Print, FilterDispatcher},
|
|
||||||
/// RequestError,
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// let _ = FilterDispatcher::<'_, Infallible, _>::new::<RequestError>(Print);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// And then you register filters and handlers using the methods defined below,
|
|
||||||
/// and then you call [`.dispatch(updater)`]. Filters and handlers are executed
|
|
||||||
/// in order of registering. The following flowchart represents how this
|
|
||||||
/// dispatcher acts:
|
|
||||||
///
|
|
||||||
/// <div align="center">
|
|
||||||
/// <img src="https://raw.githubusercontent.com/teloxide/teloxide/dev/media/FILTER_DP_FLOWCHART.png" />
|
|
||||||
/// </div>
|
|
||||||
///
|
|
||||||
/// ## Examples
|
|
||||||
///
|
|
||||||
/// The simplest example:
|
|
||||||
/// ```no_run
|
|
||||||
/// # async fn run() {
|
|
||||||
/// use std::convert::Infallible;
|
|
||||||
///
|
|
||||||
/// use teloxide::{
|
|
||||||
/// dispatching::{updaters::polling_default, FilterDispatcher},
|
|
||||||
/// types::Message,
|
|
||||||
/// Bot,
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// async fn handle_edited_message(mes: Message) -> Result<(), Infallible> {
|
|
||||||
/// println!("Edited message: {:?}", mes);
|
|
||||||
/// Ok(())
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// let bot = Bot::new("TOKEN");
|
|
||||||
///
|
|
||||||
/// // Create a dispatcher which handlers can't fail with the
|
|
||||||
/// // error handler that just ignores all errors (that can't ever happen).
|
|
||||||
/// let mut dp = FilterDispatcher::<Infallible, _>::new(|_| async {})
|
|
||||||
/// // Add a handler, which handles all messages sent to the bot.
|
|
||||||
/// .message_handler(true, |mes: Message| {
|
|
||||||
/// async move {
|
|
||||||
/// println!("New message: {:?}", mes);
|
|
||||||
/// Ok(())
|
|
||||||
/// }
|
|
||||||
/// })
|
|
||||||
/// // Add a handler, which handles all messages edited in a chat
|
|
||||||
/// // with the bot.
|
|
||||||
/// .edited_message_handler(true, handle_edited_message);
|
|
||||||
///
|
|
||||||
/// // Start dispatching updates using long polling.
|
|
||||||
/// dp.dispatch(polling_default(&bot)).await;
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// [`std::fmt::Debug`]: std::fmt::Debug
|
|
||||||
/// [`.dispatch(updater)`]: FilterDispatcher::dispatch
|
|
||||||
/// [`ErrorHandler`]: crate::dispatching::error_handlers::ErrorHandler
|
|
||||||
/// [`Updater`]: crate::dispatching::updaters::Updater
|
|
||||||
/// [`Handler`]: crate::dispatching::Handler
|
|
||||||
/// [`FilterDispatcher::new(eh)`]: FilterDispatcher::new
|
|
||||||
/// [`Either<UpdaterE, HandlerE>`]: either::Either
|
|
||||||
/// [`Print`]: crate::dispatching::error_handlers::Print
|
|
||||||
pub struct FilterDispatcher<'a, HandlerE> {
|
|
||||||
message_handlers: FiltersWithHandlers<'a, Message, HandlerE>,
|
|
||||||
edited_message_handlers: FiltersWithHandlers<'a, Message, HandlerE>,
|
|
||||||
channel_post_handlers: FiltersWithHandlers<'a, Message, HandlerE>,
|
|
||||||
edited_channel_post_handlers: FiltersWithHandlers<'a, Message, HandlerE>,
|
|
||||||
inline_query_handlers: FiltersWithHandlers<'a, InlineQuery, HandlerE>,
|
|
||||||
chosen_inline_result_handlers:
|
|
||||||
FiltersWithHandlers<'a, ChosenInlineResult, HandlerE>,
|
|
||||||
callback_query_handlers: FiltersWithHandlers<'a, CallbackQuery, HandlerE>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, HandlerE> FilterDispatcher<'a, HandlerE> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
FilterDispatcher {
|
|
||||||
message_handlers: Vec::new(),
|
|
||||||
edited_message_handlers: Vec::new(),
|
|
||||||
channel_post_handlers: Vec::new(),
|
|
||||||
edited_channel_post_handlers: Vec::new(),
|
|
||||||
inline_query_handlers: Vec::new(),
|
|
||||||
chosen_inline_result_handlers: Vec::new(),
|
|
||||||
callback_query_handlers: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn message_handler<F, H>(mut self, filter: F, handler: H) -> Self
|
|
||||||
where
|
|
||||||
F: Filter<Message> + 'a,
|
|
||||||
H: Handler<Message, HandlerE> + 'a,
|
|
||||||
{
|
|
||||||
self.message_handlers
|
|
||||||
.push((Box::new(filter), Box::new(handler)));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn edited_message_handler<F, H>(mut self, filter: F, handler: H) -> Self
|
|
||||||
where
|
|
||||||
F: Filter<Message> + 'a,
|
|
||||||
H: Handler<Message, HandlerE> + 'a,
|
|
||||||
{
|
|
||||||
self.edited_message_handlers
|
|
||||||
.push((Box::new(filter), Box::new(handler)));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn channel_post_handler<F, H>(mut self, filter: F, handler: H) -> Self
|
|
||||||
where
|
|
||||||
F: Filter<Message> + 'a,
|
|
||||||
H: Handler<Message, HandlerE> + 'a,
|
|
||||||
{
|
|
||||||
self.channel_post_handlers
|
|
||||||
.push((Box::new(filter), Box::new(handler)));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn edited_channel_post_handler<F, H>(
|
|
||||||
mut self,
|
|
||||||
filter: F,
|
|
||||||
handler: H,
|
|
||||||
) -> Self
|
|
||||||
where
|
|
||||||
F: Filter<Message> + 'a,
|
|
||||||
H: Handler<Message, HandlerE> + 'a,
|
|
||||||
{
|
|
||||||
self.edited_channel_post_handlers
|
|
||||||
.push((Box::new(filter), Box::new(handler)));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inline_query_handler<F, H>(mut self, filter: F, handler: H) -> Self
|
|
||||||
where
|
|
||||||
F: Filter<InlineQuery> + 'a,
|
|
||||||
H: Handler<InlineQuery, HandlerE> + 'a,
|
|
||||||
{
|
|
||||||
self.inline_query_handlers
|
|
||||||
.push((Box::new(filter), Box::new(handler)));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn chosen_inline_result_handler<F, H>(
|
|
||||||
mut self,
|
|
||||||
filter: F,
|
|
||||||
handler: H,
|
|
||||||
) -> Self
|
|
||||||
where
|
|
||||||
F: Filter<ChosenInlineResult> + 'a,
|
|
||||||
H: Handler<ChosenInlineResult, HandlerE> + 'a,
|
|
||||||
{
|
|
||||||
self.chosen_inline_result_handlers
|
|
||||||
.push((Box::new(filter), Box::new(handler)));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn callback_query_handler<F, H>(mut self, filter: F, handler: H) -> Self
|
|
||||||
where
|
|
||||||
F: Filter<CallbackQuery> + 'a,
|
|
||||||
H: Handler<CallbackQuery, HandlerE> + 'a,
|
|
||||||
{
|
|
||||||
self.callback_query_handlers
|
|
||||||
.push((Box::new(filter), Box::new(handler)));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn dispatch<U>(
|
|
||||||
&mut self,
|
|
||||||
update: Update,
|
|
||||||
) -> Result<(), HandlerE> {
|
|
||||||
let Update { kind, id } = update;
|
|
||||||
|
|
||||||
let res = match kind.clone() {
|
|
||||||
UpdateKind::Message(mes) => {
|
|
||||||
Self::handle(mes, &self.message_handlers).await
|
|
||||||
}
|
|
||||||
UpdateKind::EditedMessage(mes) => {
|
|
||||||
Self::handle(mes, &self.edited_message_handlers).await
|
|
||||||
}
|
|
||||||
UpdateKind::ChannelPost(post) => {
|
|
||||||
Self::handle(post, &self.channel_post_handlers).await
|
|
||||||
}
|
|
||||||
UpdateKind::EditedChannelPost(post) => {
|
|
||||||
Self::handle(post, &self.edited_channel_post_handlers).await
|
|
||||||
}
|
|
||||||
UpdateKind::InlineQuery(query) => {
|
|
||||||
Self::handle(query, &self.inline_query_handlers).await
|
|
||||||
}
|
|
||||||
UpdateKind::ChosenInlineResult(result) => {
|
|
||||||
Self::handle(result, &self.chosen_inline_result_handlers).await
|
|
||||||
}
|
|
||||||
UpdateKind::CallbackQuery(callback) => {
|
|
||||||
Self::handle(callback, &self.callback_query_handlers).await
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
log::debug!("dispatched update#{id:?}: {kind:?}", id = id, kind = kind);
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle<T>(
|
|
||||||
update: T,
|
|
||||||
handlers: &[FilterWithHandler<'a, T, HandlerE>],
|
|
||||||
) -> Result<(), HandlerE>
|
|
||||||
where
|
|
||||||
T: std::fmt::Debug,
|
|
||||||
{
|
|
||||||
for x in handlers {
|
|
||||||
if x.0.test(&update) {
|
|
||||||
return x.1.handle(update).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log::warn!("unhandled update {:?}", update);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::{
|
|
||||||
convert::Infallible,
|
|
||||||
sync::atomic::{AtomicI32, Ordering},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
dispatching::{FilterDispatcher, Updater},
|
|
||||||
types::{
|
|
||||||
Chat, ChatKind, ForwardKind, MediaKind, Message, MessageKind,
|
|
||||||
Sender, Update, UpdateKind, User,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn first_handler_executes_1_time() {
|
|
||||||
let counter = &AtomicI32::new(0);
|
|
||||||
let counter2 = &AtomicI32::new(0);
|
|
||||||
|
|
||||||
let mut dp = FilterDispatcher::<Infallible, _>::new(|_| async {})
|
|
||||||
.message_handler(true, |_mes: Message| async move {
|
|
||||||
counter.fetch_add(1, Ordering::SeqCst);
|
|
||||||
Ok::<_, Infallible>(())
|
|
||||||
})
|
|
||||||
.message_handler(true, |_mes: Message| async move {
|
|
||||||
counter2.fetch_add(1, Ordering::SeqCst);
|
|
||||||
Ok::<_, Infallible>(())
|
|
||||||
});
|
|
||||||
|
|
||||||
dp.dispatch(one_message_updater()).await;
|
|
||||||
|
|
||||||
assert_eq!(counter.load(Ordering::SeqCst), 1);
|
|
||||||
assert_eq!(counter2.load(Ordering::SeqCst), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn message() -> Message {
|
|
||||||
Message {
|
|
||||||
id: 6534,
|
|
||||||
date: 1_567_898_953,
|
|
||||||
chat: Chat {
|
|
||||||
id: 218_485_655,
|
|
||||||
photo: None,
|
|
||||||
kind: ChatKind::Private {
|
|
||||||
type_: (),
|
|
||||||
first_name: Some("W".to_string()),
|
|
||||||
last_name: None,
|
|
||||||
username: Some("WaffleLapkin".to_string()),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
kind: MessageKind::Common {
|
|
||||||
from: Sender::User(User {
|
|
||||||
id: 457_569_668,
|
|
||||||
is_bot: true,
|
|
||||||
first_name: "BT".to_string(),
|
|
||||||
last_name: None,
|
|
||||||
username: Some("BloodyTestBot".to_string()),
|
|
||||||
language_code: None,
|
|
||||||
}),
|
|
||||||
forward_kind: ForwardKind::Origin {
|
|
||||||
reply_to_message: None,
|
|
||||||
},
|
|
||||||
edit_date: None,
|
|
||||||
media_kind: MediaKind::Text {
|
|
||||||
text: "text".to_string(),
|
|
||||||
entities: vec![],
|
|
||||||
},
|
|
||||||
reply_markup: None,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn message_update() -> Update {
|
|
||||||
Update {
|
|
||||||
id: 0,
|
|
||||||
kind: UpdateKind::Message(message()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn one_message_updater() -> impl Updater<Infallible> {
|
|
||||||
use futures::{future::ready, stream};
|
|
||||||
|
|
||||||
stream::once(ready(Ok(message_update())))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
pub mod filter;
|
|
|
@ -1,149 +0,0 @@
|
||||||
//! Error handlers.
|
|
||||||
|
|
||||||
// Infallible used here instead of `!` to be compatible with rust <1.41.
|
|
||||||
use std::{convert::Infallible, fmt::Debug, future::Future, pin::Pin};
|
|
||||||
|
|
||||||
/// An asynchronous handler of an error.
|
|
||||||
pub trait ErrorHandler<E> {
|
|
||||||
#[must_use]
|
|
||||||
fn handle_error<'a>(
|
|
||||||
&'a self,
|
|
||||||
error: E,
|
|
||||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>>
|
|
||||||
where
|
|
||||||
E: 'a;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A handler that silently ignores all errors.
|
|
||||||
///
|
|
||||||
/// ## Example
|
|
||||||
/// ```
|
|
||||||
/// # #[tokio::main]
|
|
||||||
/// # async fn main_() {
|
|
||||||
/// use teloxide::dispatching::error_handlers::{ErrorHandler, Ignore};
|
|
||||||
///
|
|
||||||
/// Ignore.handle_error(()).await;
|
|
||||||
/// Ignore.handle_error(404).await;
|
|
||||||
/// Ignore.handle_error(String::from("error")).await;
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
pub struct Ignore;
|
|
||||||
|
|
||||||
impl<E> ErrorHandler<E> for Ignore {
|
|
||||||
#[must_use]
|
|
||||||
fn handle_error<'a>(
|
|
||||||
&'a self,
|
|
||||||
_: E,
|
|
||||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>>
|
|
||||||
where
|
|
||||||
E: 'a,
|
|
||||||
{
|
|
||||||
Box::pin(async {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An error handler that silently ignores all errors that can never happen
|
|
||||||
/// (e.g.: [`!`] or [`Infallible`]).
|
|
||||||
///
|
|
||||||
/// ## Examples
|
|
||||||
/// ```
|
|
||||||
/// # #[tokio::main]
|
|
||||||
/// # async fn main_() {
|
|
||||||
/// use std::convert::{Infallible, TryInto};
|
|
||||||
///
|
|
||||||
/// use teloxide::dispatching::error_handlers::{ErrorHandler, IgnoreSafe};
|
|
||||||
///
|
|
||||||
/// let result: Result<String, Infallible> = "str".try_into();
|
|
||||||
/// match result {
|
|
||||||
/// Ok(string) => println!("{}", string),
|
|
||||||
/// Err(inf) => IgnoreSafe.handle_error(inf).await,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// IgnoreSafe.handle_error(return).await; // return type of `return` is `!` (aka never)
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ```compile_fail
|
|
||||||
/// use teloxide::dispatching::dispatchers::filter::error_policy::{
|
|
||||||
/// ErrorPolicy, IgnoreSafe,
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// IgnoreSafe.handle_error(0);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// [`!`]: https://doc.rust-lang.org/std/primitive.never.html
|
|
||||||
/// [`Infallible`]: std::convert::Infallible
|
|
||||||
pub struct IgnoreSafe;
|
|
||||||
|
|
||||||
#[allow(unreachable_code)]
|
|
||||||
impl ErrorHandler<Infallible> for IgnoreSafe {
|
|
||||||
fn handle_error<'a>(
|
|
||||||
&'a self,
|
|
||||||
_: Infallible,
|
|
||||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>>
|
|
||||||
where
|
|
||||||
Infallible: 'a,
|
|
||||||
{
|
|
||||||
Box::pin(async {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An error handler that prints all errors passed into it.
|
|
||||||
///
|
|
||||||
/// ## Example
|
|
||||||
/// ```
|
|
||||||
/// # #[tokio::main]
|
|
||||||
/// # async fn main_() {
|
|
||||||
/// use teloxide::dispatching::error_handlers::{ErrorHandler, Print};
|
|
||||||
///
|
|
||||||
/// Print.handle_error(()).await;
|
|
||||||
/// Print.handle_error(404).await;
|
|
||||||
/// Print.handle_error(String::from("error")).await;
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
pub struct Print;
|
|
||||||
|
|
||||||
impl<E> ErrorHandler<E> for Print
|
|
||||||
where
|
|
||||||
E: Debug,
|
|
||||||
{
|
|
||||||
fn handle_error<'a>(
|
|
||||||
&'a self,
|
|
||||||
error: E,
|
|
||||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>>
|
|
||||||
where
|
|
||||||
E: 'a,
|
|
||||||
{
|
|
||||||
log::debug!("error: {:?}", error);
|
|
||||||
Box::pin(async {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The implementation of `ErrorHandler` for `Fn(error) -> Future<Output = ()>`.
|
|
||||||
///
|
|
||||||
/// ## Example
|
|
||||||
/// ```
|
|
||||||
/// # #[tokio::main]
|
|
||||||
/// # async fn main_() {
|
|
||||||
/// use teloxide::dispatching::error_handlers::ErrorHandler;
|
|
||||||
///
|
|
||||||
/// let mut closure = |e: i32| async move { eprintln!("Error code{}", e) };
|
|
||||||
///
|
|
||||||
/// closure.handle_error(404).await;
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
impl<E, F, Fut> ErrorHandler<E> for F
|
|
||||||
where
|
|
||||||
F: Fn(E) -> Fut,
|
|
||||||
Fut: Future<Output = ()>,
|
|
||||||
{
|
|
||||||
fn handle_error<'a>(
|
|
||||||
&'a self,
|
|
||||||
error: E,
|
|
||||||
) -> Pin<Box<dyn Future<Output = ()> + 'a>>
|
|
||||||
where
|
|
||||||
E: 'a,
|
|
||||||
{
|
|
||||||
Box::pin(async move { self(error).await })
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +1,49 @@
|
||||||
use std::{future::Future, pin::Pin};
|
use std::{future::Future, pin::Pin};
|
||||||
|
|
||||||
/// An asynchronous handler of a value.
|
/// Continue or terminate a user session.
|
||||||
pub trait Handler<T, E> {
|
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
|
||||||
|
pub enum SessionState<Session> {
|
||||||
|
Continue(Session),
|
||||||
|
Terminate,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A handler of a user session and an update.
|
||||||
|
///
|
||||||
|
/// ## Returns
|
||||||
|
/// Returns [`SessionState::Continue(session)`] if it wants to be called again
|
||||||
|
/// after a new update, or [`SessionState::Terminate`] if not.
|
||||||
|
///
|
||||||
|
/// [`SessionState::Continue(session)`]:
|
||||||
|
/// crate::dispatching::SessionState::Continue
|
||||||
|
/// [`SessionState::Terminate`]: crate::dispatching::SessionState::Terminate
|
||||||
|
pub trait Handler<Session, U> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn handle<'a>(
|
fn handle<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
value: T,
|
session: Session,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<(), E>> + 'a>>
|
update: U,
|
||||||
|
) -> Pin<Box<dyn Future<Output = SessionState<Session>> + 'a>>
|
||||||
where
|
where
|
||||||
T: 'a;
|
Session: 'a,
|
||||||
|
U: 'a;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The implementation of `Handler` for `Fn(U) -> Future<Output = Result<(),
|
/// The implementation of `Handler` for `Fn(Session, Update) -> Future<Output =
|
||||||
/// E>>`.
|
/// SessionState<Session>>`.
|
||||||
impl<T, E, F, Fut> Handler<T, E> for F
|
impl<Session, U, F, Fut> Handler<Session, U> for F
|
||||||
where
|
where
|
||||||
F: Fn(T) -> Fut,
|
F: Fn(Session, U) -> Fut,
|
||||||
Fut: Future<Output = Result<(), E>>,
|
Fut: Future<Output = SessionState<Session>>,
|
||||||
{
|
{
|
||||||
fn handle<'a>(
|
fn handle<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
value: T,
|
session: Session,
|
||||||
|
update: U,
|
||||||
) -> Pin<Box<dyn Future<Output = Fut::Output> + 'a>>
|
) -> Pin<Box<dyn Future<Output = Fut::Output> + 'a>>
|
||||||
where
|
where
|
||||||
T: 'a,
|
Session: 'a,
|
||||||
|
U: 'a,
|
||||||
{
|
{
|
||||||
Box::pin(async move { self(value).await })
|
Box::pin(async move { self(session, update).await })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
//! Update dispatching.
|
//! Update dispatching.
|
||||||
|
|
||||||
mod dispatchers;
|
/// If an update was handled by a dispatcher or not.
|
||||||
pub mod error_handlers;
|
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
|
||||||
|
pub enum DispatchResult {
|
||||||
|
Handled,
|
||||||
|
Unhandled,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod chat;
|
||||||
pub mod filters;
|
pub mod filters;
|
||||||
mod handler;
|
mod handler;
|
||||||
pub mod updaters;
|
pub mod update_listeners;
|
||||||
|
|
||||||
pub use dispatchers::filter::FilterDispatcher;
|
|
||||||
pub use error_handlers::ErrorHandler;
|
|
||||||
pub use filters::Filter;
|
pub use filters::Filter;
|
||||||
pub use handler::Handler;
|
pub use handler::*;
|
||||||
pub use updaters::Updater;
|
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
//! Receiving updates from Telegram.
|
//! Receiving updates from Telegram.
|
||||||
//!
|
//!
|
||||||
//! The key trait here is [`Updater`]. You can get it by these functions:
|
//! The key trait here is [`UpdateListener`]. You can get it by these functions:
|
||||||
//!
|
//!
|
||||||
//! - [`polling_default`], which returns a default long polling updater.
|
//! - [`polling_default`], which returns a default long polling listener.
|
||||||
//! - [`polling`], which returns a long/short polling updater with your
|
//! - [`polling`], which returns a long/short polling listener with your
|
||||||
//! configuration.
|
//! configuration.
|
||||||
//!
|
//!
|
||||||
//! And then you can pass it directly to a dispatcher.
|
//! And then you can extract updates from it and pass them directly to a
|
||||||
|
//! dispatcher.
|
||||||
//!
|
//!
|
||||||
//! Telegram supports two ways of [getting updates]: [long]/[short] polling and
|
//! Telegram supports two ways of [getting updates]: [long]/[short] polling and
|
||||||
//! [webhook].
|
//! [webhook].
|
||||||
|
@ -90,10 +91,9 @@
|
||||||
//! <a id="4" href="#4b">^4</a> `offset = N` means that we've already received
|
//! <a id="4" href="#4b">^4</a> `offset = N` means that we've already received
|
||||||
//! updates `0..=N`.
|
//! updates `0..=N`.
|
||||||
//!
|
//!
|
||||||
//! [`Updater`]: Updater
|
//! [`UpdateListener`]: UpdateListener
|
||||||
//! [`polling_default`]: polling_default
|
//! [`polling_default`]: polling_default
|
||||||
//! [`polling`]: polling
|
//! [`polling`]: polling
|
||||||
//! [`Dispatcher`]: crate::dispatching::Dispatcher::dispatch
|
|
||||||
//! [`Box::get_updates`]: crate::Bot::get_updates
|
//! [`Box::get_updates`]: crate::Bot::get_updates
|
||||||
//! [getting updates]: https://core.telegram.org/bots/api#getting-updates
|
//! [getting updates]: https://core.telegram.org/bots/api#getting-updates
|
||||||
//! [long]: https://en.wikipedia.org/wiki/Push_technology#Long_polling
|
//! [long]: https://en.wikipedia.org/wiki/Push_technology#Long_polling
|
||||||
|
@ -110,22 +110,22 @@ use crate::{
|
||||||
};
|
};
|
||||||
use std::{convert::TryInto, time::Duration};
|
use std::{convert::TryInto, time::Duration};
|
||||||
|
|
||||||
/// A generic updater.
|
/// A generic update listener.
|
||||||
pub trait Updater<E>: Stream<Item = Result<Update, E>> {
|
pub trait UpdateListener<E>: Stream<Item = Result<Update, E>> {
|
||||||
// TODO: add some methods here (.shutdown(), etc).
|
// TODO: add some methods here (.shutdown(), etc).
|
||||||
}
|
}
|
||||||
impl<S, E> Updater<E> for S where S: Stream<Item = Result<Update, E>> {}
|
impl<S, E> UpdateListener<E> for S where S: Stream<Item = Result<Update, E>> {}
|
||||||
|
|
||||||
/// Returns a long polling updater with the default configuration.
|
/// Returns a long polling update listener with the default configuration.
|
||||||
///
|
///
|
||||||
/// See also: [`polling`](polling).
|
/// See also: [`polling`](polling).
|
||||||
pub fn polling_default(bot: &Bot) -> impl Updater<RequestError> + '_ {
|
pub fn polling_default(bot: &Bot) -> impl UpdateListener<RequestError> + '_ {
|
||||||
polling(bot, None, None, None)
|
polling(bot, None, None, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a long/short polling updater with some additional options.
|
/// Returns a long/short polling update listener with some additional options.
|
||||||
///
|
///
|
||||||
/// - `bot`: Using this bot, the returned updater will receive updates.
|
/// - `bot`: Using this bot, the returned update listener will receive updates.
|
||||||
/// - `timeout`: A timeout for polling.
|
/// - `timeout`: A timeout for polling.
|
||||||
/// - `limit`: Limits the number of updates to be retrieved at once. Values
|
/// - `limit`: Limits the number of updates to be retrieved at once. Values
|
||||||
/// between 1—100 are accepted.
|
/// between 1—100 are accepted.
|
||||||
|
@ -140,7 +140,7 @@ pub fn polling(
|
||||||
timeout: Option<Duration>,
|
timeout: Option<Duration>,
|
||||||
limit: Option<u8>,
|
limit: Option<u8>,
|
||||||
allowed_updates: Option<Vec<AllowedUpdate>>,
|
allowed_updates: Option<Vec<AllowedUpdate>>,
|
||||||
) -> impl Updater<RequestError> + '_ {
|
) -> impl UpdateListener<RequestError> + '_ {
|
||||||
let timeout =
|
let timeout =
|
||||||
timeout.map(|t| t.as_secs().try_into().expect("timeout is too big"));
|
timeout.map(|t| t.as_secs().try_into().expect("timeout is too big"));
|
||||||
|
|
|
@ -57,7 +57,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`EditMessageText`]
|
/// 1. [`EditMessageText`]
|
||||||
///
|
///
|
||||||
/// [`EditMessageText`]: crate::requests::payloads::EditMessageText
|
/// [`EditMessageText`]: crate::requests::EditMessageText
|
||||||
#[serde(rename = "Bad Request: message is not modified: specified new \
|
#[serde(rename = "Bad Request: message is not modified: specified new \
|
||||||
message content and reply markup are exactly the same \
|
message content and reply markup are exactly the same \
|
||||||
as a current content and reply markup of the message")]
|
as a current content and reply markup of the message")]
|
||||||
|
@ -69,8 +69,8 @@ pub enum ApiErrorKind {
|
||||||
/// 1. [`ForwardMessage`]
|
/// 1. [`ForwardMessage`]
|
||||||
/// 2. [`DeleteMessage`]
|
/// 2. [`DeleteMessage`]
|
||||||
///
|
///
|
||||||
/// [`ForwardMessage`]: crate::requests::payloads::ForwardMessage
|
/// [`ForwardMessage`]: crate::requests::ForwardMessage
|
||||||
/// [`DeleteMessage`]: crate::requests::payloads::DeleteMessage
|
/// [`DeleteMessage`]: crate::requests::DeleteMessage
|
||||||
#[serde(rename = "Bad Request: MESSAGE_ID_INVALID")]
|
#[serde(rename = "Bad Request: MESSAGE_ID_INVALID")]
|
||||||
MessageIdInvalid,
|
MessageIdInvalid,
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`ForwardMessage`]
|
/// 1. [`ForwardMessage`]
|
||||||
///
|
///
|
||||||
/// [`ForwardMessage`]: crate::requests::payloads::ForwardMessage
|
/// [`ForwardMessage`]: crate::requests::ForwardMessage
|
||||||
#[serde(rename = "Bad Request: message to forward not found")]
|
#[serde(rename = "Bad Request: message to forward not found")]
|
||||||
MessageToForwardNotFound,
|
MessageToForwardNotFound,
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`DeleteMessage`]
|
/// 1. [`DeleteMessage`]
|
||||||
///
|
///
|
||||||
/// [`DeleteMessage`]: crate::requests::payloads::DeleteMessage
|
/// [`DeleteMessage`]: crate::requests::DeleteMessage
|
||||||
#[serde(rename = "Bad Request: message to delete not found")]
|
#[serde(rename = "Bad Request: message to delete not found")]
|
||||||
MessageToDeleteNotFound,
|
MessageToDeleteNotFound,
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SendMessage`]
|
/// 1. [`SendMessage`]
|
||||||
///
|
///
|
||||||
/// [`SendMessage`]: crate::requests::payloads::SendMessage
|
/// [`SendMessage`]: crate::requests::SendMessage
|
||||||
#[serde(rename = "Bad Request: message text is empty")]
|
#[serde(rename = "Bad Request: message text is empty")]
|
||||||
MessageTextIsEmpty,
|
MessageTextIsEmpty,
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`EditMessageText`]
|
/// 1. [`EditMessageText`]
|
||||||
///
|
///
|
||||||
/// [`EditMessageText`]: crate::requests::payloads::EditMessageText
|
/// [`EditMessageText`]: crate::requests::EditMessageText
|
||||||
#[serde(rename = "Bad Request: message can't be edited")]
|
#[serde(rename = "Bad Request: message can't be edited")]
|
||||||
MessageCantBeEdited,
|
MessageCantBeEdited,
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`DeleteMessage`]
|
/// 1. [`DeleteMessage`]
|
||||||
///
|
///
|
||||||
/// [`DeleteMessage`]: crate::requests::payloads::DeleteMessage
|
/// [`DeleteMessage`]: crate::requests::DeleteMessage
|
||||||
#[serde(rename = "Bad Request: message can't be deleted")]
|
#[serde(rename = "Bad Request: message can't be deleted")]
|
||||||
MessageCantBeDeleted,
|
MessageCantBeDeleted,
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`EditMessageText`]
|
/// 1. [`EditMessageText`]
|
||||||
///
|
///
|
||||||
/// [`EditMessageText`]: crate::requests::payloads::EditMessageText
|
/// [`EditMessageText`]: crate::requests::EditMessageText
|
||||||
#[serde(rename = "Bad Request: message to edit not found")]
|
#[serde(rename = "Bad Request: message to edit not found")]
|
||||||
MessageToEditNotFound,
|
MessageToEditNotFound,
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SendMessage`]
|
/// 1. [`SendMessage`]
|
||||||
///
|
///
|
||||||
/// [`SendMessage`]: crate::requests::payloads::SendMessage
|
/// [`SendMessage`]: crate::requests::SendMessage
|
||||||
#[serde(rename = "Bad Request: reply message not found")]
|
#[serde(rename = "Bad Request: reply message not found")]
|
||||||
MessageToReplyNotFound,
|
MessageToReplyNotFound,
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SendMessage`]
|
/// 1. [`SendMessage`]
|
||||||
///
|
///
|
||||||
/// [`SendMessage`]: crate::requests::payloads::SendMessage
|
/// [`SendMessage`]: crate::requests::SendMessage
|
||||||
#[serde(rename = "Bad Request: message is too long")]
|
#[serde(rename = "Bad Request: message is too long")]
|
||||||
MessageIsTooLong,
|
MessageIsTooLong,
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SendMediaGroup`]
|
/// 1. [`SendMediaGroup`]
|
||||||
///
|
///
|
||||||
/// [`SendMediaGroup`]: crate::requests::payloads::SendMediaGroup
|
/// [`SendMediaGroup`]: crate::requests::SendMediaGroup
|
||||||
#[serde(rename = "Bad Request: Too much messages to send as an album")]
|
#[serde(rename = "Bad Request: Too much messages to send as an album")]
|
||||||
ToMuchMessages,
|
ToMuchMessages,
|
||||||
|
|
||||||
|
@ -166,7 +166,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SendPoll`]
|
/// 1. [`SendPoll`]
|
||||||
///
|
///
|
||||||
/// [`SendPoll`]: crate::requests::payloads::SendPoll
|
/// [`SendPoll`]: crate::requests::SendPoll
|
||||||
#[serde(rename = "Bad Request: poll has already been closed")]
|
#[serde(rename = "Bad Request: poll has already been closed")]
|
||||||
PollHasAlreadyClosed,
|
PollHasAlreadyClosed,
|
||||||
|
|
||||||
|
@ -175,7 +175,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SendPoll`]
|
/// 1. [`SendPoll`]
|
||||||
///
|
///
|
||||||
/// [`SendPoll`]: crate::requests::payloads::SendPoll
|
/// [`SendPoll`]: crate::requests::SendPoll
|
||||||
#[serde(rename = "Bad Request: poll must have at least 2 option")]
|
#[serde(rename = "Bad Request: poll must have at least 2 option")]
|
||||||
PollMustHaveMoreOptions,
|
PollMustHaveMoreOptions,
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SendPoll`]
|
/// 1. [`SendPoll`]
|
||||||
///
|
///
|
||||||
/// [`SendPoll`]: crate::requests::payloads::SendPoll
|
/// [`SendPoll`]: crate::requests::SendPoll
|
||||||
#[serde(rename = "Bad Request: poll can't have more than 10 options")]
|
#[serde(rename = "Bad Request: poll can't have more than 10 options")]
|
||||||
PollCantHaveMoreOptions,
|
PollCantHaveMoreOptions,
|
||||||
|
|
||||||
|
@ -193,7 +193,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SendPoll`]
|
/// 1. [`SendPoll`]
|
||||||
///
|
///
|
||||||
/// [`SendPoll`]: crate::requests::payloads::SendPoll
|
/// [`SendPoll`]: crate::requests::SendPoll
|
||||||
#[serde(rename = "Bad Request: poll options must be non-empty")]
|
#[serde(rename = "Bad Request: poll options must be non-empty")]
|
||||||
PollOptionsMustBeNonEmpty,
|
PollOptionsMustBeNonEmpty,
|
||||||
|
|
||||||
|
@ -202,7 +202,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SendPoll`]
|
/// 1. [`SendPoll`]
|
||||||
///
|
///
|
||||||
/// [`SendPoll`]: crate::requests::payloads::SendPoll
|
/// [`SendPoll`]: crate::requests::SendPoll
|
||||||
#[serde(rename = "Bad Request: poll question must be non-empty")]
|
#[serde(rename = "Bad Request: poll question must be non-empty")]
|
||||||
PollQuestionMustBeNonEmpty,
|
PollQuestionMustBeNonEmpty,
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SendPoll`]
|
/// 1. [`SendPoll`]
|
||||||
///
|
///
|
||||||
/// [`SendPoll`]: crate::requests::payloads::SendPoll
|
/// [`SendPoll`]: crate::requests::SendPoll
|
||||||
#[serde(rename = "Bad Request: poll options length must not exceed 100")]
|
#[serde(rename = "Bad Request: poll options length must not exceed 100")]
|
||||||
PollOptionsLengthTooLong,
|
PollOptionsLengthTooLong,
|
||||||
|
|
||||||
|
@ -222,7 +222,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SendPoll`]
|
/// 1. [`SendPoll`]
|
||||||
///
|
///
|
||||||
/// [`SendPoll`]: crate::requests::payloads::SendPoll
|
/// [`SendPoll`]: crate::requests::SendPoll
|
||||||
#[serde(rename = "Bad Request: poll question length must not exceed 255")]
|
#[serde(rename = "Bad Request: poll question length must not exceed 255")]
|
||||||
PollQuestionLengthTooLong,
|
PollQuestionLengthTooLong,
|
||||||
|
|
||||||
|
@ -231,7 +231,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`StopPoll`]
|
/// 1. [`StopPoll`]
|
||||||
///
|
///
|
||||||
/// [`StopPoll`]: crate::requests::payloads::StopPoll
|
/// [`StopPoll`]: crate::requests::StopPoll
|
||||||
#[serde(rename = "Bad Request: message with poll to stop not found")]
|
#[serde(rename = "Bad Request: message with poll to stop not found")]
|
||||||
MessageWithPollNotFound,
|
MessageWithPollNotFound,
|
||||||
|
|
||||||
|
@ -240,7 +240,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`StopPoll`]
|
/// 1. [`StopPoll`]
|
||||||
///
|
///
|
||||||
/// [`StopPoll`]: crate::requests::payloads::StopPoll
|
/// [`StopPoll`]: crate::requests::StopPoll
|
||||||
#[serde(rename = "Bad Request: message is not a poll")]
|
#[serde(rename = "Bad Request: message is not a poll")]
|
||||||
MessageIsNotAPoll,
|
MessageIsNotAPoll,
|
||||||
|
|
||||||
|
@ -250,7 +250,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SendMessage`]
|
/// 1. [`SendMessage`]
|
||||||
///
|
///
|
||||||
/// [`SendMessage`]: crate::requests::payloads::SendMessage
|
/// [`SendMessage`]: crate::requests::SendMessage
|
||||||
#[serde(rename = "Bad Request: chat not found")]
|
#[serde(rename = "Bad Request: chat not found")]
|
||||||
ChatNotFound,
|
ChatNotFound,
|
||||||
|
|
||||||
|
@ -260,7 +260,7 @@ pub enum ApiErrorKind {
|
||||||
/// 1. [`getUserProfilePhotos`]
|
/// 1. [`getUserProfilePhotos`]
|
||||||
///
|
///
|
||||||
/// [`getUserProfilePhotos`]:
|
/// [`getUserProfilePhotos`]:
|
||||||
/// crate::requests::payloads::getUserProfilePhotos
|
/// crate::requests::GetUserProfilePhotos
|
||||||
#[serde(rename = "Bad Request: user not found")]
|
#[serde(rename = "Bad Request: user not found")]
|
||||||
UserNotFound,
|
UserNotFound,
|
||||||
|
|
||||||
|
@ -270,7 +270,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SetChatDescription`]
|
/// 1. [`SetChatDescription`]
|
||||||
///
|
///
|
||||||
/// [`SetChatDescription`]: crate::requests::payloads::SetChatDescription
|
/// [`SetChatDescription`]: crate::requests::SetChatDescription
|
||||||
#[serde(rename = "Bad Request: chat description is not modified")]
|
#[serde(rename = "Bad Request: chat description is not modified")]
|
||||||
ChatDescriptionIsNotModified,
|
ChatDescriptionIsNotModified,
|
||||||
|
|
||||||
|
@ -279,7 +279,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`AnswerCallbackQuery`]
|
/// 1. [`AnswerCallbackQuery`]
|
||||||
///
|
///
|
||||||
/// [`AnswerCallbackQuery`]: crate::requests::payloads::AnswerCallbackQuery
|
/// [`AnswerCallbackQuery`]: crate::requests::AnswerCallbackQuery
|
||||||
#[serde(rename = "Bad Request: query is too old and response timeout \
|
#[serde(rename = "Bad Request: query is too old and response timeout \
|
||||||
expired or query id is invalid")]
|
expired or query id is invalid")]
|
||||||
InvalidQueryID,
|
InvalidQueryID,
|
||||||
|
@ -290,7 +290,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SendMessage`]
|
/// 1. [`SendMessage`]
|
||||||
///
|
///
|
||||||
/// [`SendMessage`]: crate::requests::payloads::SendMessage
|
/// [`SendMessage`]: crate::requests::SendMessage
|
||||||
#[serde(rename = "Bad Request: BUTTON_URL_INVALID")]
|
#[serde(rename = "Bad Request: BUTTON_URL_INVALID")]
|
||||||
ButtonURLInvalid,
|
ButtonURLInvalid,
|
||||||
|
|
||||||
|
@ -299,7 +299,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SendMessage`]
|
/// 1. [`SendMessage`]
|
||||||
///
|
///
|
||||||
/// [`SendMessage`]: crate::requests::payloads::SendMessage
|
/// [`SendMessage`]: crate::requests::SendMessage
|
||||||
#[serde(rename = "Bad Request: BUTTON_DATA_INVALID")]
|
#[serde(rename = "Bad Request: BUTTON_DATA_INVALID")]
|
||||||
ButtonDataInvalid,
|
ButtonDataInvalid,
|
||||||
|
|
||||||
|
@ -308,7 +308,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SendMessage`]
|
/// 1. [`SendMessage`]
|
||||||
///
|
///
|
||||||
/// [`SendMessage`]: crate::requests::payloads::SendMessage
|
/// [`SendMessage`]: crate::requests::SendMessage
|
||||||
#[serde(rename = "Bad Request: can't parse inline keyboard button: Text \
|
#[serde(rename = "Bad Request: can't parse inline keyboard button: Text \
|
||||||
buttons are unallowed in the inline keyboard")]
|
buttons are unallowed in the inline keyboard")]
|
||||||
TextButtonsAreUnallowed,
|
TextButtonsAreUnallowed,
|
||||||
|
@ -318,7 +318,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`GetFile`]
|
/// 1. [`GetFile`]
|
||||||
///
|
///
|
||||||
/// [`GetFile`]: crate::requests::payloads::GetFile
|
/// [`GetFile`]: crate::requests::GetFile
|
||||||
#[serde(rename = "Bad Request: wrong file id")]
|
#[serde(rename = "Bad Request: wrong file id")]
|
||||||
WrongFileID,
|
WrongFileID,
|
||||||
|
|
||||||
|
@ -331,7 +331,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SetChatPhoto`]
|
/// 1. [`SetChatPhoto`]
|
||||||
///
|
///
|
||||||
/// [`SetChatPhoto`]: crate::requests::payloads::SetChatPhoto
|
/// [`SetChatPhoto`]: crate::requests::SetChatPhoto
|
||||||
#[serde(rename = "Bad Request: Photo should be uploaded as an InputFile")]
|
#[serde(rename = "Bad Request: Photo should be uploaded as an InputFile")]
|
||||||
PhotoAsInputFileRequired,
|
PhotoAsInputFileRequired,
|
||||||
|
|
||||||
|
@ -340,7 +340,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`AddStickerToSet`]
|
/// 1. [`AddStickerToSet`]
|
||||||
///
|
///
|
||||||
/// [`AddStickerToSet`]: crate::requests::payloads::AddStickerToSet
|
/// [`AddStickerToSet`]: crate::requests::AddStickerToSet
|
||||||
#[serde(rename = "Bad Request: STICKERSET_INVALID")]
|
#[serde(rename = "Bad Request: STICKERSET_INVALID")]
|
||||||
InvalidStickersSet,
|
InvalidStickersSet,
|
||||||
|
|
||||||
|
@ -350,7 +350,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`PinMessage`]
|
/// 1. [`PinMessage`]
|
||||||
///
|
///
|
||||||
/// [`PinMessage`]: crate::requests::payloads::PinMessage
|
/// [`PinMessage`]: crate::requests::PinMessage
|
||||||
#[serde(rename = "Bad Request: not enough rights to pin a message")]
|
#[serde(rename = "Bad Request: not enough rights to pin a message")]
|
||||||
NotEnoughRightsToPinMessage,
|
NotEnoughRightsToPinMessage,
|
||||||
|
|
||||||
|
@ -365,7 +365,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`PromoteChatMember`]
|
/// 1. [`PromoteChatMember`]
|
||||||
///
|
///
|
||||||
/// [`PromoteChatMember`]: crate::requests::payloads::PromoteChatMember
|
/// [`PromoteChatMember`]: crate::requests::PromoteChatMember
|
||||||
#[serde(rename = "Bad Request: can't demote chat creator")]
|
#[serde(rename = "Bad Request: can't demote chat creator")]
|
||||||
CantDemoteChatCreator,
|
CantDemoteChatCreator,
|
||||||
|
|
||||||
|
@ -374,7 +374,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`RestrictChatMember`]
|
/// 1. [`RestrictChatMember`]
|
||||||
///
|
///
|
||||||
/// [`RestrictChatMember`]: crate::requests::payloads::RestrictChatMember
|
/// [`RestrictChatMember`]: crate::requests::RestrictChatMember
|
||||||
#[serde(rename = "Bad Request: can't restrict self")]
|
#[serde(rename = "Bad Request: can't restrict self")]
|
||||||
CantRestrictSelf,
|
CantRestrictSelf,
|
||||||
|
|
||||||
|
@ -384,7 +384,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`RestrictChatMember`]
|
/// 1. [`RestrictChatMember`]
|
||||||
///
|
///
|
||||||
/// [`RestrictChatMember`]: crate::requests::payloads::RestrictChatMember
|
/// [`RestrictChatMember`]: crate::requests::RestrictChatMember
|
||||||
#[serde(rename = "Bad Request: not enough rights to restrict/unrestrict \
|
#[serde(rename = "Bad Request: not enough rights to restrict/unrestrict \
|
||||||
chat member")]
|
chat member")]
|
||||||
NotEnoughRightsToRestrict,
|
NotEnoughRightsToRestrict,
|
||||||
|
@ -394,7 +394,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SetWebhook`]
|
/// 1. [`SetWebhook`]
|
||||||
///
|
///
|
||||||
/// [`SetWebhook`]: crate::requests::payloads::SetWebhook
|
/// [`SetWebhook`]: crate::requests::SetWebhook
|
||||||
#[serde(rename = "Bad Request: bad webhook: HTTPS url must be provided \
|
#[serde(rename = "Bad Request: bad webhook: HTTPS url must be provided \
|
||||||
for webhook")]
|
for webhook")]
|
||||||
WebhookRequireHTTPS,
|
WebhookRequireHTTPS,
|
||||||
|
@ -405,7 +405,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SetWebhook`]
|
/// 1. [`SetWebhook`]
|
||||||
///
|
///
|
||||||
/// [`SetWebhook`]: crate::requests::payloads::SetWebhook
|
/// [`SetWebhook`]: crate::requests::SetWebhook
|
||||||
#[serde(rename = "Bad Request: bad webhook: Webhook can be set up only \
|
#[serde(rename = "Bad Request: bad webhook: Webhook can be set up only \
|
||||||
on ports 80, 88, 443 or 8443")]
|
on ports 80, 88, 443 or 8443")]
|
||||||
BadWebhookPort,
|
BadWebhookPort,
|
||||||
|
@ -415,7 +415,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SetWebhook`]
|
/// 1. [`SetWebhook`]
|
||||||
///
|
///
|
||||||
/// [`SetWebhook`]: crate::requests::payloads::SetWebhook
|
/// [`SetWebhook`]: crate::requests::SetWebhook
|
||||||
#[serde(rename = "Bad Request: bad webhook: Failed to resolve host: \
|
#[serde(rename = "Bad Request: bad webhook: Failed to resolve host: \
|
||||||
Name or service not known")]
|
Name or service not known")]
|
||||||
UnknownHost,
|
UnknownHost,
|
||||||
|
@ -425,7 +425,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SetWebhook`]
|
/// 1. [`SetWebhook`]
|
||||||
///
|
///
|
||||||
/// [`SetWebhook`]: crate::requests::payloads::SetWebhook
|
/// [`SetWebhook`]: crate::requests::SetWebhook
|
||||||
#[serde(rename = "Bad Request: can't parse URL")]
|
#[serde(rename = "Bad Request: can't parse URL")]
|
||||||
CantParseUrl,
|
CantParseUrl,
|
||||||
|
|
||||||
|
@ -434,7 +434,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SendMessage`]
|
/// 1. [`SendMessage`]
|
||||||
///
|
///
|
||||||
/// [`SendMessage`]: crate::requests::payloads::SendMessage
|
/// [`SendMessage`]: crate::requests::SendMessage
|
||||||
#[serde(rename = "Bad Request: can't parse entities")]
|
#[serde(rename = "Bad Request: can't parse entities")]
|
||||||
CantParseEntities,
|
CantParseEntities,
|
||||||
|
|
||||||
|
@ -443,7 +443,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`GetUpdates`]
|
/// 1. [`GetUpdates`]
|
||||||
///
|
///
|
||||||
/// [`GetUpdates`]: crate::requests::payloads::GetUpdates
|
/// [`GetUpdates`]: crate::requests::GetUpdates
|
||||||
#[serde(rename = "can't use getUpdates method while webhook is active")]
|
#[serde(rename = "can't use getUpdates method while webhook is active")]
|
||||||
CantGetUpdates,
|
CantGetUpdates,
|
||||||
|
|
||||||
|
@ -452,7 +452,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SendMessage`]
|
/// 1. [`SendMessage`]
|
||||||
///
|
///
|
||||||
/// [`SendMessage`]: crate::requests::payloads::SendMessage
|
/// [`SendMessage`]: crate::requests::SendMessage
|
||||||
#[serde(rename = "Unauthorized: bot was kicked from a chat")]
|
#[serde(rename = "Unauthorized: bot was kicked from a chat")]
|
||||||
BotKicked,
|
BotKicked,
|
||||||
|
|
||||||
|
@ -461,7 +461,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SendMessage`]
|
/// 1. [`SendMessage`]
|
||||||
///
|
///
|
||||||
/// [`SendMessage`]: crate::requests::payloads::SendMessage
|
/// [`SendMessage`]: crate::requests::SendMessage
|
||||||
#[serde(rename = "Unauthorized: user is deactivated")]
|
#[serde(rename = "Unauthorized: user is deactivated")]
|
||||||
UserDeactivated,
|
UserDeactivated,
|
||||||
|
|
||||||
|
@ -470,7 +470,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SendMessage`]
|
/// 1. [`SendMessage`]
|
||||||
///
|
///
|
||||||
/// [`SendMessage`]: crate::requests::payloads::SendMessage
|
/// [`SendMessage`]: crate::requests::SendMessage
|
||||||
#[serde(
|
#[serde(
|
||||||
rename = "Unauthorized: bot can't initiate conversation with a user"
|
rename = "Unauthorized: bot can't initiate conversation with a user"
|
||||||
)]
|
)]
|
||||||
|
@ -481,7 +481,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SendMessage`]
|
/// 1. [`SendMessage`]
|
||||||
///
|
///
|
||||||
/// [`SendMessage`]: crate::requests::payloads::SendMessage
|
/// [`SendMessage`]: crate::requests::SendMessage
|
||||||
#[serde(rename = "Unauthorized: bot can't send messages to bots")]
|
#[serde(rename = "Unauthorized: bot can't send messages to bots")]
|
||||||
CantTalkWithBots,
|
CantTalkWithBots,
|
||||||
|
|
||||||
|
@ -490,7 +490,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`SendMessage`]
|
/// 1. [`SendMessage`]
|
||||||
///
|
///
|
||||||
/// [`SendMessage`]: crate::requests::payloads::SendMessage
|
/// [`SendMessage`]: crate::requests::SendMessage
|
||||||
#[serde(rename = "Bad Request: wrong HTTP URL")]
|
#[serde(rename = "Bad Request: wrong HTTP URL")]
|
||||||
WrongHTTPurl,
|
WrongHTTPurl,
|
||||||
|
|
||||||
|
@ -500,7 +500,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`GetUpdates`]
|
/// 1. [`GetUpdates`]
|
||||||
///
|
///
|
||||||
/// [`GetUpdates`]: crate::requests::payloads::GetUpdates
|
/// [`GetUpdates`]: crate::requests::GetUpdates
|
||||||
#[serde(rename = "Conflict: terminated by other getUpdates request; \
|
#[serde(rename = "Conflict: terminated by other getUpdates request; \
|
||||||
make sure that only one bot instance is running")]
|
make sure that only one bot instance is running")]
|
||||||
TerminatedByOtherGetUpdates,
|
TerminatedByOtherGetUpdates,
|
||||||
|
@ -510,7 +510,7 @@ pub enum ApiErrorKind {
|
||||||
/// May happen in methods:
|
/// May happen in methods:
|
||||||
/// 1. [`GetFile`]
|
/// 1. [`GetFile`]
|
||||||
///
|
///
|
||||||
/// [`GetFile`]: crate::requests::payloads::GetFile
|
/// [`GetFile`]: crate::requests::GetFile
|
||||||
#[serde(rename = "Bad Request: invalid file id")]
|
#[serde(rename = "Bad Request: invalid file id")]
|
||||||
FileIdInvalid,
|
FileIdInvalid,
|
||||||
|
|
||||||
|
|
|
@ -39,13 +39,11 @@ pub async fn download_file_stream(
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?;
|
.error_for_status()?;
|
||||||
|
|
||||||
Ok(futures::stream::unfold(res, |mut res| {
|
Ok(futures::stream::unfold(res, |mut res| async {
|
||||||
async {
|
match res.chunk().await {
|
||||||
match res.chunk().await {
|
Err(err) => Some((Err(err), res)),
|
||||||
Err(err) => Some((Err(err), res)),
|
Ok(Some(c)) => Some((Ok(c), res)),
|
||||||
Ok(Some(c)) => Some((Ok(c), res)),
|
Ok(None) => None,
|
||||||
Ok(None) => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::User;
|
use crate::types::{Message, User};
|
||||||
|
|
||||||
/// This object represents one special entity in a text message. For example,
|
/// This object represents one special entity in a text message. For example,
|
||||||
/// hashtags, usernames, URLs, etc.
|
/// hashtags, usernames, URLs, etc.
|
||||||
|
@ -39,21 +39,85 @@ pub enum MessageEntityKind {
|
||||||
Strikethrough,
|
Strikethrough,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
impl MessageEntity {
|
||||||
fn recursive_kind() {
|
pub fn text_from(&self, message: &Message) -> Option<String> {
|
||||||
use serde_json::from_str;
|
let text = message.text();
|
||||||
|
Some(String::from(&text?[self.offset..self.offset + self.length]))
|
||||||
assert_eq!(
|
}
|
||||||
MessageEntity {
|
}
|
||||||
kind: MessageEntityKind::TextLink {
|
|
||||||
url: "ya.ru".into()
|
#[cfg(test)]
|
||||||
},
|
mod tests {
|
||||||
offset: 1,
|
use super::*;
|
||||||
length: 2,
|
use crate::types::{
|
||||||
},
|
Chat, ChatKind, ForwardKind, MediaKind, MessageKind, Sender,
|
||||||
from_str::<MessageEntity>(
|
};
|
||||||
r#"{"type":"text_link","url":"ya.ru","offset":1,"length":2}"#
|
|
||||||
)
|
#[test]
|
||||||
.unwrap()
|
fn recursive_kind() {
|
||||||
);
|
use serde_json::from_str;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
MessageEntity {
|
||||||
|
kind: MessageEntityKind::TextLink {
|
||||||
|
url: "ya.ru".into()
|
||||||
|
},
|
||||||
|
offset: 1,
|
||||||
|
length: 2,
|
||||||
|
},
|
||||||
|
from_str::<MessageEntity>(
|
||||||
|
r#"{"type":"text_link","url":"ya.ru","offset":1,"length":2}"#
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn text_from() {
|
||||||
|
let message = message();
|
||||||
|
let expected = Some("yes".to_string());
|
||||||
|
let entity = message.entities().unwrap()[0].clone();
|
||||||
|
let actual = entity.text_from(&message);
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn message() -> Message {
|
||||||
|
Message {
|
||||||
|
id: 0,
|
||||||
|
date: 0,
|
||||||
|
chat: Chat {
|
||||||
|
id: 0,
|
||||||
|
kind: ChatKind::Private {
|
||||||
|
type_: (),
|
||||||
|
username: None,
|
||||||
|
first_name: None,
|
||||||
|
last_name: None,
|
||||||
|
},
|
||||||
|
photo: None,
|
||||||
|
},
|
||||||
|
kind: MessageKind::Common {
|
||||||
|
from: Sender::User(User {
|
||||||
|
id: 0,
|
||||||
|
is_bot: false,
|
||||||
|
first_name: "".to_string(),
|
||||||
|
last_name: None,
|
||||||
|
username: None,
|
||||||
|
language_code: None,
|
||||||
|
}),
|
||||||
|
forward_kind: ForwardKind::Origin {
|
||||||
|
reply_to_message: None,
|
||||||
|
},
|
||||||
|
edit_date: None,
|
||||||
|
media_kind: MediaKind::Text {
|
||||||
|
text: "no yes no".to_string(),
|
||||||
|
entities: vec![MessageEntity {
|
||||||
|
kind: MessageEntityKind::Mention,
|
||||||
|
offset: 3,
|
||||||
|
length: 3,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
reply_markup: None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::{CallbackQuery, ChosenInlineResult, InlineQuery, Message};
|
use crate::types::{
|
||||||
|
CallbackQuery, ChosenInlineResult, InlineQuery, Message, Poll,
|
||||||
|
PreCheckoutQuery, ShippingQuery,
|
||||||
|
};
|
||||||
|
|
||||||
/// This [object] represents an incoming update.
|
/// This [object] represents an incoming update.
|
||||||
///
|
///
|
||||||
|
@ -57,7 +60,17 @@ pub enum UpdateKind {
|
||||||
|
|
||||||
/// New incoming callback query.
|
/// New incoming callback query.
|
||||||
CallbackQuery(CallbackQuery),
|
CallbackQuery(CallbackQuery),
|
||||||
// TODO: Add more variants
|
|
||||||
|
/// New incoming shipping query. Only for invoices with flexible price.
|
||||||
|
ShippingQuery(ShippingQuery),
|
||||||
|
|
||||||
|
/// New incoming pre-checkout query. Contains full information about
|
||||||
|
/// checkout.
|
||||||
|
PreCheckoutQuery(PreCheckoutQuery),
|
||||||
|
|
||||||
|
/// New poll state. Bots receive only updates about stopped polls and
|
||||||
|
/// polls, which are sent by the bot.
|
||||||
|
Poll(Poll),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -92,7 +105,7 @@ mod test {
|
||||||
}
|
}
|
||||||
}"#;
|
}"#;
|
||||||
|
|
||||||
let expected: Update = Update {
|
let expected = Update {
|
||||||
id: 892_252_934,
|
id: 892_252_934,
|
||||||
kind: UpdateKind::Message(Message {
|
kind: UpdateKind::Message(Message {
|
||||||
id: 6557,
|
id: 6557,
|
||||||
|
|
Loading…
Reference in a new issue