Add MaybeInaccessibleMessage to the Message and CallbackQuery

This commit is contained in:
Сырцев Вадим Игоревич 2024-07-17 15:05:46 +03:00 committed by Andrey Brusnik
parent 6c967231ef
commit c8f7bd745c
No known key found for this signature in database
GPG key ID: D33232F28CFF442C
8 changed files with 145 additions and 23 deletions

View file

@ -41,6 +41,7 @@ pub use game::*;
pub use game_high_score::*; pub use game_high_score::*;
pub use general_forum_topic_hidden::*; pub use general_forum_topic_hidden::*;
pub use general_forum_topic_unhidden::*; pub use general_forum_topic_unhidden::*;
pub use inaccessible_message::*;
pub use inline_keyboard_button::*; pub use inline_keyboard_button::*;
pub use inline_keyboard_markup::*; pub use inline_keyboard_markup::*;
pub use inline_query::*; pub use inline_query::*;
@ -79,6 +80,7 @@ pub use label_price::*;
pub use location::*; pub use location::*;
pub use login_url::*; pub use login_url::*;
pub use mask_position::*; pub use mask_position::*;
pub use maybe_inaccessible_message::*;
pub use me::*; pub use me::*;
pub use menu_button::*; pub use menu_button::*;
pub use message::*; pub use message::*;
@ -170,6 +172,7 @@ mod game;
mod game_high_score; mod game_high_score;
mod general_forum_topic_hidden; mod general_forum_topic_hidden;
mod general_forum_topic_unhidden; mod general_forum_topic_unhidden;
mod inaccessible_message;
mod inline_keyboard_button; mod inline_keyboard_button;
mod inline_keyboard_markup; mod inline_keyboard_markup;
mod inline_query_results_button; mod inline_query_results_button;
@ -186,6 +189,7 @@ mod label_price;
mod location; mod location;
mod login_url; mod login_url;
mod mask_position; mod mask_position;
mod maybe_inaccessible_message;
mod me; mod me;
mod menu_button; mod menu_button;
mod message; mod message;

View file

@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::{Message, User}; use crate::types::{MaybeInaccessibleMessage, Message, User};
/// This object represents an incoming callback query from a callback button in /// This object represents an incoming callback query from a callback button in
/// an [inline keyboard]. /// an [inline keyboard].
@ -24,10 +24,9 @@ pub struct CallbackQuery {
/// A sender. /// A sender.
pub from: User, pub from: User,
/// A message with the callback button that originated the query. Note that /// Message sent by the bot with the callback button that originated the
/// message content and message date will not be available if the message /// query
/// is too old. pub message: Option<MaybeInaccessibleMessage>,
pub message: Option<Message>,
/// An identifier of the message sent via the bot in inline mode, that /// An identifier of the message sent via the bot in inline mode, that
/// originated the query. /// originated the query.
@ -58,7 +57,13 @@ impl CallbackQuery {
use crate::util::flatten; use crate::util::flatten;
use std::iter::once; use std::iter::once;
once(&self.from).chain(flatten(self.message.as_ref().map(Message::mentioned_users))) once(&self.from).chain(flatten(
self.message
.as_ref()
// If we can access the message
.and_then(|maybe| maybe.message())
.map(Message::mentioned_users),
))
} }
} }

View file

@ -0,0 +1,13 @@
use serde::{Deserialize, Serialize};
use crate::types::{Chat, MessageId};
/// This object describes a message that was deleted or is otherwise
/// inaccessible to the bot.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct InaccessibleMessage {
/// Chat the message belonged to
pub chat: Chat,
/// Unique message identifier inside the chat
pub message_id: MessageId,
}

View file

@ -0,0 +1,98 @@
use serde::{Deserialize, Serialize};
use crate::types::{Chat, InaccessibleMessage, Message, MessageId};
#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(untagged)]
pub enum MaybeInaccessibleMessage {
Inaccessible(InaccessibleMessage),
Regular(Message),
}
impl MaybeInaccessibleMessage {
pub fn id(&self) -> MessageId {
match self {
Self::Inaccessible(i_message) => i_message.message_id,
Self::Regular(message) => message.id,
}
}
pub fn message(&self) -> Option<&Message> {
match self {
Self::Regular(message) => Some(message),
Self::Inaccessible(_) => None,
}
}
pub fn chat_and_id(&self) -> (&Chat, MessageId) {
(self.chat(), self.id())
}
pub fn chat(&self) -> &Chat {
match self {
Self::Regular(message) => &message.chat,
Self::Inaccessible(i_message) => &i_message.chat,
}
}
}
impl<'de> Deserialize<'de> for MaybeInaccessibleMessage {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let message: Message = Message::deserialize(deserializer)?;
// Thank you, TBA 7.0 authors!
if message.date.timestamp() == 0 {
return Ok(MaybeInaccessibleMessage::Inaccessible(InaccessibleMessage {
chat: message.chat,
message_id: message.id,
}));
}
Ok(MaybeInaccessibleMessage::Regular(message))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_inaccessible_message() {
let json = r#"{
"chat": {
"id": 42,
"first_name": "Вадим Игоревич",
"last_name": "Сырцев",
"username": "syrtcevvi",
"type": "private"
},
"message_id": 4,
"date": 0
}"#;
let inaccessible_message = serde_json::from_str::<MaybeInaccessibleMessage>(json);
assert!(inaccessible_message.is_ok());
assert!(matches!(inaccessible_message.unwrap(), MaybeInaccessibleMessage::Inaccessible(_)));
}
#[test]
fn test_regular_message() {
let json = r#"{
"chat": {
"id": 42,
"first_name": "Вадим Игоревич",
"last_name": "Сырцев",
"username": "syrtcevvi",
"type": "private"
},
"message_id": 4,
"date": 1
}"#;
let regular_message = serde_json::from_str::<MaybeInaccessibleMessage>(json);
assert!(regular_message.is_ok());
assert!(matches!(regular_message.unwrap(), MaybeInaccessibleMessage::Regular(_)));
}
}

View file

@ -8,10 +8,11 @@ use crate::types::{
Animation, Audio, BareChatId, Chat, ChatId, ChatShared, Contact, Dice, Document, Animation, Audio, BareChatId, Chat, ChatId, ChatShared, Contact, Dice, Document,
ForumTopicClosed, ForumTopicCreated, ForumTopicEdited, ForumTopicReopened, Game, ForumTopicClosed, ForumTopicCreated, ForumTopicEdited, ForumTopicReopened, Game,
GeneralForumTopicHidden, GeneralForumTopicUnhidden, InlineKeyboardMarkup, Invoice, Location, GeneralForumTopicHidden, GeneralForumTopicUnhidden, InlineKeyboardMarkup, Invoice, Location,
MessageAutoDeleteTimerChanged, MessageEntity, MessageEntityRef, MessageId, MessageOrigin, MaybeInaccessibleMessage, MessageAutoDeleteTimerChanged, MessageEntity, MessageEntityRef,
PassportData, PhotoSize, Poll, ProximityAlertTriggered, Sticker, Story, SuccessfulPayment, MessageId, MessageOrigin, PassportData, PhotoSize, Poll, ProximityAlertTriggered, Sticker,
ThreadId, True, User, UsersShared, Venue, Video, VideoChatEnded, VideoChatParticipantsInvited, Story, SuccessfulPayment, ThreadId, True, User, UsersShared, Venue, Video, VideoChatEnded,
VideoChatScheduled, VideoChatStarted, VideoNote, Voice, WebAppData, WriteAccessAllowed, VideoChatParticipantsInvited, VideoChatScheduled, VideoChatStarted, VideoNote, Voice,
WebAppData, WriteAccessAllowed,
}; };
/// This object represents a message. /// This object represents a message.
@ -245,7 +246,7 @@ pub struct MessagePinned {
/// field will not contain further `reply_to_message` fields even if it /// field will not contain further `reply_to_message` fields even if it
/// is itself a reply. /// is itself a reply.
#[serde(rename = "pinned_message")] #[serde(rename = "pinned_message")]
pub pinned: Box<Message>, pub pinned: Box<MaybeInaccessibleMessage>,
} }
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@ -627,14 +628,14 @@ mod getters {
use std::ops::Deref; use std::ops::Deref;
use crate::types::{ use crate::types::{
self, message::MessageKind::*, Chat, ChatId, ChatMigration, MediaAnimation, MediaAudio, self, message::MessageKind::*, Chat, ChatId, ChatMigration, MaybeInaccessibleMessage,
MediaContact, MediaDocument, MediaGame, MediaKind, MediaLocation, MediaPhoto, MediaPoll, MediaAnimation, MediaAudio, MediaContact, MediaDocument, MediaGame, MediaKind,
MediaSticker, MediaStory, MediaText, MediaVenue, MediaVideo, MediaVideoNote, MediaVoice, MediaLocation, MediaPhoto, MediaPoll, MediaSticker, MediaStory, MediaText, MediaVenue,
Message, MessageChannelChatCreated, MessageChatShared, MessageCommon, MediaVideo, MediaVideoNote, MediaVoice, Message, MessageChannelChatCreated,
MessageConnectedWebsite, MessageDeleteChatPhoto, MessageDice, MessageEntity, MessageChatShared, MessageCommon, MessageConnectedWebsite, MessageDeleteChatPhoto,
MessageGroupChatCreated, MessageId, MessageInvoice, MessageLeftChatMember, MessageDice, MessageEntity, MessageGroupChatCreated, MessageId, MessageInvoice,
MessageNewChatMembers, MessageNewChatPhoto, MessageNewChatTitle, MessageOrigin, MessageLeftChatMember, MessageNewChatMembers, MessageNewChatPhoto, MessageNewChatTitle,
MessagePassportData, MessagePinned, MessageProximityAlertTriggered, MessageOrigin, MessagePassportData, MessagePinned, MessageProximityAlertTriggered,
MessageSuccessfulPayment, MessageSupergroupChatCreated, MessageUsersShared, MessageSuccessfulPayment, MessageSupergroupChatCreated, MessageUsersShared,
MessageVideoChatParticipantsInvited, PhotoSize, User, MessageVideoChatParticipantsInvited, PhotoSize, User,
}; };
@ -1208,7 +1209,7 @@ mod getters {
} }
#[must_use] #[must_use]
pub fn pinned_message(&self) -> Option<&Message> { pub fn pinned_message(&self) -> Option<&MaybeInaccessibleMessage> {
match &self.kind { match &self.kind {
Pinned(MessagePinned { pinned }) => Some(pinned), Pinned(MessagePinned { pinned }) => Some(pinned),
_ => None, _ => None,

View file

@ -220,7 +220,7 @@ impl Update {
let chat = match &self.kind { let chat = match &self.kind {
Message(m) | EditedMessage(m) | ChannelPost(m) | EditedChannelPost(m) => &m.chat, Message(m) | EditedMessage(m) | ChannelPost(m) | EditedChannelPost(m) => &m.chat,
CallbackQuery(q) => &q.message.as_ref()?.chat, CallbackQuery(q) => q.message.as_ref()?.chat(),
ChatMember(m) => &m.chat, ChatMember(m) => &m.chat,
MyChatMember(m) => &m.chat, MyChatMember(m) => &m.chat,
ChatJoinRequest(c) => &c.chat, ChatJoinRequest(c) => &c.chat,

View file

@ -116,7 +116,8 @@ async fn callback_handler(bot: Bot, q: CallbackQuery) -> Result<(), Box<dyn Erro
bot.answer_callback_query(q.id).await?; bot.answer_callback_query(q.id).await?;
// Edit text of the message to which the buttons were attached // Edit text of the message to which the buttons were attached
if let Some(Message { id, chat, .. }) = q.message { if let Some(maybe_message) = q.message {
let (chat, id) = maybe_message.chat_and_id();
bot.edit_message_text(chat.id, id, text).await?; bot.edit_message_text(chat.id, id, text).await?;
} else if let Some(id) = q.inline_message_id { } else if let Some(id) = q.inline_message_id {
bot.edit_message_text_inline(id, text).await?; bot.edit_message_text_inline(id, text).await?;

View file

@ -16,7 +16,7 @@ impl GetChatId for Message {
impl GetChatId for CallbackQuery { impl GetChatId for CallbackQuery {
fn chat_id(&self) -> Option<ChatId> { fn chat_id(&self) -> Option<ChatId> {
self.message.as_ref().map(|mes| mes.chat.id) self.message.as_ref().map(|mes| mes.chat().id)
} }
} }