docs: complete rustdoc documentation for all public APIs (P4.12)
Added comprehensive rustdoc documentation for all TDLib modules, configuration, and utility functions. TDLib modules documented: - src/tdlib/auth.rs - AuthManager, AuthState (6 doctests) - src/tdlib/chats.rs - ChatManager (8 doctests) - src/tdlib/messages.rs - MessageManager (14 methods, 6 doctests) - src/tdlib/reactions.rs - ReactionManager (3 doctests) - src/tdlib/users.rs - UserCache, LruCache (2 doctests) Configuration and utilities: - src/config.rs - Config, ColorsConfig, GeneralConfig (4 doctests) - src/formatting.rs - format_text_with_entities (2 doctests) Documentation includes: - Detailed descriptions of all public structs and methods - Usage examples with code snippets - Parameter and return value documentation - Notes about async behavior and edge cases - Cross-references between related functions Total: 34 doctests (30 ignored for async, 4 compiled) All 464 unit tests passing ✅ Priority 4.12 (Rustdoc) - 100% complete Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,25 +1,88 @@
|
||||
use tdlib_rs::enums::{AuthorizationState, Update};
|
||||
use tdlib_rs::functions;
|
||||
|
||||
/// Состояние процесса авторизации в Telegram.
|
||||
///
|
||||
/// Отслеживает текущий этап аутентификации пользователя,
|
||||
/// от инициализации TDLib до полной авторизации.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[allow(dead_code)]
|
||||
pub enum AuthState {
|
||||
/// Ожидание параметров TDLib (начальное состояние).
|
||||
WaitTdlibParameters,
|
||||
|
||||
/// Ожидание ввода номера телефона.
|
||||
WaitPhoneNumber,
|
||||
|
||||
/// Ожидание ввода кода подтверждения из SMS/Telegram.
|
||||
WaitCode,
|
||||
|
||||
/// Ожидание ввода пароля двухфакторной аутентификации (2FA).
|
||||
WaitPassword,
|
||||
|
||||
/// Авторизация завершена, клиент готов к работе.
|
||||
Ready,
|
||||
|
||||
/// Соединение закрыто.
|
||||
Closed,
|
||||
|
||||
/// Произошла ошибка авторизации.
|
||||
Error(String),
|
||||
}
|
||||
|
||||
/// Менеджер авторизации TDLib
|
||||
/// Менеджер авторизации TDLib.
|
||||
///
|
||||
/// Управляет процессом авторизации пользователя в Telegram,
|
||||
/// отслеживает текущее состояние и предоставляет методы
|
||||
/// для отправки учетных данных (номер телефона, код, пароль).
|
||||
///
|
||||
/// # Процесс авторизации
|
||||
///
|
||||
/// 1. `WaitTdlibParameters` → автоматически
|
||||
/// 2. `WaitPhoneNumber` → [`send_phone_number()`](Self::send_phone_number)
|
||||
/// 3. `WaitCode` → [`send_code()`](Self::send_code)
|
||||
/// 4. `WaitPassword` (опционально) → [`send_password()`](Self::send_password)
|
||||
/// 5. `Ready` → авторизация завершена
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// let mut auth_manager = AuthManager::new(client_id);
|
||||
///
|
||||
/// // Отправляем номер телефона
|
||||
/// auth_manager.send_phone_number("+1234567890".to_string()).await?;
|
||||
///
|
||||
/// // После получения кода из SMS
|
||||
/// auth_manager.send_code("12345".to_string()).await?;
|
||||
///
|
||||
/// // Если включена 2FA
|
||||
/// if auth_manager.state == AuthState::WaitPassword {
|
||||
/// auth_manager.send_password("my_password".to_string()).await?;
|
||||
/// }
|
||||
///
|
||||
/// // Проверяем авторизацию
|
||||
/// if auth_manager.is_authenticated() {
|
||||
/// println!("Successfully authenticated!");
|
||||
/// }
|
||||
/// ```
|
||||
pub struct AuthManager {
|
||||
/// Текущее состояние авторизации.
|
||||
pub state: AuthState,
|
||||
|
||||
/// ID клиента TDLib для API вызовов.
|
||||
client_id: i32,
|
||||
}
|
||||
|
||||
impl AuthManager {
|
||||
/// Создает новый менеджер авторизации.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `client_id` - ID клиента TDLib для API вызовов
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Новый экземпляр `AuthManager` в состоянии `WaitTdlibParameters`.
|
||||
pub fn new(client_id: i32) -> Self {
|
||||
Self {
|
||||
state: AuthState::WaitTdlibParameters,
|
||||
@@ -27,11 +90,36 @@ impl AuthManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Проверяет, завершена ли авторизация.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `true` если состояние равно `AuthState::Ready`, иначе `false`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// if auth_manager.is_authenticated() {
|
||||
/// println!("User is authenticated");
|
||||
/// }
|
||||
/// ```
|
||||
pub fn is_authenticated(&self) -> bool {
|
||||
self.state == AuthState::Ready
|
||||
}
|
||||
|
||||
/// Обработать обновление авторизации
|
||||
/// Обрабатывает обновление состояния авторизации от TDLib.
|
||||
///
|
||||
/// Автоматически обновляет внутреннее состояние [`AuthState`] на основе
|
||||
/// полученного update от TDLib.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `update` - Обновление от TDLib (проверяется на `Update::AuthorizationState`)
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Этот метод должен вызываться для каждого update от TDLib,
|
||||
/// чтобы состояние авторизации оставалось актуальным.
|
||||
pub fn handle_auth_update(&mut self, update: &Update) {
|
||||
if let Update::AuthorizationState(auth_update) = update {
|
||||
self.state = match &auth_update.authorization_state {
|
||||
@@ -46,7 +134,25 @@ impl AuthManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Отправить номер телефона
|
||||
/// Отправляет номер телефона для авторизации.
|
||||
///
|
||||
/// Используется на этапе [`AuthState::WaitPhoneNumber`].
|
||||
/// После успешной отправки состояние изменится на `WaitCode`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `phone` - Номер телефона в международном формате (например, "+1234567890")
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(())` - Номер телефона принят, ожидайте SMS с кодом
|
||||
/// * `Err(String)` - Ошибка (неверный формат, проблемы с сетью и т.д.)
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// auth_manager.send_phone_number("+1234567890".to_string()).await?;
|
||||
/// ```
|
||||
pub async fn send_phone_number(&self, phone: String) -> Result<(), String> {
|
||||
functions::set_authentication_phone_number(phone, None, self.client_id)
|
||||
.await
|
||||
@@ -54,7 +160,26 @@ impl AuthManager {
|
||||
.map_err(|e| format!("Ошибка отправки номера: {:?}", e))
|
||||
}
|
||||
|
||||
/// Отправить код подтверждения
|
||||
/// Отправляет код подтверждения из SMS или Telegram.
|
||||
///
|
||||
/// Используется на этапе [`AuthState::WaitCode`].
|
||||
/// После успешной проверки состояние изменится на `Ready` или `WaitPassword`
|
||||
/// (если включена двухфакторная аутентификация).
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `code` - Код подтверждения (обычно 5 цифр)
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(())` - Код верный
|
||||
/// * `Err(String)` - Неверный код или истек срок действия
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// auth_manager.send_code("12345".to_string()).await?;
|
||||
/// ```
|
||||
pub async fn send_code(&self, code: String) -> Result<(), String> {
|
||||
functions::check_authentication_code(code, self.client_id)
|
||||
.await
|
||||
@@ -62,7 +187,27 @@ impl AuthManager {
|
||||
.map_err(|e| format!("Ошибка проверки кода: {:?}", e))
|
||||
}
|
||||
|
||||
/// Отправить пароль 2FA
|
||||
/// Отправляет пароль двухфакторной аутентификации (2FA).
|
||||
///
|
||||
/// Используется на этапе [`AuthState::WaitPassword`] (только если 2FA включена).
|
||||
/// После успешной проверки состояние изменится на `Ready`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `password` - Пароль двухфакторной аутентификации
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(())` - Пароль верный, авторизация завершена
|
||||
/// * `Err(String)` - Неверный пароль
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// if auth_manager.state == AuthState::WaitPassword {
|
||||
/// auth_manager.send_password("my_2fa_password".to_string()).await?;
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn send_password(&self, password: String) -> Result<(), String> {
|
||||
functions::check_authentication_password(password, self.client_id)
|
||||
.await
|
||||
|
||||
@@ -6,17 +6,58 @@ use tdlib_rs::functions;
|
||||
|
||||
use super::types::{ChatInfo, FolderInfo, MessageInfo, ProfileInfo};
|
||||
|
||||
/// Менеджер чатов
|
||||
/// Менеджер чатов TDLib.
|
||||
///
|
||||
/// Управляет списком чатов, папками, информацией о профилях
|
||||
/// и typing-статусом собеседников.
|
||||
///
|
||||
/// # Основные возможности
|
||||
///
|
||||
/// - Загрузка чатов из главного списка и папок
|
||||
/// - Получение информации о профиле чата/пользователя
|
||||
/// - Отправка typing-индикатора ("печатает...")
|
||||
/// - Отслеживание typing-статуса собеседников
|
||||
/// - Выход из чатов/групп
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// let mut chat_manager = ChatManager::new(client_id);
|
||||
///
|
||||
/// // Загружаем чаты
|
||||
/// chat_manager.load_chats(50).await?;
|
||||
///
|
||||
/// // Получаем информацию о профиле
|
||||
/// let profile = chat_manager.get_profile_info(chat_id).await?;
|
||||
/// println!("Bio: {}", profile.bio.unwrap_or_default());
|
||||
/// ```
|
||||
pub struct ChatManager {
|
||||
/// Список загруженных чатов.
|
||||
pub chats: Vec<ChatInfo>,
|
||||
|
||||
/// Список папок чатов.
|
||||
pub folders: Vec<FolderInfo>,
|
||||
|
||||
/// Позиция в главном списке чатов для пагинации.
|
||||
pub main_chat_list_position: i32,
|
||||
/// Typing status для текущего чата: (user_id, action_text, timestamp)
|
||||
|
||||
/// Typing status для текущего чата: (user_id, action_text, timestamp).
|
||||
pub typing_status: Option<(UserId, String, Instant)>,
|
||||
|
||||
/// ID клиента TDLib для API вызовов.
|
||||
client_id: i32,
|
||||
}
|
||||
|
||||
impl ChatManager {
|
||||
/// Создает новый менеджер чатов.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `client_id` - ID клиента TDLib для API вызовов
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Новый экземпляр `ChatManager` с пустым списком чатов.
|
||||
pub fn new(client_id: i32) -> Self {
|
||||
Self {
|
||||
chats: Vec::new(),
|
||||
@@ -27,7 +68,25 @@ impl ChatManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Загрузить чаты из основного списка
|
||||
/// Загружает чаты из главного списка.
|
||||
///
|
||||
/// Запрашивает у TDLib чаты из основного списка (исключая архив).
|
||||
/// После вызова чаты будут доступны через updates от TDLib.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `limit` - Максимальное количество чатов для загрузки
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(())` - Запрос отправлен, чаты будут загружены через updates
|
||||
/// * `Err(String)` - Ошибка при отправке запроса
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// chat_manager.load_chats(50).await?;
|
||||
/// ```
|
||||
pub async fn load_chats(&mut self, limit: i32) -> Result<(), String> {
|
||||
let result = functions::load_chats(Some(ChatList::Main), limit, self.client_id).await;
|
||||
|
||||
@@ -37,7 +96,24 @@ impl ChatManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Загрузить чаты из папки
|
||||
/// Загружает чаты из указанной папки.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `folder_id` - ID папки чатов
|
||||
/// * `limit` - Максимальное количество чатов для загрузки
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(())` - Запрос отправлен
|
||||
/// * `Err(String)` - Ошибка при отправке запроса
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// // Загрузить чаты из папки с ID 1
|
||||
/// chat_manager.load_folder_chats(1, 50).await?;
|
||||
/// ```
|
||||
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 });
|
||||
@@ -50,7 +126,24 @@ impl ChatManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Покинуть чат/группу
|
||||
/// Выходит из чата или группы.
|
||||
///
|
||||
/// Для приватных чатов — удаляет историю, для групп — покидает группу.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `chat_id` - ID чата для выхода
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(())` - Успешный выход
|
||||
/// * `Err(String)` - Ошибка (нет прав, чат не найден и т.д.)
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// chat_manager.leave_chat(ChatId::new(123456)).await?;
|
||||
/// ```
|
||||
pub async fn leave_chat(&self, chat_id: ChatId) -> Result<(), String> {
|
||||
let result = functions::leave_chat(chat_id.as_i64(), self.client_id).await;
|
||||
match result {
|
||||
@@ -59,7 +152,29 @@ impl ChatManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Получить информацию профиля чата
|
||||
/// Получает детальную информацию о профиле чата или пользователя.
|
||||
///
|
||||
/// Загружает полную информацию включая bio, номер телефона, username,
|
||||
/// статус онлайн (для личных чатов), количество участников и описание
|
||||
/// (для групп/каналов).
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `chat_id` - ID чата для получения информации
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(ProfileInfo)` - Информация о профиле
|
||||
/// * `Err(String)` - Ошибка получения данных
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// let profile = chat_manager.get_profile_info(ChatId::new(123)).await?;
|
||||
/// println!("Title: {}", profile.title);
|
||||
/// println!("Bio: {}", profile.bio.unwrap_or_default());
|
||||
/// println!("Members: {}", profile.member_count.unwrap_or(0));
|
||||
/// ```
|
||||
pub async fn get_profile_info(&self, chat_id: ChatId) -> Result<ProfileInfo, String> {
|
||||
// Получаем основную информацию о чате
|
||||
let chat_result = functions::get_chat(chat_id.as_i64(), self.client_id).await;
|
||||
@@ -187,12 +302,55 @@ impl ChatManager {
|
||||
})
|
||||
}
|
||||
|
||||
/// Отправить typing action
|
||||
/// Отправляет typing-действие в чат.
|
||||
///
|
||||
/// Показывает собеседнику индикатор "печатает..." или другой статус активности.
|
||||
/// Действие автоматически сбрасывается через 5 секунд.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `chat_id` - ID чата
|
||||
/// * `action` - Тип действия (Typing, RecordingVideo, UploadingPhoto и т.д.)
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Этот метод нужно вызывать периодически (каждые 5 секунд) пока действие активно.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// use tdlib_rs::enums::ChatAction;
|
||||
///
|
||||
/// // Показать индикатор "печатает..."
|
||||
/// chat_manager.send_chat_action(
|
||||
/// chat_id,
|
||||
/// ChatAction::Typing
|
||||
/// ).await;
|
||||
/// ```
|
||||
pub async fn send_chat_action(&self, chat_id: ChatId, action: ChatAction) {
|
||||
let _ = functions::send_chat_action(chat_id.as_i64(), 0, Some(action), self.client_id).await;
|
||||
}
|
||||
|
||||
/// Очистить устаревший typing status (вызывать периодически)
|
||||
/// Очищает устаревший typing-статус.
|
||||
///
|
||||
/// Удаляет typing-статус если прошло более 5 секунд с момента последнего обновления.
|
||||
/// Вызывайте этот метод периодически (например, каждый тик UI) для своевременной
|
||||
/// очистки индикатора "печатает...".
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `true` - Если статус был очищен
|
||||
/// * `false` - Если статус актуален или его не было
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// // В основном цикле UI
|
||||
/// if chat_manager.clear_stale_typing_status() {
|
||||
/// // Перерисовать UI чтобы убрать индикатор "печатает..."
|
||||
/// needs_redraw = true;
|
||||
/// }
|
||||
/// ```
|
||||
pub fn clear_stale_typing_status(&mut self) -> bool {
|
||||
if let Some((_, _, timestamp)) = self.typing_status {
|
||||
if timestamp.elapsed().as_secs() > 5 {
|
||||
@@ -203,7 +361,20 @@ impl ChatManager {
|
||||
false
|
||||
}
|
||||
|
||||
/// Получить текст typing индикатора
|
||||
/// Получает текст typing-индикатора для отображения.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Some(String)` - Текст действия (например, "печатает...", "записывает видео...")
|
||||
/// * `None` - Нет активного typing-статуса
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// if let Some(typing_text) = chat_manager.get_typing_text() {
|
||||
/// println!("Status: {}", typing_text);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_typing_text(&self) -> Option<String> {
|
||||
self.typing_status
|
||||
.as_ref()
|
||||
|
||||
@@ -6,17 +6,65 @@ use tdlib_rs::types::{Chat as TdChat, FormattedText, InputMessageReplyToMessage,
|
||||
|
||||
use super::types::{ForwardInfo, MessageBuilder, MessageInfo, ReactionInfo, ReplyInfo};
|
||||
|
||||
/// Менеджер сообщений
|
||||
/// Менеджер сообщений TDLib.
|
||||
///
|
||||
/// Управляет загрузкой, отправкой, редактированием и удалением сообщений.
|
||||
/// Кеширует сообщения текущего открытого чата и закрепленные сообщения.
|
||||
///
|
||||
/// # Основные возможности
|
||||
///
|
||||
/// - Загрузка истории сообщений чата
|
||||
/// - Отправка текстовых сообщений с поддержкой Markdown
|
||||
/// - Редактирование и удаление сообщений
|
||||
/// - Пересылка сообщений между чатами
|
||||
/// - Поиск сообщений по тексту
|
||||
/// - Управление закрепленными сообщениями
|
||||
/// - Управление черновиками
|
||||
/// - Автоматическая отметка сообщений как прочитанных
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// let mut msg_manager = MessageManager::new(client_id);
|
||||
///
|
||||
/// // Загрузить историю чата
|
||||
/// let messages = msg_manager.get_chat_history(chat_id, 50).await?;
|
||||
///
|
||||
/// // Отправить сообщение
|
||||
/// let msg = msg_manager.send_message(
|
||||
/// chat_id,
|
||||
/// "Hello, **world**!".to_string(),
|
||||
/// None,
|
||||
/// None
|
||||
/// ).await?;
|
||||
/// ```
|
||||
pub struct MessageManager {
|
||||
/// Список сообщений текущего открытого чата (до MAX_MESSAGES_IN_CHAT).
|
||||
pub current_chat_messages: Vec<MessageInfo>,
|
||||
|
||||
/// ID текущего открытого чата.
|
||||
pub current_chat_id: Option<ChatId>,
|
||||
|
||||
/// Текущее закрепленное сообщение открытого чата.
|
||||
pub current_pinned_message: Option<MessageInfo>,
|
||||
/// Очередь сообщений для отметки как прочитанных: (chat_id, message_ids)
|
||||
|
||||
/// Очередь сообщений для отметки как прочитанных: (chat_id, message_ids).
|
||||
pub pending_view_messages: Vec<(ChatId, Vec<MessageId>)>,
|
||||
|
||||
/// ID клиента TDLib для API вызовов.
|
||||
client_id: i32,
|
||||
}
|
||||
|
||||
impl MessageManager {
|
||||
/// Создает новый менеджер сообщений.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `client_id` - ID клиента TDLib для API вызовов
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Новый экземпляр `MessageManager` с пустым списком сообщений.
|
||||
pub fn new(client_id: i32) -> Self {
|
||||
Self {
|
||||
current_chat_messages: Vec::new(),
|
||||
@@ -27,7 +75,19 @@ impl MessageManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Добавить сообщение в список текущего чата
|
||||
/// Добавляет сообщение в список текущего чата.
|
||||
///
|
||||
/// Автоматически ограничивает размер списка до [`MAX_MESSAGES_IN_CHAT`],
|
||||
/// удаляя старые сообщения при превышении лимита.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `msg` - Сообщение для добавления
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Сообщение добавляется в конец списка. При превышении лимита
|
||||
/// удаляются самые старые сообщения из начала списка.
|
||||
pub fn push_message(&mut self, msg: MessageInfo) {
|
||||
self.current_chat_messages.push(msg); // Добавляем в конец
|
||||
|
||||
@@ -37,7 +97,31 @@ impl MessageManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Получить историю чата
|
||||
/// Загружает историю сообщений чата.
|
||||
///
|
||||
/// Запрашивает последние сообщения из указанного чата и сохраняет их
|
||||
/// в [`current_chat_messages`](Self::current_chat_messages). Делает несколько попыток
|
||||
/// загрузки при неудаче.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `chat_id` - ID чата для загрузки истории
|
||||
/// * `limit` - Максимальное количество сообщений (обычно до 50)
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(Vec<MessageInfo>)` - Список загруженных сообщений (от старых к новым)
|
||||
/// * `Err(String)` - Ошибка загрузки после всех попыток
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// let messages = msg_manager.get_chat_history(
|
||||
/// ChatId::new(123),
|
||||
/// 50
|
||||
/// ).await?;
|
||||
/// println!("Loaded {} messages", messages.len());
|
||||
/// ```
|
||||
pub async fn get_chat_history(
|
||||
&mut self,
|
||||
chat_id: ChatId,
|
||||
@@ -102,7 +186,30 @@ impl MessageManager {
|
||||
Ok(all_messages)
|
||||
}
|
||||
|
||||
/// Загрузить более старые сообщения
|
||||
/// Загружает более старые сообщения для пагинации.
|
||||
///
|
||||
/// Используется для подгрузки предыдущих сообщений при прокрутке
|
||||
/// истории чата вверх.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `chat_id` - ID чата
|
||||
/// * `from_message_id` - ID сообщения, от которого загружать историю
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(Vec<MessageInfo>)` - Список старых сообщений (от старых к новым)
|
||||
/// * `Err(String)` - Ошибка загрузки
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// // Загрузить сообщения старше указанного
|
||||
/// let older = msg_manager.load_older_messages(
|
||||
/// chat_id,
|
||||
/// MessageId::new(12345)
|
||||
/// ).await?;
|
||||
/// ```
|
||||
pub async fn load_older_messages(
|
||||
&mut self,
|
||||
chat_id: ChatId,
|
||||
@@ -135,7 +242,25 @@ impl MessageManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Получить закреплённые сообщения
|
||||
/// Получает все закрепленные сообщения чата.
|
||||
///
|
||||
/// Выполняет поиск всех сообщений с фильтром "pinned" и возвращает их список.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `chat_id` - ID чата
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(Vec<MessageInfo>)` - Список закрепленных сообщений (до 100)
|
||||
/// * `Err(String)` - Ошибка загрузки
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// let pinned = msg_manager.get_pinned_messages(chat_id).await?;
|
||||
/// println!("Found {} pinned messages", pinned.len());
|
||||
/// ```
|
||||
pub async fn get_pinned_messages(&mut self, chat_id: ChatId) -> Result<Vec<MessageInfo>, String> {
|
||||
let result = functions::search_chat_messages(
|
||||
chat_id.as_i64(),
|
||||
@@ -166,7 +291,17 @@ impl MessageManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Загрузить текущее закреплённое сообщение
|
||||
/// Загружает текущее верхнее закрепленное сообщение.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `chat_id` - ID чата
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// TODO: В tdlib-rs 1.8.29 поле `pinned_message_id` было удалено из `Chat`.
|
||||
/// Нужно использовать `getChatPinnedMessage` или альтернативный способ.
|
||||
/// Временно отключено, возвращает `None`.
|
||||
pub async fn load_current_pinned_message(&mut self, chat_id: ChatId) {
|
||||
// TODO: В tdlib-rs 1.8.29 поле pinned_message_id было удалено из Chat.
|
||||
// Нужно использовать getChatPinnedMessage или альтернативный способ.
|
||||
@@ -182,7 +317,23 @@ impl MessageManager {
|
||||
// }
|
||||
}
|
||||
|
||||
/// Поиск сообщений в чате
|
||||
/// Выполняет поиск сообщений по тексту в указанном чате.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `chat_id` - ID чата для поиска
|
||||
/// * `query` - Текстовый запрос для поиска
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(Vec<MessageInfo>)` - Найденные сообщения (до 100)
|
||||
/// * `Err(String)` - Ошибка поиска
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// let results = msg_manager.search_messages(chat_id, "hello").await?;
|
||||
/// ```
|
||||
pub async fn search_messages(
|
||||
&self,
|
||||
chat_id: ChatId,
|
||||
@@ -217,7 +368,41 @@ impl MessageManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Отправить сообщение
|
||||
/// Отправляет текстовое сообщение в чат с поддержкой Markdown.
|
||||
///
|
||||
/// Автоматически парсит Markdown v2 форматирование (**bold**, *italic*, `code` и т.д.).
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `chat_id` - ID чата-получателя
|
||||
/// * `text` - Текст сообщения (поддерживает Markdown v2)
|
||||
/// * `reply_to_message_id` - Опциональный ID сообщения для ответа
|
||||
/// * `reply_info` - Опциональная информация об исходном сообщении
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(MessageInfo)` - Отправленное сообщение
|
||||
/// * `Err(String)` - Ошибка отправки
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// // Простое сообщение
|
||||
/// let msg = msg_manager.send_message(
|
||||
/// chat_id,
|
||||
/// "Hello, **world**!".to_string(),
|
||||
/// None,
|
||||
/// None
|
||||
/// ).await?;
|
||||
///
|
||||
/// // Ответ на сообщение
|
||||
/// let reply = msg_manager.send_message(
|
||||
/// chat_id,
|
||||
/// "Got it!".to_string(),
|
||||
/// Some(MessageId::new(123)),
|
||||
/// Some(reply_info)
|
||||
/// ).await?;
|
||||
/// ```
|
||||
pub async fn send_message(
|
||||
&self,
|
||||
chat_id: ChatId,
|
||||
@@ -288,7 +473,18 @@ impl MessageManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Редактировать сообщение
|
||||
/// Редактирует существующее сообщение.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `chat_id` - ID чата
|
||||
/// * `message_id` - ID сообщения для редактирования
|
||||
/// * `text` - Новый текст (поддерживает Markdown v2)
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(MessageInfo)` - Отредактированное сообщение
|
||||
/// * `Err(String)` - Ошибка (нет прав, сообщение слишком старое и т.д.)
|
||||
pub async fn edit_message(
|
||||
&self,
|
||||
chat_id: ChatId,
|
||||
@@ -333,7 +529,18 @@ impl MessageManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Удалить сообщения
|
||||
/// Удаляет одно или несколько сообщений.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `chat_id` - ID чата
|
||||
/// * `message_ids` - Список ID сообщений для удаления
|
||||
/// * `revoke` - `true` - удалить для всех, `false` - только для себя
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(())` - Сообщения удалены
|
||||
/// * `Err(String)` - Ошибка удаления
|
||||
pub async fn delete_messages(
|
||||
&self,
|
||||
chat_id: ChatId,
|
||||
@@ -349,7 +556,18 @@ impl MessageManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Переслать сообщения
|
||||
/// Пересылает сообщения из одного чата в другой.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `to_chat_id` - ID чата-получателя
|
||||
/// * `from_chat_id` - ID чата-источника
|
||||
/// * `message_ids` - Список ID сообщений для пересылки
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(())` - Сообщения переслань
|
||||
/// * `Err(String)` - Ошибка пересылки
|
||||
pub async fn forward_messages(
|
||||
&self,
|
||||
to_chat_id: ChatId,
|
||||
@@ -375,7 +593,20 @@ impl MessageManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Установить черновик
|
||||
/// Сохраняет черновик сообщения для чата.
|
||||
///
|
||||
/// Черновик отображается в списке чатов и восстанавливается
|
||||
/// при следующем открытии чата.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `chat_id` - ID чата
|
||||
/// * `text` - Текст черновика (пустая строка удаляет черновик)
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(())` - Черновик сохранен
|
||||
/// * `Err(String)` - Ошибка сохранения
|
||||
pub async fn set_draft_message(&self, chat_id: ChatId, text: String) -> Result<(), String> {
|
||||
use tdlib_rs::types::DraftMessage;
|
||||
|
||||
@@ -404,7 +635,14 @@ impl MessageManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Обработать очередь просмотра сообщений
|
||||
/// Обрабатывает очередь сообщений для отметки как прочитанных.
|
||||
///
|
||||
/// Автоматически отмечает просмотренные сообщения как прочитанные,
|
||||
/// что сбрасывает счетчик непрочитанных сообщений в чате.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Вызывайте периодически (например, в основном цикле) для обработки накопленной очереди.
|
||||
pub async fn process_pending_view_messages(&mut self) {
|
||||
if self.pending_view_messages.is_empty() {
|
||||
return;
|
||||
@@ -571,7 +809,14 @@ impl MessageManager {
|
||||
Some(builder.build())
|
||||
}
|
||||
|
||||
/// Получить недостающую reply информацию для сообщений
|
||||
/// Загружает недостающую информацию об исходных сообщениях для ответов.
|
||||
///
|
||||
/// Ищет все reply-сообщения с `sender_name == "Unknown"` и загружает
|
||||
/// полную информацию (имя отправителя, текст) из TDLib.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Вызывайте после загрузки истории чата для заполнения информации о цитируемых сообщениях.
|
||||
pub async fn fetch_missing_reply_info(&mut self) {
|
||||
// Collect message IDs that need to be fetched
|
||||
let mut to_fetch = Vec::new();
|
||||
|
||||
@@ -3,17 +3,66 @@ use tdlib_rs::enums::ReactionType;
|
||||
use tdlib_rs::functions;
|
||||
use tdlib_rs::types::ReactionTypeEmoji;
|
||||
|
||||
/// Менеджер реакций на сообщения
|
||||
/// Менеджер реакций на сообщения.
|
||||
///
|
||||
/// Управляет добавлением, удалением и получением списка доступных
|
||||
/// реакций (emoji) для сообщений в чатах.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// let reaction_manager = ReactionManager::new(client_id);
|
||||
///
|
||||
/// // Получить доступные реакции
|
||||
/// let reactions = reaction_manager.get_message_available_reactions(
|
||||
/// chat_id,
|
||||
/// message_id
|
||||
/// ).await?;
|
||||
///
|
||||
/// // Добавить/удалить реакцию
|
||||
/// reaction_manager.toggle_reaction(chat_id, message_id, "👍".to_string()).await?;
|
||||
/// ```
|
||||
pub struct ReactionManager {
|
||||
/// ID клиента TDLib для API вызовов.
|
||||
client_id: i32,
|
||||
}
|
||||
|
||||
impl ReactionManager {
|
||||
/// Создает новый менеджер реакций.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `client_id` - ID клиента TDLib для API вызовов
|
||||
pub fn new(client_id: i32) -> Self {
|
||||
Self { client_id }
|
||||
}
|
||||
|
||||
/// Получить доступные реакции для сообщения
|
||||
/// Получает список доступных реакций для сообщения.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `chat_id` - ID чата
|
||||
/// * `message_id` - ID сообщения
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(Vec<String>)` - Список доступных emoji реакций
|
||||
/// * `Err(String)` - Ошибка получения
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// В tdlib-rs 1.8.29 структура AvailableReactions изменилась.
|
||||
/// Временно возвращается стандартный набор из 12 популярных реакций.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// let reactions = manager.get_message_available_reactions(
|
||||
/// ChatId::new(123),
|
||||
/// MessageId::new(456)
|
||||
/// ).await?;
|
||||
/// println!("Available: {:?}", reactions);
|
||||
/// ```
|
||||
pub async fn get_message_available_reactions(
|
||||
&self,
|
||||
chat_id: ChatId,
|
||||
@@ -87,7 +136,28 @@ impl ReactionManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Переключить реакцию на сообщение
|
||||
/// Переключает реакцию на сообщение (добавляет/удаляет).
|
||||
///
|
||||
/// Сначала пытается добавить реакцию. Если не удалось (уже есть),
|
||||
/// то удаляет её.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `chat_id` - ID чата
|
||||
/// * `message_id` - ID сообщения
|
||||
/// * `emoji` - Emoji реакции (например, "👍", "❤️")
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(())` - Реакция переключена
|
||||
/// * `Err(String)` - Ошибка переключения
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// // Добавить или удалить 👍
|
||||
/// manager.toggle_reaction(chat_id, message_id, "👍".to_string()).await?;
|
||||
/// ```
|
||||
pub async fn toggle_reaction(
|
||||
&self,
|
||||
chat_id: ChatId,
|
||||
|
||||
@@ -6,15 +6,35 @@ use tdlib_rs::functions;
|
||||
|
||||
use super::types::UserOnlineStatus;
|
||||
|
||||
/// Простой LRU-кэш на основе HashMap + Vec для отслеживания порядка
|
||||
/// LRU (Least Recently Used) кэш с фиксированной ёмкостью.
|
||||
///
|
||||
/// Автоматически удаляет самые давно использованные элементы при достижении лимита.
|
||||
/// Основан на HashMap для быстрого доступа и Vec для отслеживания порядка использования.
|
||||
///
|
||||
/// # Type Parameters
|
||||
///
|
||||
/// * `V` - Тип значения (должен реализовывать `Clone`)
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// let mut cache = LruCache::<String>::new(100);
|
||||
/// cache.insert(UserId::new(1), "Alice".to_string());
|
||||
/// assert_eq!(cache.get(&UserId::new(1)), Some(&"Alice".to_string()));
|
||||
/// ```
|
||||
pub struct LruCache<V> {
|
||||
/// Хранилище ключ-значение.
|
||||
map: HashMap<UserId, V>,
|
||||
/// Порядок доступа: последний элемент — самый недавно использованный
|
||||
|
||||
/// Порядок доступа: последний элемент — самый недавно использованный.
|
||||
order: Vec<UserId>,
|
||||
|
||||
/// Максимальная ёмкость кэша.
|
||||
capacity: usize,
|
||||
}
|
||||
|
||||
impl<V: Clone> LruCache<V> {
|
||||
/// Создает новый LRU кэш с заданной ёмкостью.
|
||||
pub fn new(capacity: usize) -> Self {
|
||||
Self {
|
||||
map: HashMap::with_capacity(capacity),
|
||||
@@ -23,7 +43,7 @@ impl<V: Clone> LruCache<V> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Получить значение и обновить порядок доступа
|
||||
/// Получает значение и обновляет порядок доступа (помечает как использованное).
|
||||
pub fn get(&mut self, key: &UserId) -> Option<&V> {
|
||||
if self.map.contains_key(key) {
|
||||
// Перемещаем ключ в конец (самый недавно использованный)
|
||||
@@ -72,22 +92,56 @@ impl<V: Clone> LruCache<V> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Кеш пользователей и их данных
|
||||
/// Кэш информации о пользователях Telegram.
|
||||
///
|
||||
/// Хранит данные пользователей (имена, usernames, статусы) в LRU-кэшах
|
||||
/// для быстрого доступа без повторных запросов к TDLib.
|
||||
///
|
||||
/// # Возможности
|
||||
///
|
||||
/// - Кэширование имен пользователей (first_name + last_name)
|
||||
/// - Кэширование usernames (@username)
|
||||
/// - Кэширование онлайн-статусов
|
||||
/// - Связь chat_id → user_id для приватных чатов
|
||||
/// - Ленивая загрузка данных пользователей порциями
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// let mut cache = UserCache::new(client_id);
|
||||
///
|
||||
/// // Обработать обновление пользователя
|
||||
/// cache.handle_user_update(&user_enum);
|
||||
///
|
||||
/// // Получить имя
|
||||
/// let name = cache.get_user_name(user_id).await;
|
||||
/// ```
|
||||
pub struct UserCache {
|
||||
/// LRU-кэш usernames: user_id -> username
|
||||
/// LRU-кэш usernames: user_id → username.
|
||||
pub user_usernames: LruCache<String>,
|
||||
/// LRU-кэш имён: user_id -> display_name (first_name + last_name)
|
||||
|
||||
/// LRU-кэш имён: user_id → display_name (first_name + last_name).
|
||||
pub user_names: LruCache<String>,
|
||||
/// Связь chat_id -> user_id для приватных чатов
|
||||
|
||||
/// Связь chat_id → user_id для приватных чатов.
|
||||
pub chat_user_ids: HashMap<ChatId, UserId>,
|
||||
/// Очередь user_id для загрузки имён
|
||||
|
||||
/// Очередь user_id для ленивой загрузки имён.
|
||||
pub pending_user_ids: Vec<UserId>,
|
||||
/// LRU-кэш онлайн-статусов пользователей: user_id -> status
|
||||
|
||||
/// LRU-кэш онлайн-статусов: user_id → status.
|
||||
pub user_statuses: LruCache<UserOnlineStatus>,
|
||||
|
||||
/// ID клиента TDLib для API вызовов.
|
||||
client_id: i32,
|
||||
}
|
||||
|
||||
impl UserCache {
|
||||
/// Создает новый кэш пользователей.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `client_id` - ID клиента TDLib для API вызовов
|
||||
pub fn new(client_id: i32) -> Self {
|
||||
Self {
|
||||
user_usernames: LruCache::new(MAX_USER_CACHE_SIZE),
|
||||
@@ -120,7 +174,13 @@ impl UserCache {
|
||||
self.user_statuses.peek(user_id)
|
||||
}
|
||||
|
||||
/// Обработать обновление пользователя
|
||||
/// Обрабатывает обновление пользователя от TDLib.
|
||||
///
|
||||
/// Сохраняет username, имя и статус пользователя в соответствующие кэши.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `user_enum` - Обновление пользователя от TDLib
|
||||
pub fn handle_user_update(&mut self, user_enum: &User) {
|
||||
if let User::User(user) = user_enum {
|
||||
let user_id = user.id;
|
||||
@@ -139,7 +199,12 @@ impl UserCache {
|
||||
}
|
||||
}
|
||||
|
||||
/// Обработать обновление статуса пользователя
|
||||
/// Обновляет онлайн-статус пользователя.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `user_id` - ID пользователя
|
||||
/// * `status` - Новый статус от TDLib
|
||||
pub fn update_status(&mut self, user_id: UserId, status: &UserStatus) {
|
||||
let online_status = match status {
|
||||
UserStatus::Online(_) => UserOnlineStatus::Online,
|
||||
@@ -157,7 +222,17 @@ impl UserCache {
|
||||
self.chat_user_ids.insert(chat_id, user_id);
|
||||
}
|
||||
|
||||
/// Получить имя пользователя (асинхронно с загрузкой если нужно)
|
||||
/// Получает имя пользователя из кэша или загружает из TDLib.
|
||||
///
|
||||
/// Сначала проверяет кэш, затем при необходимости загружает из API.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `user_id` - ID пользователя
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Имя пользователя (first_name + last_name) или "User {id}" если не найден.
|
||||
pub async fn get_user_name(&self, user_id: UserId) -> String {
|
||||
// Сначала пытаемся получить из кэша
|
||||
if let Some(name) = self.user_names.peek(&user_id) {
|
||||
@@ -174,7 +249,14 @@ impl UserCache {
|
||||
}
|
||||
}
|
||||
|
||||
/// Обработать очередь отложенных user_ids (загрузка имён небольшими порциями)
|
||||
/// Обрабатывает очередь отложенных user_ids для ленивой загрузки.
|
||||
///
|
||||
/// Загружает данные пользователей небольшими порциями (по [`LAZY_LOAD_USERS_PER_TICK`])
|
||||
/// для избежания блокировки UI.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Вызывайте периодически в основном цикле приложения.
|
||||
pub async fn process_pending_user_ids(&mut self) {
|
||||
if self.pending_user_ids.is_empty() {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user