This commit is contained in:
Mikhail Kilin
2026-01-30 23:55:01 +03:00
parent 433233d766
commit bba5cbd22d
25 changed files with 5896 additions and 1469 deletions

211
src/tdlib/chats.rs Normal file
View File

@@ -0,0 +1,211 @@
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<ChatInfo>,
pub folders: Vec<FolderInfo>,
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<ProfileInfo, String> {
// Получаем основную информацию о чате
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<String> {
self.typing_status
.as_ref()
.map(|(_, action, _)| action.clone())
}
}