mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-22 22:46:39 +01:00
TBA 6.3: types
This commit is contained in:
parent
a84a90e038
commit
976100e3ec
11 changed files with 238 additions and 9 deletions
|
@ -27,6 +27,10 @@ pub use encrypted_credentials::*;
|
|||
pub use encrypted_passport_element::*;
|
||||
pub use file::*;
|
||||
pub use force_reply::*;
|
||||
pub use forum_topic::*;
|
||||
pub use forum_topic_closed::*;
|
||||
pub use forum_topic_created::*;
|
||||
pub use forum_topic_reopened::*;
|
||||
pub use game::*;
|
||||
pub use game_high_score::*;
|
||||
pub use inline_keyboard_button::*;
|
||||
|
@ -86,7 +90,6 @@ pub use reply_keyboard_remove::*;
|
|||
pub use reply_markup::*;
|
||||
pub use response_parameters::*;
|
||||
pub use sent_web_app_message::*;
|
||||
use serde::Serialize;
|
||||
pub use shipping_address::*;
|
||||
pub use shipping_option::*;
|
||||
pub use shipping_query::*;
|
||||
|
@ -136,6 +139,10 @@ mod dice_emoji;
|
|||
mod document;
|
||||
mod file;
|
||||
mod force_reply;
|
||||
mod forum_topic;
|
||||
mod forum_topic_closed;
|
||||
mod forum_topic_created;
|
||||
mod forum_topic_reopened;
|
||||
mod game;
|
||||
mod game_high_score;
|
||||
mod inline_keyboard_button;
|
||||
|
@ -239,6 +246,8 @@ pub use chat_id::*;
|
|||
pub use recipient::*;
|
||||
pub use user_id::*;
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
/// Converts an `i64` timestump to a `choro::DateTime`, producing serde error
|
||||
/// for invalid timestumps
|
||||
pub(crate) fn serde_timestamp<E: serde::de::Error>(
|
||||
|
@ -420,3 +429,49 @@ where
|
|||
{
|
||||
this.map(|MessageId(id)| id).serialize(serializer)
|
||||
}
|
||||
|
||||
pub(crate) mod serde_rgb {
|
||||
use serde::{de::Visitor, Deserializer, Serializer};
|
||||
|
||||
pub fn serialize<S: Serializer>(&this: &[u8; 3], s: S) -> Result<S::Ok, S::Error> {
|
||||
s.serialize_u32(to_u32(this))
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<[u8; 3], D::Error> {
|
||||
struct V;
|
||||
|
||||
impl Visitor<'_> for V {
|
||||
type Value = [u8; 3];
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("an integer represeting an RGB color")
|
||||
}
|
||||
|
||||
fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(from_u32(v))
|
||||
}
|
||||
}
|
||||
d.deserialize_u32(V)
|
||||
}
|
||||
|
||||
fn to_u32([r, g, b]: [u8; 3]) -> u32 {
|
||||
u32::from_be_bytes([0, r, g, b])
|
||||
}
|
||||
|
||||
fn from_u32(rgb: u32) -> [u8; 3] {
|
||||
let [_, r, g, b] = rgb.to_be_bytes();
|
||||
[r, g, b]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bytes() {
|
||||
assert_eq!(to_u32([0xAA, 0xBB, 0xCC]), 0x00AABBCC);
|
||||
assert_eq!(from_u32(0x00AABBCC), [0xAA, 0xBB, 0xCC]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn json() {}
|
||||
}
|
||||
|
|
|
@ -88,6 +88,13 @@ pub struct ChatPrivate {
|
|||
/// A last name of the other party in a private chat.
|
||||
pub last_name: Option<String>,
|
||||
|
||||
/// Custom emoji identifier of emoji status of the other party in a private
|
||||
/// chat. Returned only in [`GetChat`].
|
||||
///
|
||||
/// [`GetChat`]: crate::payloads::GetChat
|
||||
// FIXME: CustomEmojiId
|
||||
pub emoji_status_custom_emoji_id: Option<String>,
|
||||
|
||||
/// Bio of the other party in a private chat. Returned only in [`GetChat`].
|
||||
///
|
||||
/// [`GetChat`]: crate::payloads::GetChat
|
||||
|
@ -148,6 +155,16 @@ pub struct PublicChatSupergroup {
|
|||
/// available.
|
||||
pub username: Option<String>,
|
||||
|
||||
/// If non-empty, the list of all active chat usernames; for private chats,
|
||||
/// supergroups and channels. Returned only from [`GetChat`].
|
||||
///
|
||||
/// [`GetChat`]: crate::payloads::GetChat
|
||||
pub active_usernames: Option<Vec<String>>,
|
||||
|
||||
/// `true`, if the supergroup chat is a forum (has topics enabled).
|
||||
#[serde(default)]
|
||||
pub is_forum: bool,
|
||||
|
||||
/// For supergroups, name of group sticker set. Returned only from
|
||||
/// [`GetChat`].
|
||||
///
|
||||
|
@ -485,6 +502,7 @@ mod serde_helper {
|
|||
bio: Option<String>,
|
||||
has_private_forwards: Option<True>,
|
||||
has_restricted_voice_and_video_messages: Option<True>,
|
||||
emoji_status_custom_emoji_id: Option<String>,
|
||||
}
|
||||
|
||||
impl From<ChatPrivate> for super::ChatPrivate {
|
||||
|
@ -497,6 +515,7 @@ mod serde_helper {
|
|||
bio,
|
||||
has_private_forwards,
|
||||
has_restricted_voice_and_video_messages,
|
||||
emoji_status_custom_emoji_id,
|
||||
}: ChatPrivate,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
@ -506,6 +525,7 @@ mod serde_helper {
|
|||
bio,
|
||||
has_private_forwards,
|
||||
has_restricted_voice_and_video_messages,
|
||||
emoji_status_custom_emoji_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -519,6 +539,7 @@ mod serde_helper {
|
|||
bio,
|
||||
has_private_forwards,
|
||||
has_restricted_voice_and_video_messages,
|
||||
emoji_status_custom_emoji_id,
|
||||
}: super::ChatPrivate,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
@ -529,6 +550,7 @@ mod serde_helper {
|
|||
bio,
|
||||
has_private_forwards,
|
||||
has_restricted_voice_and_video_messages,
|
||||
emoji_status_custom_emoji_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -574,6 +596,7 @@ mod tests {
|
|||
bio: None,
|
||||
has_private_forwards: None,
|
||||
has_restricted_voice_and_video_messages: None,
|
||||
emoji_status_custom_emoji_id: None
|
||||
}),
|
||||
photo: None,
|
||||
pinned_message: None,
|
||||
|
@ -595,6 +618,7 @@ mod tests {
|
|||
bio: None,
|
||||
has_private_forwards: None,
|
||||
has_restricted_voice_and_video_messages: None,
|
||||
emoji_status_custom_emoji_id: None,
|
||||
}),
|
||||
photo: None,
|
||||
pinned_message: None,
|
||||
|
|
|
@ -45,4 +45,8 @@ pub struct ChatAdministratorRights {
|
|||
/// `true`, if the user is allowed to pin messages; groups and
|
||||
/// supergroups only
|
||||
pub can_pin_messages: Option<bool>,
|
||||
|
||||
/// `true`, if the user is allowed to create, rename, close, and reopen
|
||||
/// forum topics; supergroups only
|
||||
pub can_manage_topics: Option<bool>,
|
||||
}
|
||||
|
|
|
@ -87,6 +87,10 @@ pub struct Administrator {
|
|||
/// `true` if the administrator can pin messages, supergroups only.
|
||||
pub can_pin_messages: Option<bool>,
|
||||
|
||||
/// `true`, if the user is allowed to create, rename, close, and reopen
|
||||
/// forum topics; supergroups only
|
||||
pub can_manage_topics: Option<bool>,
|
||||
|
||||
/// `true` if the administrator can add new administrators with a subset of
|
||||
/// his own privileges or demote administrators that he has promoted,
|
||||
/// directly or indirectly (promoted by administrators that were appointed
|
||||
|
@ -130,6 +134,10 @@ pub struct Restricted {
|
|||
/// `true` if the user is allowed to pin messages.
|
||||
pub can_pin_messages: bool,
|
||||
|
||||
/// `true`, if the user is allowed to create, rename, close, and reopen
|
||||
/// forum topics
|
||||
pub can_manage_topics: bool,
|
||||
|
||||
/// `true` if the user is allowed to send polls.
|
||||
pub can_send_polls: bool,
|
||||
}
|
||||
|
@ -514,6 +522,27 @@ impl ChatMemberKind {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the user is allowed to manage topics.
|
||||
///
|
||||
/// I.e. returns `true` if the user
|
||||
/// - is the owner of the chat (even if the chat is not a supergroup)
|
||||
/// - is an administrator in the given chat and has the
|
||||
/// [`Administrator::can_manage_topics`] privilege.
|
||||
/// - is restricted, but does have [`Restricted::can_manage_topics`]
|
||||
/// privilege
|
||||
/// Returns `false` otherwise.
|
||||
#[must_use]
|
||||
pub fn can_manage_topics(&self) -> bool {
|
||||
match self {
|
||||
ChatMemberKind::Owner(_) => true,
|
||||
ChatMemberKind::Administrator(Administrator { can_manage_topics, .. }) => {
|
||||
can_manage_topics.unwrap_or_default()
|
||||
}
|
||||
ChatMemberKind::Restricted(Restricted { can_manage_topics, .. }) => *can_manage_topics,
|
||||
ChatMemberKind::Member | ChatMemberKind::Left | ChatMemberKind::Banned(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the user can add new administrators with a subset of
|
||||
/// his own privileges or demote administrators that he has promoted,
|
||||
/// directly or indirectly (promoted by administrators that were appointed
|
||||
|
@ -780,6 +809,7 @@ mod tests {
|
|||
can_restrict_members: true,
|
||||
can_pin_messages: Some(true),
|
||||
can_promote_members: true,
|
||||
can_manage_topics: None,
|
||||
}),
|
||||
};
|
||||
let actual = serde_json::from_str::<ChatMember>(json).unwrap();
|
||||
|
|
|
@ -46,7 +46,7 @@ bitflags::bitflags! {
|
|||
/// ```
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(from = "ChatPermissionsRaw", into = "ChatPermissionsRaw")]
|
||||
pub struct ChatPermissions: u8 {
|
||||
pub struct ChatPermissions: u16 {
|
||||
/// Set if the user is allowed to send text messages, contacts,
|
||||
/// locations and venues.
|
||||
const SEND_MESSAGES = 1;
|
||||
|
@ -78,9 +78,14 @@ bitflags::bitflags! {
|
|||
/// Set if the user is allowed to pin messages. Ignored in public
|
||||
/// supergroups.
|
||||
const PIN_MESSAGES = (1 << 7);
|
||||
|
||||
/// Set if the user is allowed to create, rename, close, and reopen forum topics.
|
||||
const MANAGE_TOPICS = (1 << 8);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: add `can_*` methods for convinience
|
||||
|
||||
/// Helper for (de)serialization
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct ChatPermissionsRaw {
|
||||
|
@ -107,6 +112,13 @@ struct ChatPermissionsRaw {
|
|||
|
||||
#[serde(default, skip_serializing_if = "Not::not")]
|
||||
can_pin_messages: bool,
|
||||
|
||||
// HACK: do not `skip_serializing_if = "Not::not"`, from tg docs:
|
||||
// > If omitted defaults to the value of `can_pin_messages`
|
||||
// but we don't have two different values for "absent" and "false"...
|
||||
// or did they mean that `can_pin_messages` implies `can_manage_topics`?..
|
||||
#[serde(default)]
|
||||
can_manage_topics: bool,
|
||||
}
|
||||
|
||||
impl From<ChatPermissions> for ChatPermissionsRaw {
|
||||
|
@ -120,6 +132,7 @@ impl From<ChatPermissions> for ChatPermissionsRaw {
|
|||
can_change_info: this.contains(ChatPermissions::CHANGE_INFO),
|
||||
can_invite_users: this.contains(ChatPermissions::INVITE_USERS),
|
||||
can_pin_messages: this.contains(ChatPermissions::PIN_MESSAGES),
|
||||
can_manage_topics: this.contains(ChatPermissions::MANAGE_TOPICS),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -135,6 +148,7 @@ impl From<ChatPermissionsRaw> for ChatPermissions {
|
|||
can_change_info,
|
||||
can_invite_users,
|
||||
can_pin_messages,
|
||||
can_manage_topics,
|
||||
}: ChatPermissionsRaw,
|
||||
) -> Self {
|
||||
let mut this = Self::empty();
|
||||
|
@ -163,6 +177,10 @@ impl From<ChatPermissionsRaw> for ChatPermissions {
|
|||
if can_pin_messages {
|
||||
this |= Self::PIN_MESSAGES;
|
||||
}
|
||||
// FIXME: should we do `|| can_pin_messages` here? (the same tg doc weirdness)
|
||||
if can_manage_topics {
|
||||
this |= Self::MANAGE_TOPICS
|
||||
}
|
||||
|
||||
this
|
||||
}
|
||||
|
@ -175,8 +193,7 @@ mod tests {
|
|||
#[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 expected = r#"{"can_send_messages":true,"can_send_media_messages":true,"can_pin_messages":true,"can_manage_topics":false}"#;
|
||||
let actual = serde_json::to_string(&permissions).unwrap();
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
|
24
crates/teloxide-core/src/types/forum_topic.rs
Normal file
24
crates/teloxide-core/src/types/forum_topic.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// This object represents a forum topic.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#forumtopiccreated).
|
||||
#[serde_with_macros::skip_serializing_none]
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ForumTopic {
|
||||
/// Unique identifier of the forum topic
|
||||
// FIXME: MessageThreadId or something
|
||||
pub message_thread_id: i32,
|
||||
|
||||
/// Name of the topic.
|
||||
pub name: String,
|
||||
|
||||
/// Color of the topic icon in RGB format.
|
||||
// FIXME: use/add a specialized rgb color type?
|
||||
#[serde(with = "crate::types::serde_rgb")]
|
||||
pub icon_color: [u8; 3],
|
||||
|
||||
/// Unique identifier of the custom emoji shown as the topic icon.
|
||||
// FIXME: CustomEmojiId
|
||||
pub icon_custom_emoji_id: Option<String>,
|
||||
}
|
9
crates/teloxide-core/src/types/forum_topic_closed.rs
Normal file
9
crates/teloxide-core/src/types/forum_topic_closed.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// This object represents a service message about a forum topic closed in the
|
||||
/// chat. Currently holds no information.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#forumtopiccreated).
|
||||
#[serde_with_macros::skip_serializing_none]
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ForumTopicClosed;
|
21
crates/teloxide-core/src/types/forum_topic_created.rs
Normal file
21
crates/teloxide-core/src/types/forum_topic_created.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// This object represents a service message about a new forum topic created in
|
||||
/// the chat.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#forumtopiccreated).
|
||||
#[serde_with_macros::skip_serializing_none]
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ForumTopicCreated {
|
||||
/// Name of the topic.
|
||||
pub name: String,
|
||||
|
||||
/// Color of the topic icon in RGB format.
|
||||
// FIXME: use/add a specialized rgb color type?
|
||||
#[serde(with = "crate::types::serde_rgb")]
|
||||
pub icon_color: [u8; 3],
|
||||
|
||||
/// Unique identifier of the custom emoji shown as the topic icon.
|
||||
// FIXME: CustomEmojiId
|
||||
pub icon_custom_emoji_id: Option<String>,
|
||||
}
|
9
crates/teloxide-core/src/types/forum_topic_reopened.rs
Normal file
9
crates/teloxide-core/src/types/forum_topic_reopened.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// This object represents a service message about a forum topic reopened in the
|
||||
/// chat. Currently holds no information.
|
||||
///
|
||||
/// [The official docs](https://core.telegram.org/bots/api#forumtopiccreated).
|
||||
#[serde_with_macros::skip_serializing_none]
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ForumTopicReopened;
|
|
@ -5,11 +5,12 @@ use serde::{Deserialize, Serialize};
|
|||
use url::Url;
|
||||
|
||||
use crate::types::{
|
||||
Animation, Audio, BareChatId, Chat, ChatId, Contact, Dice, Document, Game,
|
||||
InlineKeyboardMarkup, Invoice, Location, MessageAutoDeleteTimerChanged, MessageEntity,
|
||||
MessageEntityRef, MessageId, PassportData, PhotoSize, Poll, ProximityAlertTriggered, Sticker,
|
||||
SuccessfulPayment, True, User, Venue, Video, VideoChatEnded, VideoChatParticipantsInvited,
|
||||
VideoChatScheduled, VideoChatStarted, VideoNote, Voice, WebAppData,
|
||||
Animation, Audio, BareChatId, Chat, ChatId, Contact, Dice, Document, ForumTopicClosed,
|
||||
ForumTopicCreated, ForumTopicReopened, Game, InlineKeyboardMarkup, Invoice, Location,
|
||||
MessageAutoDeleteTimerChanged, MessageEntity, MessageEntityRef, MessageId, PassportData,
|
||||
PhotoSize, Poll, ProximityAlertTriggered, Sticker, SuccessfulPayment, True, User, Venue, Video,
|
||||
VideoChatEnded, VideoChatParticipantsInvited, VideoChatScheduled, VideoChatStarted, VideoNote,
|
||||
Voice, WebAppData,
|
||||
};
|
||||
|
||||
/// This object represents a message.
|
||||
|
@ -21,6 +22,11 @@ pub struct Message {
|
|||
#[serde(flatten)]
|
||||
pub id: MessageId,
|
||||
|
||||
/// Unique identifier of a message thread to which the message belongs; for
|
||||
/// supergroups only.
|
||||
// FIXME: MessageThreadId or such
|
||||
pub thread_id: Option<i32>,
|
||||
|
||||
/// Date the message was sent in Unix time.
|
||||
#[serde(with = "crate::types::serde_date_from_unix_timestamp")]
|
||||
pub date: DateTime<Utc>,
|
||||
|
@ -55,6 +61,9 @@ pub enum MessageKind {
|
|||
PassportData(MessagePassportData),
|
||||
Dice(MessageDice),
|
||||
ProximityAlertTriggered(MessageProximityAlertTriggered),
|
||||
ForumTopicCreated(ForumTopicCreated),
|
||||
ForumTopicClosed(ForumTopicClosed),
|
||||
ForumTopicReopened(ForumTopicReopened),
|
||||
VideoChatScheduled(MessageVideoChatScheduled),
|
||||
VideoChatStarted(MessageVideoChatStarted),
|
||||
VideoChatEnded(MessageVideoChatEnded),
|
||||
|
@ -98,6 +107,10 @@ pub struct MessageCommon {
|
|||
/// represented as ordinary `url` buttons.
|
||||
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||
|
||||
/// `true`, if the message is sent to a forum topic.
|
||||
#[serde(default)]
|
||||
pub is_topic_message: bool,
|
||||
|
||||
/// `true`, if the message is a channel post that was automatically
|
||||
/// forwarded to the connected discussion group.
|
||||
#[serde(default)]
|
||||
|
@ -493,6 +506,24 @@ pub struct MessageProximityAlertTriggered {
|
|||
pub proximity_alert_triggered: ProximityAlertTriggered,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct MessageForumTopicCreated {
|
||||
/// Service message: forum topic created.
|
||||
pub forum_topic_created: ForumTopicCreated,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct MessageForumTopicClosed {
|
||||
/// Service message: forum topic closed.
|
||||
pub forum_topic_closed: ForumTopicClosed,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct MessageForumTopicReopened {
|
||||
/// Service message: forum topic reopened.
|
||||
pub forum_topic_reopened: ForumTopicReopened,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct MessageVideoChatScheduled {
|
||||
/// Service message: video chat scheduled
|
||||
|
@ -1543,6 +1574,8 @@ mod tests {
|
|||
location: None,
|
||||
join_by_request: None,
|
||||
join_to_send_messages: None,
|
||||
active_usernames: None,
|
||||
is_forum: false,
|
||||
}),
|
||||
description: None,
|
||||
invite_link: None,
|
||||
|
|
|
@ -332,6 +332,7 @@ mod test {
|
|||
kind: UpdateKind::Message(Message {
|
||||
via_bot: None,
|
||||
id: MessageId(6557),
|
||||
thread_id: None,
|
||||
date,
|
||||
chat: Chat {
|
||||
id: ChatId(218_485_655),
|
||||
|
@ -342,6 +343,7 @@ mod test {
|
|||
bio: None,
|
||||
has_private_forwards: None,
|
||||
has_restricted_voice_and_video_messages: None,
|
||||
emoji_status_custom_emoji_id: None,
|
||||
}),
|
||||
photo: None,
|
||||
pinned_message: None,
|
||||
|
@ -368,6 +370,7 @@ mod test {
|
|||
reply_markup: None,
|
||||
sender_chat: None,
|
||||
author_signature: None,
|
||||
is_topic_message: false,
|
||||
is_automatic_forward: false,
|
||||
has_protected_content: false,
|
||||
}),
|
||||
|
|
Loading…
Reference in a new issue