mirror of
https://github.com/teloxide/teloxide.git
synced 2025-03-14 11:44:04 +01:00
Merge branch 'dev' into rework-dispatching
This commit is contained in:
commit
99c7bf5955
11 changed files with 192 additions and 16 deletions
|
@ -2,7 +2,7 @@ use super::BotWrapper;
|
|||
use crate::{
|
||||
net,
|
||||
requests::{Request, ResponseResult},
|
||||
types::User,
|
||||
types::Me,
|
||||
Bot,
|
||||
};
|
||||
use serde::Serialize;
|
||||
|
@ -18,11 +18,11 @@ pub struct GetMe<'a> {
|
|||
|
||||
#[async_trait::async_trait]
|
||||
impl Request for GetMe<'_> {
|
||||
type Output = User;
|
||||
type Output = Me;
|
||||
|
||||
/// Returns basic information about the bot.
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
async fn send(&self) -> ResponseResult<User> {
|
||||
async fn send(&self) -> ResponseResult<Me> {
|
||||
net::request_json(self.bot.client(), self.bot.token(), "getMe", &self)
|
||||
.await
|
||||
}
|
||||
|
|
|
@ -4,12 +4,11 @@ use super::BotWrapper;
|
|||
use crate::{
|
||||
net,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, Message, ReplyMarkup},
|
||||
types::{ChatId, Message, PollType, ReplyMarkup},
|
||||
Bot,
|
||||
};
|
||||
|
||||
/// Use this method to send a native poll. A native poll can't be sent to a
|
||||
/// private chat.
|
||||
/// Use this method to send a native poll.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#sendpoll).
|
||||
#[serde_with_macros::skip_serializing_none]
|
||||
|
@ -20,6 +19,11 @@ pub struct SendPoll<'a> {
|
|||
chat_id: ChatId,
|
||||
question: String,
|
||||
options: Vec<String>,
|
||||
is_anonymous: Option<bool>,
|
||||
poll_type: Option<PollType>,
|
||||
allows_multiple_answers: Option<bool>,
|
||||
correct_option_id: Option<i32>,
|
||||
is_closed: Option<bool>,
|
||||
disable_notification: Option<bool>,
|
||||
reply_to_message_id: Option<i32>,
|
||||
reply_markup: Option<ReplyMarkup>,
|
||||
|
@ -60,6 +64,11 @@ impl<'a> SendPoll<'a> {
|
|||
chat_id,
|
||||
question,
|
||||
options,
|
||||
is_anonymous: None,
|
||||
poll_type: None,
|
||||
allows_multiple_answers: None,
|
||||
correct_option_id: None,
|
||||
is_closed: None,
|
||||
disable_notification: None,
|
||||
reply_to_message_id: None,
|
||||
reply_markup: None,
|
||||
|
@ -96,8 +105,57 @@ impl<'a> SendPoll<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Sends the message [silently]. Users will receive a notification with no
|
||||
/// sound.
|
||||
/// `true`, if the poll needs to be anonymous, defaults to `true`.
|
||||
pub fn is_anonymous<T>(mut self, val: T) -> Self
|
||||
where
|
||||
T: Into<bool>,
|
||||
{
|
||||
self.is_anonymous = Some(val.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Poll type, `quiz` or `regular`, defaults to `regular`.
|
||||
pub fn poll_type(mut self, val: PollType) -> Self {
|
||||
self.poll_type = Some(val);
|
||||
self
|
||||
}
|
||||
|
||||
/// `true`, if the poll allows multiple answers, ignored for polls in quiz
|
||||
/// mode.
|
||||
///
|
||||
/// Defaults to `false`.
|
||||
pub fn allows_multiple_answers<T>(mut self, val: T) -> Self
|
||||
where
|
||||
T: Into<bool>,
|
||||
{
|
||||
self.allows_multiple_answers = Some(val.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// 0-based identifier of the correct answer option, required for polls in
|
||||
/// quiz mode.
|
||||
pub fn correct_option_id<T>(mut self, val: T) -> Self
|
||||
where
|
||||
T: Into<i32>,
|
||||
{
|
||||
self.correct_option_id = Some(val.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Pass `true`, if the poll needs to be immediately closed.
|
||||
///
|
||||
/// This can be useful for poll preview.
|
||||
pub fn is_closed<T>(mut self, val: T) -> Self
|
||||
where
|
||||
T: Into<bool>,
|
||||
{
|
||||
self.is_closed = Some(val.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sends the message [silently].
|
||||
///
|
||||
/// Users will receive a notification with no sound.
|
||||
///
|
||||
/// [silently]: https://telegram.org/blog/channels-2-0#silent-messages
|
||||
pub fn disable_notification(mut self, val: bool) -> Self {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use crate::types::True;
|
||||
use crate::types::{KeyboardButtonPollType, True};
|
||||
|
||||
/// This object represents one button of the reply keyboard.
|
||||
///
|
||||
|
@ -47,25 +47,32 @@ impl KeyboardButton {
|
|||
}
|
||||
|
||||
// Serialize + Deserialize are implemented by hand
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum ButtonRequest {
|
||||
Location,
|
||||
Contact,
|
||||
KeyboardButtonPollType(KeyboardButtonPollType),
|
||||
}
|
||||
|
||||
/// Helper struct for (de)serializing [`ButtonRequest`](ButtonRequest)
|
||||
#[serde_with_macros::skip_serializing_none]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct RawRequest {
|
||||
/// Optional. If True, the user's phone number will be sent as a contact
|
||||
/// when the button is pressed. Available in private chats only
|
||||
/// If `true`, the user's phone number will be sent as a contact
|
||||
/// when the button is pressed. Available in private chats only.
|
||||
#[serde(rename = "request_contact")]
|
||||
contact: Option<True>,
|
||||
|
||||
/// Optional. If True, the user's current location will be sent when the
|
||||
/// button is pressed. Available in private chats only
|
||||
/// If `true`, the user's current location will be sent when the
|
||||
/// button is pressed. Available in private chats only.
|
||||
#[serde(rename = "request_location")]
|
||||
location: Option<True>,
|
||||
|
||||
/// If specified, the user will be asked to create a poll and
|
||||
/// send it to the bot when the button is pressed. Available in private
|
||||
/// chats only.
|
||||
#[serde(rename = "request_poll")]
|
||||
poll: Option<KeyboardButtonPollType>,
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ButtonRequest {
|
||||
|
@ -78,6 +85,7 @@ impl<'de> Deserialize<'de> for ButtonRequest {
|
|||
RawRequest {
|
||||
contact: Some(_),
|
||||
location: Some(_),
|
||||
poll: Some(_),
|
||||
} => Err(D::Error::custom(
|
||||
"`request_contact` and `request_location` fields are mutually \
|
||||
exclusive, but both were provided",
|
||||
|
@ -88,6 +96,10 @@ impl<'de> Deserialize<'de> for ButtonRequest {
|
|||
RawRequest {
|
||||
location: Some(_), ..
|
||||
} => Ok(Self::Location),
|
||||
RawRequest {
|
||||
poll: Some(poll_type),
|
||||
..
|
||||
} => Ok(Self::KeyboardButtonPollType(poll_type)),
|
||||
_ => Err(D::Error::custom(
|
||||
"Either one of `request_contact` and `request_location` \
|
||||
fields is required",
|
||||
|
@ -105,11 +117,19 @@ impl Serialize for ButtonRequest {
|
|||
Self::Contact => RawRequest {
|
||||
contact: Some(True),
|
||||
location: None,
|
||||
poll: None,
|
||||
}
|
||||
.serialize(serializer),
|
||||
Self::Location => RawRequest {
|
||||
contact: None,
|
||||
location: Some(True),
|
||||
poll: None,
|
||||
}
|
||||
.serialize(serializer),
|
||||
Self::KeyboardButtonPollType(poll_type) => RawRequest {
|
||||
contact: None,
|
||||
location: None,
|
||||
poll: Some(poll_type.clone()),
|
||||
}
|
||||
.serialize(serializer),
|
||||
}
|
||||
|
|
6
src/types/keyboard_button_poll_type.rs
Normal file
6
src/types/keyboard_button_poll_type.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub struct KeyboardButtonPollType {
|
||||
poll_type: String,
|
||||
}
|
|
@ -33,7 +33,7 @@ pub enum MessageEntityKind {
|
|||
Bold,
|
||||
Italic,
|
||||
Code,
|
||||
Pre,
|
||||
Pre { language: Option<String> },
|
||||
TextLink { url: String },
|
||||
TextMention { user: User },
|
||||
Underline,
|
||||
|
@ -73,6 +73,25 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre() {
|
||||
use serde_json::from_str;
|
||||
|
||||
assert_eq!(
|
||||
MessageEntity {
|
||||
kind: MessageEntityKind::Pre {
|
||||
language: Some("rust".to_string()),
|
||||
},
|
||||
offset: 1,
|
||||
length: 2,
|
||||
},
|
||||
from_str::<MessageEntity>(
|
||||
r#"{"type":"pre","url":"ya.ru","offset":1,"length":2,"language":"rust"}"#
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn text_from() {
|
||||
let message = message();
|
||||
|
|
|
@ -50,6 +50,7 @@ pub use input_media::*;
|
|||
pub use input_message_content::*;
|
||||
pub use invoice::*;
|
||||
pub use keyboard_button::*;
|
||||
pub use keyboard_button_poll_type::*;
|
||||
pub use label_price::*;
|
||||
pub use location::*;
|
||||
pub use login_url::*;
|
||||
|
@ -63,6 +64,8 @@ pub use passport_element_error::*;
|
|||
pub use passport_file::*;
|
||||
pub use photo_size::*;
|
||||
pub use poll::*;
|
||||
pub use poll_answer::*;
|
||||
pub use poll_type::*;
|
||||
pub use pre_checkout_query::*;
|
||||
pub use reply_keyboard_markup::*;
|
||||
pub use reply_keyboard_remove::*;
|
||||
|
@ -112,6 +115,7 @@ mod input_media;
|
|||
mod input_message_content;
|
||||
mod invoice;
|
||||
mod keyboard_button;
|
||||
mod keyboard_button_poll_type;
|
||||
mod label_price;
|
||||
mod location;
|
||||
mod login_url;
|
||||
|
@ -122,6 +126,8 @@ mod order_info;
|
|||
mod parse_mode;
|
||||
mod photo_size;
|
||||
mod poll;
|
||||
mod poll_answer;
|
||||
mod poll_type;
|
||||
mod pre_checkout_query;
|
||||
mod reply_keyboard_markup;
|
||||
mod reply_keyboard_remove;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::types::PollType;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// This object contains information about a poll.
|
||||
|
@ -16,6 +17,23 @@ pub struct Poll {
|
|||
|
||||
/// `true`, if the poll is closed.
|
||||
pub is_closed: bool,
|
||||
|
||||
/// Total number of users that voted in the poll
|
||||
pub total_voter_count: i32,
|
||||
|
||||
/// True, if the poll is anonymous
|
||||
pub is_anonymous: bool,
|
||||
|
||||
/// Poll type, currently can be “regular” or “quiz”
|
||||
pub poll_type: PollType,
|
||||
|
||||
/// True, if the poll allows multiple answers
|
||||
pub allows_multiple_answers: bool,
|
||||
|
||||
/// 0-based identifier of the correct answer option. Available only for
|
||||
/// polls in the quiz mode, which are closed, or was sent (not
|
||||
/// forwarded) by the bot or to the private chat with the bot.
|
||||
pub correct_option_id: Option<i32>,
|
||||
}
|
||||
|
||||
/// This object contains information about one answer option in a poll.
|
||||
|
|
16
src/types/poll_answer.rs
Normal file
16
src/types/poll_answer.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use crate::types::User;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub struct PollAnswer {
|
||||
/// Unique poll identifier.
|
||||
pub poll_id: String,
|
||||
|
||||
/// The user, who changed the answer to the poll.
|
||||
pub user: User,
|
||||
|
||||
/// 0-based identifiers of answer options, chosen by the user.
|
||||
///
|
||||
/// May be empty if the user retracted their vote.
|
||||
pub option_ids: Vec<i32>,
|
||||
}
|
8
src/types/poll_type.rs
Normal file
8
src/types/poll_type.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum PollType {
|
||||
Quiz,
|
||||
Regular,
|
||||
}
|
|
@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
use crate::types::{
|
||||
CallbackQuery, Chat, ChosenInlineResult, InlineQuery, Message, Poll,
|
||||
PreCheckoutQuery, Sender, ShippingQuery, User,
|
||||
PollAnswer, PreCheckoutQuery, Sender, ShippingQuery, User,
|
||||
};
|
||||
|
||||
/// This [object] represents an incoming update.
|
||||
|
@ -71,6 +71,10 @@ pub enum UpdateKind {
|
|||
/// New poll state. Bots receive only updates about stopped polls and
|
||||
/// polls, which are sent by the bot.
|
||||
Poll(Poll),
|
||||
|
||||
/// A user changed their answer in a non-anonymous poll. Bots receive new
|
||||
/// votes only in polls that were sent by the bot itself.
|
||||
PollAnswer(PollAnswer),
|
||||
}
|
||||
|
||||
impl Update {
|
||||
|
@ -89,6 +93,7 @@ impl Update {
|
|||
UpdateKind::InlineQuery(query) => Some(&query.from),
|
||||
UpdateKind::ShippingQuery(query) => Some(&query.from),
|
||||
UpdateKind::PreCheckoutQuery(query) => Some(&query.from),
|
||||
UpdateKind::PollAnswer(answer) => Some(&answer.user),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,26 @@ impl User {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returned only in [`Bot::get_me`].
|
||||
///
|
||||
/// [`Bot::get_me`]: crate::Bot::get_me
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Me {
|
||||
#[serde(flatten)]
|
||||
pub user: User,
|
||||
|
||||
/// `true`, if the bot can be invited to groups.
|
||||
pub can_join_groups: bool,
|
||||
|
||||
/// `true`, if [privacy mode] is disabled for the bot.
|
||||
///
|
||||
/// [privacy mode]: https://core.telegram.org/bots#privacy-mode
|
||||
pub can_read_all_group_messages: bool,
|
||||
|
||||
/// `true`, if the bot supports inline queries.
|
||||
pub supports_inline_queries: bool,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
Loading…
Add table
Reference in a new issue