use crate::constants::TDLIB_CHAT_LIMIT; use std::time::Instant; use tdlib_rs::enums::{ChatAction, ChatList, ChatType}; use tdlib_rs::functions; use super::types::{ChatInfo, FolderInfo, MessageInfo, ProfileInfo}; /// Менеджер чатов pub struct ChatManager { pub chats: Vec, pub folders: Vec, pub main_chat_list_position: i32, /// Typing status для текущего чата: (user_id, action_text, timestamp) pub typing_status: Option<(i64, String, Instant)>, client_id: i32, } impl ChatManager { pub fn new(client_id: i32) -> Self { Self { chats: Vec::new(), folders: Vec::new(), main_chat_list_position: 0, typing_status: None, client_id, } } /// Загрузить чаты из основного списка pub async fn load_chats(&mut self, limit: i32) -> Result<(), String> { let result = functions::load_chats(Some(ChatList::Main), limit, self.client_id).await; match result { Ok(_) => Ok(()), Err(e) => Err(format!("Ошибка загрузки чатов: {:?}", e)), } } /// Загрузить чаты из папки pub async fn load_folder_chats(&mut self, folder_id: i32, limit: i32) -> Result<(), String> { let chat_list = ChatList::Folder(tdlib_rs::types::ChatListFolder { chat_folder_id: folder_id }); let result = functions::load_chats(Some(chat_list), limit, self.client_id).await; match result { Ok(_) => Ok(()), Err(e) => Err(format!("Ошибка загрузки папки: {:?}", e)), } } /// Покинуть чат/группу pub async fn leave_chat(&self, chat_id: i64) -> Result<(), String> { let result = functions::leave_chat(chat_id, self.client_id).await; match result { Ok(_) => Ok(()), Err(e) => Err(format!("Ошибка выхода из чата: {:?}", e)), } } /// Получить информацию профиля чата pub async fn get_profile_info(&self, chat_id: i64) -> Result { // Получаем основную информацию о чате let chat_result = functions::get_chat(chat_id, self.client_id).await; let chat_enum = match chat_result { Ok(c) => c, Err(e) => return Err(format!("Ошибка получения чата: {:?}", e)), }; let chat = match chat_enum { tdlib_rs::enums::Chat::Chat(c) => c, _ => return Err("Неожиданный тип чата".to_string()), }; let chat_type_str = match &chat.r#type { ChatType::Private(_) => "Личный чат", ChatType::Supergroup(sg) => { if sg.is_channel { "Канал" } else { "Группа" } } ChatType::BasicGroup(_) => "Группа", ChatType::Secret(_) => "Секретный чат", }; let is_group = matches!( &chat.r#type, ChatType::Supergroup(_) | ChatType::BasicGroup(_) ); // Для личных чатов получаем информацию о пользователе let (bio, phone_number, username, online_status) = if let ChatType::Private(private_chat) = &chat.r#type { match functions::get_user(private_chat.user_id, self.client_id).await { Ok(tdlib_rs::enums::User::User(user)) => { let bio_opt = if let Ok(tdlib_rs::enums::UserFullInfo::UserFullInfo(full_info)) = functions::get_user_full_info(private_chat.user_id, self.client_id).await { full_info.bio.map(|b| b.text) } else { None }; let online_status_str = match user.status { tdlib_rs::enums::UserStatus::Online(_) => Some("В сети".to_string()), tdlib_rs::enums::UserStatus::Recently(_) => { Some("Был(а) недавно".to_string()) } tdlib_rs::enums::UserStatus::LastWeek(_) => { Some("Был(а) на этой неделе".to_string()) } tdlib_rs::enums::UserStatus::LastMonth(_) => { Some("Был(а) в этом месяце".to_string()) } tdlib_rs::enums::UserStatus::Offline(s) => { // Форматируем время последнего визита Some(format!("Был(а) в сети {}", s.was_online)) } _ => None, }; let username_opt = user .usernames .as_ref() .map(|u| u.editable_username.clone()); (bio_opt, Some(user.phone_number.clone()), username_opt, online_status_str) } _ => (None, None, None, None), } } else { (None, None, None, None) }; // Для групп/каналов получаем полную информацию let (member_count, description, invite_link) = if is_group { if let ChatType::Supergroup(sg) = &chat.r#type { match functions::get_supergroup_full_info(sg.supergroup_id, self.client_id).await { Ok(tdlib_rs::enums::SupergroupFullInfo::SupergroupFullInfo(full_info)) => { let desc = if !full_info.description.is_empty() { Some(full_info.description.clone()) } else { None }; let link = full_info.invite_link.as_ref().map(|l| l.invite_link.clone()); (Some(full_info.member_count), desc, link) } _ => (None, None, None), } } else if let ChatType::BasicGroup(bg) = &chat.r#type { match functions::get_basic_group_full_info(bg.basic_group_id, self.client_id).await { Ok(tdlib_rs::enums::BasicGroupFullInfo::BasicGroupFullInfo(full_info)) => { let desc = if !full_info.description.is_empty() { Some(full_info.description.clone()) } else { None }; let link = full_info.invite_link.map(|l| l.invite_link); (Some(full_info.members.len() as i32), desc, link) } Err(_) => (None, None, None), } } else { (None, None, None) } } else { (None, None, None) }; Ok(ProfileInfo { chat_id, title: chat.title, username, bio, phone_number, chat_type: chat_type_str.to_string(), member_count, description, invite_link, is_group, online_status, }) } /// Отправить typing action pub async fn send_chat_action(&self, chat_id: i64, action: ChatAction) { let _ = functions::send_chat_action(chat_id, 0, Some(action), self.client_id).await; } /// Очистить устаревший typing status (вызывать периодически) pub fn clear_stale_typing_status(&mut self) -> bool { if let Some((_, _, timestamp)) = self.typing_status { if timestamp.elapsed().as_secs() > 5 { self.typing_status = None; return true; } } false } /// Получить текст typing индикатора pub fn get_typing_text(&self) -> Option { self.typing_status .as_ref() .map(|(_, action, _)| action.clone()) } }