Merge pull request #231 from teloxide/bot-builder

Implement BotBuilder
This commit is contained in:
Temirkhan Myrzamadi 2020-07-17 01:02:02 +06:00 committed by GitHub
commit 57d120b399
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 230 additions and 11 deletions

View file

@ -5,6 +5,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.3.0] - ???
### Added
- `BotBuilder`, which allows setting a default `ParseMode`.
### Deprecated
- `Bot::{from_env_with_client, new, with_client}`.
### Changed
- Now methods which can send file to Telegram returns tokio::io::Result<T>. Early its could panic. ([issue 216](https://github.com/teloxide/teloxide/issues/216))
- Now provided description of unknown telegram error, by splitting ApiErrorKind at `ApiErrorKind` and `ApiErrorKindKnown` enums. ([issue 199](https://github.com/teloxide/teloxide/issues/199))

View file

@ -108,6 +108,12 @@ impl Bot {
/// - `chat_id`: Unique identifier for the target chat or username of the
/// target supergroup or channel (in the format `@channelusername`).
/// - `text`: Text of the message to be sent.
///
/// # Notes
/// Uses [a default parse mode] if specified in [`BotBuilder`].
///
/// [a default parse mode]: crate::BotBuilder::parse_mode
/// [`BotBuilder`]: crate::BotBuilder
pub fn send_message<C, T>(
self: &Arc<Bot>,
chat_id: C,
@ -117,7 +123,13 @@ impl Bot {
C: Into<ChatId>,
T: Into<String>,
{
SendMessage::new(Arc::clone(self), chat_id, text)
match self.parse_mode {
None => SendMessage::new(Arc::clone(self), chat_id, text),
Some(parse_mode) => {
SendMessage::new(Arc::clone(self), chat_id, text)
.parse_mode(parse_mode)
}
}
}
/// Use this method to forward messages of any kind.
@ -166,6 +178,12 @@ impl Bot {
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
///
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
///
/// # Notes
/// Uses [a default parse mode] if specified in [`BotBuilder`].
///
/// [a default parse mode]: crate::BotBuilder::parse_mode
/// [`BotBuilder`]: crate::BotBuilder
pub fn send_photo<C>(
self: &Arc<Bot>,
chat_id: C,
@ -174,7 +192,13 @@ impl Bot {
where
C: Into<ChatId>,
{
SendPhoto::new(Arc::clone(self), chat_id, photo)
match self.parse_mode {
None => SendPhoto::new(Arc::clone(self), chat_id, photo),
Some(parse_mode) => {
SendPhoto::new(Arc::clone(self), chat_id, photo)
.parse_mode(parse_mode)
}
}
}
///
@ -182,6 +206,12 @@ impl Bot {
/// # Params
/// - `chat_id`: Unique identifier for the target chat or username of the
/// target supergroup or channel (in the format `@channelusername`).
///
/// # Notes
/// Uses [a default parse mode] if specified in [`BotBuilder`].
///
/// [a default parse mode]: crate::BotBuilder::parse_mode
/// [`BotBuilder`]: crate::BotBuilder
pub fn send_audio<C>(
self: &Arc<Bot>,
chat_id: C,
@ -190,7 +220,13 @@ impl Bot {
where
C: Into<ChatId>,
{
SendAudio::new(Arc::clone(self), chat_id, audio)
match self.parse_mode {
None => SendAudio::new(Arc::clone(self), chat_id, audio),
Some(parse_mode) => {
SendAudio::new(Arc::clone(self), chat_id, audio)
.parse_mode(parse_mode)
}
}
}
/// Use this method to send general files.
@ -211,6 +247,12 @@ impl Bot {
/// `multipart/form-data`. [More info on Sending Files »].
///
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
///
/// # Notes
/// Uses [a default parse mode] if specified in [`BotBuilder`].
///
/// [a default parse mode]: crate::BotBuilder::parse_mode
/// [`BotBuilder`]: crate::BotBuilder
pub fn send_document<C>(
self: &Arc<Bot>,
chat_id: C,
@ -219,7 +261,13 @@ impl Bot {
where
C: Into<ChatId>,
{
SendDocument::new(Arc::clone(self), chat_id, document)
match self.parse_mode {
None => SendDocument::new(Arc::clone(self), chat_id, document),
Some(parse_mode) => {
SendDocument::new(Arc::clone(self), chat_id, document)
.parse_mode(parse_mode)
}
}
}
/// Use this method to send video files, Telegram clients support mp4 videos
@ -243,6 +291,12 @@ impl Bot {
/// [`InputFile::File`]: crate::types::InputFile::File
/// [`InputFile::Url`]: crate::types::InputFile::Url
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
///
/// # Notes
/// Uses [a default parse mode] if specified in [`BotBuilder`].
///
/// [a default parse mode]: crate::BotBuilder::parse_mode
/// [`BotBuilder`]: crate::BotBuilder
pub fn send_video<C>(
self: &Arc<Bot>,
chat_id: C,
@ -251,7 +305,13 @@ impl Bot {
where
C: Into<ChatId>,
{
SendVideo::new(Arc::clone(self), chat_id, video)
match self.parse_mode {
None => SendVideo::new(Arc::clone(self), chat_id, video),
Some(parse_mode) => {
SendVideo::new(Arc::clone(self), chat_id, video)
.parse_mode(parse_mode)
}
}
}
/// Use this method to send animation files (GIF or H.264/MPEG-4 AVC video
@ -266,6 +326,12 @@ impl Bot {
/// - `chat_id`: Unique identifier for the target chat or username of the
/// target supergroup or channel (in the format `@channelusername`).
/// - `animation`: Animation to send.
///
/// # Notes
/// Uses [a default parse mode] if specified in [`BotBuilder`].
///
/// [a default parse mode]: crate::BotBuilder::parse_mode
/// [`BotBuilder`]: crate::BotBuilder
pub fn send_animation<C>(
self: &Arc<Bot>,
chat_id: C,
@ -274,7 +340,13 @@ impl Bot {
where
C: Into<ChatId>,
{
SendAnimation::new(Arc::clone(self), chat_id, animation)
match self.parse_mode {
None => SendAnimation::new(Arc::clone(self), chat_id, animation),
Some(parse_mode) => {
SendAnimation::new(Arc::clone(self), chat_id, animation)
.parse_mode(parse_mode)
}
}
}
/// Use this method to send audio files, if you want Telegram clients to
@ -304,6 +376,12 @@ impl Bot {
/// [`InputFile::Url`]: crate::types::InputFile::Url
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
///
/// # Notes
/// Uses [a default parse mode] if specified in [`BotBuilder`].
///
/// [a default parse mode]: crate::BotBuilder::parse_mode
/// [`BotBuilder`]: crate::BotBuilder
pub fn send_voice<C>(
self: &Arc<Bot>,
chat_id: C,
@ -312,7 +390,13 @@ impl Bot {
where
C: Into<ChatId>,
{
SendVoice::new(Arc::clone(self), chat_id, voice)
match self.parse_mode {
None => SendVoice::new(Arc::clone(self), chat_id, voice),
Some(parse_mode) => {
SendVoice::new(Arc::clone(self), chat_id, voice)
.parse_mode(parse_mode)
}
}
}
/// As of [v.4.0], Telegram clients support rounded square mp4 videos of up
@ -335,6 +419,7 @@ impl Bot {
/// [`InputFile::Url`]: crate::types::InputFile::Url
/// [`InputFile::FileId`]: crate::types::InputFile::FileId
/// [More info on Sending Files »]: https://core.telegram.org/bots/api#sending-files
pub fn send_video_note<C>(
self: &Arc<Bot>,
chat_id: C,
@ -515,6 +600,7 @@ impl Bot {
Q: Into<String>,
O: Into<Vec<String>>,
{
// FIXME: parse_mode
SendPoll::new(Arc::clone(self), chat_id, question, options)
}
@ -1044,6 +1130,12 @@ impl Bot {
///
/// [`Message`]: crate::types::Message
/// [`True`]: crate::types::True
///
/// # Notes
/// Uses [a default parse mode] if specified in [`BotBuilder`].
///
/// [a default parse mode]: crate::BotBuilder::parse_mode
/// [`BotBuilder`]: crate::BotBuilder
pub fn edit_message_text<T>(
self: &Arc<Bot>,
chat_or_inline_message: ChatOrInlineMessage,
@ -1052,7 +1144,19 @@ impl Bot {
where
T: Into<String>,
{
EditMessageText::new(Arc::clone(self), chat_or_inline_message, text)
match self.parse_mode {
None => EditMessageText::new(
Arc::clone(self),
chat_or_inline_message,
text,
),
Some(parse_mode) => EditMessageText::new(
Arc::clone(self),
chat_or_inline_message,
text,
)
.parse_mode(parse_mode),
}
}
/// Use this method to edit captions of messages.
@ -1064,11 +1168,27 @@ impl Bot {
///
/// [`Message`]: crate::types::Message
/// [`True`]: crate::types::True
///
/// # Notes
/// Uses [a default parse mode] if specified in [`BotBuilder`].
///
/// [a default parse mode]: crate::BotBuilder::parse_mode
/// [`BotBuilder`]: crate::BotBuilder
pub fn edit_message_caption(
self: &Arc<Bot>,
chat_or_inline_message: ChatOrInlineMessage,
) -> EditMessageCaption {
EditMessageCaption::new(Arc::clone(self), chat_or_inline_message)
match self.parse_mode {
None => EditMessageCaption::new(
Arc::clone(self),
chat_or_inline_message,
),
Some(parse_mode) => EditMessageCaption::new(
Arc::clone(self),
chat_or_inline_message,
)
.parse_mode(parse_mode),
}
}
/// Use this method to edit animation, audio, document, photo, or video

View file

@ -1,3 +1,4 @@
use crate::types::ParseMode;
use reqwest::Client;
use std::sync::Arc;
@ -9,6 +10,7 @@ mod download;
pub struct Bot {
token: String,
client: Client,
parse_mode: Option<ParseMode>,
}
impl Bot {
@ -19,6 +21,7 @@ impl Bot {
/// If cannot get the `TELOXIDE_TOKEN` environmental variable.
///
/// [`reqwest::Client`]: https://docs.rs/reqwest/0.10.1/reqwest/struct.Client.html
#[allow(deprecated)]
pub fn from_env() -> Arc<Self> {
Self::from_env_with_client(Client::new())
}
@ -30,6 +33,8 @@ impl Bot {
/// If cannot get the `TELOXIDE_TOKEN` environmental variable.
///
/// [`reqwest::Client`]: https://docs.rs/reqwest/0.10.1/reqwest/struct.Client.html
#[deprecated]
#[allow(deprecated)]
pub fn from_env_with_client(client: Client) -> Arc<Self> {
Self::with_client(
&std::env::var("TELOXIDE_TOKEN")
@ -42,6 +47,8 @@ impl Bot {
/// [`reqwest::Client`].
///
/// [`reqwest::Client`]: https://docs.rs/reqwest/0.10.1/reqwest/struct.Client.html
#[deprecated]
#[allow(deprecated)]
pub fn new<S>(token: S) -> Arc<Self>
where
S: Into<String>,
@ -53,11 +60,13 @@ impl Bot {
/// [`reqwest::Client`].
///
/// [`reqwest::Client`]: https://docs.rs/reqwest/0.10.1/reqwest/struct.Client.html
#[deprecated]
#[allow(deprecated)]
pub fn with_client<S>(token: S, client: Client) -> Arc<Self>
where
S: Into<String>,
{
Arc::new(Self { token: token.into(), client })
Arc::new(Self { token: token.into(), client, parse_mode: None })
}
}
@ -72,3 +81,86 @@ impl Bot {
&self.client
}
}
#[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.
#[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`].
///
/// # Panics
/// If cannot get the `TELOXIDE_TOKEN` environmental variable.
///
/// [`reqwest::Client`]: https://docs.rs/reqwest/0.10.1/reqwest/struct.Client.html
///
/// [`Bot`]: crate::Bot
#[must_use]
pub fn build(self) -> Bot {
Bot {
client: self.client.unwrap_or_default(),
token: self.token.unwrap_or_else(|| {
std::env::var("TELOXIDE_TOKEN")
.expect("Cannot get the TELOXIDE_TOKEN env variable")
}),
parse_mode: self.parse_mode,
}
}
}

View file

@ -188,6 +188,7 @@ mod tests {
};
#[tokio::test]
#[allow(deprecated)]
async fn updates_from_same_chat_executed_sequentially() {
#[derive(Debug)]
struct MyUpdate {

View file

@ -16,7 +16,7 @@
#![allow(clippy::match_bool)]
#![forbid(unsafe_code)]
pub use bot::Bot;
pub use bot::{Bot, BotBuilder};
pub use errors::{ApiErrorKind, DownloadError, RequestError};
mod errors;