Refactor Bot for a bit

- Move default parse mode to an adaptor
- Remove bot builder (it's not usefull anymore, since parse_mode is moved away)
    - Undeprecate bot constructors
This commit is contained in:
Waffle 2020-11-27 01:18:57 +03:00
parent df7aac88a9
commit 45cf62ce8e
6 changed files with 207 additions and 229 deletions

View file

@ -19,6 +19,8 @@ pub mod cache_me;
#[cfg_attr(all(docsrs, feature = "nightly"), doc(cfg(feature = "throttle")))]
pub mod throttle;
mod parse_mode;
#[cfg(feature = "auto_send")]
#[cfg_attr(all(docsrs, feature = "nightly"), doc(cfg(feature = "auto_send")))]
pub use auto_send::AutoSend;
@ -29,4 +31,6 @@ pub use cache_me::CacheMe;
#[cfg_attr(all(docsrs, feature = "nightly"), doc(cfg(feature = "throttle")))]
pub use throttle::Throttle;
pub use parse_mode::DefaultParseMode;
// FIXME: move default `parse_mode` to adaptor

108
src/adaptors/parse_mode.rs Normal file
View file

@ -0,0 +1,108 @@
use crate::{
prelude::Requester,
requests::HasPayload,
types::{ChatId, InputFile, ParseMode, *},
};
/// Default parse mode adaptor, see
/// [`RequesterExt::parse_mode`](crate::requests::RequesterExt::parse_mode)
pub struct DefaultParseMode<B> {
bot: B,
mode: ParseMode,
}
impl<B> DefaultParseMode<B> {
/// Creates new [`DefaultParseMode`]
///
/// Note: it's recommended to use [`RequesterExt::parse_mode`] instead.
///
/// [`RequesterExt::parse_mode`]: crate::requests::RequesterExt::parse_mode
pub fn new(bot: B, parse_mode: ParseMode) -> Self {
Self { bot, mode: parse_mode }
}
/// Allows to access inner bot
pub fn inner(&self) -> &B {
&self.bot
}
/// Unwraps inner bot
pub fn into_inner(self) -> B {
self.bot
}
}
macro_rules! f {
($m:ident $this:ident ($($arg:ident : $T:ty),*)) => {
{
let mut req = $this.inner().$m($($arg),*);
req.payload_mut().parse_mode = Some($this.mode);
req
}
};
}
macro_rules! fty {
($T:ident) => {
B::$T
};
}
macro_rules! fid {
($m:ident $this:ident ($($arg:ident : $T:ty),*)) => {
$this.inner().$m($($arg),*)
};
}
impl<B: Requester> Requester for DefaultParseMode<B> {
type Err = B::Err;
requester_forward! {
send_message,
send_photo,
send_video,
send_audio,
send_document,
send_animation,
send_voice,
edit_message_text,
edit_message_text_inline,
edit_message_caption,
edit_message_caption_inline => f, fty
}
type SendPoll = B::SendPoll;
fn send_poll<C, Q, O>(
&self,
chat_id: C,
question: Q,
options: O,
type_: PollType,
) -> Self::SendPoll
where
C: Into<ChatId>,
Q: Into<String>,
O: IntoIterator<Item = String>,
{
let mut req = self.inner().send_poll(chat_id, question, options, type_);
req.payload_mut().explanation_parse_mode = Some(self.mode);
req
}
requester_forward! {
get_me, get_updates, set_webhook, delete_webhook, get_webhook_info,
forward_message, send_video_note, send_media_group, send_location,
edit_message_live_location, edit_message_live_location_inline, stop_message_live_location,
stop_message_live_location_inline, send_venue, send_contact, send_dice,
send_chat_action, get_user_profile_photos, get_file, kick_chat_member, unban_chat_member,
restrict_chat_member, promote_chat_member, set_chat_administrator_custom_title, set_chat_permissions,
export_chat_invite_link, set_chat_photo, delete_chat_photo, set_chat_title, set_chat_description,
pin_chat_message, unpin_chat_message, leave_chat, get_chat, get_chat_administrators, get_chat_members_count,
get_chat_member, set_chat_sticker_set, delete_chat_sticker_set, answer_callback_query, set_my_commands,
get_my_commands, answer_inline_query, edit_message_media, edit_message_media_inline, edit_message_reply_markup,
edit_message_reply_markup_inline, stop_poll, delete_message, send_sticker, get_sticker_set, upload_sticker_file,
create_new_sticker_set, add_sticker_to_set, set_sticker_position_in_set, delete_sticker_from_set,
set_sticker_set_thumb, send_invoice, answer_shipping_query, answer_pre_checkout_query, set_passport_data_errors => fid, fty
}
}

View file

@ -21,7 +21,7 @@ use crate::{
},
types::{
BotCommand, ChatId, ChatPermissions, InlineQueryResult, InputFile, InputMedia,
InputSticker, LabeledPrice, ParseMode, TargetMessage,
InputSticker, LabeledPrice, TargetMessage,
},
Bot,
};
@ -130,10 +130,7 @@ impl Bot {
C: Into<ChatId>,
T: Into<String>,
{
self.with_default_parse_mode_if_specified(
SendMessage::new(self.clone(), chat_id, text),
SendMessage::parse_mode,
)
SendMessage::new(self.clone(), chat_id, text)
}
/// Use this method to forward messages of any kind.
@ -192,10 +189,7 @@ impl Bot {
where
C: Into<ChatId>,
{
self.with_default_parse_mode_if_specified(
SendPhoto::new(self.clone(), chat_id, photo),
SendPhoto::parse_mode,
)
SendPhoto::new(self.clone(), chat_id, photo)
}
///
@ -213,10 +207,7 @@ impl Bot {
where
C: Into<ChatId>,
{
self.with_default_parse_mode_if_specified(
SendAudio::new(self.clone(), chat_id, audio),
SendAudio::parse_mode,
)
SendAudio::new(self.clone(), chat_id, audio)
}
/// Use this method to send general files.
@ -247,10 +238,7 @@ impl Bot {
where
C: Into<ChatId>,
{
self.with_default_parse_mode_if_specified(
SendDocument::new(self.clone(), chat_id, document),
SendDocument::parse_mode,
)
SendDocument::new(self.clone(), chat_id, document)
}
/// Use this method to send video files, Telegram clients support mp4 videos
@ -284,10 +272,7 @@ impl Bot {
where
C: Into<ChatId>,
{
self.with_default_parse_mode_if_specified(
SendVideo::new(self.clone(), chat_id, video),
SendVideo::parse_mode,
)
SendVideo::new(self.clone(), chat_id, video)
}
/// Use this method to send animation files (GIF or H.264/MPEG-4 AVC video
@ -312,10 +297,7 @@ impl Bot {
where
C: Into<ChatId>,
{
self.with_default_parse_mode_if_specified(
SendAnimation::new(self.clone(), chat_id, animation),
SendAnimation::parse_mode,
)
SendAnimation::new(self.clone(), chat_id, animation)
}
/// Use this method to send audio files, if you want Telegram clients to
@ -355,10 +337,7 @@ impl Bot {
where
C: Into<ChatId>,
{
self.with_default_parse_mode_if_specified(
SendVoice::new(self.clone(), chat_id, voice),
SendVoice::parse_mode,
)
SendVoice::new(self.clone(), chat_id, voice)
}
/// As of [v.4.0], Telegram clients support rounded square mp4 videos of up
@ -583,10 +562,7 @@ impl Bot {
Q: Into<String>,
O: Into<Vec<String>>,
{
self.with_default_parse_mode_if_specified(
SendPoll::new(self.clone(), chat_id, question, options),
SendPoll::explanation_parse_mode,
)
SendPoll::new(self.clone(), chat_id, question, options)
}
/// Use this method when you need to tell the user that something is
@ -1069,12 +1045,7 @@ impl Bot {
C: Into<ChatId>,
T: Into<String>,
{
match self.parse_mode {
None => EditMessageText::new(self.clone(), chat_id, message_id, text),
Some(parse_mode) => {
EditMessageText::new(self.clone(), chat_id, message_id, text).parse_mode(parse_mode)
}
}
EditMessageText::new(self.clone(), chat_id, message_id, text)
}
/// Use this method to edit text and game messages sent via the bot.
@ -1105,11 +1076,7 @@ impl Bot {
I: Into<String>,
T: Into<String>,
{
match self.parse_mode {
None => EditInlineMessageText::new(self.clone(), inline_message_id, text),
Some(parse_mode) => EditInlineMessageText::new(self.clone(), inline_message_id, text)
.parse_mode(parse_mode),
}
EditInlineMessageText::new(self.clone(), inline_message_id, text)
}
/// Use this method to edit captions of messages sent via the bot.
@ -1130,12 +1097,7 @@ impl Bot {
where
C: Into<ChatId>,
{
match self.parse_mode {
None => EditMessageCaption::new(self.clone(), chat_id, message_id),
Some(parse_mode) => {
EditMessageCaption::new(self.clone(), chat_id, message_id).parse_mode(parse_mode)
}
}
EditMessageCaption::new(self.clone(), chat_id, message_id)
}
/// Use this method to edit captions of messages sent via the bot.
@ -1155,11 +1117,7 @@ impl Bot {
where
I: Into<String>,
{
match self.parse_mode {
None => EditInlineMessageCaption::new(self.clone(), inline_message_id),
Some(parse_mode) => EditInlineMessageCaption::new(self.clone(), inline_message_id)
.parse_mode(parse_mode),
}
EditInlineMessageCaption::new(self.clone(), inline_message_id)
}
/// Use this method to edit animation, audio, document, photo, or video
@ -1691,17 +1649,6 @@ impl Bot {
{
SetStickerSetThumb::new(self.clone(), name, user_id)
}
fn with_default_parse_mode_if_specified<Builder>(
&self,
builder: Builder,
f: fn(Builder, ParseMode) -> Builder,
) -> Builder {
match self.parse_mode {
None => builder,
Some(parse_mode) => f(builder, parse_mode),
}
}
}
impl Requester for Bot {

View file

@ -11,7 +11,6 @@ use crate::{
net,
requests::{Payload, ResponseResult},
serde_multipart,
types::ParseMode,
};
mod api;
@ -31,56 +30,15 @@ pub struct Bot {
token: Arc<str>,
api_url: ApiUrl,
client: Client,
parse_mode: Option<ParseMode>,
}
impl Bot {
/// Creates a new `Bot` with the `TELOXIDE_TOKEN` & `TELOXIDE_PROXY`
/// environmental variables (a bot's token & a proxy) and the default
/// [`reqwest::Client`].
///
/// This function passes the value of `TELOXIDE_PROXY` into
/// [`reqwest::Proxy::all`], if it exists, otherwise returns the default
/// client.
///
/// # Panics
/// - If cannot get the `TELOXIDE_TOKEN` environmental variable.
/// - If it cannot create [`reqwest::Client`].
///
/// [`reqwest::Client`]: https://docs.rs/reqwest/0.10.1/reqwest/struct.Client.html
/// [`reqwest::Proxy::all`]: https://docs.rs/reqwest/latest/reqwest/struct.Proxy.html#method.all
#[must_use]
pub fn from_env() -> Self {
BotBuilder::new().build()
}
/// Creates a new `Bot` with the `TELOXIDE_TOKEN` environmental variable (a
/// bot's token) and your [`reqwest::Client`].
///
/// # Panics
/// If cannot get the `TELOXIDE_TOKEN` environmental variable.
///
/// # Caution
/// Your custom client might not be configured correctly to be able to work
/// in long time durations, see [issue 223].
///
/// [`reqwest::Client`]: https://docs.rs/reqwest/0.10.1/reqwest/struct.Client.html
/// [issue 223]: https://github.com/teloxide/teloxide/issues/223
#[deprecated]
#[allow(deprecated)]
pub fn from_env_with_client(client: Client) -> Self {
Self::with_client(&get_env(TELOXIDE_TOKEN), client)
}
/// Creates a new `Bot` with the specified token and the default
/// [`reqwest::Client`].
/// [http-client](reqwest::Client).
///
/// # Panics
/// If it cannot create [`reqwest::Client`].
///
/// [`reqwest::Client`]: https://docs.rs/reqwest/latest/reqwest/struct.Client.html
#[deprecated]
#[allow(deprecated)]
/// If it cannot create [`reqwest::Client`].
pub fn new<S>(token: S) -> Self
where
S: Into<String>,
@ -97,8 +55,6 @@ impl Bot {
///
/// [`reqwest::Client`]: https://docs.rs/reqwest/latest/reqwest/struct.Client.html
/// [issue 223]: https://github.com/teloxide/teloxide/issues/223
#[deprecated]
#[allow(deprecated)]
pub fn with_client<S>(token: S, client: Client) -> Self
where
S: Into<String>,
@ -107,9 +63,52 @@ impl Bot {
token: Into::<Arc<str>>::into(Into::<String>::into(token)),
api_url: ApiUrl::Default,
client,
parse_mode: None,
}
}
/// Creates a new `Bot` with the `TELOXIDE_TOKEN` & `TELOXIDE_PROXY`
/// environmental variables (a bot's token & a proxy) and the default
/// [`reqwest::Client`].
///
/// This function passes the value of `TELOXIDE_PROXY` into
/// [`reqwest::Proxy::all`], if it exists, otherwise returns the default
/// client.
///
/// # Panics
/// - If cannot get the `TELOXIDE_TOKEN` environmental variable.
/// - If it cannot create [`reqwest::Client`].
///
/// [`reqwest::Client`]: https://docs.rs/reqwest/0.10.1/reqwest/struct.Client.html
/// [`reqwest::Proxy::all`]: https://docs.rs/reqwest/latest/reqwest/struct.Proxy.html#method.all
pub fn from_env() -> Self {
Self::from_env_with_client(crate::client_from_env())
}
/// Creates a new `Bot` with the `TELOXIDE_TOKEN` environmental variable (a
/// bot's token) and your [`reqwest::Client`].
///
/// # Panics
/// If cannot get the `TELOXIDE_TOKEN` environmental variable.
///
/// # Caution
/// Your custom client might not be configured correctly to be able to work
/// in long time durations, see [issue 223].
///
/// [`reqwest::Client`]: https://docs.rs/reqwest/0.10.1/reqwest/struct.Client.html
/// [issue 223]: https://github.com/teloxide/teloxide/issues/223
pub fn from_env_with_client(client: Client) -> Self {
Self::with_client(&get_env(TELOXIDE_TOKEN), client)
}
/// Returns currently used token
pub fn token(&self) -> &str {
&self.token
}
/// Returns currently used http-client
pub fn client(&self) -> &Client {
&self.client
}
}
impl Bot {
@ -182,115 +181,3 @@ pub(crate) fn build_sound_bot() -> Client {
fn get_env(env: &'static str) -> String {
std::env::var(env).unwrap_or_else(|_| panic!("Cannot get the {} env variable", env))
}
impl Bot {
pub fn token(&self) -> &str {
&self.token
}
pub fn client(&self) -> &Client {
&self.client
}
}
/// A builder of [`Bot`], supporting some extra settings.
///
/// [`Bot`] crate::Bot
#[derive(Debug, Default)]
pub struct BotBuilder {
token: Option<String>,
client: Option<Client>,
parse_mode: Option<ParseMode>,
}
impl BotBuilder {
#[must_use]
pub fn new() -> Self {
Self::default()
}
/// Specifies a custom HTTPS client. Otherwise, the default will be used.
///
/// # Caution
/// - Your custom client might not be configured correctly to be able to
/// work
/// in long time durations, see [issue 223].
///
/// - If this method is used, the `TELOXIDE_PROXY` environmental variable
/// won't be extracted in [`BotBuilder::build`].
///
/// [issue 223]: https://github.com/teloxide/teloxide/issues/223
/// [`BotBuilder::build`]: crate::BotBuilder::build
#[must_use]
pub fn client(mut self, client: Client) -> Self {
self.client = Some(client);
self
}
/// Specified a custom token.
///
/// Otherwise, a token will be extracted from the `TELOXIDE_TOKEN`
/// environmental variable.
#[must_use]
pub fn token<S>(mut self, token: S) -> Self
where
S: Into<String>,
{
self.token = Some(token.into());
self
}
/// Specifies [`ParseMode`], which will be used during all calls to:
///
/// - [`send_message`]
/// - [`send_photo`]
/// - [`send_video`]
/// - [`send_audio`]
/// - [`send_document`]
/// - [`send_animation`]
/// - [`send_voice`]
/// - [`send_poll`]
/// - [`edit_message_text`]
/// - [`edit_message_caption`]
///
/// [`send_message`]: crate::Bot::send_message
/// [`send_photo`]: crate::Bot::send_photo
/// [`send_video`]: crate::Bot::send_video
/// [`send_audio`]: crate::Bot::send_audio
/// [`send_document`]: crate::Bot::send_document
/// [`send_animation`]: crate::Bot::send_animation
/// [`send_voice`]: crate::Bot::send_voice
/// [`send_poll`]: crate::Bot::send_poll
/// [`edit_message_text`]: crate::Bot::edit_message_text
/// [`edit_message_caption`]: crate::Bot::edit_message_caption
#[must_use]
pub fn parse_mode(mut self, parse_mode: ParseMode) -> Self {
self.parse_mode = Some(parse_mode);
self
}
/// Builds [`Bot`].
///
/// This method will attempt to build a new client with a proxy, specified
/// in the `TELOXIDE_PROXY` (passed into [`reqwest::Proxy::all`])
/// environmental variable, if a client haven't been specified. If
/// `TELOXIDE_PROXY` is unspecified, it'll use no proxy.
///
/// # Panics
/// - If cannot get the `TELOXIDE_TOKEN` environmental variable.
/// - If it cannot create [`reqwest::Client`].
///
/// [`reqwest::Client`]: https://docs.rs/reqwest/0.10.1/reqwest/struct.Client.html
///
/// [`Bot`]: crate::Bot
/// [`reqwest::Proxy::all`]: https://docs.rs/reqwest/latest/reqwest/struct.Proxy.html#method.all
#[must_use]
pub fn build(self) -> Bot {
Bot {
token: self.token.unwrap_or_else(|| get_env(TELOXIDE_TOKEN)).into(),
api_url: ApiUrl::Default,
client: self.client.unwrap_or_else(crate::client_from_env),
parse_mode: self.parse_mode,
}
}
}

View file

@ -7,19 +7,19 @@
// ```console
// $ RUSTDOCFLAGS="--cfg docsrs" cargo doc --open --all-features
// ```
#![forbid(unsafe_code)]
#![cfg_attr(all(docsrs, feature = "nightly"), feature(doc_cfg, doc_spotlight))]
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
#![forbid(unsafe_code)]
#![cfg_attr(feature = "full", deny(broken_intra_doc_links))]
//#![deny(missing_docs)]
#[macro_use]
// The internal helper macros.
#[macro_use]
mod local_macros;
// FIXME(waffle): rethink modules, find a place for wrappers.
pub use self::{
bot::{Bot, BotBuilder},
bot::Bot,
errors::{ApiError, DownloadError, RequestError},
};

View file

@ -1,4 +1,4 @@
use crate::requests::Requester;
use crate::{adaptors::DefaultParseMode, requests::Requester, types::ParseMode};
#[cfg(feature = "cache_me")]
use crate::adaptors::CacheMe;
@ -46,6 +46,38 @@ pub trait RequesterExt: Requester {
{
Throttle::new_spawn(self, limits)
}
/// Specifies default [`ParseMode`], which will be used during all calls to:
///
/// - [`send_message`]
/// - [`send_photo`]
/// - [`send_video`]
/// - [`send_audio`]
/// - [`send_document`]
/// - [`send_animation`]
/// - [`send_voice`]
/// - [`send_poll`]
/// - [`edit_message_text`] (and [`edit_message_text_inline`])
/// - [`edit_message_caption`] (and [`edit_message_caption_inline`])
///
/// [`send_message`]: crate::Requester::send_message
/// [`send_photo`]: crate::Requester::send_photo
/// [`send_video`]: crate::Requester::send_video
/// [`send_audio`]: crate::Requester::send_audio
/// [`send_document`]: crate::Requester::send_document
/// [`send_animation`]: crate::Requester::send_animation
/// [`send_voice`]: crate::Requester::send_voice
/// [`send_poll`]: crate::Requester::send_poll
/// [`edit_message_text`]: crate::Requester::edit_message_text
/// [`edit_message_text`]: crate::Requester::edit_message_text_inline
/// [`edit_message_caption`]: crate::Requester::edit_message_caption
/// [`edit_message_caption`]: crate::Requester::edit_message_caption_inline
fn parse_mode(self, parse_mode: ParseMode) -> DefaultParseMode<Self>
where
Self: Sized,
{
DefaultParseMode::new(self, parse_mode)
}
}
impl<T> RequesterExt for T