Merge branch 'master' into tolerant_updates_for_all

This commit is contained in:
Hirrolot 2022-01-12 15:14:37 +07:00 committed by GitHub
commit 1bcf621c7d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 265 additions and 109 deletions

View file

@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `ApiError::TooMuchInlineQueryResults` ([#135][pr135])
- `ApiError::NotEnoughRightsToChangeChatPermissions` ([#155][pr155])
- Support for 5.4 telegram bot API ([#133][pr133])
- Support for 5.5 telegram bot API ([#143][pr143])
- Support for 5.5 telegram bot API ([#143][pr143], [#164][pr164])
- `EditedMessageIsTooLong` error ([#109][pr109])
- `UntilDate` enum and use it for `{Restricted, Banned}::until_date` ([#116][pr116])
- `Limits::messages_per_min_channel` ([#121][pr121])
@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[pr151]: https://github.com/teloxide/teloxide-core/pull/151
[pr155]: https://github.com/teloxide/teloxide-core/pull/155
[pr156]: https://github.com/teloxide/teloxide-core/pull/156
[pr164]: https://github.com/teloxide/teloxide-core/pull/164
### Changed
@ -45,11 +46,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `InputFile` and related structures now do **not** implement `PartialEq`, `Eq` and `Hash` ([#133][pr133])
- How forwarded messages are represented ([#151][pr151])
- `RequestError::InvalidJson` now has a `raw` field with raw json for easier debugability ([#150][pr150])
- `ChatPermissions` is now bitflags ([#157][pr157])
[pr115]: https://github.com/teloxide/teloxide-core/pull/115
[pr125]: https://github.com/teloxide/teloxide-core/pull/125
[pr134]: https://github.com/teloxide/teloxide-core/pull/134
[pr150]: https://github.com/teloxide/teloxide-core/pull/150
[pr157]: https://github.com/teloxide/teloxide-core/pull/157
### Fixed

View file

@ -42,9 +42,9 @@ once_cell = "1.5.0"
never = "0.1.0"
chrono = { version = "0.4.19", default-features = false }
either = "1.6.1"
bitflags = { version = "1.2" }
vecrem = { version = "0.1", optional = true }
bitflags = { version = "1.2", optional = true }
[dev-dependencies]
pretty_env_logger = "0.4"
@ -66,7 +66,7 @@ nightly = []
throttle = ["vecrem"]
# Trace bot adaptor
trace_adaptor = ["bitflags"]
trace_adaptor = []
# Erased bot adaptor
erased = []

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2020 teloxide
Copyright (c) 2020-2022 teloxide
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

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.3%20(inclusively)-green.svg">
<img src="https://img.shields.io/badge/API%20coverage-Up%20to%205.6%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

@ -81,7 +81,7 @@
// (see: `teloxide`). We can't use `docsrs` as it breaks tokio compilation in this case.
#![cfg_attr(
all(any(docsrs, dep_docsrs), feature = "nightly"),
feature(doc_cfg, doc_notable_trait)
feature(doc_cfg, doc_auto_cfg, doc_notable_trait)
)]
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
#![cfg_attr(all(feature = "full", docsrs), deny(rustdoc::broken_intra_doc_links))]

View file

@ -37,6 +37,8 @@ impl_payload! {
///
/// [silently]: https://telegram.org/blog/channels-2-0#silent-messages
pub disable_notification: bool,
/// Protects the contents of sent messages from forwarding and saving
pub protect_content: bool,
/// If the message is a reply, ID of the original message
pub reply_to_message_id: i32,
/// Pass _True_, if the message should be sent even if the specified replied-to message is not found

View file

@ -29,6 +29,8 @@ impl_payload! {
///
/// [silently]: https://telegram.org/blog/channels-2-0#silent-messages
pub disable_notification: bool,
/// Protects the contents of sent messages from forwarding and saving
pub protect_content: bool,
}
}
}

View file

@ -48,6 +48,8 @@ impl_payload! {
///
/// [silently]: https://telegram.org/blog/channels-2-0#silent-messages
pub disable_notification: bool,
/// Protects the contents of sent messages from forwarding and saving
pub protect_content: bool,
/// If the message is a reply, ID of the original message
pub reply_to_message_id: i32,
/// Pass _True_, if the message should be sent even if the specified replied-to message is not found

View file

@ -51,6 +51,8 @@ impl_payload! {
///
/// [silently]: https://telegram.org/blog/channels-2-0#silent-messages
pub disable_notification: bool,
/// Protects the contents of sent messages from forwarding and saving
pub protect_content: bool,
/// If the message is a reply, ID of the original message
pub reply_to_message_id: i32,
/// Pass _True_, if the message should be sent even if the specified replied-to message is not found

View file

@ -35,6 +35,8 @@ impl_payload! {
///
/// [silently]: https://telegram.org/blog/channels-2-0#silent-messages
pub disable_notification: bool,
/// Protects the contents of sent messages from forwarding and saving
pub protect_content: bool,
/// If the message is a reply, ID of the original message
pub reply_to_message_id: i32,
/// Pass _True_, if the message should be sent even if the specified replied-to message is not found

View file

@ -27,6 +27,8 @@ impl_payload! {
///
/// [silently]: https://telegram.org/blog/channels-2-0#silent-messages
pub disable_notification: bool,
/// Protects the contents of sent messages from forwarding and saving
pub protect_content: bool,
/// If the message is a reply, ID of the original message
pub reply_to_message_id: i32,
/// Pass _True_, if the message should be sent even if the specified replied-to message is not found

View file

@ -44,6 +44,8 @@ impl_payload! {
///
/// [silently]: https://telegram.org/blog/channels-2-0#silent-messages
pub disable_notification: bool,
/// Protects the contents of sent messages from forwarding and saving
pub protect_content: bool,
/// If the message is a reply, ID of the original message
pub reply_to_message_id: i32,
/// Pass _True_, if the message should be sent even if the specified replied-to message is not found

View file

@ -27,6 +27,8 @@ impl_payload! {
///
/// [silently]: https://telegram.org/blog/channels-2-0#silent-messages
pub disable_notification: bool,
/// Protects the contents of sent messages from forwarding and saving
pub protect_content: bool,
/// If the message is a reply, ID of the original message
pub reply_to_message_id: i32,
/// Pass _True_, if the message should be sent even if the specified replied-to message is not found

View file

@ -72,6 +72,8 @@ impl_payload! {
///
/// [silently]: https://telegram.org/blog/channels-2-0#silent-messages
pub disable_notification: bool,
/// Protects the contents of sent messages from forwarding and saving
pub protect_content: bool,
/// If the message is a reply, ID of the original message
pub reply_to_message_id: i32,
/// Pass _True_, if the message should be sent even if the specified replied-to message is not found

View file

@ -39,6 +39,8 @@ impl_payload! {
///
/// [silently]: https://telegram.org/blog/channels-2-0#silent-messages
pub disable_notification: bool,
/// Protects the contents of sent messages from forwarding and saving
pub protect_content: bool,
/// If the message is a reply, ID of the original message
pub reply_to_message_id: i32,
/// Pass _True_, if the message should be sent even if the specified replied-to message is not found

View file

@ -27,6 +27,8 @@ impl_payload! {
///
/// [silently]: https://telegram.org/blog/channels-2-0#silent-messages
pub disable_notification: bool,
/// Protects the contents of sent messages from forwarding and saving
pub protect_content: bool,
/// If the message is a reply, ID of the original message
pub reply_to_message_id: i32,
/// Pass _True_, if the message should be sent even if the specified replied-to message is not found

View file

@ -35,6 +35,8 @@ impl_payload! {
///
/// [silently]: https://telegram.org/blog/channels-2-0#silent-messages
pub disable_notification: bool,
/// Protects the contents of sent messages from forwarding and saving
pub protect_content: bool,
/// If the message is a reply, ID of the original message
pub reply_to_message_id: i32,
/// Pass _True_, if the message should be sent even if the specified replied-to message is not found

View file

@ -38,6 +38,8 @@ impl_payload! {
///
/// [silently]: https://telegram.org/blog/channels-2-0#silent-messages
pub disable_notification: bool,
/// Protects the contents of sent messages from forwarding and saving
pub protect_content: bool,
/// If the message is a reply, ID of the original message
pub reply_to_message_id: i32,
/// Pass _True_, if the message should be sent even if the specified replied-to message is not found

View file

@ -53,6 +53,8 @@ impl_payload! {
///
/// [silently]: https://telegram.org/blog/channels-2-0#silent-messages
pub disable_notification: bool,
/// Protects the contents of sent messages from forwarding and saving
pub protect_content: bool,
/// If the message is a reply, ID of the original message
pub reply_to_message_id: i32,
/// Pass _True_, if the message should be sent even if the specified replied-to message is not found

View file

@ -30,6 +30,8 @@ impl_payload! {
///
/// [silently]: https://telegram.org/blog/channels-2-0#silent-messages
pub disable_notification: bool,
/// Protects the contents of sent messages from forwarding and saving
pub protect_content: bool,
/// If the message is a reply, ID of the original message
pub reply_to_message_id: i32,
/// Pass _True_, if the message should be sent even if the specified replied-to message is not found

View file

@ -43,6 +43,8 @@ impl_payload! {
///
/// [silently]: https://telegram.org/blog/channels-2-0#silent-messages
pub disable_notification: bool,
/// Protects the contents of sent messages from forwarding and saving
pub protect_content: bool,
/// If the message is a reply, ID of the original message
pub reply_to_message_id: i32,
/// Pass _True_, if the message should be sent even if the specified replied-to message is not found

View file

@ -51,6 +51,8 @@ impl_payload! {
///
/// [silently]: https://telegram.org/blog/channels-2-0#silent-messages
pub disable_notification: bool,
/// Protects the contents of sent messages from forwarding and saving
pub protect_content: bool,
/// If the message is a reply, ID of the original message
pub reply_to_message_id: i32,
/// Pass _True_, if the message should be sent even if the specified replied-to message is not found

View file

@ -39,6 +39,8 @@ impl_payload! {
///
/// [silently]: https://telegram.org/blog/channels-2-0#silent-messages
pub disable_notification: bool,
/// Protects the contents of sent messages from forwarding and saving
pub protect_content: bool,
/// If the message is a reply, ID of the original message
pub reply_to_message_id: i32,
/// Pass _True_, if the message should be sent even if the specified replied-to message is not found

View file

@ -31,6 +31,7 @@ pub(crate) fn to_form<T: ?Sized + Serialize>(val: &T) -> impl Future<Output = Re
}
// https://github.com/teloxide/teloxide/issues/473
#[cfg(test)]
#[tokio::test]
async fn issue_473() {
use crate::{

View file

@ -244,16 +244,11 @@ pub(crate) mod serde_opt_date_from_unix_timestamp {
.map(|timestamp| DateTime::from_utc(NaiveDateTime::from_timestamp(timestamp, 0), Utc)))
}
pub(crate) fn none<T>() -> Option<T> {
None
}
#[test]
fn test() {
#[derive(Serialize, Deserialize)]
struct Struct {
#[serde(with = "crate::types::serde_opt_date_from_unix_timestamp")]
#[serde(default = "crate::types::serde_opt_date_from_unix_timestamp::none")]
#[serde(default, with = "crate::types::serde_opt_date_from_unix_timestamp")]
date: Option<DateTime<Utc>>,
}

View file

@ -70,6 +70,12 @@ pub struct ChatPublic {
///
/// [`GetChat`]: crate::payloads::GetChat
pub invite_link: Option<String>,
/// `True`, if messages from the chat can't be forwarded to other chats.
/// Returned only in [`GetChat`].
///
/// [`GetChat`]: crate::payloads::GetChat
pub has_protected_content: Option<True>,
}
#[serde_with_macros::skip_serializing_none]
@ -97,7 +103,7 @@ pub struct ChatPrivate {
pub bio: Option<String>,
/// `True`, if privacy settings of the other party in the private chat
/// allows to use tg://user?id=<user_id> links only in chats with the
/// allows to use `tg://user?id=<user_id>` links only in chats with the
/// user. Returned only in [`GetChat`].
///
/// [`GetChat`]: crate::payloads::GetChat
@ -389,6 +395,17 @@ impl Chat {
}
}
/// `True`, if messages from the chat can't be forwarded to other chats.
/// Returned only in [`GetChat`].
///
/// [`GetChat`]: crate::payloads::GetChat
pub fn has_protected_content(&self) -> Option<True> {
match &self.kind {
ChatKind::Public(this) => this.has_protected_content,
_ => None,
}
}
/// A first name of the other party in a private chat.
pub fn first_name(&self) -> Option<&str> {
match &self.kind {
@ -446,6 +463,7 @@ mod tests {
}),
description: None,
invite_link: None,
has_protected_content: None,
}),
photo: None,
pinned_message: None,
@ -466,7 +484,7 @@ mod tests {
first_name: Some("Anon".into()),
last_name: None,
bio: None,
has_private_forwards: None
has_private_forwards: None,
}),
photo: None,
pinned_message: None,

View file

@ -21,8 +21,7 @@ pub struct ChatInviteLink {
pub name: Option<String>,
/// Point in time when the link will expire or has been
/// expired
#[serde(with = "crate::types::serde_opt_date_from_unix_timestamp")]
#[serde(default = "crate::types::serde_opt_date_from_unix_timestamp::none")]
#[serde(default, with = "crate::types::serde_opt_date_from_unix_timestamp")]
pub expire_date: Option<DateTime<Utc>>,
/// Maximum number of users that can be members of the chat simultaneously
/// after joining the chat via this invite link; 1-99999

View file

@ -1,96 +1,192 @@
use serde::{Deserialize, Serialize};
use std::ops::Not;
/// Describes actions that a non-administrator user is allowed to take in a
/// chat.
///
/// [The official docs](https://core.telegram.org/bots/api#chatpermissions).
#[serde_with_macros::skip_serializing_none]
#[derive(Copy, Clone, Default, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct ChatPermissions {
/// `true`, if the user is allowed to send text messages, contacts,
/// locations and venues.
pub can_send_messages: Option<bool>,
bitflags::bitflags! {
/// Describes actions that a non-administrator user is allowed to take in a
/// chat.
///
/// [The official docs](https://core.telegram.org/bots/api#chatpermissions).
///
/// ## Examples
///
/// ```
/// use teloxide_core::types::ChatPermissions;
///
/// // No permissions, nothing is allowed
/// let _ = ChatPermissions::empty();
///
/// // All permissions, everything is allowed
/// let _ = ChatPermissions::all();
///
/// // One particular permission
/// let permissions_v0 = ChatPermissions::INVITE_USERS;
///
/// // Check what is permitted
/// assert!(permissions_v0.contains(ChatPermissions::INVITE_USERS));
/// assert!(!permissions_v0.contains(ChatPermissions::SEND_MESSAGES));
///
/// // Union, add permissions
/// let permissions_v1 = permissions_v0 | ChatPermissions::SEND_MEDIA_MESSAGES;
/// assert!(permissions_v1.contains(ChatPermissions::INVITE_USERS));
/// assert!(permissions_v1.contains(ChatPermissions::SEND_MEDIA_MESSAGES));
///
/// // Implied by `SEND_MEDIA_MESSAGES`
/// assert!(permissions_v1.contains(ChatPermissions::SEND_MESSAGES));
///
/// // Difference, remove permissions
/// let permissions_v2 = permissions_v1 - ChatPermissions::SEND_MEDIA_MESSAGES;
/// assert!(!permissions_v2.contains(ChatPermissions::SEND_MEDIA_MESSAGES));
///
/// // Removing `SEND_MEDIA_MESSAGES` also removes `SEND_MESSAGES` and vice versa
/// // because `SEND_MESSAGES` is implied by `SEND_MEDIA_MESSAGES`
/// assert!(!permissions_v2.contains(ChatPermissions::SEND_MESSAGES));
///
/// let permissions_v3 = permissions_v1 - ChatPermissions::SEND_MESSAGES;
/// assert!(!permissions_v3.contains(ChatPermissions::SEND_MEDIA_MESSAGES));
/// ```
#[derive(Serialize, Deserialize)]
#[serde(from = "ChatPermissionsRaw", into = "ChatPermissionsRaw")]
pub struct ChatPermissions: u8 {
/// Set if the user is allowed to send text messages, contacts,
/// locations and venues.
const SEND_MESSAGES = 1;
/// `true`, if the user is allowed to send audios, documents,
/// photos, videos, video notes and voice notes, implies
/// `can_send_messages`.
pub can_send_media_messages: Option<bool>,
/// Set if the user is allowed to send audios, documents,
/// photos, videos, video notes and voice notes, implies
/// `SEND_MESSAGES`.
const SEND_MEDIA_MESSAGES = (1 << 1) | Self::SEND_MESSAGES.bits;
/// `true`, if the user is allowed to send polls, implies
/// `can_send_messages`.
pub can_send_polls: Option<bool>,
/// Set if the user is allowed to send polls, implies
/// `SEND_MESSAGES`.
const SEND_POLLS = (1 << 2) | Self::SEND_MESSAGES.bits;
/// `true`, if the user is allowed to send animations, games, stickers and
/// use inline bots, implies `can_send_media_messages`.
pub can_send_other_messages: Option<bool>,
/// Set if the user is allowed to send animations, games, stickers and
/// use inline bots, implies `SEND_MEDIA_MESSAGES`.
const SEND_OTHER_MESSAGES = (1 << 3) | Self::SEND_MEDIA_MESSAGES.bits;
/// `true`, if the user is allowed to add web page previews to
/// their messages, implies `can_send_media_messages`.
pub can_add_web_page_previews: Option<bool>,
/// Set if the user is allowed to add web page previews to
/// their messages, implies `SEND_MEDIA_MESSAGES`.
const ADD_WEB_PAGE_PREVIEWS = (1 << 4) | Self::SEND_MEDIA_MESSAGES.bits;
/// `true`, if the user is allowed to change the chat title, photo and
/// other settings. Ignored in public supergroups.
pub can_change_info: Option<bool>,
/// Set if the user is allowed to change the chat title, photo and
/// other settings. Ignored in public supergroups.
const CHANGE_INFO = (1 << 5);
/// `true`, if the user is allowed to invite new users to the chat.
pub can_invite_users: Option<bool>,
/// Set if the user is allowed to invite new users to the chat.
const INVITE_USERS = (1 << 6);
/// `true`, if the user is allowed to pin messages. Ignored in public
/// supergroups.
pub can_pin_messages: Option<bool>,
/// Set if the user is allowed to pin messages. Ignored in public
/// supergroups.
const PIN_MESSAGES = (1 << 7);
}
}
impl ChatPermissions {
pub const fn new() -> Self {
/// Helper for (de)serialization
#[derive(Serialize, Deserialize)]
struct ChatPermissionsRaw {
#[serde(default, skip_serializing_if = "Not::not")]
can_send_messages: bool,
#[serde(default, skip_serializing_if = "Not::not")]
can_send_media_messages: bool,
#[serde(default, skip_serializing_if = "Not::not")]
can_send_polls: bool,
#[serde(default, skip_serializing_if = "Not::not")]
can_send_other_messages: bool,
#[serde(default, skip_serializing_if = "Not::not")]
can_add_web_page_previews: bool,
#[serde(default, skip_serializing_if = "Not::not")]
can_change_info: bool,
#[serde(default, skip_serializing_if = "Not::not")]
can_invite_users: bool,
#[serde(default, skip_serializing_if = "Not::not")]
can_pin_messages: bool,
}
impl From<ChatPermissions> for ChatPermissionsRaw {
fn from(this: ChatPermissions) -> Self {
Self {
can_send_messages: None,
can_send_media_messages: None,
can_send_polls: None,
can_send_other_messages: None,
can_add_web_page_previews: None,
can_change_info: None,
can_invite_users: None,
can_pin_messages: None,
can_send_messages: this.contains(ChatPermissions::SEND_MESSAGES),
can_send_media_messages: this.contains(ChatPermissions::SEND_MEDIA_MESSAGES),
can_send_polls: this.contains(ChatPermissions::SEND_POLLS),
can_send_other_messages: this.contains(ChatPermissions::SEND_OTHER_MESSAGES),
can_add_web_page_previews: this.contains(ChatPermissions::ADD_WEB_PAGE_PREVIEWS),
can_change_info: this.contains(ChatPermissions::CHANGE_INFO),
can_invite_users: this.contains(ChatPermissions::INVITE_USERS),
can_pin_messages: this.contains(ChatPermissions::PIN_MESSAGES),
}
}
}
pub const fn can_send_messages(mut self, val: bool) -> Self {
self.can_send_messages = Some(val);
self
}
impl From<ChatPermissionsRaw> for ChatPermissions {
fn from(
ChatPermissionsRaw {
can_send_messages,
can_send_media_messages,
can_send_polls,
can_send_other_messages,
can_add_web_page_previews,
can_change_info,
can_invite_users,
can_pin_messages,
}: ChatPermissionsRaw,
) -> Self {
let mut this = Self::empty();
pub const fn can_send_media_messages(mut self, val: bool) -> Self {
self.can_send_media_messages = Some(val);
self
}
if can_send_messages {
this |= Self::SEND_MESSAGES;
}
if can_send_media_messages {
this |= Self::SEND_MEDIA_MESSAGES
}
if can_send_polls {
this |= Self::SEND_POLLS;
}
if can_send_other_messages {
this |= Self::SEND_OTHER_MESSAGES;
}
if can_add_web_page_previews {
this |= Self::ADD_WEB_PAGE_PREVIEWS;
}
if can_change_info {
this |= Self::CHANGE_INFO;
}
if can_invite_users {
this |= Self::INVITE_USERS;
}
if can_pin_messages {
this |= Self::PIN_MESSAGES;
}
pub const fn can_send_polls(mut self, val: bool) -> Self {
self.can_send_polls = Some(val);
self
}
pub const fn can_send_other_messages(mut self, val: bool) -> Self {
self.can_send_other_messages = Some(val);
self
}
pub const fn can_add_web_page_previews(mut self, val: bool) -> Self {
self.can_add_web_page_previews = Some(val);
self
}
pub const fn can_change_info(mut self, val: bool) -> Self {
self.can_change_info = Some(val);
self
}
pub const fn can_invite_users(mut self, val: bool) -> Self {
self.can_invite_users = Some(val);
self
}
pub const fn can_pin_messages(mut self, val: bool) -> Self {
self.can_pin_messages = Some(val);
self
this
}
}
#[cfg(test)]
mod tests {
use super::ChatPermissions;
#[test]
fn serialization() {
let permissions = ChatPermissions::SEND_MEDIA_MESSAGES | ChatPermissions::PIN_MESSAGES;
let expected =
r#"{"can_send_messages":true,"can_send_media_messages":true,"can_pin_messages":true}"#;
let actual = serde_json::to_string(&permissions).unwrap();
assert_eq!(expected, actual);
}
#[test]
fn deserialization() {
let json =
r#"{"can_send_messages":true,"can_send_media_messages":true,"can_pin_messages":true}"#;
let expected = ChatPermissions::SEND_MEDIA_MESSAGES | ChatPermissions::PIN_MESSAGES;
let actual = serde_json::from_str(json).unwrap();
assert_eq!(expected, actual);
}
}

View file

@ -29,7 +29,7 @@ pub struct Document {
pub file_name: Option<String>,
/// A MIME type of the file as defined by a sender.
#[serde(with = "crate::types::non_telegram_types::mime::opt_deser")]
#[serde(default, with = "crate::types::non_telegram_types::mime::opt_deser")]
pub mime_type: Option<Mime>,
/// A size of a file.

View file

@ -85,8 +85,7 @@ pub struct MessageCommon {
pub reply_to_message: Option<Box<Message>>,
/// Date the message was last edited in Unix time.
#[serde(with = "crate::types::serde_opt_date_from_unix_timestamp")]
#[serde(default = "crate::types::serde_opt_date_from_unix_timestamp::none")]
#[serde(default, with = "crate::types::serde_opt_date_from_unix_timestamp")]
pub edit_date: Option<DateTime<Utc>>,
#[serde(flatten)]
@ -363,7 +362,7 @@ pub struct MediaDocument {
/// For messages with a caption, special entities like usernames, URLs,
/// bot commands, etc. that appear in the caption.
#[serde(default = "Vec::new")]
#[serde(default)]
pub caption_entities: Vec<MessageEntity>,
/// The unique identifier of a media message group this message belongs
@ -1340,10 +1339,11 @@ mod tests {
}),
description: None,
invite_link: None,
has_protected_content: None,
}),
message_auto_delete_time: None,
photo: None,
pinned_message: None,
message_auto_delete_time: None,
};
assert!(message.from().unwrap().is_anonymous());

View file

@ -64,6 +64,7 @@ pub enum MessageEntityKind {
TextMention { user: User },
Underline,
Strikethrough,
Spoiler,
}
#[cfg(test)]

View file

@ -12,13 +12,20 @@ use serde::{Deserialize, Serialize};
/// Formatting options.
///
/// The Bot API supports basic formatting for messages. You can use bold,
/// italic, underlined and strikethrough text, as well as inline links and
/// pre-formatted code in your bots' messages. Telegram clients will render
/// italic, underlined, strikethrough, and spoiler text, as well as inline links
/// and pre-formatted code in your bots' messages. Telegram clients will render
/// them accordingly. You can use either markdown-style or HTML-style
/// formatting.
///
/// Note that Telegram clients will display an **alert** to the user before
/// opening an inline link (Open this link? together with the full URL).
/// opening an inline link ('Open this link?' together with the full URL).
///
/// Message entities can be nested, providing following restrictions are met:
/// - If two entities have common characters then one of them is fully contained
/// inside another.
/// - bold, italic, underline, strikethrough, and spoiler entities can contain
/// and can be part of any other entities, except pre and code.
/// - All other entities can't contain each other.
///
/// Links `tg://user?id=<user_id>` can be used to mention a user by their ID
/// without using a username. Please note:
@ -39,7 +46,8 @@ use serde::{Deserialize, Serialize};
/// _italic \*text_
/// __underline__
/// ~strikethrough~
/// *bold _italic bold ~italic bold strikethrough~ __underline italic bold___ bold*
/// ||spoiler||
/// *bold _italic bold ~italic bold strikethrough ||italic bold strikethrough spoiler||~ __underline italic bold___ bold*
/// [inline URL](http://www.example.com/)
/// [inline mention of a user](tg://user?id=123456789)
/// `inline fixed-width code`
@ -77,9 +85,10 @@ use serde::{Deserialize, Serialize};
/// <i>italic</i>, <em>italic</em>
/// <u>underline</u>, <ins>underline</ins>
/// <s>strikethrough</s>, <strike>strikethrough</strike>, <del>strikethrough</del>
/// <b>bold <i>italic bold <s>italic bold strikethrough</s> <u>underline italic bold</u></i> bold</b>
/// <a href="http:// www.example.com/">inline URL</a>
/// <a href="tg:// user?id=123456789">inline mention of a user</a>
/// <span class="tg-spoiler">spoiler</span>, <tg-spoiler>spoiler</tg-spoiler>
/// <b>bold <i>italic bold <s>italic bold strikethrough <span class="tg-spoiler">italic bold strikethrough spoiler</span></s> <u>underline italic bold</u></i> bold</b>
/// <a href="http://www.example.com/">inline URL</a>
/// <a href="tg://user?id=123456789">inline mention of a user</a>
/// <code>inline fixed-width code</code>
/// <pre>pre-formatted fixed-width code block</pre>
#[doc = "<pre><code class=\"language-rust\">pre-formatted fixed-width code block written in the \

View file

@ -51,8 +51,7 @@ pub struct Poll {
pub open_period: Option<u16>,
/// Point in time when the poll will be automatically closed.
#[serde(with = "crate::types::serde_opt_date_from_unix_timestamp")]
#[serde(default = "crate::types::serde_opt_date_from_unix_timestamp::none")]
#[serde(default, with = "crate::types::serde_opt_date_from_unix_timestamp")]
pub close_date: Option<DateTime<Utc>>,
}

View file

@ -22,8 +22,7 @@ pub struct WebhookInfo {
/// Time of the most recent error that happened when trying to
/// deliver an update via webhook.
#[serde(with = "crate::types::serde_opt_date_from_unix_timestamp")]
#[serde(default = "crate::types::serde_opt_date_from_unix_timestamp::none")]
#[serde(default, with = "crate::types::serde_opt_date_from_unix_timestamp")]
pub last_error_date: Option<DateTime<Utc>>,
/// Error message in human-readable format for the most recent error that