Merge pull request #86 from teloxide/api-5.2

API 5.2 support
This commit is contained in:
Hirrolot 2021-05-04 10:55:24 -07:00 committed by GitHub
commit 152f053c6a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 364 additions and 94 deletions

View file

@ -12,10 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `impl Clone` for {`CacheMe`, `DefaultParseMode`, `Throttle`} ([#75][pr75])
- Getters for fields nested in `Chat` ([#80][pr80])
- API errors: `ApiError::NotEnoughRightsToManagePins`, `ApiError::BotKickedFromSupergroup` ([#84][pr84])
- Telegram bot API 5.2 support ([#86][pr86])
[pr75]: https://github.com/teloxide/teloxide-core/pull/75
[pr80]: https://github.com/teloxide/teloxide-core/pull/80
[pr84]: https://github.com/teloxide/teloxide-core/pull/84
[pr86]: https://github.com/teloxide/teloxide-core/pull/86
### Changed
@ -23,7 +25,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Refactor `ChatMember` methods ([#74][pr74])
- impl `Deref<Target = ChatMemberKind>` to make `ChatMemberKind`'s methods callible directly on `ChatMember`
- Add `ChatMemberKind::is_{creator,administrator,member,restricted,left,kicked}` which check `kind` along with `is_privileged` and `is_in_chat` which combine some of the above.
- Refactor privilege getters
- Refactor privilege getters
- Rename `ChatAction::{RecordAudio => RecordVoice, UploadAudio => UploadVoice}` ([#86][pr86])
[pr74]: https://github.com/teloxide/teloxide-core/pull/74

View file

@ -37,6 +37,7 @@ tokio-util = "0.6.0"
pin-project = "1.0.3"
bytes = "1.0.0"
reqwest = { version = "0.11.0", features = ["json", "stream", "multipart"], default-features = false }
url = { version = "2", features = ["serde"] }
log = "0.4"
serde = { version = "1.0.114", features = ["derive"] }

View file

@ -15,7 +15,7 @@
<img src="https://img.shields.io/badge/license-MIT-blue.svg">
</a>
<a href="https://core.telegram.org/bots/api">
<img src="https://img.shields.io/badge/API%20coverage-Up%20to%205.1%20(inclusively)-green.svg">
<img src="https://img.shields.io/badge/API%20coverage-Up%20to%205.2%20(inclusively)-green.svg">
</a>
<a href="https://crates.io/crates/teloxide_core">
<img src="https://img.shields.io/crates/v/teloxide_core.svg">

View file

@ -411,45 +411,7 @@ where
send_message, forward_message, copy_message, send_photo, send_audio,
send_document, send_video, send_animation, send_voice, send_video_note,
send_media_group, send_location, send_venue, send_contact, send_poll,
send_dice, send_sticker, => f, fty
}
type SendInvoice = ThrottlingRequest<B::SendInvoice>;
fn send_invoice<T, D, Pa, P, S, C, Pri>(
&self,
chat_id: i32,
title: T,
description: D,
payload: Pa,
provider_token: P,
start_parameter: S,
currency: C,
prices: Pri,
) -> Self::SendInvoice
where
T: Into<String>,
D: Into<String>,
Pa: Into<String>,
P: Into<String>,
S: Into<String>,
C: Into<String>,
Pri: IntoIterator<Item = LabeledPrice>,
{
ThrottlingRequest {
request: self.inner().send_invoice(
chat_id,
title,
description,
payload,
provider_token,
start_parameter,
currency,
prices,
),
chat_id: |p| ChatIdHash::Id(p.payload_ref().chat_id as _),
worker: self.queue.clone(),
}
send_dice, send_sticker, send_invoice => f, fty
}
requester_forward! {

View file

@ -875,23 +875,22 @@ impl Requester for Bot {
type SendInvoice = JsonRequest<payloads::SendInvoice>;
fn send_invoice<T, D, Pa, P, S, C, Pri>(
fn send_invoice<Ch, T, D, Pa, P, C, Pri>(
&self,
chat_id: i32,
chat_id: Ch,
title: T,
description: D,
payload: Pa,
provider_token: P,
start_parameter: S,
currency: C,
prices: Pri,
) -> Self::SendInvoice
where
Ch: Into<ChatId>,
T: Into<String>,
D: Into<String>,
Pa: Into<String>,
P: Into<String>,
S: Into<String>,
C: Into<String>,
Pri: IntoIterator<Item = LabeledPrice>,
{
@ -903,7 +902,6 @@ impl Requester for Bot {
description,
payload,
provider_token,
start_parameter,
currency,
prices,
),

View file

@ -392,6 +392,37 @@ macro_rules! impl_payload {
(@[] $($ignored:tt)*) => {}
}
#[macro_use]
macro_rules! download_forward {
($l:lifetime $T:ident $S:ty {$this:ident => $inner:expr}) => {
impl<$l, $T: $crate::net::Download<$l>> $crate::net::Download<$l> for $S {
type Err = <$T as $crate::net::Download<$l>>::Err;
type Fut = <$T as $crate::net::Download<$l>>::Fut;
fn download_file(
&self,
path: &str,
destination: &'w mut (dyn tokio::io::AsyncWrite
+ core::marker::Unpin
+ core::marker::Send),
) -> Self::Fut {
let $this = self;
($inner).download_file(path, destination)
}
type StreamErr = <$T as $crate::net::Download<$l>>::StreamErr;
type Stream = <$T as $crate::net::Download<$l>>::Stream;
fn download_file_stream(&self, path: &str) -> Self::Stream {
let $this = self;
($inner).download_file_stream(path)
}
}
};
}
#[macro_use]
// This macro is auto generated by [`cg`] from [`schema`].
//
@ -1040,15 +1071,15 @@ macro_rules! requester_forward {
(@method send_invoice $body:ident $ty:ident) => {
type SendInvoice = $ty![SendInvoice];
fn send_invoice<T, D, Pa, P, S, C, Pri>(&self, chat_id: i32, title: T, description: D, payload: Pa, provider_token: P, start_parameter: S, currency: C, prices: Pri) -> Self::SendInvoice where T: Into<String>,
fn send_invoice<Ch, T, D, Pa, P, C, Pri>(&self, chat_id: Ch, title: T, description: D, payload: Pa, provider_token: P, currency: C, prices: Pri) -> Self::SendInvoice where Ch: Into<ChatId>,
T: Into<String>,
D: Into<String>,
Pa: Into<String>,
P: Into<String>,
S: Into<String>,
C: Into<String>,
Pri: IntoIterator<Item = LabeledPrice> {
let this = self;
$body!(send_invoice this (chat_id: i32, title: T, description: D, payload: Pa, provider_token: P, start_parameter: S, currency: C, prices: Pri))
$body!(send_invoice this (chat_id: Ch, title: T, description: D, payload: Pa, provider_token: P, currency: C, prices: Pri))
}
};
(@method answer_shipping_query $body:ident $ty:ident) => {
@ -1116,34 +1147,3 @@ macro_rules! requester_forward {
}
};
}
#[macro_use]
macro_rules! download_forward {
($l:lifetime $T:ident $S:ty {$this:ident => $inner:expr}) => {
impl<$l, $T: crate::net::Download<$l>> crate::net::Download<$l> for $S {
type Err = <$T as crate::net::Download<$l>>::Err;
type Fut = <$T as crate::net::Download<$l>>::Fut;
fn download_file(
&self,
path: &str,
destination: &'w mut (dyn tokio::io::AsyncWrite
+ core::marker::Unpin
+ core::marker::Send),
) -> Self::Fut {
let $this = self;
($inner).download_file(path, destination)
}
type StreamErr = <$T as crate::net::Download<$l>>::StreamErr;
type Stream = <$T as crate::net::Download<$l>>::Stream;
fn download_file_stream(&self, path: &str) -> Self::Stream {
let $this = self;
($inner).download_file_stream(path)
}
}
};
}

View file

@ -8,7 +8,7 @@
// [`schema`]: https://github.com/WaffleLapkin/tg-methods-schema
use serde::Serialize;
use crate::types::{InlineKeyboardMarkup, LabeledPrice, Message};
use crate::types::{ChatId, InlineKeyboardMarkup, LabeledPrice, Message};
impl_payload! {
/// Use this method to send invoices. On success, the sent [`Message`] is returned.
@ -18,7 +18,7 @@ impl_payload! {
pub SendInvoice (SendInvoiceSetters) => Message {
required {
/// Unique identifier for the target private chat
pub chat_id: i32,
pub chat_id: ChatId [into],
/// Product name, 1-32 characters
pub title: String [into],
/// Product description, 1-255 characters
@ -29,14 +29,20 @@ impl_payload! {
///
/// [Botfather]: https://t.me/botfather
pub provider_token: String [into],
/// Unique deep-linking parameter that can be used to generate this invoice when used as a start parameter
pub start_parameter: String [into],
/// Three-letter ISO 4217 currency code, see more on currencies
pub currency: String [into],
/// Price breakdown, a JSON-serialized list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)
pub prices: Vec<LabeledPrice> [collect],
}
optional {
/// The maximum accepted amount for tips in the smallest units of the currency (integer, **not** float/double). For example, for a maximum tip of `US$ 1.45` pass `max_tip_amount = 145`. See the exp parameter in [`currencies.json`], it shows the number of digits past the decimal point for each currency (2 for the majority of currencies). Defaults to 0
///
/// [`currencies.json`]: https://core.telegram.org/bots/payments/currencies.json
pub max_tip_amount: u32,
/// A JSON-serialized array of suggested amounts of tips in the smallest units of the currency (integer, **not** float/double). At most 4 suggested tip amounts can be specified. The suggested tip amounts must be positive, passed in a strictly increased order and must not exceed _max_tip_amount_.
pub suggested_tip_amounts: Vec<u32> [collect],
/// Unique deep-linking parameter. If left empty, **forwarded copies** of the sent message will have a Pay button, allowing multiple users to pay directly from the forwarded message, using the same invoice. If non-empty, forwarded copies of the sent message will have a URL button with a deep link to the bot (instead of a Pay button), with the value used as the start parameter
pub start_parameter: String [into],
/// A JSON-serialized data about the invoice, which will be shared with the payment provider. A detailed description of required fields should be provided by the payment provider.
pub provider_data: String [into],
/// URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for.

View file

@ -60,7 +60,6 @@ pub trait Requester {
//
// [cg]: https://github.com/teloxide/cg
// [`schema`]: https://github.com/WaffleLapkin/tg-methods-schema
type GetUpdates: Request<Payload = GetUpdates, Err = Self::Err>;
/// For Telegram documentation see [`GetUpdates`].
@ -723,23 +722,22 @@ pub trait Requester {
type SendInvoice: Request<Payload = SendInvoice, Err = Self::Err>;
/// For Telegram documentation see [`SendInvoice`].
fn send_invoice<T, D, Pa, P, S, C, Pri>(
fn send_invoice<Ch, T, D, Pa, P, C, Pri>(
&self,
chat_id: i32,
chat_id: Ch,
title: T,
description: D,
payload: Pa,
provider_token: P,
start_parameter: S,
currency: C,
prices: Pri,
) -> Self::SendInvoice
where
Ch: Into<ChatId>,
T: Into<String>,
D: Into<String>,
Pa: Into<String>,
P: Into<String>,
S: Into<String>,
C: Into<String>,
Pri: IntoIterator<Item = LabeledPrice>;

View file

@ -15,6 +15,7 @@ pub use chat_member::*;
pub use chat_member_updated::*;
pub use chat_permissions::*;
pub use chat_photo::*;
pub use chat_type::*;
pub use chosen_inline_result::*;
pub use contact::*;
pub use dice::*;
@ -99,6 +100,7 @@ pub use video_note::*;
pub use voice::*;
pub use voice_chat_ended::*;
pub use voice_chat_participants_invited::*;
pub use voice_chat_scheduled::*;
pub use voice_chat_started::*;
pub use webhook_info::*;
@ -117,6 +119,7 @@ mod chat_member;
mod chat_member_updated;
mod chat_permissions;
mod chat_photo;
mod chat_type;
mod chosen_inline_result;
mod contact;
mod dice;
@ -174,6 +177,7 @@ mod video_note;
mod voice;
mod voice_chat_ended;
mod voice_chat_participants_invited;
mod voice_chat_scheduled;
mod voice_chat_started;
mod webhook_info;

View file

@ -7,8 +7,8 @@ pub enum ChatAction {
UploadPhoto,
RecordVideo,
UploadVideo,
RecordAudio,
UploadAudio,
RecordVoice,
UploadVoice,
UploadDocument,
FindLocation,
RecordVideoNote,

13
src/types/chat_type.rs Normal file
View file

@ -0,0 +1,13 @@
use serde::{Deserialize, Serialize};
/// Type of the chat, from which the inline query was sent.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ChatType {
/// Private chat with the inline query sender.
Sender,
Private,
Group,
Supergroup,
Channel,
}

View file

@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};
use crate::types::{Location, User};
use crate::types::{ChatType, Location, User};
/// This object represents an incoming inline query.
///
@ -25,8 +25,16 @@ pub struct InlineQuery {
/// Offset of the results to be returned, can be controlled by the bot.
pub offset: String,
/// Type of the chat, from which the inline query was sent.
///
/// The chat type should be always known for requests sent from official
/// clients and most third-party clients, unless the request was sent
/// from a secret chat.
pub chat_type: Option<ChatType>,
}
// TODO(waffle): remove
impl InlineQuery {
pub fn new<S1, S2, S3>(id: S1, from: User, query: S2, offset: S3) -> Self
where
@ -40,6 +48,7 @@ impl InlineQuery {
location: None,
query: query.into(),
offset: offset.into(),
chat_type: None,
}
}

View file

@ -1,6 +1,7 @@
use reqwest::Url;
use serde::{Deserialize, Serialize};
use crate::types::{MessageEntity, ParseMode};
use crate::types::{Currency, LabeledPrice, MessageEntity, ParseMode};
/// This object represents the content of a message to be sent as a result of an
/// inline query.
@ -13,6 +14,7 @@ pub enum InputMessageContent {
Location(InputMessageContentLocation),
Venue(InputMessageContentVenue),
Contact(InputMessageContentContact),
Invoice(InputMessageContentInvoice),
}
/// Represents the content of a text message to be sent as the result of an
/// inline query.
@ -297,6 +299,263 @@ impl InputMessageContentContact {
}
}
/// Represents the [content] of an invoice message to be sent as the result of
/// an inline query.
///
/// [content]: InputMessageContent
#[serde_with_macros::skip_serializing_none]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct InputMessageContentInvoice {
/// Product name, 1-32 characters
pub title: String,
/// Product description, 1-255 characters
pub description: String,
/// Bot-defined invoice payload, 1-128 bytes. This will not be displayed to
/// the user, use for your internal processes.
pub payload: String,
/// Payment provider token, obtained via [@Botfather]
///
/// [@Botfather]: https://t.me/Botfather
pub provider_token: String,
/// Three-letter ISO 4217 currency code, see [more on currencies]
///
/// [more on currencies]: https://core.telegram.org/bots/payments#supported-currencies
pub currency: Currency,
/// Price breakdown, list of components (e.g. product price, tax, discount,
/// delivery cost, delivery tax, bonus, etc.)
pub prices: Vec<LabeledPrice>,
/// ----The maximum accepted amount for tips in the smallest units of the
/// currency (integer, not float/double). For example, for a maximum tip of
/// US$ 1.45 pass max_tip_amount = 145. See the exp parameter in
/// currencies.json, it shows the number of digits past the decimal point
/// for each currency (2 for the majority of currencies). Defaults to 0
pub max_tip_amount: Option<u32>,
/// List of suggested amounts of tip in the smallest units of the currency
/// (integer, not float/double). At most 4 suggested tip amounts can be
/// specified. The suggested tip amounts must be positive, passed in a
/// strictly increased order and must not exceed max_tip_amount.
pub suggested_tip_amounts: Option<Vec<u32>>,
/// ----A JSON-serialized object for data about the invoice, which will be
/// shared with the payment provider. A detailed description of the required
/// fields should be provided by the payment provider.
pub provider_data: Option<String>,
/// URL of the product photo for the invoice. Can be a photo of the goods or
/// a marketing image for a service. People like it better when they see
/// what they are paying for.
pub photo_url: Option<Url>,
/// Photo size
pub photo_size: Option<u32>,
/// Photo width
pub photo_width: Option<u32>,
/// Photo height
pub photo_height: Option<u32>,
/// Pass `true`, if you require the user's full name to complete the order
pub need_name: Option<bool>,
/// Pass `true`, if you require the user's phone number to complete the
/// order
pub need_phone_number: Option<bool>,
/// Pass `true`, if you require the user's email address to complete the
/// order
pub need_email: Option<bool>,
/// Pass `true`, if you require the user's shipping address to complete the
/// order
pub need_shipping_address: Option<bool>,
/// Pass True, if user's phone number should be sent to provider
pub send_phone_number_to_provider: Option<bool>,
/// Pass True, if user's email address should be sent to provider
pub send_email_to_provider: Option<bool>,
/// Pass True, if the final price depends on the shipping method
pub is_flexible: Option<bool>,
}
impl InputMessageContentInvoice {
pub fn new<T, D, PA, PT, PR>(
title: T,
description: D,
payload: PA,
provider_token: PT,
currency: Currency,
prices: PR,
) -> Self
where
T: Into<String>,
D: Into<String>,
PA: Into<String>,
PT: Into<String>,
PR: IntoIterator<Item = LabeledPrice>,
{
let title = title.into();
let description = description.into();
let payload = payload.into();
let provider_token = provider_token.into();
let prices = prices.into_iter().collect();
Self {
title,
description,
payload,
provider_token,
currency,
prices,
max_tip_amount: None,
suggested_tip_amounts: None,
provider_data: None,
photo_url: None,
photo_size: None,
photo_width: None,
photo_height: None,
need_name: None,
need_phone_number: None,
need_email: None,
need_shipping_address: None,
send_phone_number_to_provider: None,
send_email_to_provider: None,
is_flexible: None,
}
}
pub fn title<T>(mut self, val: T) -> Self
where
T: Into<String>,
{
self.title = val.into();
self
}
pub fn description<T>(mut self, val: T) -> Self
where
T: Into<String>,
{
self.description = val.into();
self
}
pub fn payload<T>(mut self, val: T) -> Self
where
T: Into<String>,
{
self.payload = val.into();
self
}
pub fn provider_token<T>(mut self, val: T) -> Self
where
T: Into<String>,
{
self.provider_token = val.into();
self
}
pub fn currency(mut self, val: Currency) -> Self {
self.currency = val;
self
}
pub fn prices<T>(mut self, val: T) -> Self
where
T: IntoIterator<Item = LabeledPrice>,
{
self.prices = val.into_iter().collect();
self
}
pub fn max_tip_amount(mut self, val: u32) -> Self {
self.max_tip_amount = Some(val);
self
}
pub fn suggested_tip_amounts<T>(mut self, val: T) -> Self
where
T: IntoIterator<Item = u32>,
{
self.suggested_tip_amounts = Some(val.into_iter().collect());
self
}
pub fn provider_data<T>(mut self, val: T) -> Self
where
T: Into<String>,
{
self.provider_data = Some(val.into());
self
}
pub fn photo_url(mut self, val: Url) -> Self {
self.photo_url = Some(val);
self
}
pub fn photo_size(mut self, val: u32) -> Self {
self.photo_size = Some(val);
self
}
pub fn photo_width(mut self, val: u32) -> Self {
self.photo_width = Some(val);
self
}
pub fn photo_height(mut self, val: u32) -> Self {
self.photo_height = Some(val);
self
}
pub fn need_name(mut self, val: bool) -> Self {
self.need_name = Some(val);
self
}
pub fn need_phone_number(mut self, val: bool) -> Self {
self.need_phone_number = Some(val);
self
}
pub fn need_email(mut self, val: bool) -> Self {
self.need_email = Some(val);
self
}
pub fn need_shipping_address(mut self, val: bool) -> Self {
self.need_shipping_address = Some(val);
self
}
pub fn send_phone_number_to_provider(mut self, val: bool) -> Self {
self.send_phone_number_to_provider = Some(val);
self
}
pub fn send_email_to_provider(mut self, val: bool) -> Self {
self.send_email_to_provider = Some(val);
self
}
#[allow(clippy::wrong_self_convention)]
pub fn is_flexible(mut self, val: bool) -> Self {
self.is_flexible = Some(val);
self
}
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -6,7 +6,7 @@ use crate::types::{
Animation, Audio, Chat, Contact, Dice, Document, Game, InlineKeyboardMarkup, Invoice, Location,
MessageAutoDeleteTimerChanged, MessageEntity, PassportData, PhotoSize, Poll,
ProximityAlertTriggered, Sticker, SuccessfulPayment, True, User, Venue, Video, VideoNote,
Voice, VoiceChatEnded, VoiceChatParticipantsInvited, VoiceChatStarted,
Voice, VoiceChatEnded, VoiceChatParticipantsInvited, VoiceChatScheduled, VoiceChatStarted,
};
/// This object represents a message.
@ -52,6 +52,7 @@ pub enum MessageKind {
PassportData(MessagePassportData),
Dice(MessageDice),
ProximityAlertTriggered(MessageProximityAlertTriggered),
VoiceChatScheduled(MessageVoiceChatScheduled),
VoiceChatStarted(MessageVoiceChatStarted),
VoiceChatEnded(MessageVoiceChatEnded),
VoiceChatParticipantsInvited(MessageVoiceChatParticipantsInvited),
@ -454,6 +455,12 @@ pub struct MessageProximityAlertTriggered {
pub proximity_alert_triggered: ProximityAlertTriggered,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct MessageVoiceChatScheduled {
/// Service message: voice chat scheduled
pub voice_chat_scheduled: VoiceChatScheduled,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct MessageVoiceChatStarted {
/// Service message: voice chat started.

View file

@ -0,0 +1,10 @@
use serde::{Deserialize, Serialize};
/// This object represents a service message about a voice chat scheduled in the
/// chat.
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct VoiceChatScheduled {
/// Point in time (Unix timestamp) when the voice chat is supposed to be
/// started by a chat administrator.
pub start_date: u64,
}