use tdlib_rs::types::TextEntity; use crate::types::{ChatId, MessageId}; #[derive(Debug, Clone)] #[allow(dead_code)] pub struct ChatInfo { pub id: ChatId, pub title: String, pub username: Option, pub last_message: String, pub last_message_date: i32, pub unread_count: i32, /// Количество непрочитанных упоминаний (@) pub unread_mention_count: i32, pub is_pinned: bool, pub order: i64, /// ID последнего прочитанного исходящего сообщения (для галочек) pub last_read_outbox_message_id: MessageId, /// ID папок, в которых находится чат pub folder_ids: Vec, /// Чат замьючен (уведомления отключены) pub is_muted: bool, /// Черновик сообщения pub draft_text: Option, } /// Информация о сообщении, на которое отвечают #[derive(Debug, Clone)] pub struct ReplyInfo { /// ID сообщения, на которое отвечают pub message_id: MessageId, /// Имя отправителя оригинального сообщения pub sender_name: String, /// Текст оригинального сообщения (превью) pub text: String, } /// Информация о пересланном сообщении #[derive(Debug, Clone)] pub struct ForwardInfo { /// Имя оригинального отправителя pub sender_name: String, /// Дата оригинального сообщения (для будущего использования) #[allow(dead_code)] pub date: i32, } /// Информация о реакции на сообщение #[derive(Debug, Clone)] pub struct ReactionInfo { /// Эмодзи реакции (например, "👍") pub emoji: String, /// Количество людей, поставивших эту реакцию pub count: i32, /// Поставил ли текущий пользователь эту реакцию pub is_chosen: bool, } /// Метаданные сообщения (ID, отправитель, время) #[derive(Debug, Clone)] pub struct MessageMetadata { pub id: MessageId, pub sender_name: String, pub date: i32, /// Дата редактирования (0 если не редактировалось) pub edit_date: i32, } /// Контент сообщения (текст и форматирование) #[derive(Debug, Clone, PartialEq)] pub struct MessageContent { pub text: String, /// Сущности форматирования (bold, italic, code и т.д.) pub entities: Vec, } /// Состояние и права доступа к сообщению #[derive(Debug, Clone)] pub struct MessageState { pub is_outgoing: bool, pub is_read: bool, /// Можно ли редактировать сообщение pub can_be_edited: bool, /// Можно ли удалить только для себя pub can_be_deleted_only_for_self: bool, /// Можно ли удалить для всех pub can_be_deleted_for_all_users: bool, } /// Взаимодействия с сообщением (reply, forward, reactions) #[derive(Debug, Clone)] pub struct MessageInteractions { /// Информация о reply (если это ответ на сообщение) pub reply_to: Option, /// Информация о forward (если сообщение переслано) pub forward_from: Option, /// Реакции на сообщение pub reactions: Vec, } #[derive(Debug, Clone)] pub struct MessageInfo { pub metadata: MessageMetadata, pub content: MessageContent, pub state: MessageState, pub interactions: MessageInteractions, } impl MessageInfo { /// Создать новое сообщение pub fn new( id: MessageId, sender_name: String, is_outgoing: bool, content: String, entities: Vec, date: i32, edit_date: i32, is_read: bool, can_be_edited: bool, can_be_deleted_only_for_self: bool, can_be_deleted_for_all_users: bool, reply_to: Option, forward_from: Option, reactions: Vec, ) -> Self { Self { metadata: MessageMetadata { id, sender_name, date, edit_date, }, content: MessageContent { text: content, entities, }, state: MessageState { is_outgoing, is_read, can_be_edited, can_be_deleted_only_for_self, can_be_deleted_for_all_users, }, interactions: MessageInteractions { reply_to, forward_from, reactions, }, } } // Удобные getter'ы для частых операций pub fn id(&self) -> MessageId { self.metadata.id } pub fn sender_name(&self) -> &str { &self.metadata.sender_name } pub fn date(&self) -> i32 { self.metadata.date } pub fn edit_date(&self) -> i32 { self.metadata.edit_date } pub fn is_edited(&self) -> bool { self.metadata.edit_date > 0 } pub fn text(&self) -> &str { &self.content.text } pub fn entities(&self) -> &[TextEntity] { &self.content.entities } pub fn is_outgoing(&self) -> bool { self.state.is_outgoing } pub fn is_read(&self) -> bool { self.state.is_read } pub fn can_be_edited(&self) -> bool { self.state.can_be_edited } pub fn can_be_deleted_only_for_self(&self) -> bool { self.state.can_be_deleted_only_for_self } pub fn can_be_deleted_for_all_users(&self) -> bool { self.state.can_be_deleted_for_all_users } pub fn reply_to(&self) -> Option<&ReplyInfo> { self.interactions.reply_to.as_ref() } pub fn forward_from(&self) -> Option<&ForwardInfo> { self.interactions.forward_from.as_ref() } pub fn reactions(&self) -> &[ReactionInfo] { &self.interactions.reactions } } /// Builder для удобного создания MessageInfo с fluent API /// /// # Примеры /// /// ``` /// use tele_tui::tdlib::MessageBuilder; /// use tele_tui::types::MessageId; /// /// let message = MessageBuilder::new(MessageId::new(123)) /// .sender_name("Alice") /// .text("Hello, world!") /// .outgoing() /// .date(1640000000) /// .build(); /// ``` pub struct MessageBuilder { id: MessageId, sender_name: String, is_outgoing: bool, text: String, entities: Vec, date: i32, edit_date: i32, is_read: bool, can_be_edited: bool, can_be_deleted_only_for_self: bool, can_be_deleted_for_all_users: bool, reply_to: Option, forward_from: Option, reactions: Vec, } impl MessageBuilder { /// Создать новый builder с обязательным ID сообщения pub fn new(id: MessageId) -> Self { Self { id, sender_name: String::new(), is_outgoing: false, text: String::new(), entities: Vec::new(), date: 0, edit_date: 0, is_read: false, can_be_edited: false, can_be_deleted_only_for_self: true, can_be_deleted_for_all_users: false, reply_to: None, forward_from: None, reactions: Vec::new(), } } /// Установить имя отправителя pub fn sender_name(mut self, name: impl Into) -> Self { self.sender_name = name.into(); self } /// Пометить сообщение как исходящее pub fn outgoing(mut self) -> Self { self.is_outgoing = true; self.can_be_edited = true; self.can_be_deleted_for_all_users = true; self } /// Пометить сообщение как входящее pub fn incoming(mut self) -> Self { self.is_outgoing = false; self.can_be_edited = false; self.can_be_deleted_for_all_users = false; self } /// Установить текст сообщения pub fn text(mut self, text: impl Into) -> Self { self.text = text.into(); self } /// Установить entities для форматирования pub fn entities(mut self, entities: Vec) -> Self { self.entities = entities; self } /// Установить дату сообщения (unix timestamp) pub fn date(mut self, date: i32) -> Self { self.date = date; self } /// Установить дату редактирования (unix timestamp) pub fn edit_date(mut self, edit_date: i32) -> Self { self.edit_date = edit_date; self } /// Пометить сообщение как отредактированное (edit_date = date + 60) pub fn edited(mut self) -> Self { self.edit_date = self.date + 60; self } /// Пометить сообщение как прочитанное pub fn read(mut self) -> Self { self.is_read = true; self } /// Пометить сообщение как непрочитанное pub fn unread(mut self) -> Self { self.is_read = false; self } /// Разрешить редактирование pub fn editable(mut self) -> Self { self.can_be_edited = true; self } /// Разрешить удаление только для себя pub fn deletable_for_self(mut self) -> Self { self.can_be_deleted_only_for_self = true; self } /// Разрешить удаление для всех pub fn deletable_for_all(mut self) -> Self { self.can_be_deleted_for_all_users = true; self } /// Установить информацию об ответе pub fn reply_to(mut self, reply: ReplyInfo) -> Self { self.reply_to = Some(reply); self } /// Установить информацию о пересылке pub fn forward_from(mut self, forward: ForwardInfo) -> Self { self.forward_from = Some(forward); self } /// Установить реакции pub fn reactions(mut self, reactions: Vec) -> Self { self.reactions = reactions; self } /// Добавить одну реакцию pub fn add_reaction(mut self, reaction: ReactionInfo) -> Self { self.reactions.push(reaction); self } /// Построить MessageInfo из данных builder'а pub fn build(self) -> MessageInfo { MessageInfo::new( self.id, self.sender_name, self.is_outgoing, self.text, self.entities, self.date, self.edit_date, self.is_read, self.can_be_edited, self.can_be_deleted_only_for_self, self.can_be_deleted_for_all_users, self.reply_to, self.forward_from, self.reactions, ) } } #[cfg(test)] mod tests { use super::*; use crate::types::MessageId; #[test] fn test_message_builder_basic() { let message = MessageBuilder::new(MessageId::new(123)) .sender_name("Alice") .text("Hello, world!") .date(1640000000) .build(); assert_eq!(message.id(), MessageId::new(123)); assert_eq!(message.sender_name(), "Alice"); assert_eq!(message.text(), "Hello, world!"); assert_eq!(message.date(), 1640000000); assert!(!message.is_outgoing()); } #[test] fn test_message_builder_outgoing() { let message = MessageBuilder::new(MessageId::new(456)) .sender_name("Me") .text("Test message") .outgoing() .read() .build(); assert!(message.is_outgoing()); assert!(message.is_read()); assert!(message.can_be_edited()); assert!(message.can_be_deleted_for_all_users()); } #[test] fn test_message_builder_edited() { let message = MessageBuilder::new(MessageId::new(789)) .text("Original text") .date(1640000000) .edited() .build(); assert!(message.is_edited()); assert_eq!(message.edit_date(), 1640000060); } #[test] fn test_message_builder_with_reply() { let reply = ReplyInfo { message_id: MessageId::new(100), sender_name: "Bob".to_string(), text: "Original message".to_string(), }; let message = MessageBuilder::new(MessageId::new(200)) .text("Reply text") .reply_to(reply) .build(); assert!(message.reply_to().is_some()); assert_eq!(message.reply_to().unwrap().sender_name, "Bob"); } #[test] fn test_message_builder_with_reactions() { let reaction = ReactionInfo { emoji: "👍".to_string(), count: 5, is_chosen: true, }; let message = MessageBuilder::new(MessageId::new(300)) .text("Cool message") .add_reaction(reaction.clone()) .build(); assert_eq!(message.reactions().len(), 1); assert_eq!(message.reactions()[0].emoji, "👍"); assert_eq!(message.reactions()[0].count, 5); } #[test] fn test_message_builder_fluent_api() { let message = MessageBuilder::new(MessageId::new(999)) .sender_name("Charlie") .text("Complex message") .date(1640000000) .outgoing() .read() .editable() .deletable_for_all() .build(); assert_eq!(message.sender_name(), "Charlie"); assert_eq!(message.text(), "Complex message"); assert!(message.is_outgoing()); assert!(message.is_read()); assert!(message.can_be_edited()); assert!(message.can_be_deleted_for_all_users()); } } #[derive(Debug, Clone)] pub struct FolderInfo { pub id: i32, pub name: String, } /// Информация о профиле чата/пользователя #[derive(Debug, Clone)] pub struct ProfileInfo { pub chat_id: ChatId, pub title: String, pub username: Option, pub bio: Option, pub phone_number: Option, pub chat_type: String, // "Личный чат", "Группа", "Канал" pub member_count: Option, pub description: Option, pub invite_link: Option, pub is_group: bool, pub online_status: Option, } /// Состояние сетевого соединения #[derive(Debug, Clone, PartialEq)] pub enum NetworkState { /// Ожидание подключения к сети WaitingForNetwork, /// Подключение к прокси ConnectingToProxy, /// Подключение к серверам Telegram Connecting, /// Обновление данных Updating, /// Подключено Ready, } /// Онлайн-статус пользователя #[derive(Debug, Clone, PartialEq)] pub enum UserOnlineStatus { /// Онлайн Online, /// Был недавно (менее часа назад) Recently, /// Был на этой неделе LastWeek, /// Был в этом месяце LastMonth, /// Давно не был LongTimeAgo, /// Оффлайн с указанием времени (unix timestamp) Offline(i32), }