Merge pull request #63 from Gymmasssorla/context

Replace RequestContext with Bot
This commit is contained in:
Temirkhan Myrzamadi 2019-10-17 04:48:13 +00:00 committed by GitHub
commit c5a780d298
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 686 additions and 554 deletions

View file

@ -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)
}
}

View file

@ -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");

View file

@ -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
}
}

View file

@ -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)
}
}

View file

@ -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))
}
}

View file

@ -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);
}

View file

@ -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))))
}
}
}

View file

@ -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()))))
}
}

View file

@ -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> {}

View file

@ -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

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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))
}
}

View file

@ -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(),

View file

@ -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,
)

View file

@ -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(),
}
}

View file

@ -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 }
}
}

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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,
}

View file

@ -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,
)

View file

@ -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,

View file

@ -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(),

View file

@ -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,

View file

@ -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,

View file

@ -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(),
}

View file

@ -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(),

View file

@ -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,

View file

@ -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(),

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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(),

View file

@ -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(),

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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(),
}

View file

@ -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(),
}
}

View file

@ -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(),

View file

@ -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;