diff --git a/src/bot/api.rs b/src/bot/api.rs index a75328c1..01b4d8ff 100644 --- a/src/bot/api.rs +++ b/src/bot/api.rs @@ -1,12 +1,12 @@ +use crate::bot::Bot; use crate::{ - bot::Bot, requests::{ AnswerPreCheckoutQuery, AnswerShippingQuery, EditMessageLiveLocation, - ForwardMessage, GetFile, GetMe, KickChatMember, PinChatMessage, - PromoteChatMember, RestrictChatMember, SendAudio, SendChatAction, - SendContact, SendLocation, SendMediaGroup, SendMessage, SendPhoto, - SendPoll, SendVenue, SendVideoNote, SendVoice, StopMessageLiveLocation, - UnbanChatMember, UnpinChatMessage, GetUpdates + ForwardMessage, GetFile, GetMe, GetUpdates, KickChatMember, + PinChatMessage, PromoteChatMember, RestrictChatMember, SendAudio, + SendChatAction, SendContact, SendLocation, SendMediaGroup, SendMessage, + SendPhoto, SendPoll, SendVenue, SendVideoNote, SendVoice, + StopMessageLiveLocation, UnbanChatMember, UnpinChatMessage, }, types::{ChatAction, ChatId, ChatPermissions, InputFile, InputMedia}, }; @@ -14,11 +14,11 @@ use crate::{ /// Telegram functions impl Bot { pub fn get_me(&self) -> GetMe { - GetMe::new(self.ctx()) + GetMe::new(&self) } pub fn get_updates(&self) -> GetUpdates { - GetUpdates::new(self.ctx()) + GetUpdates::new(&self) } pub fn send_message<C, T>(&self, chat_id: C, text: T) -> SendMessage @@ -26,7 +26,7 @@ impl Bot { C: Into<ChatId>, T: Into<String>, { - SendMessage::new(self.ctx(), chat_id, text) + SendMessage::new(&self, chat_id, text) } pub fn edit_message_live_location<Lt, Lg>( @@ -38,7 +38,7 @@ impl Bot { Lt: Into<f64>, Lg: Into<f64>, { - EditMessageLiveLocation::new(self.ctx(), latitude, longitude) + EditMessageLiveLocation::new(&self, latitude, longitude) } pub fn forward_message<C, F, M>( @@ -52,7 +52,7 @@ impl Bot { F: Into<ChatId>, M: Into<i32>, { - ForwardMessage::new(self.ctx(), chat_id, from_chat_id, message_id) + ForwardMessage::new(&self, chat_id, from_chat_id, message_id) } pub fn send_audio<C, A>(&self, chat_id: C, audio: A) -> SendAudio @@ -60,7 +60,7 @@ impl Bot { C: Into<ChatId>, A: Into<InputFile>, { - SendAudio::new(self.ctx(), chat_id, audio) + SendAudio::new(&self, chat_id, audio) } pub fn send_location<C, Lt, Lg>( @@ -74,7 +74,7 @@ impl Bot { Lt: Into<f64>, Lg: Into<f64>, { - SendLocation::new(self.ctx(), chat_id, latitude, longitude) + SendLocation::new(&self, chat_id, latitude, longitude) } pub fn send_media_group<C, M>(&self, chat_id: C, media: M) -> SendMediaGroup @@ -82,7 +82,7 @@ impl Bot { C: Into<ChatId>, M: Into<Vec<InputMedia>>, { - SendMediaGroup::new(self.ctx(), chat_id, media) + SendMediaGroup::new(&self, chat_id, media) } pub fn send_photo<C, P>(&self, chat_id: C, photo: P) -> SendPhoto @@ -90,18 +90,18 @@ impl Bot { C: Into<ChatId>, P: Into<InputFile>, { - SendPhoto::new(self.ctx(), chat_id, photo) + SendPhoto::new(&self, chat_id, photo) } pub fn stop_message_live_location(&self) -> StopMessageLiveLocation { - StopMessageLiveLocation::new(self.ctx()) + StopMessageLiveLocation::new(&self) } pub fn get_file<F>(&self, file_id: F) -> GetFile where F: Into<String>, { - GetFile::new(self.ctx(), file_id) + GetFile::new(&self, file_id) } pub fn answer_pre_checkout_query<I, O>( @@ -113,7 +113,7 @@ impl Bot { I: Into<String>, O: Into<bool>, { - AnswerPreCheckoutQuery::new(self.ctx(), pre_checkout_query_id, ok) + AnswerPreCheckoutQuery::new(&self, pre_checkout_query_id, ok) } pub fn answer_shipping_query<I, O>( @@ -125,7 +125,7 @@ impl Bot { I: Into<String>, O: Into<bool>, { - AnswerShippingQuery::new(self.ctx(), shipping_query_id, ok) + AnswerShippingQuery::new(&self, shipping_query_id, ok) } pub fn kick_chat_member<C, U>( @@ -137,7 +137,7 @@ impl Bot { C: Into<ChatId>, U: Into<i32>, { - KickChatMember::new(self.ctx(), chat_id, user_id) + KickChatMember::new(&self, chat_id, user_id) } pub fn pin_chat_message<C, M>( @@ -149,7 +149,7 @@ impl Bot { C: Into<ChatId>, M: Into<i32>, { - PinChatMessage::new(self.ctx(), chat_id, message_id) + PinChatMessage::new(&self, chat_id, message_id) } pub fn promote_chat_member<C, U>( @@ -161,7 +161,7 @@ impl Bot { C: Into<ChatId>, U: Into<i32>, { - PromoteChatMember::new(self.ctx(), chat_id, user_id) + PromoteChatMember::new(&self, chat_id, user_id) } pub fn restrict_chat_member<C, U, P>( @@ -175,7 +175,7 @@ impl Bot { U: Into<i32>, P: Into<ChatPermissions>, { - RestrictChatMember::new(self.ctx(), chat_id, user_id, permissions) + RestrictChatMember::new(&self, chat_id, user_id, permissions) } pub fn send_chat_action<C, A>( @@ -187,7 +187,7 @@ impl Bot { C: Into<ChatId>, A: Into<ChatAction>, { - SendChatAction::new(self.ctx(), chat_id, action) + SendChatAction::new(&self, chat_id, action) } pub fn send_contact<C, P, F>( @@ -201,7 +201,7 @@ impl Bot { P: Into<String>, F: Into<String>, { - SendContact::new(self.ctx(), chat_id, phone_number, first_name) + SendContact::new(&self, chat_id, phone_number, first_name) } pub fn send_poll<C, Q, O>( @@ -215,7 +215,7 @@ impl Bot { Q: Into<String>, O: Into<Vec<String>>, { - SendPoll::new(self.ctx(), chat_id, question, options) + SendPoll::new(&self, chat_id, question, options) } pub fn send_venue<C, Lt, Lg, T, A>( @@ -233,7 +233,7 @@ impl Bot { T: Into<String>, A: Into<String>, { - SendVenue::new(self.ctx(), chat_id, latitude, longitude, title, address) + SendVenue::new(&self, chat_id, latitude, longitude, title, address) } pub fn send_video_note<C, V>( @@ -245,7 +245,7 @@ impl Bot { C: Into<ChatId>, V: Into<String>, // TODO: InputFile { - SendVideoNote::new(self.ctx(), chat_id, video_note) + SendVideoNote::new(&self, chat_id, video_note) } pub fn send_voice<C, V>(&self, chat_id: C, voice: V) -> SendVoice @@ -253,7 +253,7 @@ impl Bot { C: Into<ChatId>, V: Into<String>, // TODO: InputFile { - SendVoice::new(self.ctx(), chat_id, voice) + SendVoice::new(&self, chat_id, voice) } pub fn unban_chat_member<C, U>( @@ -265,13 +265,13 @@ impl Bot { C: Into<ChatId>, U: Into<i32>, { - UnbanChatMember::new(self.ctx(), chat_id, user_id) + UnbanChatMember::new(&self, chat_id, user_id) } pub fn unpin_chat_message<C>(&self, chat_id: C) -> UnpinChatMessage where C: Into<ChatId>, { - UnpinChatMessage::new(self.ctx(), chat_id) + UnpinChatMessage::new(&self, chat_id) } } diff --git a/src/bot/download.rs b/src/bot/download.rs index c6100791..331de593 100644 --- a/src/bot/download.rs +++ b/src/bot/download.rs @@ -3,9 +3,10 @@ use tokio::io::AsyncWrite; #[cfg(feature = "unstable-stream")] use ::{bytes::Bytes, tokio::stream::Stream}; +use crate::bot::Bot; #[cfg(feature = "unstable-stream")] use crate::network::download_file_stream; -use crate::{bot::Bot, network::download_file, DownloadError}; +use crate::{network::download_file, DownloadError}; impl Bot { /// Download file from telegram into `destination`. @@ -16,11 +17,10 @@ impl Bot { /// ## Examples /// /// ```no_run - /// use telebofr::{ - /// bot::Bot, requests::Request, types::File as TgFile, - /// }; + /// use telebofr::types::File as TgFile; /// use tokio::fs::File; /// # use telebofr::RequestError; + /// use telebofr::bot::Bot; /// /// # async fn run() -> Result<(), Box<dyn std::error::Error>> { /// let bot = Bot::new("TOKEN"); diff --git a/src/bot/mod.rs b/src/bot/mod.rs index 34020623..d4e6d884 100644 --- a/src/bot/mod.rs +++ b/src/bot/mod.rs @@ -2,11 +2,10 @@ use reqwest::Client; -use crate::requests::RequestContext; - mod api; mod download; +#[derive(Debug, Clone)] pub struct Bot { token: String, client: Client, @@ -30,10 +29,11 @@ impl Bot { } impl Bot { - fn ctx(&self) -> RequestContext { - RequestContext { - token: &self.token, - client: &self.client, - } + pub fn token(&self) -> &str { + &self.token + } + + pub fn client(&self) -> &Client { + &self.client } } diff --git a/src/dispatcher/filter.rs b/src/dispatcher/filter.rs index d5afadf5..bcb59f16 100644 --- a/src/dispatcher/filter.rs +++ b/src/dispatcher/filter.rs @@ -28,7 +28,9 @@ impl<T, F: Fn(&T) -> bool> Filter<T> for F { /// assert_eq!(false.test(&()), false); /// ``` impl<T> Filter<T> for bool { - fn test(&self, _: &T) -> bool { *self } + fn test(&self, _: &T) -> bool { + *self + } } /// And filter. @@ -82,7 +84,6 @@ pub fn and<A, B>(a: A, b: B) -> And<A, B> { And::new(a, b) } - /// Or filter. /// /// Passes if at least one underlying filters passes. @@ -92,7 +93,7 @@ pub fn and<A, B>(a: A, b: B) -> And<A, B> { /// /// ## Examples /// ``` -/// use telebofr::dispatcher::filter::{Or, Filter}; +/// use telebofr::dispatcher::filter::{Filter, Or}; /// /// // Note: bool can be treated as `Filter` that always return self. /// assert!(Or::new(true, false).test(&())); @@ -134,14 +135,13 @@ pub fn or<A, B>(a: A, b: B) -> Or<A, B> { Or::new(a, b) } - /// Not filter. /// /// Passes if underlying filter don't pass. /// /// ## Examples /// ``` -/// use telebofr::dispatcher::filter::{Not, Filter}; +/// use telebofr::dispatcher::filter::{Filter, Not}; /// /// // Note: bool can be treated as `Filter` that always return self. /// assert!(Not::new(false).test(&())); @@ -242,12 +242,11 @@ macro_rules! any { }; } - /// Simple wrapper around `Filter` that adds `|` and `&` operators. /// /// ## Examples /// ``` -/// use telebofr::dispatcher::filter::{Filter, f, F, And, Or}; +/// use telebofr::dispatcher::filter::{f, And, Filter, Or, F}; /// /// let flt1 = |i: &i32| -> bool { *i > 17 }; /// let flt2 = |i: &i32| -> bool { *i < 42 }; @@ -259,7 +258,6 @@ macro_rules! any { /// assert_eq!(and.test(&50), false); // `flt2` doesn't pass /// assert_eq!(and.test(&16), false); // `flt1` doesn't pass /// -/// /// let or = f(flt1) | flt3; /// assert!(or.test(&19)); // `flt1` passes /// assert!(or.test(&16)); // `flt2` passes @@ -267,9 +265,8 @@ macro_rules! any { /// /// assert_eq!(or.test(&17), false); // both don't pass /// -/// /// // Note: only first filter in chain should be wrapped in `f(...)` -/// let complicated: F<Or<And<_, _>, _>>= f(flt1) & flt2 | flt3; +/// let complicated: F<Or<And<_, _>, _>> = f(flt1) & flt2 | flt3; /// assert!(complicated.test(&2)); // `flt3` passes /// assert!(complicated.test(&21)); // `flt1` and `flt2` pass /// @@ -287,7 +284,7 @@ pub fn f<A>(a: A) -> F<A> { impl<T, A> Filter<T> for F<A> where - A: Filter<T> + A: Filter<T>, { fn test(&self, value: &T) -> bool { self.0.test(value) @@ -310,8 +307,9 @@ impl<A, B> std::ops::BitOr<B> for F<A> { } } +/* workaround for `E0207` compiler error */ /// Extensions for filters -pub trait FilterExt<T /* workaround for `E0207` compiler error */> { +pub trait FilterExt<T> { /// Alias for [`Not::new`] /// /// ## Examples @@ -325,7 +323,10 @@ pub trait FilterExt<T /* workaround for `E0207` compiler error */> { /// ``` /// /// [`Not::new`]: crate::dispatcher::filter::Not::new - fn not(self) -> Not<Self> where Self: Sized { + fn not(self) -> Not<Self> + where + Self: Sized, + { Not::new(self) } @@ -344,7 +345,10 @@ pub trait FilterExt<T /* workaround for `E0207` compiler error */> { /// ``` /// /// [`Not::new`]: crate::dispatcher::filter::And::new - fn and<B>(self, other: B) -> And<Self, B> where Self: Sized { + fn and<B>(self, other: B) -> And<Self, B> + where + Self: Sized, + { And::new(self, other) } @@ -363,7 +367,10 @@ pub trait FilterExt<T /* workaround for `E0207` compiler error */> { /// ``` /// /// [`Not::new`]: crate::dispatcher::filter::Or::new - fn or<B>(self, other: B) -> Or<Self, B> where Self: Sized { + fn or<B>(self, other: B) -> Or<Self, B> + where + Self: Sized, + { Or::new(self, other) } } diff --git a/src/dispatcher/handler.rs b/src/dispatcher/handler.rs index 7c52928b..6a456a8e 100644 --- a/src/dispatcher/handler.rs +++ b/src/dispatcher/handler.rs @@ -1,12 +1,16 @@ -use futures::FutureExt; use std::future::Future; use std::pin::Pin; +use futures::FutureExt; + pub type HandlerResult<E> = Result<(), E>; /// Asynchronous handler for event `T` (like `&self, I -> Future` fn) pub trait Handler<'a, T, E> { - fn handle(&self, value: T) -> Pin<Box<dyn Future<Output = HandlerResult<E>> + 'a>>; + fn handle( + &self, + value: T, + ) -> Pin<Box<dyn Future<Output = HandlerResult<E>> + 'a>>; } pub trait IntoHandlerResult<E> { @@ -32,7 +36,10 @@ where R: IntoHandlerResult<E> + 'a, E: 'a, { - fn handle(&self, value: T) -> Pin<Box<dyn Future<Output = HandlerResult<E>> + 'a>> { + fn handle( + &self, + value: T, + ) -> Pin<Box<dyn Future<Output = HandlerResult<E>> + 'a>> { Box::pin(self(value).map(IntoHandlerResult::into_hr)) } } diff --git a/src/dispatcher/mod.rs b/src/dispatcher/mod.rs index 2011c220..06f2471d 100644 --- a/src/dispatcher/mod.rs +++ b/src/dispatcher/mod.rs @@ -1,16 +1,15 @@ //! Update dispatching. +use async_trait::async_trait; +pub use filter::Filter; +pub use handler::Handler; + pub mod filter; pub mod handler; pub mod simple; pub mod updater; -pub use filter::Filter; -pub use handler::Handler; - -use async_trait::async_trait; - -#[async_trait(?Send)] +#[async_trait(? Send)] pub trait Dispatcher<'a, U> { async fn dispatch(&'a mut self, updater: U); } diff --git a/src/dispatcher/simple/error_policy.rs b/src/dispatcher/simple/error_policy.rs index d71b254b..676d561f 100644 --- a/src/dispatcher/simple/error_policy.rs +++ b/src/dispatcher/simple/error_policy.rs @@ -1,6 +1,6 @@ -use std::pin::Pin; -use std::future::Future; use std::fmt::Debug; +use std::future::Future; +use std::pin::Pin; // TODO: shouldn't it be trait? pub enum ErrorPolicy<'a, E> { @@ -15,14 +15,12 @@ where { pub async fn handle_error(&self, error: E) { match self { - Self::Ignore => {}, + Self::Ignore => {} Self::Log => { // TODO: better message log::error!("Error in handler: {:?}", error) } - Self::Custom(func) => { - func(error).await - } + Self::Custom(func) => func(error).await, } } @@ -33,4 +31,4 @@ where { Self::Custom(Box::new(move |e| Box::pin(f(e)))) } -} \ No newline at end of file +} diff --git a/src/dispatcher/simple/mod.rs b/src/dispatcher/simple/mod.rs index 573b9d38..23fa47ab 100644 --- a/src/dispatcher/simple/mod.rs +++ b/src/dispatcher/simple/mod.rs @@ -1,25 +1,19 @@ -pub mod error_policy; - use futures::StreamExt; + use async_trait::async_trait; use crate::{ dispatcher::{ - filter::Filter, - handler::Handler, + filter::Filter, handler::Handler, simple::error_policy::ErrorPolicy, updater::Updater, - simple::error_policy::ErrorPolicy, - }, - types::{ - Update, - Message, - UpdateKind, - CallbackQuery, - ChosenInlineResult, }, + types::{CallbackQuery, ChosenInlineResult, Message, Update, UpdateKind}, }; -type Handlers<'a, T, E> = Vec<(Box<dyn Filter<T> + 'a>, Box<dyn Handler<'a, T, E> + 'a>)>; +pub mod error_policy; + +type Handlers<'a, T, E> = + Vec<(Box<dyn Filter<T> + 'a>, Box<dyn Handler<'a, T, E> + 'a>)>; /// Dispatcher that dispatches updates from telegram. /// @@ -29,23 +23,23 @@ type Handlers<'a, T, E> = Vec<(Box<dyn Filter<T> + 'a>, Box<dyn Handler<'a, T, E /// - Handler's fututres are also boxed /// - [Custom error policy] is also boxed /// - All errors from [updater] are ignored (TODO: remove this limitation) -/// - All handlers executed in order (this means that in dispatcher have -/// 2 upadtes it will first execute some handler into complition with -/// first update and **then** search for handler for second update, -/// this is probably wrong) +/// - All handlers executed in order (this means that in dispatcher have 2 +/// upadtes it will first execute some handler into complition with first +/// update and **then** search for handler for second update, this is probably +/// wrong) /// /// ## Examples /// /// Simplest example: /// ```no_run -/// # async fn run() { +/// # use telebofr::bot::Bot; +/// use telebofr::types::Message; +/// async fn run() { /// use std::convert::Infallible; /// use telebofr::{ -/// bot::Bot, -/// types::Message, /// dispatcher::{ +/// simple::{error_policy::ErrorPolicy, Dispatcher}, /// updater::polling, -/// simple::{Dispatcher, error_policy::ErrorPolicy}, /// }, /// }; /// @@ -59,8 +53,8 @@ type Handlers<'a, T, E> = Vec<(Box<dyn Filter<T> + 'a>, Box<dyn Handler<'a, T, E /// // with error policy that just ignores all errors (that can't ever happen) /// let mut dp = Dispatcher::<Infallible>::new(ErrorPolicy::Ignore) /// // Add 'handler' that will handle all messages sent to the bot -/// .message_handler(true, |mes: Message| async move { -/// println!("New message: {:?}", mes) +/// .message_handler(true, |mes: Message| { +/// async move { println!("New message: {:?}", mes) } /// }) /// // Add 'handler' that will handle all /// // messages edited in chat with the bot @@ -72,8 +66,9 @@ type Handlers<'a, T, E> = Vec<(Box<dyn Filter<T> + 'a>, Box<dyn Handler<'a, T, E /// ``` /// /// [`std::fmt::Debug`]: std::fmt::Debug -/// [Custom error policy]: crate::dispatcher::simple::error_policy::ErrorPolicy::Custom -/// [updater]: crate::dispatcher::updater +/// [Custom error policy]: +/// crate::dispatcher::simple::error_policy::ErrorPolicy::Custom [updater]: +/// crate::dispatcher::updater pub struct Dispatcher<'a, E> { message_handlers: Handlers<'a, Message, E>, edited_message_handlers: Handlers<'a, Message, E>, @@ -98,7 +93,7 @@ where inline_query_handlers: Vec::new(), chosen_inline_result_handlers: Vec::new(), callback_query_handlers: Vec::new(), - error_policy + error_policy, } } @@ -107,7 +102,8 @@ where F: Filter<Message> + 'a, H: Handler<'a, Message, E> + 'a, { - self.message_handlers.push((Box::new(filter), Box::new(handler))); + self.message_handlers + .push((Box::new(filter), Box::new(handler))); self } @@ -116,7 +112,8 @@ where F: Filter<Message> + 'a, H: Handler<'a, Message, E> + 'a, { - self.edited_message_handlers.push((Box::new(filter), Box::new(handler))); + self.edited_message_handlers + .push((Box::new(filter), Box::new(handler))); self } @@ -125,16 +122,22 @@ where F: Filter<Message> + 'a, H: Handler<'a, Message, E> + 'a, { - self.channel_post_handlers.push((Box::new(filter), Box::new(handler))); + 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 + pub fn edited_channel_post_handler<F, H>( + mut self, + filter: F, + handler: H, + ) -> Self where F: Filter<Message> + 'a, H: Handler<'a, Message, E> + 'a, { - self.edited_channel_post_handlers.push((Box::new(filter), Box::new(handler))); + self.edited_channel_post_handlers + .push((Box::new(filter), Box::new(handler))); self } @@ -143,16 +146,22 @@ where F: Filter<()> + 'a, H: Handler<'a, (), E> + 'a, { - self.inline_query_handlers.push((Box::new(filter), Box::new(handler))); + 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 + pub fn chosen_inline_result_handler<F, H>( + mut self, + filter: F, + handler: H, + ) -> Self where F: Filter<ChosenInlineResult> + 'a, H: Handler<'a, ChosenInlineResult, E> + 'a, { - self.chosen_inline_result_handlers.push((Box::new(filter), Box::new(handler))); + self.chosen_inline_result_handlers + .push((Box::new(filter), Box::new(handler))); self } @@ -161,65 +170,91 @@ where F: Filter<CallbackQuery> + 'a, H: Handler<'a, CallbackQuery, E> + 'a, { - self.callback_query_handlers.push((Box::new(filter), Box::new(handler))); + self.callback_query_handlers + .push((Box::new(filter), Box::new(handler))); self } // TODO: Can someone simplify this? pub async fn dispatch<U>(&mut self, updates: U) where - U: Updater + 'a + U: Updater + 'a, { - updates.for_each(|res| { - async { - let res = res; - let Update { kind, id } = match res { - Ok(upd) => upd, - _ => return // TODO: proper error handling - }; + updates + .for_each(|res| { + async { + let res = res; + let Update { kind, id } = match res { + Ok(upd) => upd, + _ => return, // TODO: proper error handling + }; - log::debug!("Handled update#{id:?}: {kind:?}", id = id, kind = kind); + log::debug!( + "Handled update#{id:?}: {kind:?}", + id = id, + kind = kind + ); - // TODO: can someone extract this to a function? - macro_rules! call { - ($h:expr, $value:expr) => {{ - let value = $value; - let handler = $h.iter().find_map(|e| { - let (filter, handler) = e; - if filter.test(&value) { - Some(handler) - } else { - None + // TODO: can someone extract this to a function? + macro_rules! call { + ($h:expr, $value:expr) => {{ + let value = $value; + let handler = $h.iter().find_map(|e| { + let (filter, handler) = e; + if filter.test(&value) { + Some(handler) + } else { + None + } + }); + + match handler { + Some(handler) => { + if let Err(err) = + handler.handle(value).await + { + self.error_policy + .handle_error(err) + .await; + } + } + None => { + log::warn!("Unhandled update: {:?}", value) + } } - }); + }}; + } - match handler { - Some(handler) => { - if let Err(err) = handler.handle(value).await { - self.error_policy.handle_error(err).await; - } - }, - None => log::warn!("Unhandled update: {:?}", value) + match kind { + UpdateKind::Message(mes) => { + call!(self.message_handlers, mes) } - }}; + UpdateKind::EditedMessage(mes) => { + call!(self.edited_message_handlers, mes) + } + UpdateKind::ChannelPost(post) => { + call!(self.channel_post_handlers, post) + } + UpdateKind::EditedChannelPost(post) => { + call!(self.edited_channel_post_handlers, post) + } + UpdateKind::InlineQuery(query) => { + call!(self.inline_query_handlers, query) + } + UpdateKind::ChosenInlineResult(result) => { + call!(self.chosen_inline_result_handlers, result) + } + UpdateKind::CallbackQuery(callback) => { + call!(self.callback_query_handlers, callback) + } + } } - - match kind { - UpdateKind::Message(mes) => call!(self.message_handlers, mes), - UpdateKind::EditedMessage(mes) => call!(self.edited_message_handlers, mes), - UpdateKind::ChannelPost(post) => call!(self.channel_post_handlers, post), - UpdateKind::EditedChannelPost(post) => call!(self.edited_channel_post_handlers, post), - UpdateKind::InlineQuery(query) => call!(self.inline_query_handlers, query), - UpdateKind::ChosenInlineResult(result) => call!(self.chosen_inline_result_handlers, result), - UpdateKind::CallbackQuery(callback) => call!(self.callback_query_handlers, callback), - } - } - }) + }) .await; } } -#[async_trait(?Send)] +#[async_trait(? Send)] impl<'a, U, E> crate::dispatcher::Dispatcher<'a, U> for Dispatcher<'a, E> where E: std::fmt::Debug, @@ -235,26 +270,35 @@ mod tests { use std::convert::Infallible; use std::sync::atomic::{AtomicI32, Ordering}; - use crate::{ - types::{ - Message, ChatKind, MessageKind, Sender, ForwardKind, MediaKind, Chat, User, Update, UpdateKind - }, - dispatcher::{simple::{Dispatcher, error_policy::ErrorPolicy}, updater::StreamUpdater}, - }; use futures::Stream; + use crate::{ + dispatcher::{ + simple::{error_policy::ErrorPolicy, Dispatcher}, + updater::StreamUpdater, + }, + 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 = Dispatcher::<Infallible>::new(ErrorPolicy::Ignore) - .message_handler(true, |_mes: Message| async move { - counter.fetch_add(1, Ordering::SeqCst); + .message_handler(true, |_mes: Message| { + async move { + counter.fetch_add(1, Ordering::SeqCst); + } }) - .message_handler(true, |_mes: Message| async move { - counter2.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; @@ -300,15 +344,17 @@ mod tests { } fn message_update() -> Update { - Update { id: 0, kind: UpdateKind::Message(message()) } + Update { + id: 0, + kind: UpdateKind::Message(message()), + } } - fn one_message_updater() -> StreamUpdater<impl Stream<Item=Result<Update, Infallible>>> { + fn one_message_updater( + ) -> StreamUpdater<impl Stream<Item = Result<Update, Infallible>>> { use futures::future::ready; use futures::stream; - StreamUpdater::new( - stream::once(ready(Ok(message_update()))) - ) + StreamUpdater::new(stream::once(ready(Ok(message_update())))) } } diff --git a/src/dispatcher/updater.rs b/src/dispatcher/updater.rs index 74b0164b..7b0017fb 100644 --- a/src/dispatcher/updater.rs +++ b/src/dispatcher/updater.rs @@ -3,19 +3,18 @@ use std::{ task::{Context, Poll}, }; -use pin_project::pin_project; -use futures::{Stream, StreamExt, stream}; +use futures::{stream, Stream, StreamExt}; -use crate::{ - bot::Bot, - types::Update, - RequestError, -}; +use pin_project::pin_project; + +use crate::bot::Bot; +use crate::{types::Update, RequestError}; // Currently just a placeholder, but I'll add here some methods /// Updater is stream of updates. /// -/// Telegram supports 2 ways of [getting updates]: [long polling](Long Polling) and webhook +/// Telegram supports 2 ways of [getting updates]: [long polling](Long Polling) +/// and webhook /// /// ## Long Polling /// @@ -99,14 +98,16 @@ use crate::{ /// [GetUpdates]: crate::requests::GetUpdates /// [getting updates]: https://core.telegram.org/bots/api#getting-updates /// [wiki]: https://en.wikipedia.org/wiki/Push_technology#Long_polling -pub trait Updater: Stream<Item=Result<Update, <Self as Updater>::Error>> { +pub trait Updater: + Stream<Item = Result<Update, <Self as Updater>::Error>> +{ type Error; } #[pin_project] pub struct StreamUpdater<S> { #[pin] - stream: S + stream: S, } impl<S> StreamUpdater<S> { @@ -115,37 +116,49 @@ impl<S> StreamUpdater<S> { } } -impl<S, E> Stream for StreamUpdater<S> where S: Stream<Item=Result<Update, E>> { +impl<S, E> Stream for StreamUpdater<S> +where + S: Stream<Item = Result<Update, E>>, +{ type Item = Result<Update, E>; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll<Option<Self::Item>> { self.project().stream.poll_next(cx) } } -impl<S, E> Updater for StreamUpdater<S> where S: Stream<Item=Result<Update, E>> { +impl<S, E> Updater for StreamUpdater<S> +where + S: Stream<Item = Result<Update, E>>, +{ type Error = E; } pub fn polling<'a>(bot: &'a Bot) -> impl Updater<Error = RequestError> + 'a { - let stream = stream::unfold((bot, 0), |(bot, mut offset)| async move { - // this match converts Result<Vec<_>, _> -> Vec<Result<_, _>> - let updates = match bot.get_updates().offset(offset).send().await { - Ok(updates) => { - if let Some(upd) = updates.last() { - offset = upd.id + 1; + let stream = stream::unfold((bot, 0), |(bot, mut offset)| { + async move { + // this match converts Result<Vec<_>, _> -> Vec<Result<_, _>> + let updates = match bot.get_updates().offset(offset).send().await { + Ok(updates) => { + if let Some(upd) = updates.last() { + offset = upd.id + 1; + } + updates.into_iter().map(Ok).collect::<Vec<_>>() } - updates.into_iter().map(|u| Ok(u)).collect::<Vec<_>>() - }, - Err(err) => vec![Err(err)] - }; - Some((stream::iter(updates), (bot, offset))) + Err(err) => vec![Err(err)], + }; + Some((stream::iter(updates), (bot, offset))) + } }) - .flatten(); + .flatten(); StreamUpdater { stream } } // TODO implement webhook (this actually require webserver and probably we // should add cargo feature that adds webhook) -//pub fn webhook<'a>(bot: &'a Bot, cfg: WebhookConfig) -> Updater<impl Stream<Item=Result<Update, ???>> + 'a> {} +//pub fn webhook<'a>(bot: &'a cfg: WebhookConfig) -> Updater<impl +// Stream<Item=Result<Update, ???>> + 'a> {} diff --git a/src/network/request.rs b/src/network/request.rs index 0222fad2..40bb8b2d 100644 --- a/src/network/request.rs +++ b/src/network/request.rs @@ -11,8 +11,8 @@ pub async fn request_multipart<T>( method_name: &str, params: Form, ) -> ResponseResult<T> - where - T: DeserializeOwned, +where + T: DeserializeOwned, { process_response( client @@ -30,8 +30,8 @@ pub async fn request_simple<T>( token: &str, method_name: &str, ) -> ResponseResult<T> - where - T: DeserializeOwned, +where + T: DeserializeOwned, { process_response( client diff --git a/src/requests/answer_pre_checkout_query.rs b/src/requests/answer_pre_checkout_query.rs index b7f5bfe1..9087cab4 100644 --- a/src/requests/answer_pre_checkout_query.rs +++ b/src/requests/answer_pre_checkout_query.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::True, }; @@ -16,7 +17,7 @@ use crate::{ /// [`Update`]: crate::types::Update pub struct AnswerPreCheckoutQuery<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, /// Unique identifier for the query to be answered pub pre_checkout_query_id: String, @@ -48,8 +49,8 @@ impl Request for AnswerPreCheckoutQuery<'_> { impl AnswerPreCheckoutQuery<'_> { pub async fn send(self) -> ResponseResult<True> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "answerPreCheckoutQuery", &self, ) @@ -59,7 +60,7 @@ impl AnswerPreCheckoutQuery<'_> { impl<'a> AnswerPreCheckoutQuery<'a> { pub(crate) fn new<S, B>( - ctx: RequestContext<'a>, + bot: &'a Bot, pre_checkout_query_id: S, ok: B, ) -> Self @@ -68,7 +69,7 @@ impl<'a> AnswerPreCheckoutQuery<'a> { B: Into<bool>, { Self { - ctx, + bot, pre_checkout_query_id: pre_checkout_query_id.into(), ok: ok.into(), error_message: None, diff --git a/src/requests/answer_shipping_query.rs b/src/requests/answer_shipping_query.rs index b7a25069..4ba8fcd2 100644 --- a/src/requests/answer_shipping_query.rs +++ b/src/requests/answer_shipping_query.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::{ShippingOption, True}, }; @@ -15,7 +16,7 @@ use crate::{ /// [`Update`]: crate::types::Update pub struct AnswerShippingQuery<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, /// Unique identifier for the query to be answered pub shipping_query_id: String, @@ -49,8 +50,8 @@ impl Request for AnswerShippingQuery<'_> { impl AnswerShippingQuery<'_> { pub async fn send(self) -> ResponseResult<True> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "answerShippingQuery", &self, ) @@ -59,17 +60,13 @@ impl AnswerShippingQuery<'_> { } impl<'a> AnswerShippingQuery<'a> { - pub(crate) fn new<S, B>( - ctx: RequestContext<'a>, - shipping_query_id: S, - ok: B, - ) -> Self + pub(crate) fn new<S, B>(bot: &'a Bot, shipping_query_id: S, ok: B) -> Self where S: Into<String>, B: Into<bool>, { Self { - ctx, + bot, shipping_query_id: shipping_query_id.into(), ok: ok.into(), shipping_options: None, diff --git a/src/requests/edit_message_live_location.rs b/src/requests/edit_message_live_location.rs index c4c0fc4e..b8d820b1 100644 --- a/src/requests/edit_message_live_location.rs +++ b/src/requests/edit_message_live_location.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::{ChatId, Message, ReplyMarkup}, }; @@ -17,7 +18,7 @@ use crate::{ /// [`Message`]: crate::types::Message pub struct EditMessageLiveLocation<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, #[serde(skip_serializing_if = "Option::is_none")] /// Required if inline_message_id is not specified. Unique identifier for @@ -53,8 +54,8 @@ impl Request for EditMessageLiveLocation<'_> { impl EditMessageLiveLocation<'_> { pub async fn send(self) -> ResponseResult<Message> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "editMessageLiveLocation", &self, ) @@ -63,17 +64,13 @@ impl EditMessageLiveLocation<'_> { } impl<'a> EditMessageLiveLocation<'a> { - pub(crate) fn new<Lt, Lg>( - ctx: RequestContext<'a>, - latitude: Lt, - longitude: Lg, - ) -> Self + pub(crate) fn new<Lt, Lg>(bot: &'a Bot, latitude: Lt, longitude: Lg) -> Self where Lt: Into<f64>, Lg: Into<f64>, { Self { - ctx, + bot, chat_id: None, message_id: None, inline_message_id: None, diff --git a/src/requests/form_builder.rs b/src/requests/form_builder.rs index 7fdb6b55..01698d5c 100644 --- a/src/requests/form_builder.rs +++ b/src/requests/form_builder.rs @@ -2,11 +2,11 @@ use std::path::PathBuf; use reqwest::multipart::Form; +use crate::types::InputFile; use crate::{ requests::utils, types::{ChatId, InputMedia, ParseMode}, }; -use crate::types::InputFile; /// This is a convenient struct that builds `reqwest::multipart::Form` /// from scratch. @@ -73,7 +73,10 @@ macro_rules! impl_for_struct { impl_for_struct!(bool, i32, i64); -impl<T> IntoFormValue for Option<T> where T: IntoFormValue { +impl<T> IntoFormValue for Option<T> +where + T: IntoFormValue, +{ fn into_form_value(self) -> Option<FormValue> { self.and_then(IntoFormValue::into_form_value) } @@ -81,8 +84,8 @@ impl<T> IntoFormValue for Option<T> where T: IntoFormValue { impl IntoFormValue for &[InputMedia] { fn into_form_value(self) -> Option<FormValue> { - let json = serde_json::to_string(self) - .expect("serde_json::to_string failed"); + let json = + serde_json::to_string(self).expect("serde_json::to_string failed"); Some(FormValue::Str(json)) } } @@ -107,7 +110,7 @@ impl IntoFormValue for ChatId { fn into_form_value(self) -> Option<FormValue> { let string = match self { ChatId::Id(id) => id.to_string(), - ChatId::ChannelUsername(username) => username.clone(), + ChatId::ChannelUsername(username) => username, }; Some(FormValue::Str(string)) } @@ -115,7 +118,7 @@ impl IntoFormValue for ChatId { impl IntoFormValue for String { fn into_form_value(self) -> Option<FormValue> { - Some(FormValue::Str(self.to_owned())) + Some(FormValue::Str(self)) } } diff --git a/src/requests/forward_message.rs b/src/requests/forward_message.rs index 9c16ae67..5cce7880 100644 --- a/src/requests/forward_message.rs +++ b/src/requests/forward_message.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::{ChatId, Message}, }; @@ -11,7 +12,7 @@ use crate::{ /// [`Message`] is returned. pub struct ForwardMessage<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, /// Unique identifier for the target chat or username of the target channel /// (in the format @channelusername) @@ -40,8 +41,8 @@ impl Request for ForwardMessage<'_> { impl ForwardMessage<'_> { pub async fn send(self) -> ResponseResult<Message> { network::request_json( - self.ctx.client, - self.ctx.token, + self.bot.client(), + self.bot.token(), "forwardMessage", &self, ) @@ -51,7 +52,7 @@ impl ForwardMessage<'_> { impl<'a> ForwardMessage<'a> { pub(crate) fn new<C, Fc, M>( - ctx: RequestContext<'a>, + bot: &'a Bot, chat_id: C, from_chat_id: Fc, message_id: M, @@ -62,7 +63,7 @@ impl<'a> ForwardMessage<'a> { M: Into<i32>, { Self { - ctx, + bot, chat_id: chat_id.into(), from_chat_id: from_chat_id.into(), message_id: message_id.into(), diff --git a/src/requests/get_chat.rs b/src/requests/get_chat.rs index 7135f226..0853de27 100644 --- a/src/requests/get_chat.rs +++ b/src/requests/get_chat.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::{Chat, ChatId}, }; @@ -13,7 +14,7 @@ use crate::{ #[derive(Debug, Clone, Serialize)] pub struct GetChat<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, /// Unique identifier for the target chat or username /// of the target supergroup or channel (in the format @channelusername) chat_id: ChatId, @@ -31,8 +32,8 @@ impl Request for GetChat<'_> { impl GetChat<'_> { pub async fn send(self) -> ResponseResult<Chat> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "getChat", &self, ) diff --git a/src/requests/get_file.rs b/src/requests/get_file.rs index 59785b17..263705ac 100644 --- a/src/requests/get_file.rs +++ b/src/requests/get_file.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::File, }; @@ -16,7 +17,7 @@ use crate::{ #[derive(Debug, Clone, Serialize)] pub struct GetFile<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, /// File identifier to get info about pub file_id: String, } @@ -33,8 +34,8 @@ impl Request for GetFile<'_> { impl GetFile<'_> { pub async fn send(self) -> ResponseResult<File> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "getFile", &self, ) @@ -43,12 +44,12 @@ impl GetFile<'_> { } impl<'a> GetFile<'a> { - pub(crate) fn new<F>(ctx: RequestContext<'a>, value: F) -> Self + pub(crate) fn new<F>(bot: &'a Bot, value: F) -> Self where F: Into<String>, { Self { - ctx, + bot, file_id: value.into(), } } diff --git a/src/requests/get_me.rs b/src/requests/get_me.rs index 20cfa3ef..4b1ad322 100644 --- a/src/requests/get_me.rs +++ b/src/requests/get_me.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::User, }; @@ -10,7 +11,7 @@ use crate::{ /// A simple method for testing your bot's auth token. Requires no parameters. /// Returns basic information about the bot in form of a [`User`] object. pub struct GetMe<'a> { - ctx: RequestContext<'a>, + bot: &'a Bot, } #[async_trait] @@ -24,12 +25,13 @@ impl Request for GetMe<'_> { impl GetMe<'_> { pub async fn send(self) -> ResponseResult<User> { - network::request_simple(self.ctx.client, self.ctx.token, "getMe").await + network::request_simple(self.bot.client(), self.bot.token(), "getMe") + .await } } impl<'a> GetMe<'a> { - pub(crate) fn new(ctx: RequestContext<'a>) -> Self { - GetMe { ctx } + pub(crate) fn new(bot: &'a Bot) -> Self { + GetMe { bot } } } diff --git a/src/requests/get_updates.rs b/src/requests/get_updates.rs index 176a0e91..bc773bf3 100644 --- a/src/requests/get_updates.rs +++ b/src/requests/get_updates.rs @@ -1,15 +1,16 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::Update, }; #[derive(Debug, Clone, Serialize)] pub struct GetUpdates<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, pub offset: Option<i32>, pub limit: Option<u8>, @@ -41,8 +42,8 @@ impl Request for GetUpdates<'_> { impl GetUpdates<'_> { pub async fn send(self) -> ResponseResult<Vec<Update>> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "getUpdates", &self, ) @@ -51,9 +52,9 @@ impl GetUpdates<'_> { } impl<'a> GetUpdates<'a> { - pub(crate) fn new(ctx: RequestContext<'a>) -> Self { + pub(crate) fn new(bot: &'a Bot) -> Self { Self { - ctx, + bot, offset: None, limit: None, timeout: None, diff --git a/src/requests/get_user_profile_photos.rs b/src/requests/get_user_profile_photos.rs index 6044441d..16c57eb2 100644 --- a/src/requests/get_user_profile_photos.rs +++ b/src/requests/get_user_profile_photos.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::UserProfilePhotos, }; @@ -11,7 +12,7 @@ use crate::{ #[derive(Debug, Clone, Serialize)] pub struct GetUserProfilePhotos<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, /// Unique identifier of the target user pub user_id: i32, /// Sequential number of the first photo to be returned. By default, all @@ -36,8 +37,8 @@ impl Request for GetUserProfilePhotos<'_> { impl GetUserProfilePhotos<'_> { async fn send(self) -> ResponseResult<UserProfilePhotos> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "getUserProfilePhotos", &self, ) @@ -46,12 +47,12 @@ impl GetUserProfilePhotos<'_> { } impl<'a> GetUserProfilePhotos<'a> { - pub fn new<U>(ctx: RequestContext<'a>, user_id: U) -> Self + pub fn new<U>(bot: &'a Bot, user_id: U) -> Self where U: Into<i32>, { Self { - ctx, + bot, user_id: user_id.into(), offset: None, limit: None, diff --git a/src/requests/kick_chat_member.rs b/src/requests/kick_chat_member.rs index 9dec2cd6..c00690d3 100644 --- a/src/requests/kick_chat_member.rs +++ b/src/requests/kick_chat_member.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::{ChatId, True}, }; @@ -14,7 +15,7 @@ use crate::{ #[derive(Debug, Clone, Serialize)] pub struct KickChatMember<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, ///Unique identifier for the target group or username of the target /// supergroup or channel (in the format @channelusername) pub chat_id: ChatId, @@ -39,8 +40,8 @@ impl Request for KickChatMember<'_> { impl KickChatMember<'_> { async fn send(self) -> ResponseResult<True> { network::request_json( - self.ctx.client, - self.ctx.token, + self.bot.client(), + self.bot.token(), "kickChatMember", &self, ) @@ -49,17 +50,13 @@ impl KickChatMember<'_> { } impl<'a> KickChatMember<'a> { - pub(crate) fn new<C, U>( - ctx: RequestContext<'a>, - chat_id: C, - user_id: U, - ) -> Self + pub(crate) fn new<C, U>(bot: &'a Bot, chat_id: C, user_id: U) -> Self where C: Into<ChatId>, U: Into<i32>, { Self { - ctx, + bot, chat_id: chat_id.into(), user_id: user_id.into(), until_date: None, diff --git a/src/requests/mod.rs b/src/requests/mod.rs index 50f7ffaa..ba2ddfc9 100644 --- a/src/requests/mod.rs +++ b/src/requests/mod.rs @@ -1,6 +1,5 @@ //! Raw API functions. -use reqwest::Client; use serde::de::DeserializeOwned; use async_trait::async_trait; @@ -73,12 +72,3 @@ pub trait Request { /// Send this request. async fn send_boxed(self) -> ResponseResult<Self::Output>; } - -/// A context used to send all the requests. -#[derive(Debug, Clone)] -pub struct RequestContext<'a> { - /// An HTTPS client. - pub client: &'a Client, - /// A token of your bot. - pub token: &'a str, -} diff --git a/src/requests/pin_chat_message.rs b/src/requests/pin_chat_message.rs index 9033e4dd..d93f47bc 100644 --- a/src/requests/pin_chat_message.rs +++ b/src/requests/pin_chat_message.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::{ChatId, True}, }; @@ -13,7 +14,7 @@ use crate::{ #[derive(Debug, Clone, Serialize)] pub struct PinChatMessage<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, /// Unique identifier for the target chat or username /// of the target supergroup or channel (in the format @channelusername) pub chat_id: ChatId, @@ -22,17 +23,13 @@ pub struct PinChatMessage<'a> { } impl<'a> PinChatMessage<'a> { - pub(crate) fn new<C, M>( - ctx: RequestContext<'a>, - chat_id: C, - message_id: M, - ) -> Self + pub(crate) fn new<C, M>(bot: &'a Bot, chat_id: C, message_id: M) -> Self where C: Into<ChatId>, M: Into<i32>, { Self { - ctx, + bot, chat_id: chat_id.into(), message_id: message_id.into(), disable_notification: None, @@ -59,8 +56,8 @@ impl Request for PinChatMessage<'_> { impl PinChatMessage<'_> { async fn send(self) -> ResponseResult<True> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "pinChatMessage", &self, ) diff --git a/src/requests/promote_chat_member.rs b/src/requests/promote_chat_member.rs index d6c47d9d..f37676fb 100644 --- a/src/requests/promote_chat_member.rs +++ b/src/requests/promote_chat_member.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::{ChatId, True}, }; @@ -13,7 +14,7 @@ use crate::{ #[derive(Debug, Clone, Serialize)] pub struct PromoteChatMember<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, ///Unique identifier for the target chat or username of the target channel /// (in the format @channelusername) pub chat_id: ChatId, @@ -62,8 +63,8 @@ impl Request for PromoteChatMember<'_> { impl PromoteChatMember<'_> { pub async fn send(self) -> ResponseResult<True> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "promoteChatMember", &self, ) @@ -72,17 +73,13 @@ impl PromoteChatMember<'_> { } impl<'a> PromoteChatMember<'a> { - pub(crate) fn new<C, U>( - ctx: RequestContext<'a>, - chat_id: C, - user_id: U, - ) -> Self + pub(crate) fn new<C, U>(bot: &'a Bot, chat_id: C, user_id: U) -> Self where C: Into<ChatId>, U: Into<i32>, { Self { - ctx, + bot, chat_id: chat_id.into(), user_id: user_id.into(), can_change_info: None, diff --git a/src/requests/restrict_chat_member.rs b/src/requests/restrict_chat_member.rs index 5243484e..51dead83 100644 --- a/src/requests/restrict_chat_member.rs +++ b/src/requests/restrict_chat_member.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::{ChatId, ChatPermissions, True}, }; @@ -13,7 +14,7 @@ use crate::{ #[derive(Debug, Clone, Serialize)] pub struct RestrictChatMember<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, ///Unique identifier for the target chat or username of the target /// supergroup (in the format @supergroupusername) pub chat_id: ChatId, @@ -40,8 +41,8 @@ impl Request for RestrictChatMember<'_> { impl RestrictChatMember<'_> { async fn send(self) -> ResponseResult<True> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "restrictChatMember", &self, ) @@ -51,7 +52,7 @@ impl RestrictChatMember<'_> { impl<'a> RestrictChatMember<'a> { pub(crate) fn new<C, U, P>( - ctx: RequestContext<'a>, + bot: &'a Bot, chat_id: C, user_id: U, permissions: P, @@ -62,7 +63,7 @@ impl<'a> RestrictChatMember<'a> { P: Into<ChatPermissions>, { Self { - ctx, + bot, chat_id: chat_id.into(), user_id: user_id.into(), permissions: permissions.into(), diff --git a/src/requests/send_animation.rs b/src/requests/send_animation.rs index 96b08299..a509e6dc 100644 --- a/src/requests/send_animation.rs +++ b/src/requests/send_animation.rs @@ -1,7 +1,8 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::network; -use crate::requests::{Request, RequestContext, ResponseResult}; +use crate::requests::{Request, ResponseResult}; use crate::types::{ChatId, Message, ParseMode, ReplyMarkup}; ///TODO: add to bot api @@ -12,7 +13,7 @@ use crate::types::{ChatId, Message, ParseMode, ReplyMarkup}; #[derive(Debug, Clone, Serialize)] pub struct SendAnimation<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, ///Unique identifier for the target chat or username of the target channel /// (in the format @channelusername) pub chat_id: ChatId, @@ -76,8 +77,8 @@ impl Request for SendAnimation<'_> { impl SendAnimation<'_> { async fn send(self) -> ResponseResult<Message> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "sendAnimation", &self, ) @@ -86,17 +87,13 @@ impl SendAnimation<'_> { } impl<'a> SendAnimation<'a> { - pub(crate) fn new<C, S>( - ctx: RequestContext<'a>, - chat_id: C, - animation: S, - ) -> Self + pub(crate) fn new<C, S>(bot: &'a Bot, chat_id: C, animation: S) -> Self where C: Into<ChatId>, S: Into<String>, { Self { - ctx, + bot, chat_id: chat_id.into(), animation: animation.into(), duration: None, diff --git a/src/requests/send_audio.rs b/src/requests/send_audio.rs index 731f47ad..4b4411ef 100644 --- a/src/requests/send_audio.rs +++ b/src/requests/send_audio.rs @@ -1,10 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{ - form_builder::FormBuilder, Request, RequestContext, ResponseResult, - }, + requests::{form_builder::FormBuilder, Request, ResponseResult}, types::{ChatId, InputFile, Message, ParseMode, ReplyMarkup}, }; @@ -18,7 +17,7 @@ use crate::{ /// [`Message`]: crate::types::Message /// [`SendVoice`]: crate::requests::SendVoice pub struct SendAudio<'a> { - ctx: RequestContext<'a>, + bot: &'a Bot, /// Unique identifier for the target chat or username of the target channel /// (in the format @channelusername) @@ -86,10 +85,9 @@ impl SendAudio<'_> { .add("audio", self.audio) .add("thumb", self.thumb); - network::request_multipart( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "sendAudio", params.build(), ) @@ -98,17 +96,13 @@ impl SendAudio<'_> { } impl<'a> SendAudio<'a> { - pub(crate) fn new<C, A>( - ctx: RequestContext<'a>, - chat_id: C, - audio: A, - ) -> Self + pub(crate) fn new<C, A>(bot: &'a Bot, chat_id: C, audio: A) -> Self where C: Into<ChatId>, A: Into<InputFile>, { Self { - ctx, + bot, chat_id: chat_id.into(), audio: audio.into(), caption: None, diff --git a/src/requests/send_chat_action.rs b/src/requests/send_chat_action.rs index 54e898bb..403b88ba 100644 --- a/src/requests/send_chat_action.rs +++ b/src/requests/send_chat_action.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::{ChatAction, ChatId, True}, }; @@ -13,7 +14,7 @@ use crate::{ #[derive(Debug, Clone, Serialize)] pub struct SendChatAction<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, /// Unique identifier for the target chat or /// username of the target channel (in the format @channelusername) pub chat_id: ChatId, @@ -37,8 +38,8 @@ impl Request for SendChatAction<'_> { impl SendChatAction<'_> { pub async fn send(self) -> ResponseResult<True> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "sendChatAction", &self, ) @@ -47,17 +48,13 @@ impl SendChatAction<'_> { } impl<'a> SendChatAction<'a> { - pub(crate) fn new<Cid, Ca>( - ctx: RequestContext<'a>, - chat_id: Cid, - action: Ca, - ) -> Self + pub(crate) fn new<Cid, Ca>(bot: &'a Bot, chat_id: Cid, action: Ca) -> Self where Cid: Into<ChatId>, Ca: Into<ChatAction>, { Self { - ctx, + bot, chat_id: chat_id.into(), action: action.into(), } diff --git a/src/requests/send_contact.rs b/src/requests/send_contact.rs index 307fb321..f4465f48 100644 --- a/src/requests/send_contact.rs +++ b/src/requests/send_contact.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::{ChatId, Message, ReplyMarkup}, }; @@ -11,7 +12,7 @@ use crate::{ #[derive(Debug, Clone, Serialize)] pub struct SendContact<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, /// Unique identifier for the target chat or /// username of the target channel (in the format @channelusername) pub chat_id: ChatId, @@ -54,8 +55,8 @@ impl Request for SendContact<'_> { impl SendContact<'_> { pub async fn send(self) -> ResponseResult<Message> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "sendContact", &self, ) @@ -65,7 +66,7 @@ impl SendContact<'_> { impl<'a> SendContact<'a> { pub(crate) fn new<C, P, F>( - ctx: RequestContext<'a>, + bot: &'a Bot, chat_id: C, phone_number: P, first_name: F, @@ -76,7 +77,7 @@ impl<'a> SendContact<'a> { F: Into<String>, { Self { - ctx, + bot, chat_id: chat_id.into(), phone_number: phone_number.into(), first_name: first_name.into(), diff --git a/src/requests/send_document.rs b/src/requests/send_document.rs index 298baa02..93c5dc5b 100644 --- a/src/requests/send_document.rs +++ b/src/requests/send_document.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::{ChatId, Message, ParseMode, ReplyMarkup}, }; @@ -14,7 +15,7 @@ use crate::{ #[derive(Debug, Clone, Serialize)] pub struct SendDocument<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, /// Unique identifier for the target chat or username of the target /// channel (in the format @channelusername) pub chat_id: ChatId, @@ -69,8 +70,8 @@ impl Request for SendDocument<'_> { impl SendDocument<'_> { pub async fn send(self) -> ResponseResult<Message> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "sendDocument", &self, ) @@ -79,17 +80,13 @@ impl SendDocument<'_> { } impl<'a> SendDocument<'a> { - pub(crate) fn new<C, D>( - ctx: RequestContext<'a>, - chat_id: C, - document: D, - ) -> Self + pub(crate) fn new<C, D>(bot: &'a Bot, chat_id: C, document: D) -> Self where C: Into<ChatId>, D: Into<String>, { Self { - ctx, + bot, chat_id: chat_id.into(), document: document.into(), thumb: None, diff --git a/src/requests/send_location.rs b/src/requests/send_location.rs index a37162b2..5df4edf3 100644 --- a/src/requests/send_location.rs +++ b/src/requests/send_location.rs @@ -2,9 +2,10 @@ use serde::Serialize; use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::{ChatId, Message, ReplyMarkup}, }; @@ -13,7 +14,7 @@ use crate::{ /// is returned. pub struct SendLocation<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, /// Unique identifier for the target chat or username of the target channel /// (in the format @channelusername) @@ -50,8 +51,8 @@ impl Request for SendLocation<'_> { impl SendLocation<'_> { pub async fn send(self) -> ResponseResult<Message> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "sendLocation", &self, ) @@ -61,7 +62,7 @@ impl SendLocation<'_> { impl<'a> SendLocation<'a> { pub(crate) fn new<Lt, Lg, C>( - ctx: RequestContext<'a>, + bot: &'a Bot, chat_id: C, latitude: Lt, longitude: Lg, @@ -72,7 +73,7 @@ impl<'a> SendLocation<'a> { C: Into<ChatId>, { Self { - ctx, + bot, chat_id: chat_id.into(), latitude: latitude.into(), longitude: longitude.into(), diff --git a/src/requests/send_media_group.rs b/src/requests/send_media_group.rs index d189a926..f9e0995a 100644 --- a/src/requests/send_media_group.rs +++ b/src/requests/send_media_group.rs @@ -1,17 +1,16 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network::request_multipart, - requests::{ - form_builder::FormBuilder, Request, RequestContext, ResponseResult, - }, - types::{ChatId, InputMedia, Message, InputFile}, + requests::{form_builder::FormBuilder, Request, ResponseResult}, + types::{ChatId, InputFile, InputMedia, Message}, }; /// Use this method to send a group of photos or videos as an album. #[derive(Debug, Clone)] pub struct SendMediaGroup<'a> { - ctx: RequestContext<'a>, + bot: &'a Bot, pub chat_id: ChatId, pub media: Vec<InputMedia>, @@ -37,17 +36,20 @@ impl SendMediaGroup<'_> { .add("disable_notification", self.disable_notification) .add("reply_to_message_id", self.reply_to_message_id); - let form = self.media.into_iter().filter_map(|e| InputFile::from(e).into()) - .fold(form, |acc, path: std::path::PathBuf| - acc.add_file( - &path.file_name().unwrap().to_string_lossy().into_owned(), - path, - ) - ); + let form = self + .media + .into_iter() + .filter_map(|e| InputFile::from(e).into()) + .fold(form, |acc, path: std::path::PathBuf| { + acc.add_file( + &path.file_name().unwrap().to_string_lossy().into_owned(), + path, + ) + }); request_multipart( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "sendMediaGroup", form.build(), ) @@ -56,17 +58,13 @@ impl SendMediaGroup<'_> { } impl<'a> SendMediaGroup<'a> { - pub(crate) fn new<C, M>( - ctx: RequestContext<'a>, - chat_id: C, - media: M, - ) -> Self + pub(crate) fn new<C, M>(bot: &'a Bot, chat_id: C, media: M) -> Self where C: Into<ChatId>, M: Into<Vec<InputMedia>>, { SendMediaGroup { - ctx, + bot, chat_id: chat_id.into(), media: media.into(), disable_notification: None, diff --git a/src/requests/send_message.rs b/src/requests/send_message.rs index adca7c4f..371b640d 100644 --- a/src/requests/send_message.rs +++ b/src/requests/send_message.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::{ChatId, Message, ParseMode, ReplyMarkup}, }; @@ -11,7 +12,7 @@ use crate::{ /// returned. pub struct SendMessage<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, /// Unique identifier for the target chat or username of the target channel /// (in the format @channelusername) @@ -55,8 +56,8 @@ impl Request for SendMessage<'_> { impl SendMessage<'_> { pub async fn send(self) -> ResponseResult<Message> { network::request_json( - self.ctx.client, - self.ctx.token, + self.bot.client(), + self.bot.token(), "sendMessage", &self, ) @@ -65,17 +66,13 @@ impl SendMessage<'_> { } impl<'a> SendMessage<'a> { - pub(crate) fn new<C, S>( - ctx: RequestContext<'a>, - chat_id: C, - text: S, - ) -> Self + pub(crate) fn new<C, S>(bot: &'a Bot, chat_id: C, text: S) -> Self where C: Into<ChatId>, S: Into<String>, { SendMessage { - ctx, + bot, chat_id: chat_id.into(), text: text.into(), parse_mode: None, diff --git a/src/requests/send_photo.rs b/src/requests/send_photo.rs index 8092a55a..63a78666 100644 --- a/src/requests/send_photo.rs +++ b/src/requests/send_photo.rs @@ -1,10 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{ - form_builder::FormBuilder, Request, RequestContext, ResponseResult, - }, + requests::{form_builder::FormBuilder, Request, ResponseResult}, types::{ChatId, InputFile, Message, ParseMode, ReplyMarkup}, }; @@ -12,7 +11,7 @@ use crate::{ /// Use this method to send photos. On success, the sent [`Message`] is /// returned. pub struct SendPhoto<'a> { - ctx: RequestContext<'a>, + bot: &'a Bot, /// Unique identifier for the target chat or username of the target channel /// (in the format @channelusername) @@ -64,8 +63,8 @@ impl SendPhoto<'_> { .add("photo", self.photo); network::request_multipart( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "sendPhoto", params.build(), ) @@ -74,17 +73,13 @@ impl SendPhoto<'_> { } impl<'a> SendPhoto<'a> { - pub(crate) fn new<C, P>( - ctx: RequestContext<'a>, - chat_id: C, - photo: P, - ) -> Self + pub(crate) fn new<C, P>(bot: &'a Bot, chat_id: C, photo: P) -> Self where C: Into<ChatId>, P: Into<InputFile>, { Self { - ctx, + bot, chat_id: chat_id.into(), photo: photo.into(), caption: None, diff --git a/src/requests/send_poll.rs b/src/requests/send_poll.rs index 9d8466d3..4322f6e6 100644 --- a/src/requests/send_poll.rs +++ b/src/requests/send_poll.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::{ChatId, Message, ReplyMarkup}, }; @@ -11,7 +12,7 @@ use crate::{ #[derive(Debug, Clone, Serialize)] pub struct SendPoll<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, /// identifier for the target chat or username of the target channel (in /// the format @channelusername). A native poll can't be sent to a private /// chat. @@ -44,8 +45,8 @@ impl Request for SendPoll<'_> { impl SendPoll<'_> { pub async fn send(self) -> ResponseResult<Message> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "sendPoll", &self, ) @@ -55,7 +56,7 @@ impl SendPoll<'_> { impl<'a> SendPoll<'a> { pub(crate) fn new<C, Q, O>( - ctx: RequestContext<'a>, + bot: &'a Bot, chat_id: C, question: Q, options: O, @@ -66,7 +67,7 @@ impl<'a> SendPoll<'a> { O: Into<Vec<String>>, { Self { - ctx, + bot, chat_id: chat_id.into(), question: question.into(), options: options.into(), diff --git a/src/requests/send_venue.rs b/src/requests/send_venue.rs index 6fdd45e7..02eebeca 100644 --- a/src/requests/send_venue.rs +++ b/src/requests/send_venue.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::{ChatId, Message, ReplyMarkup}, }; @@ -11,7 +12,7 @@ use crate::{ #[derive(Debug, Clone, Serialize)] pub struct SendVenue<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, /// Unique identifier for the target chat or /// username of the target channel (in the format @channelusername) pub chat_id: ChatId, @@ -59,8 +60,8 @@ impl Request for SendVenue<'_> { impl SendVenue<'_> { pub async fn send(self) -> ResponseResult<Message> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "sendVenue", &self, ) @@ -70,7 +71,7 @@ impl SendVenue<'_> { impl<'a> SendVenue<'a> { pub(crate) fn new<Lt, Lg, C, T, A>( - ctx: RequestContext<'a>, + bot: &'a Bot, chat_id: C, latitude: Lt, longitude: Lg, @@ -85,7 +86,7 @@ impl<'a> SendVenue<'a> { A: Into<String>, { Self { - ctx, + bot, chat_id: chat_id.into(), latitude: latitude.into(), longitude: longitude.into(), diff --git a/src/requests/send_video.rs b/src/requests/send_video.rs index 5d8d6696..e6ab5712 100644 --- a/src/requests/send_video.rs +++ b/src/requests/send_video.rs @@ -1,7 +1,8 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::network; -use crate::requests::{Request, RequestContext, ResponseResult}; +use crate::requests::{Request, ResponseResult}; use crate::types::{ChatId, Message, ParseMode, ReplyMarkup}; //TODO: add action to bot api @@ -12,7 +13,7 @@ use crate::types::{ChatId, Message, ParseMode, ReplyMarkup}; #[derive(Debug, Clone, Serialize)] pub struct SendVideo<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, ///Unique identifier for the target chat or username of the target channel /// (in the format @channelusername) pub chat_id: ChatId, @@ -78,8 +79,8 @@ impl Request for SendVideo<'_> { impl SendVideo<'_> { async fn send(self) -> ResponseResult<Message> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "sendVideo", &self, ) @@ -88,17 +89,13 @@ impl SendVideo<'_> { } impl<'a> SendVideo<'a> { - pub(crate) fn new<C, V>( - ctx: RequestContext<'a>, - chat_id: C, - video: V, - ) -> Self + pub(crate) fn new<C, V>(bot: &'a Bot, chat_id: C, video: V) -> Self where C: Into<ChatId>, V: Into<String>, { Self { - ctx, + bot, chat_id: chat_id.into(), video: video.into(), duration: None, diff --git a/src/requests/send_video_note.rs b/src/requests/send_video_note.rs index c824bfdf..bd3eb316 100644 --- a/src/requests/send_video_note.rs +++ b/src/requests/send_video_note.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::{ChatId, Message, ReplyMarkup}, }; @@ -12,7 +13,7 @@ use crate::{ #[derive(Debug, Clone, Serialize)] pub struct SendVideoNote<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, ///Unique identifier for the target chat or username of the target channel /// (in the format @channelusername) pub chat_id: ChatId, @@ -65,8 +66,8 @@ impl Request for SendVideoNote<'_> { impl SendVideoNote<'_> { pub async fn send(self) -> ResponseResult<Message> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "sendVideoNote", &self, ) @@ -75,17 +76,13 @@ impl SendVideoNote<'_> { } impl<'a> SendVideoNote<'a> { - pub(crate) fn new<C, V>( - ctx: RequestContext<'a>, - chat_id: C, - video_note: V, - ) -> Self + pub(crate) fn new<C, V>(bot: &'a Bot, chat_id: C, video_note: V) -> Self where C: Into<ChatId>, V: Into<String>, { Self { - ctx, + bot, chat_id: chat_id.into(), video_note: video_note.into(), duration: None, diff --git a/src/requests/send_voice.rs b/src/requests/send_voice.rs index 75f139ea..b591defc 100644 --- a/src/requests/send_voice.rs +++ b/src/requests/send_voice.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::{ChatId, Message, ParseMode, ReplyMarkup}, }; @@ -15,7 +16,7 @@ use crate::{ #[derive(Debug, Clone, Serialize)] pub struct SendVoice<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, /// Unique identifier for the target chat or username of the target channel /// (in the format @channelusername) pub chat_id: ChatId, @@ -62,8 +63,8 @@ impl Request for SendVoice<'_> { impl SendVoice<'_> { pub async fn send(self) -> ResponseResult<Message> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "sendVoice", &self, ) @@ -72,17 +73,13 @@ impl SendVoice<'_> { } impl<'a> SendVoice<'a> { - pub(crate) fn new<C, V>( - ctx: RequestContext<'a>, - chat_id: C, - voice: V, - ) -> Self + pub(crate) fn new<C, V>(bot: &'a Bot, chat_id: C, voice: V) -> Self where C: Into<ChatId>, V: Into<String>, { Self { - ctx, + bot, chat_id: chat_id.into(), voice: voice.into(), caption: None, diff --git a/src/requests/stop_message_live_location.rs b/src/requests/stop_message_live_location.rs index 5455944e..541690c2 100644 --- a/src/requests/stop_message_live_location.rs +++ b/src/requests/stop_message_live_location.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::{ChatId, InlineKeyboardMarkup, Message}, }; @@ -12,7 +13,7 @@ use crate::{ #[derive(Debug, Clone, Serialize)] pub struct StopMessageLiveLocation<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, /// Required if inline_message_id is not specified. Unique identifier for /// the target chat or username of the target channel (in the format /// @channelusername) @@ -44,8 +45,8 @@ impl Request for StopMessageLiveLocation<'_> { impl StopMessageLiveLocation<'_> { pub async fn send(self) -> ResponseResult<Message> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "stopMessageLiveLocation", &self, ) @@ -54,9 +55,9 @@ impl StopMessageLiveLocation<'_> { } impl<'a> StopMessageLiveLocation<'a> { - pub(crate) fn new(ctx: RequestContext<'a>) -> Self { + pub(crate) fn new(bot: &'a Bot) -> Self { Self { - ctx, + bot, chat_id: None, message_id: None, inline_message_id: None, diff --git a/src/requests/unban_chat_member.rs b/src/requests/unban_chat_member.rs index fb772d0d..9a2f266c 100644 --- a/src/requests/unban_chat_member.rs +++ b/src/requests/unban_chat_member.rs @@ -1,8 +1,9 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::ChatId, }; @@ -13,7 +14,7 @@ use crate::{ #[derive(Debug, Clone, Serialize)] pub struct UnbanChatMember<'a> { #[serde(skip_serializing)] - ctx: RequestContext<'a>, + bot: &'a Bot, ///Unique identifier for the target group or username of the target /// supergroup or channel (in the format @channelusername) pub chat_id: ChatId, @@ -33,8 +34,8 @@ impl Request for UnbanChatMember<'_> { impl UnbanChatMember<'_> { pub async fn send(self) -> ResponseResult<bool> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "unbanChatMember", &self, ) @@ -43,17 +44,13 @@ impl UnbanChatMember<'_> { } impl<'a> UnbanChatMember<'a> { - pub(crate) fn new<C, U>( - ctx: RequestContext<'a>, - chat_id: C, - user_id: U, - ) -> Self + pub(crate) fn new<C, U>(bot: &'a Bot, chat_id: C, user_id: U) -> Self where C: Into<ChatId>, U: Into<i32>, { Self { - ctx, + bot, chat_id: chat_id.into(), user_id: user_id.into(), } diff --git a/src/requests/unpin_chat_message.rs b/src/requests/unpin_chat_message.rs index 6bde53d0..0571f671 100644 --- a/src/requests/unpin_chat_message.rs +++ b/src/requests/unpin_chat_message.rs @@ -1,15 +1,16 @@ use async_trait::async_trait; +use crate::bot::Bot; use crate::{ network, - requests::{Request, RequestContext, ResponseResult}, + requests::{Request, ResponseResult}, types::{ChatId, True}, }; #[derive(Debug, Clone, Serialize)] pub struct UnpinChatMessage<'a> { #[serde(skip_serializing)] - pub ctx: RequestContext<'a>, + pub bot: &'a Bot, pub chat_id: ChatId, } @@ -26,8 +27,8 @@ impl Request for UnpinChatMessage<'_> { impl UnpinChatMessage<'_> { pub async fn send(self) -> ResponseResult<True> { network::request_json( - &self.ctx.client, - &self.ctx.token, + self.bot.client(), + self.bot.token(), "unpinChatMessage", &self, ) @@ -36,12 +37,12 @@ impl UnpinChatMessage<'_> { } impl<'a> UnpinChatMessage<'a> { - pub(crate) fn new<C>(ctx: RequestContext<'a>, value: C) -> Self + pub(crate) fn new<C>(bot: &'a Bot, value: C) -> Self where C: Into<ChatId>, { Self { - ctx, + bot, chat_id: value.into(), } } diff --git a/src/types/inline_keyboard_markup.rs b/src/types/inline_keyboard_markup.rs index e52fccda..2ca92ee4 100644 --- a/src/types/inline_keyboard_markup.rs +++ b/src/types/inline_keyboard_markup.rs @@ -16,9 +16,7 @@ pub struct InlineKeyboardMarkup { /// /// Example: /// ``` -/// use telebofr::types::{ -/// InlineKeyboardButton, InlineKeyboardMarkup, -/// }; +/// use telebofr::types::{InlineKeyboardButton, InlineKeyboardMarkup}; /// /// let url_button = InlineKeyboardButton::url( /// "text".to_string(), diff --git a/src/types/message.rs b/src/types/message.rs index c658dd42..188d3bdb 100644 --- a/src/types/message.rs +++ b/src/types/message.rs @@ -1,4 +1,8 @@ -use crate::types::{Animation, Audio, Chat, Contact, Document, Game, InlineKeyboardMarkup, Invoice, Location, MessageEntity, PassportData, PhotoSize, Poll, Sticker, SuccessfulPayment, User, Venue, Video, VideoNote, Voice, True}; +use crate::types::{ + Animation, Audio, Chat, Contact, Document, Game, InlineKeyboardMarkup, + Invoice, Location, MessageEntity, PassportData, PhotoSize, Poll, Sticker, + SuccessfulPayment, True, User, Venue, Video, VideoNote, Voice, +}; #[derive(Debug, Deserialize, PartialEq, Clone)] pub struct Message { @@ -10,7 +14,6 @@ pub struct Message { pub kind: MessageKind, } - #[derive(Debug, Deserialize, PartialEq, Clone)] #[serde(untagged)] pub enum MessageKind { @@ -187,24 +190,25 @@ pub enum MediaKind { mod getters { use std::ops::Deref; + use crate::types::message::MessageKind::{Pinned, SupergroupChatCreated}; use crate::types::{ - self, Message, Sender, User, ForwardedFrom, Chat, MessageEntity, - PhotoSize, True, + self, message::{ - MessageKind::{ - Common, NewChatMembers, LeftChatMember, NewChatTitle, - NewChatPhoto, DeleteChatPhoto, GroupChatCreated, - ChannelChatCreated, Migrate, Invoice, SuccessfulPayment, - ConnectedWebsite, PassportData - }, + ForwardKind::{ChannelForward, NonChannelForward, Origin}, MediaKind::{ - Text, Video, Photo, Animation, Audio, Document, Voice, Game, - Sticker, VideoNote, Contact, Location, Poll, Venue + Animation, Audio, Contact, Document, Game, Location, Photo, + Poll, Sticker, Text, Venue, Video, VideoNote, Voice, + }, + MessageKind::{ + ChannelChatCreated, Common, ConnectedWebsite, DeleteChatPhoto, + GroupChatCreated, Invoice, LeftChatMember, Migrate, + NewChatMembers, NewChatPhoto, NewChatTitle, PassportData, + SuccessfulPayment, }, - ForwardKind::{NonChannelForward, ChannelForward, Origin} }, + Chat, ForwardedFrom, Message, MessageEntity, PhotoSize, Sender, True, + User, }; - use crate::types::message::MessageKind::{SupergroupChatCreated, Pinned}; /// Getters for [Message] fields from [telegram docs]. /// @@ -223,49 +227,67 @@ mod getters { /// `forward_sender_name` pub fn forward_from(&self) -> Option<&ForwardedFrom> { match &self.kind { - Common { forward_kind: NonChannelForward { from, .. }, .. } => - Some(from), + Common { + forward_kind: NonChannelForward { from, .. }, + .. + } => Some(from), _ => None, } } pub fn forward_from_chat(&self) -> Option<&Chat> { match &self.kind { - Common { forward_kind: ChannelForward { chat, .. }, .. } => - Some(chat), + Common { + forward_kind: ChannelForward { chat, .. }, + .. + } => Some(chat), _ => None, } } pub fn forward_from_message_id(&self) -> Option<&i32> { match &self.kind { - Common { forward_kind: ChannelForward { message_id, .. }, .. } => - Some(message_id), + Common { + forward_kind: ChannelForward { message_id, .. }, + .. + } => Some(message_id), _ => None, } } pub fn forward_signature(&self) -> Option<&str> { match &self.kind { - Common { forward_kind: ChannelForward { signature, .. }, .. } => - signature.as_ref().map(Deref::deref), + Common { + forward_kind: ChannelForward { signature, .. }, + .. + } => signature.as_ref().map(Deref::deref), _ => None, } } pub fn forward_date(&self) -> Option<&i32> { match &self.kind { - Common { forward_kind: ChannelForward { date, .. }, .. } | - Common { forward_kind: NonChannelForward { date, .. }, .. } => - Some(date), + Common { + forward_kind: ChannelForward { date, .. }, + .. + } + | Common { + forward_kind: NonChannelForward { date, .. }, + .. + } => Some(date), _ => None, } } pub fn reply_to_message(&self) -> Option<&Message> { match &self.kind { - Common { forward_kind: Origin { reply_to_message, .. }, .. } => - reply_to_message.as_ref().map(Deref::deref), + Common { + forward_kind: + Origin { + reply_to_message, .. + }, + .. + } => reply_to_message.as_ref().map(Deref::deref), _ => None, } } @@ -279,104 +301,172 @@ mod getters { pub fn media_group_id(&self) -> Option<&str> { match &self.kind { - Common { media_kind: Video { media_group_id, .. }, .. } | - Common { media_kind: Photo { media_group_id, .. }, .. } => - media_group_id.as_ref().map(Deref::deref), + Common { + media_kind: Video { media_group_id, .. }, + .. + } + | Common { + media_kind: Photo { media_group_id, .. }, + .. + } => media_group_id.as_ref().map(Deref::deref), _ => None, } } pub fn text(&self) -> Option<&str> { match &self.kind { - Common { media_kind: Text { text, .. }, .. } => Some(text), + Common { + media_kind: Text { text, .. }, + .. + } => Some(text), _ => None, } } pub fn entities(&self) -> Option<&[MessageEntity]> { match &self.kind { - Common { media_kind: Text { entities, .. }, .. } => - Some(entities), + Common { + media_kind: Text { entities, .. }, + .. + } => Some(entities), _ => None, } } pub fn caption_entities(&self) -> Option<&[MessageEntity]> { match &self.kind { - Common { media_kind: Animation { caption_entities, .. }, .. } | - Common { media_kind: Audio { caption_entities, .. }, .. } | - Common { media_kind: Document { caption_entities, .. }, .. } | - Common { media_kind: Photo { caption_entities, .. }, .. } | - Common { media_kind: Video { caption_entities, .. }, .. } | - Common { media_kind: Voice { caption_entities, .. }, .. } => - Some(caption_entities), + Common { + media_kind: + Animation { + caption_entities, .. + }, + .. + } + | Common { + media_kind: + Audio { + caption_entities, .. + }, + .. + } + | Common { + media_kind: + Document { + caption_entities, .. + }, + .. + } + | Common { + media_kind: + Photo { + caption_entities, .. + }, + .. + } + | Common { + media_kind: + Video { + caption_entities, .. + }, + .. + } + | Common { + media_kind: + Voice { + caption_entities, .. + }, + .. + } => Some(caption_entities), _ => None, } } pub fn audio(&self) -> Option<&types::Audio> { match &self.kind { - Common { media_kind: Audio { audio, .. }, .. } => Some(audio), + Common { + media_kind: Audio { audio, .. }, + .. + } => Some(audio), _ => None, } } pub fn document(&self) -> Option<&types::Document> { match &self.kind { - Common { media_kind: Document { document, .. }, .. } => - Some(document), + Common { + media_kind: Document { document, .. }, + .. + } => Some(document), _ => None, } } pub fn animation(&self) -> Option<&types::Animation> { match &self.kind { - Common { media_kind: Animation { animation, .. }, .. } => - Some(animation), + Common { + media_kind: Animation { animation, .. }, + .. + } => Some(animation), _ => None, } } pub fn game(&self) -> Option<&types::Game> { match &self.kind { - Common { media_kind: Game { game, .. }, .. } => Some(game), + Common { + media_kind: Game { game, .. }, + .. + } => Some(game), _ => None, } } pub fn photo(&self) -> Option<&[PhotoSize]> { match &self.kind { - Common { media_kind: Photo { photo, .. }, .. } => Some(photo), + Common { + media_kind: Photo { photo, .. }, + .. + } => Some(photo), _ => None, } } pub fn sticker(&self) -> Option<&types::Sticker> { match &self.kind { - Common { media_kind: Sticker { sticker, .. }, .. } => - Some(sticker), + Common { + media_kind: Sticker { sticker, .. }, + .. + } => Some(sticker), _ => None, } } pub fn video(&self) -> Option<&types::Video> { match &self.kind { - Common { media_kind: Video { video, .. }, .. } => Some(video), + Common { + media_kind: Video { video, .. }, + .. + } => Some(video), _ => None, } } pub fn voice(&self) -> Option<&types::Voice> { match &self.kind { - Common { media_kind: Voice { voice, .. }, .. } => Some(voice), + Common { + media_kind: Voice { voice, .. }, + .. + } => Some(voice), _ => None, } } pub fn video_note(&self) -> Option<&types::VideoNote> { match &self.kind { - Common { media_kind: VideoNote { video_note, .. }, .. } => - Some(video_note), + Common { + media_kind: VideoNote { video_note, .. }, + .. + } => Some(video_note), _ => None, } } @@ -384,12 +474,14 @@ mod getters { pub fn caption(&self) -> Option<&str> { match &self.kind { Common { media_kind, .. } => match media_kind { - Animation { caption, ..} | - Audio { caption, ..} | - Document { caption, ..} | - Photo { caption, ..} | - Video { caption, ..} | - Voice { caption, ..} => caption.as_ref().map(Deref::deref), + Animation { caption, .. } + | Audio { caption, .. } + | Document { caption, .. } + | Photo { caption, .. } + | Video { caption, .. } + | Voice { caption, .. } => { + caption.as_ref().map(Deref::deref) + } _ => None, }, _ => None, @@ -398,29 +490,40 @@ mod getters { pub fn contact(&self) -> Option<&types::Contact> { match &self.kind { - Common { media_kind: Contact { contact }, .. } => Some(contact), + Common { + media_kind: Contact { contact }, + .. + } => Some(contact), _ => None, } } pub fn location(&self) -> Option<&types::Location> { match &self.kind { - Common { media_kind: Location { location, .. }, .. } => - Some(location), + Common { + media_kind: Location { location, .. }, + .. + } => Some(location), _ => None, } } pub fn venue(&self) -> Option<&types::Venue> { match &self.kind { - Common { media_kind: Venue { venue, .. }, .. } => Some(venue), + Common { + media_kind: Venue { venue, .. }, + .. + } => Some(venue), _ => None, } } pub fn poll(&self) -> Option<&types::Poll> { match &self.kind { - Common { media_kind: Poll { poll, .. }, .. } => Some(poll), + Common { + media_kind: Poll { poll, .. }, + .. + } => Some(poll), _ => None, } } @@ -457,47 +560,55 @@ mod getters { // mb smt like `is_delete_chat_photo(&self) -> bool`? pub fn delete_chat_photo(&self) -> Option<True> { match &self.kind { - DeleteChatPhoto { delete_chat_photo } => - Some(*delete_chat_photo), + DeleteChatPhoto { delete_chat_photo } => { + Some(*delete_chat_photo) + } _ => None, } } pub fn group_chat_created(&self) -> Option<True> { match &self.kind { - GroupChatCreated { group_chat_created } => - Some(*group_chat_created), + GroupChatCreated { group_chat_created } => { + Some(*group_chat_created) + } _ => None, } } pub fn super_group_chat_created(&self) -> Option<True> { match &self.kind { - SupergroupChatCreated { supergroup_chat_created } => - Some(*supergroup_chat_created), + SupergroupChatCreated { + supergroup_chat_created, + } => Some(*supergroup_chat_created), _ => None, } } pub fn channel_chat_created(&self) -> Option<True> { match &self.kind { - ChannelChatCreated { channel_chat_created } => - Some(*channel_chat_created), + ChannelChatCreated { + channel_chat_created, + } => Some(*channel_chat_created), _ => None, } } pub fn migrate_to_chat_id(&self) -> Option<&i64> { match &self.kind { - Migrate { migrate_to_chat_id, .. } => Some(migrate_to_chat_id), + Migrate { + migrate_to_chat_id, .. + } => Some(migrate_to_chat_id), _ => None, } } pub fn migrate_from_chat_id(&self) -> Option<&i64> { match &self.kind { - Migrate { migrate_from_chat_id, .. } => - Some(migrate_from_chat_id), + Migrate { + migrate_from_chat_id, + .. + } => Some(migrate_from_chat_id), _ => None, } } @@ -516,25 +627,24 @@ mod getters { } } - pub fn successful_payment(&self) -> Option<&types::SuccessfulPayment> { match &self.kind { - SuccessfulPayment { successful_payment } => - Some(successful_payment), + SuccessfulPayment { successful_payment } => { + Some(successful_payment) + } _ => None, } } - pub fn connected_website(&self) -> Option<&str> { match &self.kind { - ConnectedWebsite { connected_website } => - Some(connected_website), + ConnectedWebsite { connected_website } => { + Some(connected_website) + } _ => None, } } - pub fn passport_data(&self) -> Option<&types::PassportData> { match &self.kind { PassportData { passport_data } => Some(passport_data), @@ -542,7 +652,6 @@ mod getters { } } - pub fn reply_markup(&self) -> Option<&types::InlineKeyboardMarkup> { match &self.kind { Common { reply_markup, .. } => reply_markup.as_ref(), @@ -552,7 +661,6 @@ mod getters { } } - #[cfg(test)] mod tests { use serde_json::from_str;