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

View file

@ -1,6 +1,6 @@
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
/// an [inline keyboard].
@ -24,10 +24,9 @@ pub struct CallbackQuery {
/// A sender.
pub from: User,
/// A message with the callback button that originated the query. Note that
/// message content and message date will not be available if the message
/// is too old.
pub message: Option<Message>,
/// Message sent by the bot with the callback button that originated the
/// query
pub message: Option<MaybeInaccessibleMessage>,
/// An identifier of the message sent via the bot in inline mode, that
/// originated the query.
@ -58,7 +57,13 @@ impl CallbackQuery {
use crate::util::flatten;
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,
ForumTopicClosed, ForumTopicCreated, ForumTopicEdited, ForumTopicReopened, Game,
GeneralForumTopicHidden, GeneralForumTopicUnhidden, InlineKeyboardMarkup, Invoice, Location,
MessageAutoDeleteTimerChanged, MessageEntity, MessageEntityRef, MessageId, MessageOrigin,
PassportData, PhotoSize, Poll, ProximityAlertTriggered, Sticker, Story, SuccessfulPayment,
ThreadId, True, User, UsersShared, Venue, Video, VideoChatEnded, VideoChatParticipantsInvited,
VideoChatScheduled, VideoChatStarted, VideoNote, Voice, WebAppData, WriteAccessAllowed,
MaybeInaccessibleMessage, MessageAutoDeleteTimerChanged, MessageEntity, MessageEntityRef,
MessageId, MessageOrigin, PassportData, PhotoSize, Poll, ProximityAlertTriggered, Sticker,
Story, SuccessfulPayment, ThreadId, True, User, UsersShared, Venue, Video, VideoChatEnded,
VideoChatParticipantsInvited, VideoChatScheduled, VideoChatStarted, VideoNote, Voice,
WebAppData, WriteAccessAllowed,
};
/// This object represents a message.
@ -245,7 +246,7 @@ pub struct MessagePinned {
/// field will not contain further `reply_to_message` fields even if it
/// is itself a reply.
#[serde(rename = "pinned_message")]
pub pinned: Box<Message>,
pub pinned: Box<MaybeInaccessibleMessage>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@ -627,14 +628,14 @@ mod getters {
use std::ops::Deref;
use crate::types::{
self, message::MessageKind::*, Chat, ChatId, ChatMigration, MediaAnimation, MediaAudio,
MediaContact, MediaDocument, MediaGame, MediaKind, MediaLocation, MediaPhoto, MediaPoll,
MediaSticker, MediaStory, MediaText, MediaVenue, MediaVideo, MediaVideoNote, MediaVoice,
Message, MessageChannelChatCreated, MessageChatShared, MessageCommon,
MessageConnectedWebsite, MessageDeleteChatPhoto, MessageDice, MessageEntity,
MessageGroupChatCreated, MessageId, MessageInvoice, MessageLeftChatMember,
MessageNewChatMembers, MessageNewChatPhoto, MessageNewChatTitle, MessageOrigin,
MessagePassportData, MessagePinned, MessageProximityAlertTriggered,
self, message::MessageKind::*, Chat, ChatId, ChatMigration, MaybeInaccessibleMessage,
MediaAnimation, MediaAudio, MediaContact, MediaDocument, MediaGame, MediaKind,
MediaLocation, MediaPhoto, MediaPoll, MediaSticker, MediaStory, MediaText, MediaVenue,
MediaVideo, MediaVideoNote, MediaVoice, Message, MessageChannelChatCreated,
MessageChatShared, MessageCommon, MessageConnectedWebsite, MessageDeleteChatPhoto,
MessageDice, MessageEntity, MessageGroupChatCreated, MessageId, MessageInvoice,
MessageLeftChatMember, MessageNewChatMembers, MessageNewChatPhoto, MessageNewChatTitle,
MessageOrigin, MessagePassportData, MessagePinned, MessageProximityAlertTriggered,
MessageSuccessfulPayment, MessageSupergroupChatCreated, MessageUsersShared,
MessageVideoChatParticipantsInvited, PhotoSize, User,
};
@ -1208,7 +1209,7 @@ mod getters {
}
#[must_use]
pub fn pinned_message(&self) -> Option<&Message> {
pub fn pinned_message(&self) -> Option<&MaybeInaccessibleMessage> {
match &self.kind {
Pinned(MessagePinned { pinned }) => Some(pinned),
_ => None,

View file

@ -220,7 +220,7 @@ impl Update {
let chat = match &self.kind {
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,
MyChatMember(m) => &m.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?;
// 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?;
} else if let Some(id) = q.inline_message_id {
bot.edit_message_text_inline(id, text).await?;

View file

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