Add MessageReactionUpdated and MessageReactionCountUpdated structs

This commit is contained in:
Andrey Brusnik 2024-07-17 21:50:52 +04:00
parent 77882d97f7
commit 50768a1af0
No known key found for this signature in database
GPG key ID: D33232F28CFF442C
7 changed files with 390 additions and 15 deletions

View file

@ -92,6 +92,8 @@ pub use message_auto_delete_timer_changed::*;
pub use message_entity::*; pub use message_entity::*;
pub use message_id::*; pub use message_id::*;
pub use message_origin::*; pub use message_origin::*;
pub use message_reaction_count_updated::*;
pub use message_reaction_updated::*;
pub use order_info::*; pub use order_info::*;
pub use parse_mode::*; pub use parse_mode::*;
pub use passport_data::*; pub use passport_data::*;
@ -208,6 +210,8 @@ mod message_auto_delete_timer_changed;
mod message_entity; mod message_entity;
mod message_id; mod message_id;
mod message_origin; mod message_origin;
mod message_reaction_count_updated;
mod message_reaction_updated;
mod order_info; mod order_info;
mod parse_mode; mod parse_mode;
mod photo_size; mod photo_size;

View file

@ -7,6 +7,8 @@ pub enum AllowedUpdate {
EditedMessage, EditedMessage,
ChannelPost, ChannelPost,
EditedChannelPost, EditedChannelPost,
MessageReaction,
MessageReactionCount,
InlineQuery, InlineQuery,
ChosenInlineResult, ChosenInlineResult,
CallbackQuery, CallbackQuery,

View file

@ -0,0 +1,73 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::types::{Chat, MessageId, ReactionType};
/// This object represents reaction changes on a message with anonymous
/// reactions.
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct MessageReactionCountUpdated {
/// The chat containing the message
pub chat: Chat,
/// Unique message identifier inside the chat
#[serde(flatten)]
pub message_id: MessageId,
/// Date of the change in Unix time
#[serde(with = "crate::types::serde_date_from_unix_timestamp")]
pub date: DateTime<Utc>,
/// List of reactions that are present on the message
pub reactions: Vec<ReactionCount>,
}
/// Represents a reaction added to a message along with the number of times it
/// was added.
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ReactionCount {
/// Type of the reaction
pub r#type: ReactionType,
/// Number of times the reaction was added
pub total_count: u64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deserialize() {
let data = r#"
{
"chat": {
"id": -1002236736395,
"title": "Test",
"type": "channel"
},
"message_id": 36,
"date": 1721306391,
"reactions": [
{
"type": {
"type": "emoji",
"emoji": "🗿"
},
"total_count": 2
},
{
"type": {
"type": "emoji",
"emoji": "🌭"
},
"total_count": 1
}
]
}
"#;
serde_json::from_str::<MessageReactionCountUpdated>(data).unwrap();
}
}

View file

@ -0,0 +1,82 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::types::{Chat, MessageId, ReactionType, User};
/// This object represents a change of a reaction on a message performed by a
/// user.
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct MessageReactionUpdated {
/// The chat containing the message the user reacted to
pub chat: Chat,
/// Unique identifier of the message inside the chat
#[serde(flatten)]
pub message_id: MessageId,
/// The user that changed the reaction, if the user isn't anonymous
pub user: Option<User>,
/// The chat on behalf of which the reaction was changed, if the user is
/// anonymous
pub actor_chat: Option<Chat>,
/// Date of the change in Unix time
#[serde(with = "crate::types::serde_date_from_unix_timestamp")]
pub date: DateTime<Utc>,
/// Previous list of reaction types that were set by the user
pub old_reaction: Vec<ReactionType>,
/// New list of reaction types that have been set by the user
pub new_reaction: Vec<ReactionType>,
}
impl MessageReactionUpdated {
#[must_use]
pub fn actor_chat(&self) -> Option<&Chat> {
self.actor_chat.as_ref()
}
#[must_use]
pub fn user(&self) -> Option<&User> {
self.user.as_ref()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deserialize() {
let data = r#"
{
"chat": {
"id": -1002184233434,
"title": "Test",
"type": "supergroup"
},
"message_id": 35,
"user": {
"id": 1459074222,
"is_bot": false,
"first_name": "shadowchain",
"username": "shdwchn10",
"language_code": "en",
"is_premium": true
},
"date": 1721306082,
"old_reaction": [],
"new_reaction": [
{
"type": "emoji",
"emoji": "🌭"
}
]
}
"#;
serde_json::from_str::<MessageReactionUpdated>(data).unwrap();
}
}

View file

@ -4,7 +4,8 @@ use serde_json::Value;
use crate::types::{ use crate::types::{
CallbackQuery, Chat, ChatJoinRequest, ChatMemberUpdated, ChosenInlineResult, InlineQuery, CallbackQuery, Chat, ChatJoinRequest, ChatMemberUpdated, ChosenInlineResult, InlineQuery,
Message, Poll, PollAnswer, PreCheckoutQuery, ShippingQuery, User, Message, MessageReactionCountUpdated, MessageReactionUpdated, Poll, PollAnswer,
PreCheckoutQuery, ShippingQuery, User,
}; };
/// This [object] represents an incoming update. /// This [object] represents an incoming update.
@ -59,6 +60,24 @@ pub enum UpdateKind {
/// New version of a channel post that is known to the bot and was edited. /// New version of a channel post that is known to the bot and was edited.
EditedChannelPost(Message), EditedChannelPost(Message),
/// A reaction to a message was changed by a user. The bot must be an
/// administrator in the chat and must explicitly specify
/// [`AllowedUpdate::MessageReaction`] in the list of `allowed_updates`
/// to receive these updates. The update isn't received for reactions
/// set by bots.
///
/// [`AllowedUpdate::MessageReaction`]: crate::types::AllowedUpdate::MessageReaction
MessageReaction(MessageReactionUpdated),
/// Reactions to a message with anonymous reactions were changed. The bot
/// must be an administrator in the chat and must explicitly specify
/// [`AllowedUpdate::MessageReactionCount`] in the list of `allowed_updates`
/// to receive these updates. The updates are grouped and can be sent
/// with delay up to a few minutes.
///
/// [`AllowedUpdate::MessageReactionCount`]: crate::types::AllowedUpdate::MessageReactionCount
MessageReactionCount(MessageReactionCountUpdated),
/// New incoming [inline] query. /// New incoming [inline] query.
/// ///
/// [inline]: https://core.telegram.org/bots/api#inline-mode /// [inline]: https://core.telegram.org/bots/api#inline-mode
@ -133,6 +152,7 @@ impl Update {
CallbackQuery(query) => &query.from, CallbackQuery(query) => &query.from,
ChosenInlineResult(chosen) => &chosen.from, ChosenInlineResult(chosen) => &chosen.from,
MessageReaction(reaction) => return reaction.user(),
InlineQuery(query) => &query.from, InlineQuery(query) => &query.from,
ShippingQuery(query) => &query.from, ShippingQuery(query) => &query.from,
PreCheckoutQuery(query) => &query.from, PreCheckoutQuery(query) => &query.from,
@ -141,7 +161,7 @@ impl Update {
MyChatMember(m) | ChatMember(m) => &m.from, MyChatMember(m) | ChatMember(m) => &m.from,
ChatJoinRequest(r) => &r.from, ChatJoinRequest(r) => &r.from,
Poll(_) | Error(_) => return None, MessageReactionCount(_) | Poll(_) | Error(_) => return None,
}; };
Some(from) Some(from)
@ -191,6 +211,13 @@ impl Update {
| UpdateKind::ChannelPost(message) | UpdateKind::ChannelPost(message)
| UpdateKind::EditedChannelPost(message) => i0(message.mentioned_users()), | UpdateKind::EditedChannelPost(message) => i0(message.mentioned_users()),
UpdateKind::MessageReaction(answer) => {
if let Some(user) = answer.user() {
return i1(once(user));
}
i6(empty())
}
UpdateKind::InlineQuery(query) => i1(once(&query.from)), UpdateKind::InlineQuery(query) => i1(once(&query.from)),
UpdateKind::ChosenInlineResult(query) => i1(once(&query.from)), UpdateKind::ChosenInlineResult(query) => i1(once(&query.from)),
UpdateKind::CallbackQuery(query) => i2(query.mentioned_users()), UpdateKind::CallbackQuery(query) => i2(query.mentioned_users()),
@ -209,7 +236,7 @@ impl Update {
i4(member.mentioned_users()) i4(member.mentioned_users())
} }
UpdateKind::ChatJoinRequest(request) => i5(request.mentioned_users()), UpdateKind::ChatJoinRequest(request) => i5(request.mentioned_users()),
UpdateKind::Error(_) => i6(empty()), UpdateKind::MessageReactionCount(_) | UpdateKind::Error(_) => i6(empty()),
} }
} }
@ -224,6 +251,8 @@ impl Update {
ChatMember(m) => &m.chat, ChatMember(m) => &m.chat,
MyChatMember(m) => &m.chat, MyChatMember(m) => &m.chat,
ChatJoinRequest(c) => &c.chat, ChatJoinRequest(c) => &c.chat,
MessageReaction(r) => &r.chat,
MessageReactionCount(r) => &r.chat,
InlineQuery(_) InlineQuery(_)
| ChosenInlineResult(_) | ChosenInlineResult(_)
@ -293,6 +322,14 @@ impl<'de> Deserialize<'de> for UpdateKind {
"edited_channel_post" => { "edited_channel_post" => {
map.next_value::<Message>().ok().map(UpdateKind::EditedChannelPost) map.next_value::<Message>().ok().map(UpdateKind::EditedChannelPost)
} }
"message_reaction" => map
.next_value::<MessageReactionUpdated>()
.ok()
.map(UpdateKind::MessageReaction),
"message_reaction_count" => map
.next_value::<MessageReactionCountUpdated>()
.ok()
.map(UpdateKind::MessageReactionCount),
"inline_query" => { "inline_query" => {
map.next_value::<InlineQuery>().ok().map(UpdateKind::InlineQuery) map.next_value::<InlineQuery>().ok().map(UpdateKind::InlineQuery)
} }
@ -351,27 +388,33 @@ impl Serialize for UpdateKind {
UpdateKind::EditedChannelPost(v) => { UpdateKind::EditedChannelPost(v) => {
s.serialize_newtype_variant(name, 3, "edited_channel_post", v) s.serialize_newtype_variant(name, 3, "edited_channel_post", v)
} }
UpdateKind::InlineQuery(v) => s.serialize_newtype_variant(name, 4, "inline_query", v), UpdateKind::MessageReaction(v) => {
s.serialize_newtype_variant(name, 4, "message_reaction", v)
}
UpdateKind::MessageReactionCount(v) => {
s.serialize_newtype_variant(name, 5, "message_reaction_count", v)
}
UpdateKind::InlineQuery(v) => s.serialize_newtype_variant(name, 6, "inline_query", v),
UpdateKind::ChosenInlineResult(v) => { UpdateKind::ChosenInlineResult(v) => {
s.serialize_newtype_variant(name, 5, "chosen_inline_result", v) s.serialize_newtype_variant(name, 7, "chosen_inline_result", v)
} }
UpdateKind::CallbackQuery(v) => { UpdateKind::CallbackQuery(v) => {
s.serialize_newtype_variant(name, 6, "callback_query", v) s.serialize_newtype_variant(name, 8, "callback_query", v)
} }
UpdateKind::ShippingQuery(v) => { UpdateKind::ShippingQuery(v) => {
s.serialize_newtype_variant(name, 7, "shipping_query", v) s.serialize_newtype_variant(name, 9, "shipping_query", v)
} }
UpdateKind::PreCheckoutQuery(v) => { UpdateKind::PreCheckoutQuery(v) => {
s.serialize_newtype_variant(name, 8, "pre_checkout_query", v) s.serialize_newtype_variant(name, 10, "pre_checkout_query", v)
} }
UpdateKind::Poll(v) => s.serialize_newtype_variant(name, 9, "poll", v), UpdateKind::Poll(v) => s.serialize_newtype_variant(name, 11, "poll", v),
UpdateKind::PollAnswer(v) => s.serialize_newtype_variant(name, 10, "poll_answer", v), UpdateKind::PollAnswer(v) => s.serialize_newtype_variant(name, 12, "poll_answer", v),
UpdateKind::MyChatMember(v) => { UpdateKind::MyChatMember(v) => {
s.serialize_newtype_variant(name, 11, "my_chat_member", v) s.serialize_newtype_variant(name, 13, "my_chat_member", v)
} }
UpdateKind::ChatMember(v) => s.serialize_newtype_variant(name, 12, "chat_member", v), UpdateKind::ChatMember(v) => s.serialize_newtype_variant(name, 14, "chat_member", v),
UpdateKind::ChatJoinRequest(v) => { UpdateKind::ChatJoinRequest(v) => {
s.serialize_newtype_variant(name, 13, "chat_join_request", v) s.serialize_newtype_variant(name, 15, "chat_join_request", v)
} }
UpdateKind::Error(v) => v.serialize(s), UpdateKind::Error(v) => v.serialize(s),
} }
@ -385,8 +428,10 @@ fn empty_error() -> UpdateKind {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::types::{ use crate::types::{
Chat, ChatFullInfo, ChatId, ChatKind, ChatPrivate, MediaKind, MediaText, Message, Chat, ChatFullInfo, ChatId, ChatKind, ChatPrivate, ChatPublic, MediaKind, MediaText,
MessageCommon, MessageId, MessageKind, Update, UpdateId, UpdateKind, User, UserId, Message, MessageCommon, MessageId, MessageKind, MessageReactionCountUpdated,
MessageReactionUpdated, PublicChatChannel, PublicChatKind, PublicChatSupergroup,
ReactionCount, ReactionType, ReactionTypeKind, Update, UpdateId, UpdateKind, User, UserId,
}; };
use chrono::DateTime; use chrono::DateTime;
@ -726,4 +771,169 @@ mod test {
_ => panic!("Expected `MyChatMember`"), _ => panic!("Expected `MyChatMember`"),
} }
} }
#[test]
fn message_reaction_updated() {
let json = r#"
{
"update_id": 71651249,
"message_reaction": {
"chat": {
"id": -1002184233434,
"title": "Test",
"type": "supergroup"
},
"message_id": 35,
"user": {
"id": 1459074222,
"is_bot": false,
"first_name": "shadowchain",
"username": "shdwchn10",
"language_code": "en",
"is_premium": true
},
"date": 1721306082,
"old_reaction": [],
"new_reaction": [
{
"type": "emoji",
"emoji": "🌭"
}
]
}
}
"#;
let expected = Update {
id: UpdateId(71651249),
kind: UpdateKind::MessageReaction(MessageReactionUpdated {
chat: Chat {
id: ChatId(-1002184233434),
kind: ChatKind::Public(ChatPublic {
title: Some("Test".to_owned()),
kind: PublicChatKind::Supergroup(PublicChatSupergroup {
username: None,
active_usernames: None,
is_forum: false,
sticker_set_name: None,
can_set_sticker_set: None,
permissions: None,
slow_mode_delay: None,
linked_chat_id: None,
location: None,
join_to_send_messages: None,
join_by_request: None,
}),
description: None,
invite_link: None,
has_protected_content: None,
}),
photo: None,
pinned_message: None,
message_auto_delete_time: None,
has_hidden_members: false,
has_aggressive_anti_spam_enabled: false,
chat_full_info: ChatFullInfo { emoji_status_expiration_date: None },
},
message_id: MessageId(35),
user: Some(User {
id: UserId(1459074222),
is_bot: false,
first_name: "shadowchain".to_owned(),
last_name: None,
username: Some("shdwchn10".to_owned()),
language_code: Some("en".to_owned()),
is_premium: true,
added_to_attachment_menu: false,
}),
actor_chat: None,
date: DateTime::from_timestamp(1721306082, 0).unwrap(),
old_reaction: vec![],
new_reaction: vec![ReactionType {
kind: ReactionTypeKind::Emoji { emoji: "🌭".to_owned() },
}],
}),
};
let actual = serde_json::from_str::<Update>(json).unwrap();
assert_eq!(expected, actual);
}
#[test]
fn message_reaction_count_updated() {
let json = r#"
{
"update_id": 71651251,
"message_reaction_count": {
"chat": {
"id": -1002236736395,
"title": "Test",
"type": "channel"
},
"message_id": 36,
"date": 1721306391,
"reactions": [
{
"type": {
"type": "emoji",
"emoji": "🗿"
},
"total_count": 2
},
{
"type": {
"type": "emoji",
"emoji": "🌭"
},
"total_count": 1
}
]
}
}
"#;
let expected = Update {
id: UpdateId(71651251),
kind: UpdateKind::MessageReactionCount(MessageReactionCountUpdated {
chat: Chat {
id: ChatId(-1002236736395),
kind: ChatKind::Public(ChatPublic {
title: Some("Test".to_owned()),
kind: PublicChatKind::Channel(PublicChatChannel {
username: None,
linked_chat_id: None,
}),
description: None,
invite_link: None,
has_protected_content: None,
}),
photo: None,
pinned_message: None,
message_auto_delete_time: None,
has_hidden_members: false,
has_aggressive_anti_spam_enabled: false,
chat_full_info: ChatFullInfo { emoji_status_expiration_date: None },
},
message_id: MessageId(36),
date: DateTime::from_timestamp(1721306391, 0).unwrap(),
reactions: vec![
ReactionCount {
r#type: ReactionType {
kind: ReactionTypeKind::Emoji { emoji: "🗿".to_owned() },
},
total_count: 2,
},
ReactionCount {
r#type: ReactionType {
kind: ReactionTypeKind::Emoji { emoji: "🌭".to_owned() },
},
total_count: 1,
},
],
}),
};
let actual = serde_json::from_str::<Update>(json).unwrap();
assert_eq!(expected, actual);
}
} }

View file

@ -149,6 +149,8 @@ define_update_ext! {
(filter_edited_message, UpdateKind::EditedMessage, EditedMessage), (filter_edited_message, UpdateKind::EditedMessage, EditedMessage),
(filter_channel_post, UpdateKind::ChannelPost, ChannelPost), (filter_channel_post, UpdateKind::ChannelPost, ChannelPost),
(filter_edited_channel_post, UpdateKind::EditedChannelPost, EditedChannelPost), (filter_edited_channel_post, UpdateKind::EditedChannelPost, EditedChannelPost),
(filter_message_reaction_updated, UpdateKind::MessageReaction, MessageReaction),
(filter_message_reaction_count_updated, UpdateKind::MessageReactionCount, MessageReactionCount),
(filter_inline_query, UpdateKind::InlineQuery, InlineQuery), (filter_inline_query, UpdateKind::InlineQuery, InlineQuery),
(filter_chosen_inline_result, UpdateKind::ChosenInlineResult, ChosenInlineResult), (filter_chosen_inline_result, UpdateKind::ChosenInlineResult, ChosenInlineResult),
(filter_callback_query, UpdateKind::CallbackQuery, CallbackQuery), (filter_callback_query, UpdateKind::CallbackQuery, CallbackQuery),

View file

@ -65,6 +65,8 @@ impl EventKind for Kind {
EditedMessage, EditedMessage,
ChannelPost, ChannelPost,
EditedChannelPost, EditedChannelPost,
MessageReaction,
MessageReactionCount,
InlineQuery, InlineQuery,
ChosenInlineResult, ChosenInlineResult,
CallbackQuery, CallbackQuery,