//! Вспомогательные функции для конвертации TDLib сообщений в MessageInfo //! //! Этот модуль содержит функции для извлечения различных частей сообщения //! из TDLib Message и конвертации их в наш внутренний формат MessageInfo. use crate::types::MessageId; use tdlib_rs::enums::{MessageContent, MessageSender}; use tdlib_rs::types::Message as TdMessage; use super::types::{ForwardInfo, MediaInfo, PhotoDownloadState, PhotoInfo, ReactionInfo, ReplyInfo}; /// Извлекает текст контента из TDLib Message /// /// Обрабатывает различные типы сообщений (текст, фото, видео, стикеры, и т.д.) /// и возвращает текстовое представление. pub fn extract_content_text(msg: &TdMessage) -> String { match &msg.content { MessageContent::MessageText(t) => t.text.text.clone(), MessageContent::MessagePhoto(p) => { let caption_text = p.caption.text.clone(); if caption_text.is_empty() { "📷 [Фото]".to_string() } else { format!("📷 {}", caption_text) } } MessageContent::MessageVideo(v) => { let caption_text = v.caption.text.clone(); if caption_text.is_empty() { "[Видео]".to_string() } else { caption_text } } MessageContent::MessageDocument(d) => { let caption_text = d.caption.text.clone(); if caption_text.is_empty() { format!("[Файл: {}]", d.document.file_name) } else { caption_text } } MessageContent::MessageSticker(s) => { format!("[Стикер: {}]", s.sticker.emoji) } MessageContent::MessageAnimation(a) => { let caption_text = a.caption.text.clone(); if caption_text.is_empty() { "[GIF]".to_string() } else { caption_text } } MessageContent::MessageVoiceNote(v) => { let caption_text = v.caption.text.clone(); if caption_text.is_empty() { "[Голосовое]".to_string() } else { caption_text } } MessageContent::MessageAudio(a) => { let caption_text = a.caption.text.clone(); if caption_text.is_empty() { let title = a.audio.title.clone(); let performer = a.audio.performer.clone(); if !title.is_empty() || !performer.is_empty() { format!("[Аудио: {} - {}]", performer, title) } else { "[Аудио]".to_string() } } else { caption_text } } _ => "[Неподдерживаемый тип сообщения]".to_string(), } } /// Извлекает entities (форматирование) из TDLib Message pub fn extract_entities(msg: &TdMessage) -> Vec { if let MessageContent::MessageText(t) = &msg.content { t.text.entities.clone() } else { vec![] } } /// Извлекает имя отправителя из TDLib Message /// /// Для пользователей делает API вызов get_user для получения имени. /// Для чатов возвращает ID чата. pub async fn extract_sender_name(msg: &TdMessage, client_id: i32) -> String { match &msg.sender_id { MessageSender::User(user) => { match tdlib_rs::functions::get_user(user.user_id, client_id).await { Ok(tdlib_rs::enums::User::User(u)) => { format!("{} {}", u.first_name, u.last_name).trim().to_string() } _ => format!("User {}", user.user_id), } } MessageSender::Chat(chat) => format!("Chat {}", chat.chat_id), } } /// Извлекает информацию о пересылке из TDLib Message pub fn extract_forward_info(msg: &TdMessage) -> Option { msg.forward_info.as_ref().and_then(|fi| { if let tdlib_rs::enums::MessageOrigin::User(origin_user) = &fi.origin { Some(ForwardInfo { sender_name: format!("User {}", origin_user.sender_user_id), }) } else { None } }) } /// Извлекает информацию об ответе из TDLib Message pub fn extract_reply_info(msg: &TdMessage) -> Option { msg.reply_to.as_ref().and_then(|reply_to| { if let tdlib_rs::enums::MessageReplyTo::Message(reply_msg) = reply_to { Some(ReplyInfo { message_id: MessageId::new(reply_msg.message_id), sender_name: "Unknown".to_string(), text: "...".to_string(), }) } else { None } }) } /// Извлекает информацию о медиа-контенте из TDLib Message /// /// Для MessagePhoto: получает лучший размер фото, извлекает file_id, width, height. /// Возвращает None для не-медийных типов сообщений. pub fn extract_media_info(msg: &TdMessage) -> Option { match &msg.content { MessageContent::MessagePhoto(p) => { // Берём лучший (последний = самый большой) размер фото let best_size = p.photo.sizes.last()?; let file_id = best_size.photo.id; let width = best_size.width; let height = best_size.height; // Проверяем, скачан ли файл let download_state = if !best_size.photo.local.path.is_empty() && best_size.photo.local.is_downloading_completed { PhotoDownloadState::Downloaded(best_size.photo.local.path.clone()) } else { PhotoDownloadState::NotDownloaded }; Some(MediaInfo::Photo(PhotoInfo { file_id, width, height, download_state, })) } _ => None, } } /// Извлекает реакции из TDLib Message pub fn extract_reactions(msg: &TdMessage) -> Vec { msg.interaction_info .as_ref() .and_then(|ii| ii.reactions.as_ref()) .map(|reactions| { reactions .reactions .iter() .filter_map(|r| { if let tdlib_rs::enums::ReactionType::Emoji(emoji_type) = &r.r#type { Some(ReactionInfo { emoji: emoji_type.emoji.clone(), count: r.total_count, is_chosen: r.is_chosen, }) } else { None } }) .collect() }) .unwrap_or_default() }