Use the default characters per line limit

This commit is contained in:
Temirkhan Myrzamadi 2020-07-26 23:47:02 +06:00
parent f9c192aad0
commit 38a1f470ad
121 changed files with 521 additions and 1580 deletions

View file

@ -100,7 +100,7 @@ async fn main() {
Dispatcher::new(bot) Dispatcher::new(bot)
.messages_handler(|rx: DispatcherHandlerRx<Message>| { .messages_handler(|rx: DispatcherHandlerRx<Message>| {
rx.for_each(|message| async move { rx.for_each(|message| async move {
message.answer("pong").send().await.log_on_error().await; message.answer_str("pong").await.log_on_error().await;
}) })
}) })
.dispatch() .dispatch()
@ -136,28 +136,18 @@ enum Command {
Help, Help,
#[command(description = "handle a username.")] #[command(description = "handle a username.")]
Username(String), Username(String),
#[command( #[command(description = "handle a username and an age.", parse_with = "split")]
description = "handle a username and an age.",
parse_with = "split"
)]
UsernameAndAge { username: String, age: u8 }, UsernameAndAge { username: String, age: u8 },
} }
async fn answer( async fn answer(cx: UpdateWithCx<Message>, command: Command) -> ResponseResult<()> {
cx: UpdateWithCx<Message>,
command: Command,
) -> ResponseResult<()> {
match command { match command {
Command::Help => cx.answer(Command::descriptions()).send().await?, Command::Help => cx.answer(Command::descriptions()).send().await?,
Command::Username(username) => { Command::Username(username) => {
cx.answer_str(format!("Your username is @{}.", username)).await? cx.answer_str(format!("Your username is @{}.", username)).await?
} }
Command::UsernameAndAge { username, age } => { Command::UsernameAndAge { username, age } => {
cx.answer_str(format!( cx.answer_str(format!("Your username is @{} and age is {}.", username, age)).await?
"Your username is @{} and age is {}.",
username, age
))
.await?
} }
}; };
@ -176,6 +166,7 @@ async fn handle_commands(rx: DispatcherHandlerRx<Message>) {
async fn main() { async fn main() {
// Setup is omitted... // Setup is omitted...
} }
``` ```
<div align="center"> <div align="center">
@ -195,9 +186,8 @@ Below is a bot, which asks you three questions and then sends the answers back t
```rust ```rust
// Imports are omitted... // Imports are omitted...
#[derive(Transition, SmartDefault, From)] #[derive(Transition, From)]
pub enum Dialogue { pub enum Dialogue {
#[default]
Start(StartState), Start(StartState),
ReceiveFullName(ReceiveFullNameState), ReceiveFullName(ReceiveFullNameState),
ReceiveAge(ReceiveAgeState), ReceiveAge(ReceiveAgeState),
@ -219,11 +209,7 @@ impl Default for Dialogue {
pub struct StartState; pub struct StartState;
#[teloxide(transition)] #[teloxide(transition)]
async fn start( async fn start(_state: StartState, cx: TransitionIn, _ans: String) -> TransitionOut<Dialogue> {
_state: StartState,
cx: TransitionIn,
_ans: String,
) -> TransitionOut<Dialogue> {
cx.answer_str("Let's start! What's your full name?").await?; cx.answer_str("Let's start! What's your full name?").await?;
next(ReceiveFullNameState) next(ReceiveFullNameState)
} }
@ -291,11 +277,8 @@ async fn receive_location(
cx: TransitionIn, cx: TransitionIn,
ans: String, ans: String,
) -> TransitionOut<Dialogue> { ) -> TransitionOut<Dialogue> {
cx.answer_str(format!( cx.answer_str(format!("Full name: {}\nAge: {}\nLocation: {}", state.full_name, state.age, ans))
"Full name: {}\nAge: {}\nLocation: {}", .await?;
state.full_name, state.age, ans
))
.await?;
exit() exit()
} }
``` ```
@ -320,19 +303,14 @@ async fn main() {
|DialogueWithCx { cx, dialogue }: In| async move { |DialogueWithCx { cx, dialogue }: In| async move {
// No panic because of std::convert::Infallible. // No panic because of std::convert::Infallible.
let dialogue = dialogue.unwrap(); let dialogue = dialogue.unwrap();
handle_message(cx, dialogue) handle_message(cx, dialogue).await.expect("Something wrong with the bot!")
.await
.expect("Something wrong with the bot!")
}, },
)) ))
.dispatch() .dispatch()
.await; .await;
} }
async fn handle_message( async fn handle_message(cx: UpdateWithCx<Message>, dialogue: Dialogue) -> TransitionOut<Dialogue> {
cx: UpdateWithCx<Message>,
dialogue: Dialogue,
) -> TransitionOut<Dialogue> {
match cx.update.text_owned() { match cx.update.text_owned() {
None => { None => {
cx.answer_str("Send me a text message.").await?; cx.answer_str("Send me a text message.").await?;
@ -341,6 +319,7 @@ async fn handle_message(
Some(ans) => dialogue.react(cx, ans).await, Some(ans) => dialogue.react(cx, ans).await,
} }
} }
``` ```
<div align="center"> <div align="center">

View file

@ -1,8 +1,6 @@
use std::str::FromStr; use std::str::FromStr;
use teloxide::{ use teloxide::{prelude::*, types::ChatPermissions, utils::command::BotCommand};
prelude::*, types::ChatPermissions, utils::command::BotCommand
};
use futures::future; use futures::future;
@ -81,9 +79,7 @@ async fn mute_user(cx: &Cx, time: u32) -> ResponseResult<()> {
.await?; .await?;
} }
None => { None => {
cx.reply_to("Use this command in reply to another message") cx.reply_to("Use this command in reply to another message").send().await?;
.send()
.await?;
} }
} }
Ok(()) Ok(())
@ -94,15 +90,10 @@ async fn kick_user(cx: &Cx) -> ResponseResult<()> {
match cx.update.reply_to_message() { match cx.update.reply_to_message() {
Some(mes) => { Some(mes) => {
// bot.unban_chat_member can also kicks a user from a group chat. // bot.unban_chat_member can also kicks a user from a group chat.
cx.bot cx.bot.unban_chat_member(cx.update.chat_id(), mes.from().unwrap().id).send().await?;
.unban_chat_member(cx.update.chat_id(), mes.from().unwrap().id)
.send()
.await?;
} }
None => { None => {
cx.reply_to("Use this command in reply to another message") cx.reply_to("Use this command in reply to another message").send().await?;
.send()
.await?;
} }
} }
Ok(()) Ok(())
@ -122,29 +113,18 @@ async fn ban_user(cx: &Cx, time: u32) -> ResponseResult<()> {
.await?; .await?;
} }
None => { None => {
cx.reply_to("Use this command in a reply to another message!") cx.reply_to("Use this command in a reply to another message!").send().await?;
.send()
.await?;
} }
} }
Ok(()) Ok(())
} }
async fn action( async fn action(cx: UpdateWithCx<Message>, command: Command) -> ResponseResult<()> {
cx: UpdateWithCx<Message>,
command: Command,
) -> ResponseResult<()> {
match command { match command {
Command::Help => { Command::Help => cx.answer(Command::descriptions()).send().await.map(|_| ())?,
cx.answer(Command::descriptions()).send().await.map(|_| ())?
}
Command::Kick => kick_user(&cx).await?, Command::Kick => kick_user(&cx).await?,
Command::Ban { time, unit } => { Command::Ban { time, unit } => ban_user(&cx, calc_restrict_time(time, unit)).await?,
ban_user(&cx, calc_restrict_time(time, unit)).await? Command::Mute { time, unit } => mute_user(&cx, calc_restrict_time(time, unit)).await?,
}
Command::Mute { time, unit } => {
mute_user(&cx, calc_restrict_time(time, unit)).await?
}
}; };
Ok(()) Ok(())

View file

@ -1,6 +1,4 @@
use crate::dialogue::{ use crate::dialogue::{states::receive_location::ReceiveLocationState, Dialogue};
states::receive_location::ReceiveLocationState, Dialogue,
};
use teloxide::prelude::*; use teloxide::prelude::*;
use teloxide_macros::teloxide; use teloxide_macros::teloxide;

View file

@ -14,10 +14,7 @@ async fn receive_location(
cx: TransitionIn, cx: TransitionIn,
ans: String, ans: String,
) -> TransitionOut<Dialogue> { ) -> TransitionOut<Dialogue> {
cx.answer_str(format!( cx.answer_str(format!("Full name: {}\nAge: {}\nLocation: {}", state.full_name, state.age, ans))
"Full name: {}\nAge: {}\nLocation: {}", .await?;
state.full_name, state.age, ans
))
.await?;
exit() exit()
} }

View file

@ -6,11 +6,7 @@ use teloxide_macros::teloxide;
pub struct StartState; pub struct StartState;
#[teloxide(transition)] #[teloxide(transition)]
async fn start( async fn start(_state: StartState, cx: TransitionIn, _ans: String) -> TransitionOut<Dialogue> {
_state: StartState,
cx: TransitionIn,
_ans: String,
) -> TransitionOut<Dialogue> {
cx.answer_str("Let's start! What's your full name?").await?; cx.answer_str("Let's start! What's your full name?").await?;
next(ReceiveFullNameState) next(ReceiveFullNameState)
} }

View file

@ -44,19 +44,14 @@ async fn run() {
|DialogueWithCx { cx, dialogue }: In| async move { |DialogueWithCx { cx, dialogue }: In| async move {
// No panic because of std::convert::Infallible. // No panic because of std::convert::Infallible.
let dialogue = dialogue.unwrap(); let dialogue = dialogue.unwrap();
handle_message(cx, dialogue) handle_message(cx, dialogue).await.expect("Something wrong with the bot!")
.await
.expect("Something wrong with the bot!")
}, },
)) ))
.dispatch() .dispatch()
.await; .await;
} }
async fn handle_message( async fn handle_message(cx: UpdateWithCx<Message>, dialogue: Dialogue) -> TransitionOut<Dialogue> {
cx: UpdateWithCx<Message>,
dialogue: Dialogue,
) -> TransitionOut<Dialogue> {
match cx.update.text_owned() { match cx.update.text_owned() {
None => { None => {
cx.answer_str("Send me a text message.").await?; cx.answer_str("Send me a text message.").await?;

View file

@ -14,19 +14,14 @@ async fn main() {
run().await; run().await;
} }
async fn handle_rejection( async fn handle_rejection(error: warp::Rejection) -> Result<impl warp::Reply, Infallible> {
error: warp::Rejection,
) -> Result<impl warp::Reply, Infallible> {
log::error!("Cannot process the request due to: {:?}", error); log::error!("Cannot process the request due to: {:?}", error);
Ok(StatusCode::INTERNAL_SERVER_ERROR) Ok(StatusCode::INTERNAL_SERVER_ERROR)
} }
pub async fn webhook<'a>( pub async fn webhook<'a>(bot: Bot) -> impl update_listeners::UpdateListener<Infallible> {
bot: Bot,
) -> impl update_listeners::UpdateListener<Infallible> {
// Heroku defines auto defines a port value // Heroku defines auto defines a port value
let teloxide_token = env::var("TELOXIDE_TOKEN") let teloxide_token = env::var("TELOXIDE_TOKEN").expect("TELOXIDE_TOKEN env variable missing");
.expect("TELOXIDE_TOKEN env variable missing");
let port: u16 = env::var("PORT") let port: u16 = env::var("PORT")
.expect("PORT env variable missing") .expect("PORT env variable missing")
.parse() .parse()
@ -58,8 +53,7 @@ pub async fn webhook<'a>(
} }
}; };
if let Ok(update) = try_parse { if let Ok(update) = try_parse {
tx.send(Ok(update)) tx.send(Ok(update)).expect("Cannot send an incoming update from the webhook")
.expect("Cannot send an incoming update from the webhook")
} }
StatusCode::OK StatusCode::OK
@ -87,9 +81,7 @@ async fn run() {
}) })
.dispatch_with_listener( .dispatch_with_listener(
webhook(bot).await, webhook(bot).await,
LoggingErrorHandler::with_custom_text( LoggingErrorHandler::with_custom_text("An error from the update listener"),
"An error from the update listener",
),
) )
.await; .await;
} }

View file

@ -1,5 +1,5 @@
// The version of ngrok ping-pong-bot, which uses a webhook to receive updates from // The version of ngrok ping-pong-bot, which uses a webhook to receive updates
// Telegram, instead of long polling. // from Telegram, instead of long polling.
use teloxide::{dispatching::update_listeners, prelude::*}; use teloxide::{dispatching::update_listeners, prelude::*};
@ -14,16 +14,12 @@ async fn main() {
run().await; run().await;
} }
async fn handle_rejection( async fn handle_rejection(error: warp::Rejection) -> Result<impl warp::Reply, Infallible> {
error: warp::Rejection,
) -> Result<impl warp::Reply, Infallible> {
log::error!("Cannot process the request due to: {:?}", error); log::error!("Cannot process the request due to: {:?}", error);
Ok(StatusCode::INTERNAL_SERVER_ERROR) Ok(StatusCode::INTERNAL_SERVER_ERROR)
} }
pub async fn webhook<'a>( pub async fn webhook<'a>(bot: Bot) -> impl update_listeners::UpdateListener<Infallible> {
bot: Bot,
) -> impl update_listeners::UpdateListener<Infallible> {
// You might want to specify a self-signed certificate via .certificate // You might want to specify a self-signed certificate via .certificate
// method on SetWebhook. // method on SetWebhook.
bot.set_webhook("Your HTTPS ngrok URL here. Get it by 'ngrok http 80'") bot.set_webhook("Your HTTPS ngrok URL here. Get it by 'ngrok http 80'")
@ -37,8 +33,7 @@ pub async fn webhook<'a>(
.and(warp::body::json()) .and(warp::body::json())
.map(move |json: serde_json::Value| { .map(move |json: serde_json::Value| {
if let Ok(update) = Update::try_parse(&json) { if let Ok(update) = Update::try_parse(&json) {
tx.send(Ok(update)) tx.send(Ok(update)).expect("Cannot send an incoming update from the webhook")
.expect("Cannot send an incoming update from the webhook")
} }
StatusCode::OK StatusCode::OK
@ -68,9 +63,7 @@ async fn run() {
}) })
.dispatch_with_listener( .dispatch_with_listener(
webhook(bot).await, webhook(bot).await,
LoggingErrorHandler::with_custom_text( LoggingErrorHandler::with_custom_text("An error from the update listener"),
"An error from the update listener",
),
) )
.await; .await;
} }

View file

@ -38,26 +38,19 @@ async fn run() {
|DialogueWithCx { cx, dialogue }: In| async move { |DialogueWithCx { cx, dialogue }: In| async move {
// No panic because of std::convert::Infallible. // No panic because of std::convert::Infallible.
let dialogue = dialogue.unwrap(); let dialogue = dialogue.unwrap();
handle_message(cx, dialogue) handle_message(cx, dialogue).await.expect("Something wrong with the bot!")
.await
.expect("Something wrong with the bot!")
}, },
// You can also choose serializer::JSON or serializer::CBOR // You can also choose serializer::JSON or serializer::CBOR
// All serializers but JSON require enabling feature // All serializers but JSON require enabling feature
// "serializer-<name>", e. g. "serializer-cbor" // "serializer-<name>", e. g. "serializer-cbor"
// or "serializer-bincode" // or "serializer-bincode"
RedisStorage::open("redis://127.0.0.1:6379", Bincode) RedisStorage::open("redis://127.0.0.1:6379", Bincode).await.unwrap(),
.await
.unwrap(),
)) ))
.dispatch() .dispatch()
.await; .await;
} }
async fn handle_message( async fn handle_message(cx: UpdateWithCx<Message>, dialogue: Dialogue) -> TransitionOut<Dialogue> {
cx: UpdateWithCx<Message>,
dialogue: Dialogue,
) -> TransitionOut<Dialogue> {
match cx.update.text_owned() { match cx.update.text_owned() {
None => { None => {
cx.answer_str("Send me a text message.").await?; cx.answer_str("Send me a text message.").await?;

View file

@ -4,17 +4,9 @@ use teloxide_macros::teloxide;
use super::states::*; use super::states::*;
#[teloxide(transition)] #[teloxide(transition)]
async fn start( async fn start(state: StartState, cx: TransitionIn, ans: String) -> TransitionOut<Dialogue> {
state: StartState,
cx: TransitionIn,
ans: String,
) -> TransitionOut<Dialogue> {
if let Ok(number) = ans.parse() { if let Ok(number) = ans.parse() {
cx.answer_str(format!( cx.answer_str(format!("Remembered number {}. Now use /get or /reset", number)).await?;
"Remembered number {}. Now use /get or /reset",
number
))
.await?;
next(HaveNumberState { number }) next(HaveNumberState { number })
} else { } else {
cx.answer_str("Please, send me a number").await?; cx.answer_str("Please, send me a number").await?;

View file

@ -26,10 +26,7 @@ async fn run() {
let previous = MESSAGES_TOTAL.fetch_add(1, Ordering::Relaxed); let previous = MESSAGES_TOTAL.fetch_add(1, Ordering::Relaxed);
message message
.answer_str(format!( .answer_str(format!("I received {} messages in total.", previous))
"I received {} messages in total.",
previous
))
.await .await
.log_on_error() .log_on_error()
.await; .await;

View file

@ -7,28 +7,18 @@ enum Command {
Help, Help,
#[command(description = "handle a username.")] #[command(description = "handle a username.")]
Username(String), Username(String),
#[command( #[command(description = "handle a username and an age.", parse_with = "split")]
description = "handle a username and an age.",
parse_with = "split"
)]
UsernameAndAge { username: String, age: u8 }, UsernameAndAge { username: String, age: u8 },
} }
async fn answer( async fn answer(cx: UpdateWithCx<Message>, command: Command) -> ResponseResult<()> {
cx: UpdateWithCx<Message>,
command: Command,
) -> ResponseResult<()> {
match command { match command {
Command::Help => cx.answer(Command::descriptions()).send().await?, Command::Help => cx.answer(Command::descriptions()).send().await?,
Command::Username(username) => { Command::Username(username) => {
cx.answer_str(format!("Your username is @{}.", username)).await? cx.answer_str(format!("Your username is @{}.", username)).await?
} }
Command::UsernameAndAge { username, age } => { Command::UsernameAndAge { username, age } => {
cx.answer_str(format!( cx.answer_str(format!("Your username is @{} and age is {}.", username, age)).await?
"Your username is @{} and age is {}.",
username, age
))
.await?
} }
}; };

View file

@ -1,7 +1,6 @@
format_code_in_doc_comments = true format_code_in_doc_comments = true
wrap_comments = true wrap_comments = true
format_strings = true format_strings = true
max_width = 80
merge_imports = true merge_imports = true
use_small_heuristics = "Max" use_small_heuristics = "Max"
use_field_init_shorthand = true use_field_init_shorthand = true

View file

@ -1,27 +1,23 @@
use crate::{ use crate::{
requests::{ requests::{
AddStickerToSet, AnswerCallbackQuery, AnswerInlineQuery, AddStickerToSet, AnswerCallbackQuery, AnswerInlineQuery, AnswerPreCheckoutQuery,
AnswerPreCheckoutQuery, AnswerShippingQuery, CreateNewStickerSet, AnswerShippingQuery, CreateNewStickerSet, DeleteChatPhoto, DeleteChatStickerSet,
DeleteChatPhoto, DeleteChatStickerSet, DeleteMessage, DeleteMessage, DeleteStickerFromSet, DeleteWebhook, EditMessageCaption,
DeleteStickerFromSet, DeleteWebhook, EditMessageCaption, EditMessageLiveLocation, EditMessageMedia, EditMessageReplyMarkup, EditMessageText,
EditMessageLiveLocation, EditMessageMedia, EditMessageReplyMarkup, ExportChatInviteLink, ForwardMessage, GetChat, GetChatAdministrators, GetChatMember,
EditMessageText, ExportChatInviteLink, ForwardMessage, GetChat, GetChatMembersCount, GetFile, GetGameHighScores, GetMe, GetStickerSet, GetUpdates,
GetChatAdministrators, GetChatMember, GetChatMembersCount, GetFile, GetUserProfilePhotos, GetWebhookInfo, KickChatMember, LeaveChat, PinChatMessage,
GetGameHighScores, GetMe, GetStickerSet, GetUpdates, PromoteChatMember, RestrictChatMember, SendAnimation, SendAudio, SendChatAction,
GetUserProfilePhotos, GetWebhookInfo, KickChatMember, LeaveChat, SendChatActionKind, SendContact, SendDocument, SendGame, SendInvoice, SendLocation,
PinChatMessage, PromoteChatMember, RestrictChatMember, SendAnimation, SendMediaGroup, SendMessage, SendPhoto, SendPoll, SendSticker, SendVenue, SendVideo,
SendAudio, SendChatAction, SendChatActionKind, SendContact, SendVideoNote, SendVoice, SetChatAdministratorCustomTitle, SetChatDescription,
SendDocument, SendGame, SendInvoice, SendLocation, SendMediaGroup, SetChatPermissions, SetChatPhoto, SetChatStickerSet, SetChatTitle, SetGameScore,
SendMessage, SendPhoto, SendPoll, SendSticker, SendVenue, SendVideo, SetStickerPositionInSet, SetWebhook, StopMessageLiveLocation, StopPoll, UnbanChatMember,
SendVideoNote, SendVoice, SetChatAdministratorCustomTitle,
SetChatDescription, SetChatPermissions, SetChatPhoto,
SetChatStickerSet, SetChatTitle, SetGameScore, SetStickerPositionInSet,
SetWebhook, StopMessageLiveLocation, StopPoll, UnbanChatMember,
UnpinChatMessage, UploadStickerFile, UnpinChatMessage, UploadStickerFile,
}, },
types::{ types::{
ChatId, ChatOrInlineMessage, ChatPermissions, InlineQueryResult, ChatId, ChatOrInlineMessage, ChatPermissions, InlineQueryResult, InputFile, InputMedia,
InputFile, InputMedia, LabeledPrice, LabeledPrice,
}, },
Bot, Bot,
}; };
@ -121,8 +117,9 @@ impl Bot {
{ {
match self.parse_mode.deref() { match self.parse_mode.deref() {
None => SendMessage::new(self.clone(), chat_id, text), None => SendMessage::new(self.clone(), chat_id, text),
Some(parse_mode) => SendMessage::new(self.clone(), chat_id, text) Some(parse_mode) => {
.parse_mode(*parse_mode.deref()), SendMessage::new(self.clone(), chat_id, text).parse_mode(*parse_mode.deref())
}
} }
} }
@ -184,8 +181,9 @@ impl Bot {
{ {
match self.parse_mode.deref() { match self.parse_mode.deref() {
None => SendPhoto::new(self.clone(), chat_id, photo), None => SendPhoto::new(self.clone(), chat_id, photo),
Some(parse_mode) => SendPhoto::new(self.clone(), chat_id, photo) Some(parse_mode) => {
.parse_mode(*parse_mode.deref()), SendPhoto::new(self.clone(), chat_id, photo).parse_mode(*parse_mode.deref())
}
} }
} }
@ -206,8 +204,9 @@ impl Bot {
{ {
match self.parse_mode.deref() { match self.parse_mode.deref() {
None => SendAudio::new(self.clone(), chat_id, audio), None => SendAudio::new(self.clone(), chat_id, audio),
Some(parse_mode) => SendAudio::new(self.clone(), chat_id, audio) Some(parse_mode) => {
.parse_mode(*parse_mode.deref()), SendAudio::new(self.clone(), chat_id, audio).parse_mode(*parse_mode.deref())
}
} }
} }
@ -235,19 +234,14 @@ impl Bot {
/// ///
/// [a default parse mode]: crate::BotBuilder::parse_mode /// [a default parse mode]: crate::BotBuilder::parse_mode
/// [`BotBuilder`]: crate::BotBuilder /// [`BotBuilder`]: crate::BotBuilder
pub fn send_document<C>( pub fn send_document<C>(&self, chat_id: C, document: InputFile) -> SendDocument
&self,
chat_id: C,
document: InputFile,
) -> SendDocument
where where
C: Into<ChatId>, C: Into<ChatId>,
{ {
match self.parse_mode.deref() { match self.parse_mode.deref() {
None => SendDocument::new(self.clone(), chat_id, document), None => SendDocument::new(self.clone(), chat_id, document),
Some(parse_mode) => { Some(parse_mode) => {
SendDocument::new(self.clone(), chat_id, document) SendDocument::new(self.clone(), chat_id, document).parse_mode(*parse_mode.deref())
.parse_mode(*parse_mode.deref())
} }
} }
} }
@ -285,8 +279,9 @@ impl Bot {
{ {
match self.parse_mode.deref() { match self.parse_mode.deref() {
None => SendVideo::new(self.clone(), chat_id, video), None => SendVideo::new(self.clone(), chat_id, video),
Some(parse_mode) => SendVideo::new(self.clone(), chat_id, video) Some(parse_mode) => {
.parse_mode(*parse_mode.deref()), SendVideo::new(self.clone(), chat_id, video).parse_mode(*parse_mode.deref())
}
} }
} }
@ -308,19 +303,14 @@ impl Bot {
/// ///
/// [a default parse mode]: crate::BotBuilder::parse_mode /// [a default parse mode]: crate::BotBuilder::parse_mode
/// [`BotBuilder`]: crate::BotBuilder /// [`BotBuilder`]: crate::BotBuilder
pub fn send_animation<C>( pub fn send_animation<C>(&self, chat_id: C, animation: InputFile) -> SendAnimation
&self,
chat_id: C,
animation: InputFile,
) -> SendAnimation
where where
C: Into<ChatId>, C: Into<ChatId>,
{ {
match self.parse_mode.deref() { match self.parse_mode.deref() {
None => SendAnimation::new(self.clone(), chat_id, animation), None => SendAnimation::new(self.clone(), chat_id, animation),
Some(parse_mode) => { Some(parse_mode) => {
SendAnimation::new(self.clone(), chat_id, animation) SendAnimation::new(self.clone(), chat_id, animation).parse_mode(*parse_mode.deref())
.parse_mode(*parse_mode.deref())
} }
} }
} }
@ -364,8 +354,9 @@ impl Bot {
{ {
match self.parse_mode.deref() { match self.parse_mode.deref() {
None => SendVoice::new(self.clone(), chat_id, voice), None => SendVoice::new(self.clone(), chat_id, voice),
Some(parse_mode) => SendVoice::new(self.clone(), chat_id, voice) Some(parse_mode) => {
.parse_mode(*parse_mode.deref()), SendVoice::new(self.clone(), chat_id, voice).parse_mode(*parse_mode.deref())
}
} }
} }
@ -390,11 +381,7 @@ impl Bot {
/// [`InputFile::FileId`]: crate::types::InputFile::FileId /// [`InputFile::FileId`]: crate::types::InputFile::FileId
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files /// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
pub fn send_video_note<C>( pub fn send_video_note<C>(&self, chat_id: C, video_note: InputFile) -> SendVideoNote
&self,
chat_id: C,
video_note: InputFile,
) -> SendVideoNote
where where
C: Into<ChatId>, C: Into<ChatId>,
{ {
@ -427,12 +414,7 @@ impl Bot {
/// target supergroup or channel (in the format `@channelusername`). /// target supergroup or channel (in the format `@channelusername`).
/// - `latitude`: Latitude of the location. /// - `latitude`: Latitude of the location.
/// - `longitude`: Latitude of the location. /// - `longitude`: Latitude of the location.
pub fn send_location<C>( pub fn send_location<C>(&self, chat_id: C, latitude: f32, longitude: f32) -> SendLocation
&self,
chat_id: C,
latitude: f32,
longitude: f32,
) -> SendLocation
where where
C: Into<ChatId>, C: Into<ChatId>,
{ {
@ -460,12 +442,7 @@ impl Bot {
latitude: f32, latitude: f32,
longitude: f32, longitude: f32,
) -> EditMessageLiveLocation { ) -> EditMessageLiveLocation {
EditMessageLiveLocation::new( EditMessageLiveLocation::new(self.clone(), chat_or_inline_message, latitude, longitude)
self.clone(),
chat_or_inline_message,
latitude,
longitude,
)
} }
/// Use this method to stop updating a live location message before /// Use this method to stop updating a live location message before
@ -509,14 +486,7 @@ impl Bot {
T: Into<String>, T: Into<String>,
A: Into<String>, A: Into<String>,
{ {
SendVenue::new( SendVenue::new(self.clone(), chat_id, latitude, longitude, title, address)
self.clone(),
chat_id,
latitude,
longitude,
title,
address,
)
} }
/// Use this method to send phone contacts. /// Use this method to send phone contacts.
@ -529,12 +499,7 @@ impl Bot {
/// target supergroup or channel (in the format `@channelusername`). /// target supergroup or channel (in the format `@channelusername`).
/// - `phone_number`: Contact's phone number. /// - `phone_number`: Contact's phone number.
/// - `first_name`: Contact's first name. /// - `first_name`: Contact's first name.
pub fn send_contact<C, P, F>( pub fn send_contact<C, P, F>(&self, chat_id: C, phone_number: P, first_name: F) -> SendContact
&self,
chat_id: C,
phone_number: P,
first_name: F,
) -> SendContact
where where
C: Into<ChatId>, C: Into<ChatId>,
P: Into<String>, P: Into<String>,
@ -555,12 +520,7 @@ impl Bot {
/// - `question`: Poll question, 1-255 characters. /// - `question`: Poll question, 1-255 characters.
/// - `options`: List of answer options, 2-10 strings 1-100 characters /// - `options`: List of answer options, 2-10 strings 1-100 characters
/// each. /// each.
pub fn send_poll<C, Q, O>( pub fn send_poll<C, Q, O>(&self, chat_id: C, question: Q, options: O) -> SendPoll
&self,
chat_id: C,
question: Q,
options: O,
) -> SendPoll
where where
C: Into<ChatId>, C: Into<ChatId>,
Q: Into<String>, Q: Into<String>,
@ -592,11 +552,7 @@ impl Bot {
/// ///
/// [ImageBot]: https://t.me/imagebot /// [ImageBot]: https://t.me/imagebot
/// [`Bot::send_chat_action`]: crate::Bot::send_chat_action /// [`Bot::send_chat_action`]: crate::Bot::send_chat_action
pub fn send_chat_action<C>( pub fn send_chat_action<C>(&self, chat_id: C, action: SendChatActionKind) -> SendChatAction
&self,
chat_id: C,
action: SendChatActionKind,
) -> SendChatAction
where where
C: Into<ChatId>, C: Into<ChatId>,
{ {
@ -609,10 +565,7 @@ impl Bot {
/// ///
/// # Params /// # Params
/// - `user_id`: Unique identifier of the target user. /// - `user_id`: Unique identifier of the target user.
pub fn get_user_profile_photos( pub fn get_user_profile_photos(&self, user_id: i32) -> GetUserProfilePhotos {
&self,
user_id: i32,
) -> GetUserProfilePhotos {
GetUserProfilePhotos::new(self.clone(), user_id) GetUserProfilePhotos::new(self.clone(), user_id)
} }
@ -660,11 +613,7 @@ impl Bot {
/// - `user_id`: Unique identifier of the target user. /// - `user_id`: Unique identifier of the target user.
/// ///
/// [unbanned]: crate::Bot::unban_chat_member /// [unbanned]: crate::Bot::unban_chat_member
pub fn kick_chat_member<C>( pub fn kick_chat_member<C>(&self, chat_id: C, user_id: i32) -> KickChatMember
&self,
chat_id: C,
user_id: i32,
) -> KickChatMember
where where
C: Into<ChatId>, C: Into<ChatId>,
{ {
@ -682,11 +631,7 @@ impl Bot {
/// - `chat_id`: Unique identifier for the target chat or username of the /// - `chat_id`: Unique identifier for the target chat or username of the
/// target supergroup or channel (in the format `@channelusername`). /// target supergroup or channel (in the format `@channelusername`).
/// - `user_id`: Unique identifier of the target user. /// - `user_id`: Unique identifier of the target user.
pub fn unban_chat_member<C>( pub fn unban_chat_member<C>(&self, chat_id: C, user_id: i32) -> UnbanChatMember
&self,
chat_id: C,
user_id: i32,
) -> UnbanChatMember
where where
C: Into<ChatId>, C: Into<ChatId>,
{ {
@ -731,11 +676,7 @@ impl Bot {
/// - `chat_id`: Unique identifier for the target chat or username of the /// - `chat_id`: Unique identifier for the target chat or username of the
/// target supergroup or channel (in the format `@channelusername`). /// target supergroup or channel (in the format `@channelusername`).
/// - `user_id`: Unique identifier of the target user. /// - `user_id`: Unique identifier of the target user.
pub fn promote_chat_member<C>( pub fn promote_chat_member<C>(&self, chat_id: C, user_id: i32) -> PromoteChatMember
&self,
chat_id: C,
user_id: i32,
) -> PromoteChatMember
where where
C: Into<ChatId>, C: Into<ChatId>,
{ {
@ -806,11 +747,7 @@ impl Bot {
/// - `chat_id`: Unique identifier for the target chat or username of the /// - `chat_id`: Unique identifier for the target chat or username of the
/// target supergroup or channel (in the format `@channelusername`). /// target supergroup or channel (in the format `@channelusername`).
/// - `photo`: New chat photo, uploaded using `multipart/form-data`. /// - `photo`: New chat photo, uploaded using `multipart/form-data`.
pub fn set_chat_photo<C>( pub fn set_chat_photo<C>(&self, chat_id: C, photo: InputFile) -> SetChatPhoto
&self,
chat_id: C,
photo: InputFile,
) -> SetChatPhoto
where where
C: Into<ChatId>, C: Into<ChatId>,
{ {
@ -883,11 +820,7 @@ impl Bot {
/// - `chat_id`: Unique identifier for the target chat or username of the /// - `chat_id`: Unique identifier for the target chat or username of the
/// target supergroup or channel (in the format `@channelusername`). /// target supergroup or channel (in the format `@channelusername`).
/// - `message_id`: Identifier of a message to pin. /// - `message_id`: Identifier of a message to pin.
pub fn pin_chat_message<C>( pub fn pin_chat_message<C>(&self, chat_id: C, message_id: i32) -> PinChatMessage
&self,
chat_id: C,
message_id: i32,
) -> PinChatMessage
where where
C: Into<ChatId>, C: Into<ChatId>,
{ {
@ -953,10 +886,7 @@ impl Bot {
/// # Params /// # Params
/// - `chat_id`: Unique identifier for the target chat or username of the /// - `chat_id`: Unique identifier for the target chat or username of the
/// target supergroup or channel (in the format `@channelusername`). /// target supergroup or channel (in the format `@channelusername`).
pub fn get_chat_administrators<C>( pub fn get_chat_administrators<C>(&self, chat_id: C) -> GetChatAdministrators
&self,
chat_id: C,
) -> GetChatAdministrators
where where
C: Into<ChatId>, C: Into<ChatId>,
{ {
@ -1006,11 +936,7 @@ impl Bot {
/// target supergroup (in the format `@supergroupusername`). /// target supergroup (in the format `@supergroupusername`).
/// - `sticker_set_name`: Name of the sticker set to be set as the group /// - `sticker_set_name`: Name of the sticker set to be set as the group
/// sticker set. /// sticker set.
pub fn set_chat_sticker_set<C, S>( pub fn set_chat_sticker_set<C, S>(&self, chat_id: C, sticker_set_name: S) -> SetChatStickerSet
&self,
chat_id: C,
sticker_set_name: S,
) -> SetChatStickerSet
where where
C: Into<ChatId>, C: Into<ChatId>,
S: Into<String>, S: Into<String>,
@ -1051,10 +977,7 @@ impl Bot {
/// - `callback_query_id`: Unique identifier for the query to be answered. /// - `callback_query_id`: Unique identifier for the query to be answered.
/// ///
/// [inline keyboards]: https://core.telegram.org/bots#inline-keyboards-and-on-the-fly-updating /// [inline keyboards]: https://core.telegram.org/bots#inline-keyboards-and-on-the-fly-updating
pub fn answer_callback_query<C>( pub fn answer_callback_query<C>(&self, callback_query_id: C) -> AnswerCallbackQuery
&self,
callback_query_id: C,
) -> AnswerCallbackQuery
where where
C: Into<String>, C: Into<String>,
{ {
@ -1088,13 +1011,9 @@ impl Bot {
T: Into<String>, T: Into<String>,
{ {
match self.parse_mode.deref() { match self.parse_mode.deref() {
None => { None => EditMessageText::new(self.clone(), chat_or_inline_message, text),
EditMessageText::new(self.clone(), chat_or_inline_message, text) Some(parse_mode) => EditMessageText::new(self.clone(), chat_or_inline_message, text)
} .parse_mode(*parse_mode.deref()),
Some(parse_mode) => {
EditMessageText::new(self.clone(), chat_or_inline_message, text)
.parse_mode(*parse_mode.deref())
}
} }
} }
@ -1118,13 +1037,9 @@ impl Bot {
chat_or_inline_message: ChatOrInlineMessage, chat_or_inline_message: ChatOrInlineMessage,
) -> EditMessageCaption { ) -> EditMessageCaption {
match self.parse_mode.deref() { match self.parse_mode.deref() {
None => { None => EditMessageCaption::new(self.clone(), chat_or_inline_message),
EditMessageCaption::new(self.clone(), chat_or_inline_message) Some(parse_mode) => EditMessageCaption::new(self.clone(), chat_or_inline_message)
} .parse_mode(*parse_mode.deref()),
Some(parse_mode) => {
EditMessageCaption::new(self.clone(), chat_or_inline_message)
.parse_mode(*parse_mode.deref())
}
} }
} }
@ -1202,11 +1117,7 @@ impl Bot {
/// - `chat_id`: Unique identifier for the target chat or username of the /// - `chat_id`: Unique identifier for the target chat or username of the
/// target channel (in the format `@channelusername`). /// target channel (in the format `@channelusername`).
/// - `message_id`: Identifier of the message to delete. /// - `message_id`: Identifier of the message to delete.
pub fn delete_message<C>( pub fn delete_message<C>(&self, chat_id: C, message_id: i32) -> DeleteMessage
&self,
chat_id: C,
message_id: i32,
) -> DeleteMessage
where where
C: Into<ChatId>, C: Into<ChatId>,
{ {
@ -1268,11 +1179,7 @@ impl Bot {
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files /// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
/// [`Bot::create_new_sticker_set`]: crate::Bot::create_new_sticker_set /// [`Bot::create_new_sticker_set`]: crate::Bot::create_new_sticker_set
/// [`Bot::add_sticker_to_set`]: crate::Bot::add_sticker_to_set /// [`Bot::add_sticker_to_set`]: crate::Bot::add_sticker_to_set
pub fn upload_sticker_file( pub fn upload_sticker_file(&self, user_id: i32, png_sticker: InputFile) -> UploadStickerFile {
&self,
user_id: i32,
png_sticker: InputFile,
) -> UploadStickerFile {
UploadStickerFile::new(self.clone(), user_id, png_sticker) UploadStickerFile::new(self.clone(), user_id, png_sticker)
} }
@ -1317,14 +1224,7 @@ impl Bot {
T: Into<String>, T: Into<String>,
E: Into<String>, E: Into<String>,
{ {
CreateNewStickerSet::new( CreateNewStickerSet::new(self.clone(), user_id, name, title, png_sticker, emojis)
self.clone(),
user_id,
name,
title,
png_sticker,
emojis,
)
} }
/// Use this method to add a new sticker to a set created by the bot. /// Use this method to add a new sticker to a set created by the bot.
@ -1402,11 +1302,7 @@ impl Bot {
/// # Params /// # Params
/// - `inline_query_id`: Unique identifier for the answered query. /// - `inline_query_id`: Unique identifier for the answered query.
/// - `results`: A JSON-serialized array of results for the inline query. /// - `results`: A JSON-serialized array of results for the inline query.
pub fn answer_inline_query<I, R>( pub fn answer_inline_query<I, R>(&self, inline_query_id: I, results: R) -> AnswerInlineQuery
&self,
inline_query_id: I,
results: R,
) -> AnswerInlineQuery
where where
I: Into<String>, I: Into<String>,
R: Into<Vec<InlineQueryResult>>, R: Into<Vec<InlineQueryResult>>,
@ -1484,11 +1380,7 @@ impl Bot {
/// delivery to the specified address is not possible). /// delivery to the specified address is not possible).
/// ///
/// [`Update`]: crate::types::Update /// [`Update`]: crate::types::Update
pub fn answer_shipping_query<S>( pub fn answer_shipping_query<S>(&self, shipping_query_id: S, ok: bool) -> AnswerShippingQuery
&self,
shipping_query_id: S,
ok: bool,
) -> AnswerShippingQuery
where where
S: Into<String>, S: Into<String>,
{ {
@ -1607,11 +1499,6 @@ impl Bot {
C: Into<ChatId>, C: Into<ChatId>,
CT: Into<String>, CT: Into<String>,
{ {
SetChatAdministratorCustomTitle::new( SetChatAdministratorCustomTitle::new(self.clone(), chat_id, user_id, custom_title)
self.clone(),
chat_id,
user_id,
custom_title,
)
} }
} }

View file

@ -59,8 +59,7 @@ impl Bot {
pub async fn download_file_stream( pub async fn download_file_stream(
&self, &self,
path: &str, path: &str,
) -> Result<impl Stream<Item = Result<Bytes, reqwest::Error>>, reqwest::Error> ) -> Result<impl Stream<Item = Result<Bytes, reqwest::Error>>, reqwest::Error> {
{
download_file_stream(&self.client, &self.token, path).await download_file_stream(&self.client, &self.token, path).await
} }
} }

View file

@ -116,8 +116,7 @@ pub(crate) fn build_sound_bot() -> Client {
} }
fn get_token_from_env() -> String { fn get_token_from_env() -> String {
std::env::var("TELOXIDE_TOKEN") std::env::var("TELOXIDE_TOKEN").expect("Cannot get the TELOXIDE_TOKEN env variable")
.expect("Cannot get the TELOXIDE_TOKEN env variable")
} }
impl Bot { impl Bot {

View file

@ -1,7 +1,6 @@
use crate::dispatching::{ use crate::dispatching::{
dialogue::{ dialogue::{
DialogueDispatcherHandler, DialogueStage, DialogueWithCx, GetChatId, DialogueDispatcherHandler, DialogueStage, DialogueWithCx, GetChatId, InMemStorage, Storage,
InMemStorage, Storage,
}, },
DispatcherHandler, UpdateWithCx, DispatcherHandler, UpdateWithCx,
}; };
@ -100,13 +99,10 @@ where
match handler.handle(DialogueWithCx { cx, dialogue }).await { match handler.handle(DialogueWithCx { cx, dialogue }).await {
DialogueStage::Next(new_dialogue) => { DialogueStage::Next(new_dialogue) => {
if let Ok(Some(_)) = if let Ok(Some(_)) = storage.update_dialogue(chat_id, new_dialogue).await {
storage.update_dialogue(chat_id, new_dialogue).await
{
panic!( panic!(
"Oops, you have an bug in your Storage: \ "Oops, you have an bug in your Storage: update_dialogue returns \
update_dialogue returns Some after \ Some after remove_dialogue"
remove_dialogue"
); );
} }
} }
@ -135,10 +131,7 @@ where
S: Storage<D> + Send + Sync + 'static, S: Storage<D> + Send + Sync + 'static,
S::Error: Send + 'static, S::Error: Send + 'static,
{ {
fn handle( fn handle(self, updates: mpsc::UnboundedReceiver<UpdateWithCx<Upd>>) -> BoxFuture<'static, ()>
self,
updates: mpsc::UnboundedReceiver<UpdateWithCx<Upd>>,
) -> BoxFuture<'static, ()>
where where
UpdateWithCx<Upd>: 'static, UpdateWithCx<Upd>: 'static,
{ {
@ -152,19 +145,13 @@ where
// An old dialogue // An old dialogue
Some(tx) => { Some(tx) => {
if tx.1.send(cx).is_err() { if tx.1.send(cx).is_err() {
panic!( panic!("We are not dropping a receiver or call .close() on it",);
"We are not dropping a receiver or call .close() \
on it",
);
} }
} }
None => { None => {
let tx = this.new_tx(); let tx = this.new_tx();
if tx.send(cx).is_err() { if tx.send(cx).is_err() {
panic!( panic!("We are not dropping a receiver or call .close() on it",);
"We are not dropping a receiver or call .close() \
on it",
);
} }
this.senders.insert(chat_id, tx); this.senders.insert(chat_id, tx);
} }
@ -214,8 +201,8 @@ mod tests {
static ref SEQ3: Mutex<Vec<u32>> = Mutex::new(Vec::new()); static ref SEQ3: Mutex<Vec<u32>> = Mutex::new(Vec::new());
} }
let dispatcher = DialogueDispatcher::new( let dispatcher =
|cx: DialogueWithCx<MyUpdate, (), Infallible>| async move { DialogueDispatcher::new(|cx: DialogueWithCx<MyUpdate, (), Infallible>| async move {
delay_for(Duration::from_millis(300)).await; delay_for(Duration::from_millis(300)).await;
match cx.cx.update { match cx.cx.update {
@ -232,8 +219,7 @@ mod tests {
} }
DialogueStage::Next(()) DialogueStage::Next(())
}, });
);
let updates = stream::iter( let updates = stream::iter(
vec![ vec![
@ -260,10 +246,7 @@ mod tests {
MyUpdate::new(3, 1611), MyUpdate::new(3, 1611),
] ]
.into_iter() .into_iter()
.map(|update| UpdateWithCx { .map(|update| UpdateWithCx { update, bot: Bot::new("Doesn't matter here") })
update,
bot: Bot::new("Doesn't matter here"),
})
.collect::<Vec<UpdateWithCx<MyUpdate>>>(), .collect::<Vec<UpdateWithCx<MyUpdate>>>(),
); );
@ -287,13 +270,7 @@ mod tests {
delay_for(Duration::from_millis(3000)).await; delay_for(Duration::from_millis(3000)).await;
assert_eq!(*SEQ1.lock().await, vec![174, 125, 2, 193, 104, 7, 7778]); assert_eq!(*SEQ1.lock().await, vec![174, 125, 2, 193, 104, 7, 7778]);
assert_eq!( assert_eq!(*SEQ2.lock().await, vec![411, 515, 623, 2222, 737, 10, 55456]);
*SEQ2.lock().await, assert_eq!(*SEQ3.lock().await, vec![72782, 2737, 5475, 1096, 872, 5665, 1611]);
vec![411, 515, 623, 2222, 737, 10, 55456]
);
assert_eq!(
*SEQ3.lock().await,
vec![72782, 2737, 5475, 1096, 872, 5665, 1611]
);
} }
} }

View file

@ -23,10 +23,7 @@ where
F: Fn(DialogueWithCx<Upd, D, E>) -> Fut + Send + Sync + 'static, F: Fn(DialogueWithCx<Upd, D, E>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = DialogueStage<D>> + Send + 'static, Fut: Future<Output = DialogueStage<D>> + Send + 'static,
{ {
fn handle( fn handle(self: Arc<Self>, cx: DialogueWithCx<Upd, D, E>) -> BoxFuture<'static, Fut::Output>
self: Arc<Self>,
cx: DialogueWithCx<Upd, D, E>,
) -> BoxFuture<'static, Fut::Output>
where where
DialogueWithCx<Upd, D, E>: Send + 'static, DialogueWithCx<Upd, D, E>: Send + 'static,
{ {

View file

@ -153,8 +153,7 @@ pub use dialogue_stage::{exit, next, DialogueStage};
pub use dialogue_with_cx::DialogueWithCx; pub use dialogue_with_cx::DialogueWithCx;
pub use get_chat_id::GetChatId; pub use get_chat_id::GetChatId;
pub use transition::{ pub use transition::{
SubTransition, SubTransitionOutputType, Transition, TransitionIn, SubTransition, SubTransitionOutputType, Transition, TransitionIn, TransitionOut,
TransitionOut,
}; };
#[cfg(feature = "redis-storage")] #[cfg(feature = "redis-storage")]

View file

@ -43,8 +43,6 @@ impl<D> Storage<D> for InMemStorage<D> {
where where
D: Send + 'static, D: Send + 'static,
{ {
Box::pin( Box::pin(async move { Ok(self.map.lock().await.insert(chat_id, dialogue)) })
async move { Ok(self.map.lock().await.insert(chat_id, dialogue)) },
)
} }
} }

View file

@ -37,9 +37,7 @@ impl<S> RedisStorage<S> {
serializer: S, serializer: S,
) -> Result<Arc<Self>, RedisStorageError<Infallible>> { ) -> Result<Arc<Self>, RedisStorageError<Infallible>> {
Ok(Arc::new(Self { Ok(Arc::new(Self {
conn: Mutex::new( conn: Mutex::new(redis::Client::open(url)?.get_async_connection().await?),
redis::Client::open(url)?.get_async_connection().await?,
),
serializer, serializer,
})) }))
} }
@ -91,21 +89,15 @@ where
dialogue: D, dialogue: D,
) -> BoxFuture<'static, Result<Option<D>, Self::Error>> { ) -> BoxFuture<'static, Result<Option<D>, Self::Error>> {
Box::pin(async move { Box::pin(async move {
let dialogue = self let dialogue =
.serializer self.serializer.serialize(&dialogue).map_err(RedisStorageError::SerdeError)?;
.serialize(&dialogue)
.map_err(RedisStorageError::SerdeError)?;
Ok(self Ok(self
.conn .conn
.lock() .lock()
.await .await
.getset::<_, Vec<u8>, Option<Vec<u8>>>(chat_id, dialogue) .getset::<_, Vec<u8>, Option<Vec<u8>>>(chat_id, dialogue)
.await? .await?
.map(|d| { .map(|d| self.serializer.deserialize(&d).map_err(RedisStorageError::SerdeError))
self.serializer
.deserialize(&d)
.map_err(RedisStorageError::SerdeError)
})
.transpose()?) .transpose()?)
}) })
} }

View file

@ -10,11 +10,7 @@ pub trait Transition<T>: Sized {
/// Turns itself into another state, depending on the input message. /// Turns itself into another state, depending on the input message.
/// ///
/// `aux` will be passed to each subtransition function. /// `aux` will be passed to each subtransition function.
fn react( fn react(self, cx: TransitionIn, aux: T) -> BoxFuture<'static, TransitionOut<Self>>;
self,
cx: TransitionIn,
aux: T,
) -> BoxFuture<'static, TransitionOut<Self>>;
} }
/// Like [`Transition`], but from `StateN` -> `Dialogue`. /// Like [`Transition`], but from `StateN` -> `Dialogue`.

View file

@ -1,12 +1,11 @@
use crate::{ use crate::{
dispatching::{ dispatching::{
update_listeners, update_listeners::UpdateListener, DispatcherHandler, update_listeners, update_listeners::UpdateListener, DispatcherHandler, UpdateWithCx,
UpdateWithCx,
}, },
error_handlers::{ErrorHandler, LoggingErrorHandler}, error_handlers::{ErrorHandler, LoggingErrorHandler},
types::{ types::{
CallbackQuery, ChosenInlineResult, InlineQuery, Message, Poll, CallbackQuery, ChosenInlineResult, InlineQuery, Message, Poll, PollAnswer,
PollAnswer, PreCheckoutQuery, ShippingQuery, UpdateKind, PreCheckoutQuery, ShippingQuery, UpdateKind,
}, },
Bot, Bot,
}; };
@ -26,19 +25,14 @@ mod macros {
} }
} }
fn send<'a, Upd>( fn send<'a, Upd>(bot: &'a Bot, tx: &'a Tx<Upd>, update: Upd, variant: &'static str)
bot: &'a Bot, where
tx: &'a Tx<Upd>,
update: Upd,
variant: &'static str,
) where
Upd: Debug, Upd: Debug,
{ {
if let Some(tx) = tx { if let Some(tx) = tx {
if let Err(error) = tx.send(UpdateWithCx { bot: bot.clone(), update }) { if let Err(error) = tx.send(UpdateWithCx { bot: bot.clone(), update }) {
log::error!( log::error!(
"The RX part of the {} channel is closed, but an update is \ "The RX part of the {} channel is closed, but an update is received.\nError:{}\n",
received.\nError:{}\n",
variant, variant,
error error
); );
@ -206,9 +200,7 @@ impl Dispatcher {
pub async fn dispatch(&self) { pub async fn dispatch(&self) {
self.dispatch_with_listener( self.dispatch_with_listener(
update_listeners::polling_default(self.bot.clone()), update_listeners::polling_default(self.bot.clone()),
LoggingErrorHandler::with_custom_text( LoggingErrorHandler::with_custom_text("An error from the update listener"),
"An error from the update listener",
),
) )
.await; .await;
} }
@ -228,8 +220,7 @@ impl Dispatcher {
update_listener update_listener
.for_each(move |update| { .for_each(move |update| {
let update_listener_error_handler = let update_listener_error_handler = Arc::clone(&update_listener_error_handler);
Arc::clone(&update_listener_error_handler);
async move { async move {
log::trace!("Dispatcher received an update: {:?}", update); log::trace!("Dispatcher received an update: {:?}", update);
@ -237,21 +228,14 @@ impl Dispatcher {
let update = match update { let update = match update {
Ok(update) => update, Ok(update) => update,
Err(error) => { Err(error) => {
Arc::clone(&update_listener_error_handler) Arc::clone(&update_listener_error_handler).handle_error(error).await;
.handle_error(error)
.await;
return; return;
} }
}; };
match update.kind { match update.kind {
UpdateKind::Message(message) => { UpdateKind::Message(message) => {
send!( send!(&self.bot, &self.messages_queue, message, UpdateKind::Message);
&self.bot,
&self.messages_queue,
message,
UpdateKind::Message
);
} }
UpdateKind::EditedMessage(message) => { UpdateKind::EditedMessage(message) => {
send!( send!(
@ -318,12 +302,7 @@ impl Dispatcher {
); );
} }
UpdateKind::Poll(poll) => { UpdateKind::Poll(poll) => {
send!( send!(&self.bot, &self.polls_queue, poll, UpdateKind::Poll);
&self.bot,
&self.polls_queue,
poll,
UpdateKind::Poll
);
} }
UpdateKind::PollAnswer(answer) => { UpdateKind::PollAnswer(answer) => {
send!( send!(

View file

@ -11,10 +11,7 @@ use futures::future::BoxFuture;
/// [`Dispatcher`]: crate::dispatching::Dispatcher /// [`Dispatcher`]: crate::dispatching::Dispatcher
pub trait DispatcherHandler<Upd> { pub trait DispatcherHandler<Upd> {
#[must_use] #[must_use]
fn handle( fn handle(self, updates: DispatcherHandlerRx<Upd>) -> BoxFuture<'static, ()>
self,
updates: DispatcherHandlerRx<Upd>,
) -> BoxFuture<'static, ()>
where where
UpdateWithCx<Upd>: Send + 'static; UpdateWithCx<Upd>: Send + 'static;
} }

View file

@ -1,6 +1,4 @@
use crate::{ use crate::{prelude::UpdateWithCx, types::Message, utils::command::BotCommand};
prelude::UpdateWithCx, types::Message, utils::command::BotCommand,
};
use futures::{stream::BoxStream, Stream, StreamExt}; use futures::{stream::BoxStream, Stream, StreamExt};
/// An extension trait to be used with [`DispatcherHandlerRx`]. /// An extension trait to be used with [`DispatcherHandlerRx`].
@ -11,18 +9,13 @@ use futures::{stream::BoxStream, Stream, StreamExt};
/// [`DispatcherHandlerRx`]: crate::dispatching::DispatcherHandlerRx /// [`DispatcherHandlerRx`]: crate::dispatching::DispatcherHandlerRx
pub trait DispatcherHandlerRxExt { pub trait DispatcherHandlerRxExt {
/// Extracts only text messages from this stream of arbitrary messages. /// Extracts only text messages from this stream of arbitrary messages.
fn text_messages( fn text_messages(self) -> BoxStream<'static, (UpdateWithCx<Message>, String)>
self,
) -> BoxStream<'static, (UpdateWithCx<Message>, String)>
where where
Self: Stream<Item = UpdateWithCx<Message>>; Self: Stream<Item = UpdateWithCx<Message>>;
/// Extracts only commands with their arguments from this stream of /// Extracts only commands with their arguments from this stream of
/// arbitrary messages. /// arbitrary messages.
fn commands<C, N>( fn commands<C, N>(self, bot_name: N) -> BoxStream<'static, (UpdateWithCx<Message>, C)>
self,
bot_name: N,
) -> BoxStream<'static, (UpdateWithCx<Message>, C)>
where where
Self: Stream<Item = UpdateWithCx<Message>>, Self: Stream<Item = UpdateWithCx<Message>>,
C: BotCommand, C: BotCommand,
@ -33,21 +26,14 @@ impl<T> DispatcherHandlerRxExt for T
where where
T: Send + 'static, T: Send + 'static,
{ {
fn text_messages( fn text_messages(self) -> BoxStream<'static, (UpdateWithCx<Message>, String)>
self,
) -> BoxStream<'static, (UpdateWithCx<Message>, String)>
where where
Self: Stream<Item = UpdateWithCx<Message>>, Self: Stream<Item = UpdateWithCx<Message>>,
{ {
Box::pin(self.filter_map(|cx| async move { Box::pin(self.filter_map(|cx| async move { cx.update.text_owned().map(|text| (cx, text)) }))
cx.update.text_owned().map(|text| (cx, text))
}))
} }
fn commands<C, N>( fn commands<C, N>(self, bot_name: N) -> BoxStream<'static, (UpdateWithCx<Message>, C)>
self,
bot_name: N,
) -> BoxStream<'static, (UpdateWithCx<Message>, C)>
where where
Self: Stream<Item = UpdateWithCx<Message>>, Self: Stream<Item = UpdateWithCx<Message>>,
C: BotCommand, C: BotCommand,
@ -58,9 +44,7 @@ where
Box::pin(self.text_messages().filter_map(move |(cx, text)| { Box::pin(self.text_messages().filter_map(move |(cx, text)| {
let bot_name = bot_name.clone(); let bot_name = bot_name.clone();
async move { async move { C::parse(&text, &bot_name).map(|command| (cx, command)).ok() }
C::parse(&text, &bot_name).map(|command| (cx, command)).ok()
}
})) }))
} }
} }

View file

@ -145,8 +145,7 @@ pub fn polling(
limit: Option<u8>, limit: Option<u8>,
allowed_updates: Option<Vec<AllowedUpdate>>, allowed_updates: Option<Vec<AllowedUpdate>>,
) -> impl UpdateListener<RequestError> { ) -> impl UpdateListener<RequestError> {
let timeout = let timeout = timeout.map(|t| t.as_secs().try_into().expect("timeout is too big"));
timeout.map(|t| t.as_secs().try_into().expect("timeout is too big"));
stream::unfold( stream::unfold(
(allowed_updates, bot, 0), (allowed_updates, bot, 0),
@ -165,10 +164,7 @@ pub fn polling(
Ok(ok) => ok.id, Ok(ok) => ok.id,
Err((value, _)) => value["update_id"] Err((value, _)) => value["update_id"]
.as_i64() .as_i64()
.expect( .expect("The 'update_id' field must always exist in Update")
"The 'update_id' field must always exist \
in Update",
)
.try_into() .try_into()
.expect("update_id must be i32"), .expect("update_id must be i32"),
}; };
@ -176,10 +172,8 @@ pub fn polling(
offset = id + 1; offset = id + 1;
} }
let updates = updates let updates =
.into_iter() updates.into_iter().filter_map(Result::ok).collect::<Vec<Update>>();
.filter_map(Result::ok)
.collect::<Vec<Update>>();
updates.into_iter().map(Ok).collect::<Vec<_>>() updates.into_iter().map(Ok).collect::<Vec<_>>()
} }

View file

@ -1,10 +1,10 @@
use crate::{ use crate::{
dispatching::dialogue::GetChatId, dispatching::dialogue::GetChatId,
requests::{ requests::{
DeleteMessage, EditMessageCaption, EditMessageText, ForwardMessage, DeleteMessage, EditMessageCaption, EditMessageText, ForwardMessage, PinChatMessage,
PinChatMessage, Request, ResponseResult, SendAnimation, SendAudio, Request, ResponseResult, SendAnimation, SendAudio, SendContact, SendDocument, SendLocation,
SendContact, SendDocument, SendLocation, SendMediaGroup, SendMessage, SendMediaGroup, SendMessage, SendPhoto, SendSticker, SendVenue, SendVideo, SendVideoNote,
SendPhoto, SendSticker, SendVenue, SendVideo, SendVideoNote, SendVoice, SendVoice,
}, },
types::{ChatId, ChatOrInlineMessage, InputFile, InputMedia, Message}, types::{ChatId, ChatOrInlineMessage, InputFile, InputMedia, Message},
Bot, Bot,
@ -51,9 +51,7 @@ impl UpdateWithCx<Message> {
where where
T: Into<String>, T: Into<String>,
{ {
self.bot self.bot.send_message(self.chat_id(), text).reply_to_message_id(self.update.id)
.send_message(self.chat_id(), text)
.reply_to_message_id(self.update.id)
} }
pub fn answer_photo(&self, photo: InputFile) -> SendPhoto { pub fn answer_photo(&self, photo: InputFile) -> SendPhoto {
@ -87,11 +85,7 @@ impl UpdateWithCx<Message> {
self.bot.send_media_group(self.update.chat.id, media_group) self.bot.send_media_group(self.update.chat.id, media_group)
} }
pub fn answer_location( pub fn answer_location(&self, latitude: f32, longitude: f32) -> SendLocation {
&self,
latitude: f32,
longitude: f32,
) -> SendLocation {
self.bot.send_location(self.update.chat.id, latitude, longitude) self.bot.send_location(self.update.chat.id, latitude, longitude)
} }
@ -106,24 +100,14 @@ impl UpdateWithCx<Message> {
T: Into<String>, T: Into<String>,
U: Into<String>, U: Into<String>,
{ {
self.bot.send_venue( self.bot.send_venue(self.update.chat.id, latitude, longitude, title, address)
self.update.chat.id,
latitude,
longitude,
title,
address,
)
} }
pub fn answer_video_note(&self, video_note: InputFile) -> SendVideoNote { pub fn answer_video_note(&self, video_note: InputFile) -> SendVideoNote {
self.bot.send_video_note(self.update.chat.id, video_note) self.bot.send_video_note(self.update.chat.id, video_note)
} }
pub fn answer_contact<T, U>( pub fn answer_contact<T, U>(&self, phone_number: T, first_name: U) -> SendContact
&self,
phone_number: T,
first_name: U,
) -> SendContact
where where
T: Into<String>, T: Into<String>,
U: Into<String>, U: Into<String>,

View file

@ -174,9 +174,7 @@ impl ErrorHandler<Infallible> for IgnoringErrorHandlerSafe {
/// ///
/// LoggingErrorHandler::new().handle_error(()).await; /// LoggingErrorHandler::new().handle_error(()).await;
/// LoggingErrorHandler::with_custom_text("Omg1").handle_error(404).await; /// LoggingErrorHandler::with_custom_text("Omg1").handle_error(404).await;
/// LoggingErrorHandler::with_custom_text("Omg2") /// LoggingErrorHandler::with_custom_text("Omg2").handle_error("Invalid data type!").await;
/// .handle_error("Invalid data type!")
/// .await;
/// # } /// # }
/// ``` /// ```
pub struct LoggingErrorHandler { pub struct LoggingErrorHandler {

View file

@ -63,9 +63,9 @@ pub enum KnownApiErrorKind {
/// 1. [`EditMessageText`] /// 1. [`EditMessageText`]
/// ///
/// [`EditMessageText`]: crate::requests::EditMessageText /// [`EditMessageText`]: crate::requests::EditMessageText
#[serde(rename = "Bad Request: message is not modified: specified new \ #[serde(rename = "Bad Request: message is not modified: specified new message content and \
message content and reply markup are exactly the same \ reply markup are exactly the same as a current content and reply markup \
as a current content and reply markup of the message")] of the message")]
MessageNotModified, MessageNotModified,
/// Occurs when bot tries to forward or delete a message which was deleted. /// Occurs when bot tries to forward or delete a message which was deleted.
@ -285,8 +285,8 @@ pub enum KnownApiErrorKind {
/// 1. [`AnswerCallbackQuery`] /// 1. [`AnswerCallbackQuery`]
/// ///
/// [`AnswerCallbackQuery`]: crate::requests::AnswerCallbackQuery /// [`AnswerCallbackQuery`]: crate::requests::AnswerCallbackQuery
#[serde(rename = "Bad Request: query is too old and response timeout \ #[serde(rename = "Bad Request: query is too old and response timeout expired or query id is \
expired or query id is invalid")] invalid")]
InvalidQueryID, InvalidQueryID,
/// Occurs when bot tries to send InlineKeyboardMarkup with invalid button /// Occurs when bot tries to send InlineKeyboardMarkup with invalid button
@ -314,8 +314,8 @@ pub enum KnownApiErrorKind {
/// 1. [`SendMessage`] /// 1. [`SendMessage`]
/// ///
/// [`SendMessage`]: crate::requests::SendMessage /// [`SendMessage`]: crate::requests::SendMessage
#[serde(rename = "Bad Request: can't parse inline keyboard button: Text \ #[serde(rename = "Bad Request: can't parse inline keyboard button: Text buttons are \
buttons are unallowed in the inline keyboard")] unallowed in the inline keyboard")]
TextButtonsAreUnallowed, TextButtonsAreUnallowed,
/// Occurs when bot tries to get file by wrong file id. /// Occurs when bot tries to get file by wrong file id.
@ -361,8 +361,7 @@ pub enum KnownApiErrorKind {
/// Occurs when bot tries to use method in group which is allowed only in a /// Occurs when bot tries to use method in group which is allowed only in a
/// supergroup or channel. /// supergroup or channel.
#[serde(rename = "Bad Request: method is available only for supergroups \ #[serde(rename = "Bad Request: method is available only for supergroups and channel")]
and channel")]
MethodNotAvailableInPrivateChats, MethodNotAvailableInPrivateChats,
/// Occurs when bot tries to demote chat creator. /// Occurs when bot tries to demote chat creator.
@ -390,8 +389,7 @@ pub enum KnownApiErrorKind {
/// 1. [`RestrictChatMember`] /// 1. [`RestrictChatMember`]
/// ///
/// [`RestrictChatMember`]: crate::requests::RestrictChatMember /// [`RestrictChatMember`]: crate::requests::RestrictChatMember
#[serde(rename = "Bad Request: not enough rights to restrict/unrestrict \ #[serde(rename = "Bad Request: not enough rights to restrict/unrestrict chat member")]
chat member")]
NotEnoughRightsToRestrict, NotEnoughRightsToRestrict,
/// Occurs when bot tries set webhook to protocol other than HTTPS. /// Occurs when bot tries set webhook to protocol other than HTTPS.
@ -400,8 +398,7 @@ pub enum KnownApiErrorKind {
/// 1. [`SetWebhook`] /// 1. [`SetWebhook`]
/// ///
/// [`SetWebhook`]: crate::requests::SetWebhook /// [`SetWebhook`]: crate::requests::SetWebhook
#[serde(rename = "Bad Request: bad webhook: HTTPS url must be provided \ #[serde(rename = "Bad Request: bad webhook: HTTPS url must be provided for webhook")]
for webhook")]
WebhookRequireHTTPS, WebhookRequireHTTPS,
/// Occurs when bot tries to set webhook to port other than 80, 88, 443 or /// Occurs when bot tries to set webhook to port other than 80, 88, 443 or
@ -411,8 +408,8 @@ pub enum KnownApiErrorKind {
/// 1. [`SetWebhook`] /// 1. [`SetWebhook`]
/// ///
/// [`SetWebhook`]: crate::requests::SetWebhook /// [`SetWebhook`]: crate::requests::SetWebhook
#[serde(rename = "Bad Request: bad webhook: Webhook can be set up only \ #[serde(rename = "Bad Request: bad webhook: Webhook can be set up only on ports 80, 88, 443 \
on ports 80, 88, 443 or 8443")] or 8443")]
BadWebhookPort, BadWebhookPort,
/// Occurs when bot tries to set webhook to unknown host. /// Occurs when bot tries to set webhook to unknown host.
@ -421,8 +418,7 @@ pub enum KnownApiErrorKind {
/// 1. [`SetWebhook`] /// 1. [`SetWebhook`]
/// ///
/// [`SetWebhook`]: crate::requests::SetWebhook /// [`SetWebhook`]: crate::requests::SetWebhook
#[serde(rename = "Bad Request: bad webhook: Failed to resolve host: \ #[serde(rename = "Bad Request: bad webhook: Failed to resolve host: Name or service not known")]
Name or service not known")]
UnknownHost, UnknownHost,
/// Occurs when bot tries to set webhook to invalid URL. /// Occurs when bot tries to set webhook to invalid URL.
@ -476,9 +472,7 @@ pub enum KnownApiErrorKind {
/// 1. [`SendMessage`] /// 1. [`SendMessage`]
/// ///
/// [`SendMessage`]: crate::requests::SendMessage /// [`SendMessage`]: crate::requests::SendMessage
#[serde( #[serde(rename = "Unauthorized: bot can't initiate conversation with a user")]
rename = "Unauthorized: bot can't initiate conversation with a user"
)]
CantInitiateConversation, CantInitiateConversation,
/// Occurs when you tries to send message to bot. /// Occurs when you tries to send message to bot.
@ -506,8 +500,8 @@ pub enum KnownApiErrorKind {
/// 1. [`GetUpdates`] /// 1. [`GetUpdates`]
/// ///
/// [`GetUpdates`]: crate::requests::GetUpdates /// [`GetUpdates`]: crate::requests::GetUpdates
#[serde(rename = "Conflict: terminated by other getUpdates request; \ #[serde(rename = "Conflict: terminated by other getUpdates request; make sure that only one \
make sure that only one bot instance is running")] bot instance is running")]
TerminatedByOtherGetUpdates, TerminatedByOtherGetUpdates,
/// Occurs when bot tries to get file by invalid file id. /// Occurs when bot tries to get file by invalid file id.

View file

@ -17,9 +17,7 @@
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
pub use bot::{Bot, BotBuilder}; pub use bot::{Bot, BotBuilder};
pub use errors::{ pub use errors::{ApiErrorKind, DownloadError, KnownApiErrorKind, RequestError};
ApiErrorKind, DownloadError, KnownApiErrorKind, RequestError,
};
mod errors; mod errors;
mod net; mod net;

View file

@ -17,24 +17,14 @@ const TELEGRAM_API_URL: &str = "https://api.telegram.org";
/// ///
/// [Telegram documentation]: https://core.telegram.org/bots/api#making-requests /// [Telegram documentation]: https://core.telegram.org/bots/api#making-requests
fn method_url(base: &str, token: &str, method_name: &str) -> String { fn method_url(base: &str, token: &str, method_name: &str) -> String {
format!( format!("{url}/bot{token}/{method}", url = base, token = token, method = method_name,)
"{url}/bot{token}/{method}",
url = base,
token = token,
method = method_name,
)
} }
/// Creates URL for downloading a file. See the [Telegram documentation]. /// Creates URL for downloading a file. See the [Telegram documentation].
/// ///
/// [Telegram documentation]: https://core.telegram.org/bots/api#file /// [Telegram documentation]: https://core.telegram.org/bots/api#file
fn file_url(base: &str, token: &str, file_path: &str) -> String { fn file_url(base: &str, token: &str, file_path: &str) -> String {
format!( format!("{url}/file/bot{token}/{file}", url = base, token = token, file = file_path,)
"{url}/file/bot{token}/{file}",
url = base,
token = token,
file = file_path,
)
} }
#[cfg(test)] #[cfg(test)]

View file

@ -33,17 +33,10 @@ impl<R> Into<ResponseResult<R>> for TelegramResponse<R> {
fn into(self) -> Result<R, RequestError> { fn into(self) -> Result<R, RequestError> {
match self { match self {
TelegramResponse::Ok { result, .. } => Ok(result), TelegramResponse::Ok { result, .. } => Ok(result),
TelegramResponse::Err { TelegramResponse::Err { kind, error_code, response_parameters, .. } => {
kind,
error_code,
response_parameters,
..
} => {
if let Some(params) = response_parameters { if let Some(params) = response_parameters {
match params { match params {
ResponseParameters::RetryAfter(i) => { ResponseParameters::RetryAfter(i) => Err(RequestError::RetryAfter(i)),
Err(RequestError::RetryAfter(i))
}
ResponseParameters::MigrateToChatId(to) => { ResponseParameters::MigrateToChatId(to) => {
Err(RequestError::MigrateToChatId(to)) Err(RequestError::MigrateToChatId(to))
} }
@ -66,8 +59,7 @@ mod tests {
#[test] #[test]
fn terminated_by_other_get_updates() { fn terminated_by_other_get_updates() {
let expected = let expected = ApiErrorKind::Known(KnownApiErrorKind::TerminatedByOtherGetUpdates);
ApiErrorKind::Known(KnownApiErrorKind::TerminatedByOtherGetUpdates);
if let TelegramResponse::Err{ kind, .. } = serde_json::from_str::<TelegramResponse<Update>>(r#"{"ok":false,"error_code":409,"description":"Conflict: terminated by other getUpdates request; make sure that only one bot instance is running"}"#).unwrap() { if let TelegramResponse::Err{ kind, .. } = serde_json::from_str::<TelegramResponse<Update>>(r#"{"ok":false,"error_code":409,"description":"Conflict: terminated by other getUpdates request; make sure that only one bot instance is running"}"#).unwrap() {
assert_eq!(expected, kind); assert_eq!(expected, kind);
} }

View file

@ -3,8 +3,8 @@
pub use crate::{ pub use crate::{
dispatching::{ dispatching::{
dialogue::{ dialogue::{
exit, next, DialogueDispatcher, DialogueStage, DialogueWithCx, exit, next, DialogueDispatcher, DialogueStage, DialogueWithCx, GetChatId, Transition,
GetChatId, Transition, TransitionIn, TransitionOut, TransitionIn, TransitionOut,
}, },
Dispatcher, DispatcherHandlerRx, DispatcherHandlerRxExt, UpdateWithCx, Dispatcher, DispatcherHandlerRx, DispatcherHandlerRxExt, UpdateWithCx,
}, },

View file

@ -33,13 +33,7 @@ impl Request for AnswerCallbackQuery {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "answerCallbackQuery", &self).await
self.bot.client(),
self.bot.token(),
"answerCallbackQuery",
&self,
)
.await
} }
} }
@ -49,14 +43,7 @@ impl AnswerCallbackQuery {
C: Into<String>, C: Into<String>,
{ {
let callback_query_id = callback_query_id.into(); let callback_query_id = callback_query_id.into();
Self { Self { bot, callback_query_id, text: None, show_alert: None, url: None, cache_time: None }
bot,
callback_query_id,
text: None,
show_alert: None,
url: None,
cache_time: None,
}
} }
/// Unique identifier for the query to be answered. /// Unique identifier for the query to be answered.

View file

@ -31,13 +31,7 @@ impl Request for AnswerInlineQuery {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "answerInlineQuery", &self).await
self.bot.client(),
self.bot.token(),
"answerInlineQuery",
&self,
)
.await
} }
} }

View file

@ -34,13 +34,8 @@ impl Request for AnswerPreCheckoutQuery {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "answerPreCheckoutQuery", &self)
self.bot.client(), .await
self.bot.token(),
"answerPreCheckoutQuery",
&self,
)
.await
} }
} }

View file

@ -31,13 +31,7 @@ impl Request for AnswerShippingQuery {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "answerShippingQuery", &self).await
self.bot.client(),
self.bot.token(),
"answerShippingQuery",
&self,
)
.await
} }
} }
@ -47,13 +41,7 @@ impl AnswerShippingQuery {
S: Into<String>, S: Into<String>,
{ {
let shipping_query_id = shipping_query_id.into(); let shipping_query_id = shipping_query_id.into();
Self { Self { bot, shipping_query_id, ok, shipping_options: None, error_message: None }
bot,
shipping_query_id,
ok,
shipping_options: None,
error_message: None,
}
} }
/// Unique identifier for the query to be answered. /// Unique identifier for the query to be answered.

View file

@ -25,13 +25,7 @@ impl Request for DeleteChatPhoto {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "deleteChatPhoto", &self).await
self.bot.client(),
self.bot.token(),
"deleteChatPhoto",
&self,
)
.await
} }
} }

View file

@ -30,13 +30,7 @@ impl Request for DeleteChatStickerSet {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "deleteChatStickerSet", &self).await
self.bot.client(),
self.bot.token(),
"deleteChatStickerSet",
&self,
)
.await
} }
} }

View file

@ -36,13 +36,7 @@ impl Request for DeleteMessage {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "deleteMessage", &self).await
self.bot.client(),
self.bot.token(),
"deleteMessage",
&self,
)
.await
} }
} }

View file

@ -23,13 +23,7 @@ impl Request for DeleteStickerFromSet {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "deleteStickerFromSet", &self).await
self.bot.client(),
self.bot.token(),
"deleteStickerFromSet",
&self,
)
.await
} }
} }

View file

@ -26,13 +26,7 @@ impl Request for DeleteWebhook {
#[allow(clippy::trivially_copy_pass_by_ref)] #[allow(clippy::trivially_copy_pass_by_ref)]
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "deleteWebhook", &self).await
self.bot.client(),
self.bot.token(),
"deleteWebhook",
&self,
)
.await
} }
} }

View file

@ -33,28 +33,13 @@ impl Request for EditMessageCaption {
type Output = Message; type Output = Message;
async fn send(&self) -> ResponseResult<Message> { async fn send(&self) -> ResponseResult<Message> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "editMessageCaption", &self).await
self.bot.client(),
self.bot.token(),
"editMessageCaption",
&self,
)
.await
} }
} }
impl EditMessageCaption { impl EditMessageCaption {
pub(crate) fn new( pub(crate) fn new(bot: Bot, chat_or_inline_message: ChatOrInlineMessage) -> Self {
bot: Bot, Self { bot, chat_or_inline_message, caption: None, parse_mode: None, reply_markup: None }
chat_or_inline_message: ChatOrInlineMessage,
) -> Self {
Self {
bot,
chat_or_inline_message,
caption: None,
parse_mode: None,
reply_markup: None,
}
} }
pub fn chat_or_inline_message(mut self, val: ChatOrInlineMessage) -> Self { pub fn chat_or_inline_message(mut self, val: ChatOrInlineMessage) -> Self {

View file

@ -35,13 +35,8 @@ impl Request for EditMessageLiveLocation {
type Output = Message; type Output = Message;
async fn send(&self) -> ResponseResult<Message> { async fn send(&self) -> ResponseResult<Message> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "editMessageLiveLocation", &self)
self.bot.client(), .await
self.bot.token(),
"editMessageLiveLocation",
&self,
)
.await
} }
} }
@ -52,13 +47,7 @@ impl EditMessageLiveLocation {
latitude: f32, latitude: f32,
longitude: f32, longitude: f32,
) -> Self { ) -> Self {
Self { Self { bot, chat_or_inline_message, latitude, longitude, reply_markup: None }
bot,
chat_or_inline_message,
latitude,
longitude,
reply_markup: None,
}
} }
pub fn chat_or_inline_message(mut self, val: ChatOrInlineMessage) -> Self { pub fn chat_or_inline_message(mut self, val: ChatOrInlineMessage) -> Self {

View file

@ -36,13 +36,10 @@ impl Request for EditMessageMedia {
match &self.chat_or_inline_message { match &self.chat_or_inline_message {
ChatOrInlineMessage::Chat { chat_id, message_id } => { ChatOrInlineMessage::Chat { chat_id, message_id } => {
params = params params = params.add_text("chat_id", chat_id).add_text("message_id", message_id);
.add_text("chat_id", chat_id)
.add_text("message_id", message_id);
} }
ChatOrInlineMessage::Inline { inline_message_id } => { ChatOrInlineMessage::Inline { inline_message_id } => {
params = params = params.add_text("inline_message_id", inline_message_id);
params.add_text("inline_message_id", inline_message_id);
} }
} }

View file

@ -31,21 +31,13 @@ impl Request for EditMessageReplyMarkup {
type Output = Message; type Output = Message;
async fn send(&self) -> ResponseResult<Message> { async fn send(&self) -> ResponseResult<Message> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "editMessageReplyMarkup", &self)
self.bot.client(), .await
self.bot.token(),
"editMessageReplyMarkup",
&self,
)
.await
} }
} }
impl EditMessageReplyMarkup { impl EditMessageReplyMarkup {
pub(crate) fn new( pub(crate) fn new(bot: Bot, chat_or_inline_message: ChatOrInlineMessage) -> Self {
bot: Bot,
chat_or_inline_message: ChatOrInlineMessage,
) -> Self {
Self { bot, chat_or_inline_message, reply_markup: None } Self { bot, chat_or_inline_message, reply_markup: None }
} }

View file

@ -34,22 +34,12 @@ impl Request for EditMessageText {
type Output = Message; type Output = Message;
async fn send(&self) -> ResponseResult<Message> { async fn send(&self) -> ResponseResult<Message> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "editMessageText", &self).await
self.bot.client(),
self.bot.token(),
"editMessageText",
&self,
)
.await
} }
} }
impl EditMessageText { impl EditMessageText {
pub(crate) fn new<T>( pub(crate) fn new<T>(bot: Bot, chat_or_inline_message: ChatOrInlineMessage, text: T) -> Self
bot: Bot,
chat_or_inline_message: ChatOrInlineMessage,
text: T,
) -> Self
where where
T: Into<String>, T: Into<String>,
{ {

View file

@ -40,13 +40,7 @@ impl Request for ExportChatInviteLink {
/// Returns the new invite link as `String` on success. /// Returns the new invite link as `String` on success.
async fn send(&self) -> ResponseResult<String> { async fn send(&self) -> ResponseResult<String> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "exportChatInviteLink", &self).await
self.bot.client(),
self.bot.token(),
"exportChatInviteLink",
&self,
)
.await
} }
} }

View file

@ -26,36 +26,19 @@ impl Request for ForwardMessage {
type Output = Message; type Output = Message;
async fn send(&self) -> ResponseResult<Message> { async fn send(&self) -> ResponseResult<Message> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "forwardMessage", &self).await
self.bot.client(),
self.bot.token(),
"forwardMessage",
&self,
)
.await
} }
} }
impl ForwardMessage { impl ForwardMessage {
pub(crate) fn new<C, F>( pub(crate) fn new<C, F>(bot: Bot, chat_id: C, from_chat_id: F, message_id: i32) -> Self
bot: Bot,
chat_id: C,
from_chat_id: F,
message_id: i32,
) -> Self
where where
C: Into<ChatId>, C: Into<ChatId>,
F: Into<ChatId>, F: Into<ChatId>,
{ {
let chat_id = chat_id.into(); let chat_id = chat_id.into();
let from_chat_id = from_chat_id.into(); let from_chat_id = from_chat_id.into();
Self { Self { bot, chat_id, from_chat_id, message_id, disable_notification: None }
bot,
chat_id,
from_chat_id,
message_id,
disable_notification: None,
}
} }
/// Unique identifier for the target chat or username of the target channel /// Unique identifier for the target chat or username of the target channel

View file

@ -25,8 +25,7 @@ impl Request for GetChat {
type Output = Chat; type Output = Chat;
async fn send(&self) -> ResponseResult<Chat> { async fn send(&self) -> ResponseResult<Chat> {
net::request_json(self.bot.client(), self.bot.token(), "getChat", &self) net::request_json(self.bot.client(), self.bot.token(), "getChat", &self).await
.await
} }
} }

View file

@ -28,13 +28,7 @@ impl Request for GetChatAdministrators {
/// On success, returns an array that contains information about all chat /// On success, returns an array that contains information about all chat
/// administrators except other bots. /// administrators except other bots.
async fn send(&self) -> ResponseResult<Vec<ChatMember>> { async fn send(&self) -> ResponseResult<Vec<ChatMember>> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "getChatAdministrators", &self).await
self.bot.client(),
self.bot.token(),
"getChatAdministrators",
&self,
)
.await
} }
} }

View file

@ -24,13 +24,7 @@ impl Request for GetChatMember {
type Output = ChatMember; type Output = ChatMember;
async fn send(&self) -> ResponseResult<ChatMember> { async fn send(&self) -> ResponseResult<ChatMember> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "getChatMember", &self).await
self.bot.client(),
self.bot.token(),
"getChatMember",
&self,
)
.await
} }
} }

View file

@ -23,13 +23,7 @@ impl Request for GetChatMembersCount {
type Output = i32; type Output = i32;
async fn send(&self) -> ResponseResult<i32> { async fn send(&self) -> ResponseResult<i32> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "getChatMembersCount", &self).await
self.bot.client(),
self.bot.token(),
"getChatMembersCount",
&self,
)
.await
} }
} }

View file

@ -39,8 +39,7 @@ impl Request for GetFile {
type Output = File; type Output = File;
async fn send(&self) -> ResponseResult<File> { async fn send(&self) -> ResponseResult<File> {
net::request_json(self.bot.client(), self.bot.token(), "getFile", &self) net::request_json(self.bot.client(), self.bot.token(), "getFile", &self).await
.await
} }
} }

View file

@ -34,22 +34,12 @@ impl Request for GetGameHighScores {
type Output = Vec<GameHighScore>; type Output = Vec<GameHighScore>;
async fn send(&self) -> ResponseResult<Vec<GameHighScore>> { async fn send(&self) -> ResponseResult<Vec<GameHighScore>> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "getGameHighScores", &self).await
self.bot.client(),
self.bot.token(),
"getGameHighScores",
&self,
)
.await
} }
} }
impl GetGameHighScores { impl GetGameHighScores {
pub(crate) fn new( pub(crate) fn new(bot: Bot, chat_or_inline_message: ChatOrInlineMessage, user_id: i32) -> Self {
bot: Bot,
chat_or_inline_message: ChatOrInlineMessage,
user_id: i32,
) -> Self {
Self { bot, chat_or_inline_message, user_id } Self { bot, chat_or_inline_message, user_id }
} }

View file

@ -22,8 +22,7 @@ impl Request for GetMe {
/// Returns basic information about the bot. /// Returns basic information about the bot.
#[allow(clippy::trivially_copy_pass_by_ref)] #[allow(clippy::trivially_copy_pass_by_ref)]
async fn send(&self) -> ResponseResult<Me> { async fn send(&self) -> ResponseResult<Me> {
net::request_json(self.bot.client(), self.bot.token(), "getMe", &self) net::request_json(self.bot.client(), self.bot.token(), "getMe", &self).await
.await
} }
} }

View file

@ -23,13 +23,7 @@ impl Request for GetStickerSet {
type Output = StickerSet; type Output = StickerSet;
async fn send(&self) -> ResponseResult<StickerSet> { async fn send(&self) -> ResponseResult<StickerSet> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "getStickerSet", &self).await
self.bot.client(),
self.bot.token(),
"getStickerSet",
&self,
)
.await
} }
} }

View file

@ -36,23 +36,14 @@ impl Request for GetUpdates {
/// Deserialize to `Vec<serde_json::Result<Update>>` instead of /// Deserialize to `Vec<serde_json::Result<Update>>` instead of
/// `Vec<Update>`, because we want to parse the rest of updates even if our /// `Vec<Update>`, because we want to parse the rest of updates even if our
/// library hasn't parsed one. /// library hasn't parsed one.
async fn send( async fn send(&self) -> ResponseResult<Vec<Result<Update, (Value, serde_json::Error)>>> {
&self, let value: Value =
) -> ResponseResult<Vec<Result<Update, (Value, serde_json::Error)>>> { net::request_json(self.bot.client(), self.bot.token(), "getUpdates", &self).await?;
let value: Value = net::request_json(
self.bot.client(),
self.bot.token(),
"getUpdates",
&self,
)
.await?;
match value { match value {
Value::Array(array) => Ok(array Value::Array(array) => Ok(array
.into_iter() .into_iter()
.map(|value| { .map(|value| Update::try_parse(&value).map_err(|error| (value, error)))
Update::try_parse(&value).map_err(|error| (value, error))
})
.collect()), .collect()),
_ => Err(RequestError::InvalidJson( _ => Err(RequestError::InvalidJson(
serde_json::from_value::<Vec<Update>>(value) serde_json::from_value::<Vec<Update>>(value)
@ -64,13 +55,7 @@ impl Request for GetUpdates {
impl GetUpdates { impl GetUpdates {
pub(crate) fn new(bot: Bot) -> Self { pub(crate) fn new(bot: Bot) -> Self {
Self { Self { bot, offset: None, limit: None, timeout: None, allowed_updates: None }
bot,
offset: None,
limit: None,
timeout: None,
allowed_updates: None,
}
} }
/// Identifier of the first update to be returned. /// Identifier of the first update to be returned.

View file

@ -25,13 +25,7 @@ impl Request for GetUserProfilePhotos {
type Output = UserProfilePhotos; type Output = UserProfilePhotos;
async fn send(&self) -> ResponseResult<UserProfilePhotos> { async fn send(&self) -> ResponseResult<UserProfilePhotos> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "getUserProfilePhotos", &self).await
self.bot.client(),
self.bot.token(),
"getUserProfilePhotos",
&self,
)
.await
} }
} }

View file

@ -27,13 +27,7 @@ impl Request for GetWebhookInfo {
#[allow(clippy::trivially_copy_pass_by_ref)] #[allow(clippy::trivially_copy_pass_by_ref)]
async fn send(&self) -> ResponseResult<WebhookInfo> { async fn send(&self) -> ResponseResult<WebhookInfo> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "getWebhookInfo", &self).await
self.bot.client(),
self.bot.token(),
"getWebhookInfo",
&self,
)
.await
} }
} }

View file

@ -32,13 +32,7 @@ impl Request for KickChatMember {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "kickChatMember", &self).await
self.bot.client(),
self.bot.token(),
"kickChatMember",
&self,
)
.await
} }
} }

View file

@ -23,13 +23,7 @@ impl Request for LeaveChat {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "leaveChat", &self).await
self.bot.client(),
self.bot.token(),
"leaveChat",
&self,
)
.await
} }
} }

View file

@ -29,13 +29,7 @@ impl Request for PinChatMessage {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "pinChatMessage", &self).await
self.bot.client(),
self.bot.token(),
"pinChatMessage",
&self,
)
.await
} }
} }

View file

@ -36,13 +36,7 @@ impl Request for PromoteChatMember {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "promoteChatMember", &self).await
self.bot.client(),
self.bot.token(),
"promoteChatMember",
&self,
)
.await
} }
} }

View file

@ -30,23 +30,12 @@ impl Request for RestrictChatMember {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "restrictChatMember", &self).await
self.bot.client(),
self.bot.token(),
"restrictChatMember",
&self,
)
.await
} }
} }
impl RestrictChatMember { impl RestrictChatMember {
pub(crate) fn new<C>( pub(crate) fn new<C>(bot: Bot, chat_id: C, user_id: i32, permissions: ChatPermissions) -> Self
bot: Bot,
chat_id: C,
user_id: i32,
permissions: ChatPermissions,
) -> Self
where where
C: Into<ChatId>, C: Into<ChatId>,
{ {

View file

@ -75,22 +75,12 @@ impl Request for SendChatAction {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "sendChatAction", &self).await
self.bot.client(),
self.bot.token(),
"sendChatAction",
&self,
)
.await
} }
} }
impl SendChatAction { impl SendChatAction {
pub(crate) fn new<C>( pub(crate) fn new<C>(bot: Bot, chat_id: C, action: SendChatActionKind) -> Self
bot: Bot,
chat_id: C,
action: SendChatActionKind,
) -> Self
where where
C: Into<ChatId>, C: Into<ChatId>,
{ {

View file

@ -30,23 +30,12 @@ impl Request for SendContact {
type Output = Message; type Output = Message;
async fn send(&self) -> ResponseResult<Message> { async fn send(&self) -> ResponseResult<Message> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "sendContact", &self).await
self.bot.client(),
self.bot.token(),
"sendContact",
&self,
)
.await
} }
} }
impl SendContact { impl SendContact {
pub(crate) fn new<C, P, F>( pub(crate) fn new<C, P, F>(bot: Bot, chat_id: C, phone_number: P, first_name: F) -> Self
bot: Bot,
chat_id: C,
phone_number: P,
first_name: F,
) -> Self
where where
C: Into<ChatId>, C: Into<ChatId>,
P: Into<String>, P: Into<String>,

View file

@ -27,13 +27,7 @@ impl Request for SendGame {
type Output = Message; type Output = Message;
async fn send(&self) -> ResponseResult<Message> { async fn send(&self) -> ResponseResult<Message> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "sendGame", &self).await
self.bot.client(),
self.bot.token(),
"sendGame",
&self,
)
.await
} }
} }

View file

@ -45,13 +45,7 @@ impl Request for SendInvoice {
type Output = Message; type Output = Message;
async fn send(&self) -> ResponseResult<Message> { async fn send(&self) -> ResponseResult<Message> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "sendInvoice", &self).await
self.bot.client(),
self.bot.token(),
"sendInvoice",
&self,
)
.await
} }
} }

View file

@ -29,23 +29,12 @@ impl Request for SendLocation {
type Output = Message; type Output = Message;
async fn send(&self) -> ResponseResult<Message> { async fn send(&self) -> ResponseResult<Message> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "sendLocation", &self).await
self.bot.client(),
self.bot.token(),
"sendLocation",
&self,
)
.await
} }
} }
impl SendLocation { impl SendLocation {
pub(crate) fn new<C>( pub(crate) fn new<C>(bot: Bot, chat_id: C, latitude: f32, longitude: f32) -> Self
bot: Bot,
chat_id: C,
latitude: f32,
longitude: f32,
) -> Self
where where
C: Into<ChatId>, C: Into<ChatId>,
{ {

View file

@ -45,13 +45,7 @@ impl SendMediaGroup {
{ {
let chat_id = chat_id.into(); let chat_id = chat_id.into();
let media = media.into(); let media = media.into();
Self { Self { bot, chat_id, media, disable_notification: None, reply_to_message_id: None }
bot,
chat_id,
media,
disable_notification: None,
reply_to_message_id: None,
}
} }
/// Unique identifier for the target chat or username of the target channel /// Unique identifier for the target chat or username of the target channel

View file

@ -29,13 +29,7 @@ impl Request for SendMessage {
type Output = Message; type Output = Message;
async fn send(&self) -> ResponseResult<Message> { async fn send(&self) -> ResponseResult<Message> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "sendMessage", &self).await
self.bot.client(),
self.bot.token(),
"sendMessage",
&self,
)
.await
} }
} }

View file

@ -33,23 +33,12 @@ impl Request for SendPoll {
type Output = Message; type Output = Message;
async fn send(&self) -> ResponseResult<Message> { async fn send(&self) -> ResponseResult<Message> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "sendPoll", &self).await
self.bot.client(),
self.bot.token(),
"sendPoll",
&self,
)
.await
} }
} }
impl SendPoll { impl SendPoll {
pub(crate) fn new<C, Q, O>( pub(crate) fn new<C, Q, O>(bot: Bot, chat_id: C, question: Q, options: O) -> Self
bot: Bot,
chat_id: C,
question: Q,
options: O,
) -> Self
where where
C: Into<ChatId>, C: Into<ChatId>,
Q: Into<String>, Q: Into<String>,

View file

@ -32,13 +32,7 @@ impl Request for SendVenue {
type Output = Message; type Output = Message;
async fn send(&self) -> ResponseResult<Message> { async fn send(&self) -> ResponseResult<Message> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "sendVenue", &self).await
self.bot.client(),
self.bot.token(),
"sendVenue",
&self,
)
.await
} }
} }

View file

@ -37,12 +37,7 @@ impl Request for SetChatAdministratorCustomTitle {
} }
impl SetChatAdministratorCustomTitle { impl SetChatAdministratorCustomTitle {
pub(crate) fn new<C, CT>( pub(crate) fn new<C, CT>(bot: Bot, chat_id: C, user_id: i32, custom_title: CT) -> Self
bot: Bot,
chat_id: C,
user_id: i32,
custom_title: CT,
) -> Self
where where
C: Into<ChatId>, C: Into<ChatId>,
CT: Into<String>, CT: Into<String>,

View file

@ -28,13 +28,7 @@ impl Request for SetChatDescription {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "setChatDescription", &self).await
self.bot.client(),
self.bot.token(),
"setChatDescription",
&self,
)
.await
} }
} }

View file

@ -27,22 +27,12 @@ impl Request for SetChatPermissions {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "sendChatPermissions", &self).await
self.bot.client(),
self.bot.token(),
"sendChatPermissions",
&self,
)
.await
} }
} }
impl SetChatPermissions { impl SetChatPermissions {
pub(crate) fn new<C>( pub(crate) fn new<C>(bot: Bot, chat_id: C, permissions: ChatPermissions) -> Self
bot: Bot,
chat_id: C,
permissions: ChatPermissions,
) -> Self
where where
C: Into<ChatId>, C: Into<ChatId>,
{ {

View file

@ -27,13 +27,7 @@ impl Request for SetChatPhoto {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "setChatPhoto", &self).await
self.bot.client(),
self.bot.token(),
"setChatPhoto",
&self,
)
.await
} }
} }

View file

@ -28,13 +28,7 @@ impl Request for SetChatStickerSet {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "setChatStickerSet", &self).await
self.bot.client(),
self.bot.token(),
"setChatStickerSet",
&self,
)
.await
} }
} }

View file

@ -27,13 +27,7 @@ impl Request for SetChatTitle {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "setChatTitle", &self).await
self.bot.client(),
self.bot.token(),
"setChatTitle",
&self,
)
.await
} }
} }

View file

@ -36,13 +36,7 @@ impl Request for SetGameScore {
type Output = Message; type Output = Message;
async fn send(&self) -> ResponseResult<Message> { async fn send(&self) -> ResponseResult<Message> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "setGameScore", &self).await
self.bot.client(),
self.bot.token(),
"setGameScore",
&self,
)
.await
} }
} }

View file

@ -25,13 +25,8 @@ impl Request for SetStickerPositionInSet {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "setStickerPositionInSet", &self)
self.bot.client(), .await
self.bot.token(),
"setStickerPositionInSet",
&self,
)
.await
} }
} }

View file

@ -39,13 +39,7 @@ impl Request for SetWebhook {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "setWebhook", &self).await
self.bot.client(),
self.bot.token(),
"setWebhook",
&self,
)
.await
} }
} }
@ -55,13 +49,7 @@ impl SetWebhook {
U: Into<String>, U: Into<String>,
{ {
let url = url.into(); let url = url.into();
Self { Self { bot, url, certificate: None, max_connections: None, allowed_updates: None }
bot,
url,
certificate: None,
max_connections: None,
allowed_updates: None,
}
} }
/// HTTPS url to send updates to. /// HTTPS url to send updates to.

View file

@ -32,21 +32,13 @@ impl Request for StopMessageLiveLocation {
type Output = Message; type Output = Message;
async fn send(&self) -> ResponseResult<Message> { async fn send(&self) -> ResponseResult<Message> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "stopMessageLiveLocation", &self)
self.bot.client(), .await
self.bot.token(),
"stopMessageLiveLocation",
&self,
)
.await
} }
} }
impl StopMessageLiveLocation { impl StopMessageLiveLocation {
pub(crate) fn new( pub(crate) fn new(bot: Bot, chat_or_inline_message: ChatOrInlineMessage) -> Self {
bot: Bot,
chat_or_inline_message: ChatOrInlineMessage,
) -> Self {
Self { bot, chat_or_inline_message, reply_markup: None } Self { bot, chat_or_inline_message, reply_markup: None }
} }

View file

@ -28,13 +28,7 @@ impl Request for StopPoll {
/// ///
/// [`Poll`]: crate::types::Poll /// [`Poll`]: crate::types::Poll
async fn send(&self) -> ResponseResult<Poll> { async fn send(&self) -> ResponseResult<Poll> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "stopPoll", &self).await
self.bot.client(),
self.bot.token(),
"stopPoll",
&self,
)
.await
} }
} }
impl StopPoll { impl StopPoll {

View file

@ -27,13 +27,7 @@ impl Request for UnbanChatMember {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "unbanChatMember", &self).await
self.bot.client(),
self.bot.token(),
"unbanChatMember",
&self,
)
.await
} }
} }

View file

@ -27,13 +27,7 @@ impl Request for UnpinChatMessage {
type Output = True; type Output = True;
async fn send(&self) -> ResponseResult<True> { async fn send(&self) -> ResponseResult<True> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "unpinChatMessage", &self).await
self.bot.client(),
self.bot.token(),
"unpinChatMessage",
&self,
)
.await
} }
} }

View file

@ -28,13 +28,7 @@ impl Request for UploadStickerFile {
type Output = File; type Output = File;
async fn send(&self) -> ResponseResult<File> { async fn send(&self) -> ResponseResult<File> {
net::request_json( net::request_json(self.bot.client(), self.bot.token(), "uploadStickerFile", &self).await
self.bot.client(),
self.bot.token(),
"uploadStickerFile",
&self,
)
.await
} }
} }

View file

@ -5,8 +5,7 @@ use reqwest::multipart::Form;
use crate::{ use crate::{
requests::utils::{file_from_memory_to_part, file_to_part}, requests::utils::{file_from_memory_to_part, file_to_part},
types::{ types::{
ChatId, InlineKeyboardMarkup, InputFile, InputMedia, MaskPosition, ChatId, InlineKeyboardMarkup, InputFile, InputMedia, MaskPosition, ParseMode, ReplyMarkup,
ParseMode, ReplyMarkup,
}, },
}; };
@ -27,18 +26,12 @@ impl FormBuilder {
T: IntoFormText, T: IntoFormText,
{ {
match value.into_form_text() { match value.into_form_text() {
Some(val) => { Some(val) => Self { form: self.form.text(name.into().into_owned(), val) },
Self { form: self.form.text(name.into().into_owned(), val) }
}
None => self, None => self,
} }
} }
pub async fn add_input_file<'a, N>( pub async fn add_input_file<'a, N>(self, name: N, value: &InputFile) -> tokio::io::Result<Self>
self,
name: N,
value: &InputFile,
) -> tokio::io::Result<Self>
where where
N: Into<Cow<'a, str>>, N: Into<Cow<'a, str>>,
{ {
@ -53,19 +46,12 @@ impl FormBuilder {
} }
// used in SendMediaGroup // used in SendMediaGroup
pub async fn add_file<'a, N>( pub async fn add_file<'a, N>(self, name: N, path_to_file: PathBuf) -> tokio::io::Result<Self>
self,
name: N,
path_to_file: PathBuf,
) -> tokio::io::Result<Self>
where where
N: Into<Cow<'a, str>>, N: Into<Cow<'a, str>>,
{ {
Ok(Self { Ok(Self {
form: self.form.part( form: self.form.part(name.into().into_owned(), file_to_part(path_to_file).await?),
name.into().into_owned(),
file_to_part(path_to_file).await?,
),
}) })
} }
@ -79,10 +65,9 @@ impl FormBuilder {
N: Into<Cow<'a, str>>, N: Into<Cow<'a, str>>,
{ {
Self { Self {
form: self.form.part( form: self
name.into().into_owned(), .form
file_from_memory_to_part(data, file_name), .part(name.into().into_owned(), file_from_memory_to_part(data, file_name)),
),
} }
} }
@ -109,15 +94,7 @@ macro_rules! impl_for_struct {
}; };
} }
impl_for_struct!( impl_for_struct!(bool, i32, i64, u32, ReplyMarkup, InlineKeyboardMarkup, MaskPosition);
bool,
i32,
i64,
u32,
ReplyMarkup,
InlineKeyboardMarkup,
MaskPosition
);
impl<T> IntoFormText for Option<T> impl<T> IntoFormText for Option<T>
where where
@ -132,16 +109,14 @@ where
// encode files :|) // encode files :|)
impl IntoFormText for Vec<InputMedia> { impl IntoFormText for Vec<InputMedia> {
fn into_form_text(&self) -> Option<String> { fn into_form_text(&self) -> Option<String> {
let json = let json = serde_json::to_string(self).expect("serde_json::to_string failed");
serde_json::to_string(self).expect("serde_json::to_string failed");
Some(json) Some(json)
} }
} }
impl IntoFormText for InputMedia { impl IntoFormText for InputMedia {
fn into_form_text(&self) -> Option<String> { fn into_form_text(&self) -> Option<String> {
let json = let json = serde_json::to_string(self).expect("serde_json::to_string failed");
serde_json::to_string(self).expect("serde_json::to_string failed");
Some(json) Some(json)
} }
} }

View file

@ -10,10 +10,7 @@ impl Decoder for FileDecoder {
type Item = Bytes; type Item = Bytes;
type Error = std::io::Error; type Error = std::io::Error;
fn decode( fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
&mut self,
src: &mut BytesMut,
) -> Result<Option<Self::Item>, Self::Error> {
if src.is_empty() { if src.is_empty() {
return Ok(None); return Ok(None);
} }
@ -22,20 +19,13 @@ impl Decoder for FileDecoder {
} }
pub async fn file_to_part(path_to_file: PathBuf) -> std::io::Result<Part> { pub async fn file_to_part(path_to_file: PathBuf) -> std::io::Result<Part> {
let file_name = let file_name = path_to_file.file_name().unwrap().to_string_lossy().into_owned();
path_to_file.file_name().unwrap().to_string_lossy().into_owned();
let file = FramedRead::new( let file = FramedRead::new(tokio::fs::File::open(path_to_file).await?, FileDecoder);
tokio::fs::File::open(path_to_file).await?,
FileDecoder,
);
Ok(Part::stream(Body::wrap_stream(file)).file_name(file_name)) Ok(Part::stream(Body::wrap_stream(file)).file_name(file_name))
} }
pub fn file_from_memory_to_part( pub fn file_from_memory_to_part(data: Cow<'static, [u8]>, name: String) -> Part {
data: Cow<'static, [u8]>,
name: String,
) -> Part {
Part::bytes(data).file_name(name) Part::bytes(data).file_name(name)
} }

View file

@ -64,9 +64,7 @@ mod tests {
duration: 60, duration: 60,
performer: Some("Performer".to_string()), performer: Some("Performer".to_string()),
title: Some("Title".to_string()), title: Some("Title".to_string()),
mime_type: Some( mime_type: Some(serde_json::from_str("\"application/zip\"").unwrap()),
serde_json::from_str("\"application/zip\"").unwrap(),
),
file_size: Some(123_456), file_size: Some(123_456),
thumb: Some(PhotoSize { thumb: Some(PhotoSize {
file_id: "id".to_string(), file_id: "id".to_string(),

View file

@ -154,16 +154,10 @@ impl<'de> serde::de::Visitor<'de> for PrivateChatKindVisitor {
write!(f, r#"field equal to "private""#) write!(f, r#"field equal to "private""#)
} }
fn visit_borrowed_str<E: serde::de::Error>( fn visit_borrowed_str<E: serde::de::Error>(self, v: &'de str) -> Result<Self::Value, E> {
self,
v: &'de str,
) -> Result<Self::Value, E> {
match v { match v {
"private" => Ok(()), "private" => Ok(()),
_ => Err(E::invalid_value( _ => Err(E::invalid_value(serde::de::Unexpected::Str(v), &r#""private""#)),
serde::de::Unexpected::Str(v),
&r#""private""#,
)),
} }
} }
} }
@ -180,30 +174,16 @@ impl Chat {
matches!(self.kind, ChatKind::Private(_)) matches!(self.kind, ChatKind::Private(_))
} }
pub fn is_group(&self) -> bool { pub fn is_group(&self) -> bool {
matches!( matches!(self.kind, ChatKind::Public(ChatPublic { kind: PublicChatKind::Group(_), .. }))
self.kind,
ChatKind::Public(ChatPublic {
kind: PublicChatKind::Group(_), ..
})
)
} }
pub fn is_supergroup(&self) -> bool { pub fn is_supergroup(&self) -> bool {
matches!( matches!(
self.kind, self.kind,
ChatKind::Public(ChatPublic { ChatKind::Public(ChatPublic { kind: PublicChatKind::Supergroup(_), .. })
kind: PublicChatKind::Supergroup(_),
..
})
) )
} }
pub fn is_channel(&self) -> bool { pub fn is_channel(&self) -> bool {
matches!( matches!(self.kind, ChatKind::Public(ChatPublic { kind: PublicChatKind::Channel(_), .. }))
self.kind,
ChatKind::Public(ChatPublic {
kind: PublicChatKind::Channel(_),
..
})
)
} }
pub fn is_chat(&self) -> bool { pub fn is_chat(&self) -> bool {
@ -232,9 +212,7 @@ mod tests {
}), }),
photo: None, photo: None,
}; };
let actual = let actual = from_str(r#"{"id":-1,"type":"channel","username":"channelname"}"#).unwrap();
from_str(r#"{"id":-1,"type":"channel","username":"channelname"}"#)
.unwrap();
assert_eq!(expected, actual); assert_eq!(expected, actual);
} }
@ -251,9 +229,9 @@ mod tests {
}), }),
photo: None, photo: None,
}, },
from_str( from_str(r#"{"id":0,"type":"private","username":"username","first_name":"Anon"}"#)
r#"{"id":0,"type":"private","username":"username","first_name":"Anon"}"# .unwrap()
).unwrap()); );
} }
#[test] #[test]

View file

@ -3,9 +3,7 @@ use serde::{Deserialize, Serialize};
/// A unique identifier for the target chat or username of the target channel /// A unique identifier for the target chat or username of the target channel
/// (in the format `@channelusername`). /// (in the format `@channelusername`).
#[derive( #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, Display, From)]
Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, Display, From,
)]
#[serde(untagged)] #[serde(untagged)]
pub enum ChatId { pub enum ChatId {
/// A chat identifier. /// A chat identifier.
@ -32,10 +30,8 @@ mod tests {
#[test] #[test]
fn chat_id_channel_username_serialization() { fn chat_id_channel_username_serialization() {
let expected_json = String::from(r#""@username""#); let expected_json = String::from(r#""@username""#);
let actual_json = serde_json::to_string(&ChatId::ChannelUsername( let actual_json =
String::from("@username"), serde_json::to_string(&ChatId::ChannelUsername(String::from("@username"))).unwrap();
))
.unwrap();
assert_eq!(expected_json, actual_json) assert_eq!(expected_json, actual_json)
} }

View file

@ -51,8 +51,7 @@ mod tests {
secret: "secret".to_string(), secret: "secret".to_string(),
}; };
// when // when
let actual_json = let actual_json = serde_json::to_string(&encrypted_credentials).unwrap();
serde_json::to_string(&encrypted_credentials).unwrap();
//then //then
assert_eq!(actual_json, expected_json) assert_eq!(actual_json, expected_json)
} }

View file

@ -79,35 +79,21 @@ pub enum InlineKeyboardButtonKind {
/// ``` /// ```
/// use teloxide::types::InlineKeyboardButton; /// use teloxide::types::InlineKeyboardButton;
/// ///
/// let url_button = InlineKeyboardButton::url( /// let url_button = InlineKeyboardButton::url("Text".to_string(), "http://url.com".to_string());
/// "Text".to_string(),
/// "http://url.com".to_string(),
/// );
/// ``` /// ```
impl InlineKeyboardButton { impl InlineKeyboardButton {
pub fn url(text: String, url: String) -> InlineKeyboardButton { pub fn url(text: String, url: String) -> InlineKeyboardButton {
InlineKeyboardButton { text, kind: InlineKeyboardButtonKind::Url(url) } InlineKeyboardButton { text, kind: InlineKeyboardButtonKind::Url(url) }
} }
pub fn callback( pub fn callback(text: String, callback_data: String) -> InlineKeyboardButton {
text: String, InlineKeyboardButton { text, kind: InlineKeyboardButtonKind::CallbackData(callback_data) }
callback_data: String,
) -> InlineKeyboardButton {
InlineKeyboardButton {
text,
kind: InlineKeyboardButtonKind::CallbackData(callback_data),
}
} }
pub fn switch_inline_query( pub fn switch_inline_query(text: String, switch_inline_query: String) -> InlineKeyboardButton {
text: String,
switch_inline_query: String,
) -> InlineKeyboardButton {
InlineKeyboardButton { InlineKeyboardButton {
text, text,
kind: InlineKeyboardButtonKind::SwitchInlineQuery( kind: InlineKeyboardButtonKind::SwitchInlineQuery(switch_inline_query),
switch_inline_query,
),
} }
} }

View file

@ -26,10 +26,7 @@ pub struct InlineKeyboardMarkup {
/// ``` /// ```
/// use teloxide::types::{InlineKeyboardButton, InlineKeyboardMarkup}; /// use teloxide::types::{InlineKeyboardButton, InlineKeyboardMarkup};
/// ///
/// let url_button = InlineKeyboardButton::url( /// let url_button = InlineKeyboardButton::url("text".to_string(), "http://url.com".to_string());
/// "text".to_string(),
/// "http://url.com".to_string(),
/// );
/// let keyboard = InlineKeyboardMarkup::default().append_row(vec![url_button]); /// let keyboard = InlineKeyboardMarkup::default().append_row(vec![url_button]);
/// ``` /// ```
impl InlineKeyboardMarkup { impl InlineKeyboardMarkup {
@ -38,11 +35,7 @@ impl InlineKeyboardMarkup {
self self
} }
pub fn append_to_row( pub fn append_to_row(mut self, button: InlineKeyboardButton, index: usize) -> Self {
mut self,
button: InlineKeyboardButton,
index: usize,
) -> Self {
match self.inline_keyboard.get_mut(index) { match self.inline_keyboard.get_mut(index) {
Some(buttons) => buttons.push(button), Some(buttons) => buttons.push(button),
None => self.inline_keyboard.push(vec![button]), None => self.inline_keyboard.push(vec![button]),
@ -57,65 +50,41 @@ mod tests {
#[test] #[test]
fn append_row() { fn append_row() {
let button1 = InlineKeyboardButton::url( let button1 = InlineKeyboardButton::url("text 1".to_string(), "url 1".to_string());
"text 1".to_string(), let button2 = InlineKeyboardButton::url("text 2".to_string(), "url 2".to_string());
"url 1".to_string(),
);
let button2 = InlineKeyboardButton::url(
"text 2".to_string(),
"url 2".to_string(),
);
let markup = InlineKeyboardMarkup::default() let markup =
.append_row(vec![button1.clone(), button2.clone()]); InlineKeyboardMarkup::default().append_row(vec![button1.clone(), button2.clone()]);
let expected = InlineKeyboardMarkup { let expected = InlineKeyboardMarkup { inline_keyboard: vec![vec![button1, button2]] };
inline_keyboard: vec![vec![button1, button2]],
};
assert_eq!(markup, expected); assert_eq!(markup, expected);
} }
#[test] #[test]
fn append_to_row_existent_row() { fn append_to_row_existent_row() {
let button1 = InlineKeyboardButton::url( let button1 = InlineKeyboardButton::url("text 1".to_string(), "url 1".to_string());
"text 1".to_string(), let button2 = InlineKeyboardButton::url("text 2".to_string(), "url 2".to_string());
"url 1".to_string(),
);
let button2 = InlineKeyboardButton::url(
"text 2".to_string(),
"url 2".to_string(),
);
let markup = InlineKeyboardMarkup::default() let markup = InlineKeyboardMarkup::default()
.append_row(vec![button1.clone()]) .append_row(vec![button1.clone()])
.append_to_row(button2.clone(), 0); .append_to_row(button2.clone(), 0);
let expected = InlineKeyboardMarkup { let expected = InlineKeyboardMarkup { inline_keyboard: vec![vec![button1, button2]] };
inline_keyboard: vec![vec![button1, button2]],
};
assert_eq!(markup, expected); assert_eq!(markup, expected);
} }
#[test] #[test]
fn append_to_row_nonexistent_row() { fn append_to_row_nonexistent_row() {
let button1 = InlineKeyboardButton::url( let button1 = InlineKeyboardButton::url("text 1".to_string(), "url 1".to_string());
"text 1".to_string(), let button2 = InlineKeyboardButton::url("text 2".to_string(), "url 2".to_string());
"url 1".to_string(),
);
let button2 = InlineKeyboardButton::url(
"text 2".to_string(),
"url 2".to_string(),
);
let markup = InlineKeyboardMarkup::default() let markup = InlineKeyboardMarkup::default()
.append_row(vec![button1.clone()]) .append_row(vec![button1.clone()])
.append_to_row(button2.clone(), 1); .append_to_row(button2.clone(), 1);
let expected = InlineKeyboardMarkup { let expected = InlineKeyboardMarkup { inline_keyboard: vec![vec![button1], vec![button2]] };
inline_keyboard: vec![vec![button1], vec![button2]],
};
assert_eq!(markup, expected); assert_eq!(markup, expected);
} }

View file

@ -4,15 +4,13 @@ use derive_more::From;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::{ use crate::types::{
InlineQueryResultArticle, InlineQueryResultAudio, InlineQueryResultArticle, InlineQueryResultAudio, InlineQueryResultCachedAudio,
InlineQueryResultCachedAudio, InlineQueryResultCachedDocument, InlineQueryResultCachedDocument, InlineQueryResultCachedGif, InlineQueryResultCachedMpeg4Gif,
InlineQueryResultCachedGif, InlineQueryResultCachedMpeg4Gif, InlineQueryResultCachedPhoto, InlineQueryResultCachedSticker, InlineQueryResultCachedVideo,
InlineQueryResultCachedPhoto, InlineQueryResultCachedSticker, InlineQueryResultCachedVoice, InlineQueryResultContact, InlineQueryResultDocument,
InlineQueryResultCachedVideo, InlineQueryResultCachedVoice, InlineQueryResultGame, InlineQueryResultGif, InlineQueryResultLocation,
InlineQueryResultContact, InlineQueryResultDocument, InlineQueryResultGame, InlineQueryResultMpeg4Gif, InlineQueryResultPhoto, InlineQueryResultVenue,
InlineQueryResultGif, InlineQueryResultLocation, InlineQueryResultMpeg4Gif, InlineQueryResultVideo, InlineQueryResultVoice,
InlineQueryResultPhoto, InlineQueryResultVenue, InlineQueryResultVideo,
InlineQueryResultVoice,
}; };
/// This object represents one result of an inline query. /// This object represents one result of an inline query.
@ -57,25 +55,22 @@ pub enum InlineQueryResult {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::types::{ use crate::types::{
inline_keyboard_markup::InlineKeyboardMarkup, parse_mode::ParseMode, inline_keyboard_markup::InlineKeyboardMarkup, parse_mode::ParseMode, InlineQueryResult,
InlineQueryResult, InlineQueryResultCachedAudio, InputMessageContent, InlineQueryResultCachedAudio, InputMessageContent, InputMessageContentText,
InputMessageContentText,
}; };
#[test] #[test]
fn cached_audio_min_serialize() { fn cached_audio_min_serialize() {
let structure = let structure = InlineQueryResult::CachedAudio(InlineQueryResultCachedAudio {
InlineQueryResult::CachedAudio(InlineQueryResultCachedAudio { id: String::from("id"),
id: String::from("id"), audio_file_id: String::from("audio_file_id"),
audio_file_id: String::from("audio_file_id"), caption: None,
caption: None, parse_mode: None,
parse_mode: None, reply_markup: None,
reply_markup: None, input_message_content: None,
input_message_content: None, });
});
let expected_json = let expected_json = r#"{"type":"audio","id":"id","audio_file_id":"audio_file_id"}"#;
r#"{"type":"audio","id":"id","audio_file_id":"audio_file_id"}"#;
let actual_json = serde_json::to_string(&structure).unwrap(); let actual_json = serde_json::to_string(&structure).unwrap();
assert_eq!(expected_json, actual_json); assert_eq!(expected_json, actual_json);
@ -83,21 +78,18 @@ mod tests {
#[test] #[test]
fn cached_audio_full_serialize() { fn cached_audio_full_serialize() {
let structure = let structure = InlineQueryResult::CachedAudio(InlineQueryResultCachedAudio {
InlineQueryResult::CachedAudio(InlineQueryResultCachedAudio { id: String::from("id"),
id: String::from("id"), audio_file_id: String::from("audio_file_id"),
audio_file_id: String::from("audio_file_id"), caption: Some(String::from("caption")),
caption: Some(String::from("caption")), parse_mode: Some(ParseMode::HTML),
parse_mode: Some(ParseMode::HTML), reply_markup: Some(InlineKeyboardMarkup::default()),
reply_markup: Some(InlineKeyboardMarkup::default()), input_message_content: Some(InputMessageContent::Text(InputMessageContentText {
input_message_content: Some(InputMessageContent::Text( message_text: String::from("message_text"),
InputMessageContentText { parse_mode: Some(ParseMode::MarkdownV2),
message_text: String::from("message_text"), disable_web_page_preview: Some(true),
parse_mode: Some(ParseMode::MarkdownV2), })),
disable_web_page_preview: Some(true), });
},
)),
});
let expected_json = r#"{"type":"audio","id":"id","audio_file_id":"audio_file_id","caption":"caption","parse_mode":"HTML","reply_markup":{"inline_keyboard":[]},"input_message_content":{"message_text":"message_text","parse_mode":"MarkdownV2","disable_web_page_preview":true}}"#; let expected_json = r#"{"type":"audio","id":"id","audio_file_id":"audio_file_id","caption":"caption","parse_mode":"HTML","reply_markup":{"inline_keyboard":[]},"input_message_content":{"message_text":"message_text","parse_mode":"MarkdownV2","disable_web_page_preview":true}}"#;
let actual_json = serde_json::to_string(&structure).unwrap(); let actual_json = serde_json::to_string(&structure).unwrap();

View file

@ -1,8 +1,6 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::{ use crate::types::{InlineKeyboardMarkup, InputMessageContent, MimeWrapper, ParseMode};
InlineKeyboardMarkup, InputMessageContent, MimeWrapper, ParseMode,
};
/// Represents a link to a file. /// Represents a link to a file.
/// ///

View file

@ -1,8 +1,6 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::{ use crate::types::{InlineKeyboardMarkup, InputMessageContent, MimeWrapper, ParseMode};
InlineKeyboardMarkup, InputMessageContent, MimeWrapper, ParseMode,
};
/// Represents a link to a page containing an embedded video player or a video /// Represents a link to a page containing an embedded video player or a video
/// file. /// file.

View file

@ -85,19 +85,13 @@ impl Serialize for InputFile {
// multipart/form-data // multipart/form-data
serializer.serialize_str( serializer.serialize_str(
// TODO: remove unwrap (?) // TODO: remove unwrap (?)
&format!( &format!("attach://{}", path.file_name().unwrap().to_string_lossy()),
"attach://{}",
path.file_name().unwrap().to_string_lossy()
),
) )
} }
InputFile::Memory { data, .. } => { InputFile::Memory { data, .. } => {
// NOTE: file should be actually attached with // NOTE: file should be actually attached with
// multipart/form-data // multipart/form-data
serializer.serialize_str(&format!( serializer.serialize_str(&format!("attach://{}", String::from_utf8_lossy(data)))
"attach://{}",
String::from_utf8_lossy(data)
))
} }
InputFile::Url(url) => serializer.serialize_str(url), InputFile::Url(url) => serializer.serialize_str(url),
InputFile::FileId(id) => serializer.serialize_str(id), InputFile::FileId(id) => serializer.serialize_str(id),

Some files were not shown because too many files have changed in this diff Show more