Merge branch 'dev' into rework-dispatching

This commit is contained in:
Temirkhan Myrzamadi 2020-02-07 20:31:52 +06:00 committed by GitHub
commit 99c7bf5955
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 192 additions and 16 deletions

View file

@ -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
}

View file

@ -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 {

View file

@ -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),
}

View file

@ -0,0 +1,6 @@
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct KeyboardButtonPollType {
poll_type: String,
}

View file

@ -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();

View file

@ -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;

View file

@ -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
View 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
View 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,
}

View file

@ -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,
}
}

View file

@ -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::*;