Phase 6: Refactor tdlib/client.rs ✅ - Extract update handlers to update_handlers.rs (302 lines, 8 functions) - Extract message converter to message_converter.rs (250 lines, 6 functions) - Extract chat helpers to chat_helpers.rs (149 lines, 3 functions) - Result: client.rs 1259 → 599 lines (-52%) Phase 7: Refactor tdlib/messages.rs ✅ - Create message_conversion.rs module (158 lines) - Extract 6 helper functions: - extract_content_text() - content extraction (~80 lines) - extract_entities() - formatting extraction (~10 lines) - extract_sender_name() - sender name with API call (~15 lines) - extract_forward_info() - forward info (~12 lines) - extract_reply_info() - reply info (~15 lines) - extract_reactions() - reactions extraction (~26 lines) - Result: convert_message() 150 → 57 lines (-62%) - Result: messages.rs 850 → 757 lines (-11%) Summary: - ✅ All 4 large files refactored (100%) - ✅ All 629 tests passing - ✅ Category #2 "Large files/functions" COMPLETE - ✅ Documentation updated (REFACTORING_OPPORTUNITIES.md, CONTEXT.md) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
600 lines
21 KiB
Rust
600 lines
21 KiB
Rust
use crate::types::{ChatId, MessageId, UserId};
|
||
use std::env;
|
||
use tdlib_rs::enums::{
|
||
ChatList, ConnectionState, Update, UserStatus,
|
||
Chat as TdChat
|
||
};
|
||
use tdlib_rs::types::Message as TdMessage;
|
||
use tdlib_rs::functions;
|
||
|
||
|
||
|
||
use super::auth::{AuthManager, AuthState};
|
||
use super::chats::ChatManager;
|
||
use super::messages::MessageManager;
|
||
use super::reactions::ReactionManager;
|
||
use super::types::{ChatInfo, FolderInfo, MessageInfo, NetworkState, ProfileInfo, UserOnlineStatus};
|
||
use super::users::UserCache;
|
||
|
||
/// TDLib client wrapper for Telegram integration.
|
||
///
|
||
/// Provides high-level API for authentication, chat management, messaging,
|
||
/// and user caching. Delegates functionality to specialized managers:
|
||
/// - `AuthManager` for authentication flow
|
||
/// - `ChatManager` for chat operations
|
||
/// - `MessageManager` for message operations
|
||
/// - `UserCache` for user information caching
|
||
/// - `ReactionManager` for message reactions
|
||
///
|
||
/// # Examples
|
||
///
|
||
/// ```ignore
|
||
/// use tele_tui::tdlib::TdClient;
|
||
///
|
||
/// let mut client = TdClient::new();
|
||
///
|
||
/// // Start authorization
|
||
/// client.send_phone_number("+1234567890".to_string()).await?;
|
||
/// client.send_code("12345".to_string()).await?;
|
||
///
|
||
/// // Load chats
|
||
/// client.load_chats(50).await?;
|
||
/// # Ok::<(), String>(())
|
||
/// ```
|
||
pub struct TdClient {
|
||
pub api_id: i32,
|
||
pub api_hash: String,
|
||
client_id: i32,
|
||
|
||
// Менеджеры (делегируем им функциональность)
|
||
pub auth: AuthManager,
|
||
pub chat_manager: ChatManager,
|
||
pub message_manager: MessageManager,
|
||
pub user_cache: UserCache,
|
||
pub reaction_manager: ReactionManager,
|
||
|
||
// Состояние сети
|
||
pub network_state: NetworkState,
|
||
}
|
||
|
||
impl TdClient {
|
||
/// Creates a new TDLib client instance.
|
||
///
|
||
/// Reads API credentials from:
|
||
/// 1. ~/.config/tele-tui/credentials file
|
||
/// 2. Environment variables `API_ID` and `API_HASH` (fallback)
|
||
///
|
||
/// Initializes all managers and sets initial network state to Connecting.
|
||
///
|
||
/// # Returns
|
||
///
|
||
/// A new `TdClient` instance ready for authentication.
|
||
pub fn new() -> Self {
|
||
// Пробуем загрузить credentials из Config (файл или env)
|
||
let (api_id, api_hash) = crate::config::Config::load_credentials()
|
||
.unwrap_or_else(|_| {
|
||
// Fallback на прямое чтение из env (старое поведение)
|
||
let api_id = env::var("API_ID")
|
||
.unwrap_or_else(|_| "0".to_string())
|
||
.parse()
|
||
.unwrap_or(0);
|
||
let api_hash = env::var("API_HASH").unwrap_or_default();
|
||
(api_id, api_hash)
|
||
});
|
||
|
||
let client_id = tdlib_rs::create_client();
|
||
|
||
Self {
|
||
api_id,
|
||
api_hash,
|
||
client_id,
|
||
auth: AuthManager::new(client_id),
|
||
chat_manager: ChatManager::new(client_id),
|
||
message_manager: MessageManager::new(client_id),
|
||
user_cache: UserCache::new(client_id),
|
||
reaction_manager: ReactionManager::new(client_id),
|
||
network_state: NetworkState::Connecting,
|
||
}
|
||
}
|
||
|
||
// Делегирование к auth
|
||
|
||
/// Sends phone number for authentication.
|
||
///
|
||
/// This is the first step of the authentication flow.
|
||
///
|
||
/// # Arguments
|
||
///
|
||
/// * `phone` - Phone number in international format (e.g., "+1234567890")
|
||
///
|
||
/// # Errors
|
||
///
|
||
/// Returns an error if the phone number is invalid or network request fails.
|
||
pub async fn send_phone_number(&self, phone: String) -> Result<(), String> {
|
||
self.auth.send_phone_number(phone).await
|
||
}
|
||
|
||
/// Sends authentication code received via SMS.
|
||
///
|
||
/// This is the second step of the authentication flow.
|
||
///
|
||
/// # Arguments
|
||
///
|
||
/// * `code` - Authentication code (typically 5 digits)
|
||
///
|
||
/// # Errors
|
||
///
|
||
/// Returns an error if the code is invalid or expired.
|
||
pub async fn send_code(&self, code: String) -> Result<(), String> {
|
||
self.auth.send_code(code).await
|
||
}
|
||
|
||
/// Sends 2FA password if required.
|
||
///
|
||
/// This is the third step of the authentication flow (if 2FA is enabled).
|
||
///
|
||
/// # Arguments
|
||
///
|
||
/// * `password` - Two-factor authentication password
|
||
///
|
||
/// # Errors
|
||
///
|
||
/// Returns an error if the password is incorrect.
|
||
pub async fn send_password(&self, password: String) -> Result<(), String> {
|
||
self.auth.send_password(password).await
|
||
}
|
||
|
||
// Делегирование к chat_manager
|
||
|
||
/// Loads chats from the main chat list.
|
||
///
|
||
/// Loads up to `limit` chats from ChatList::Main, excluding archived chats.
|
||
/// Filters out "Deleted Account" chats automatically.
|
||
///
|
||
/// # Arguments
|
||
///
|
||
/// * `limit` - Maximum number of chats to load (typically 50-200)
|
||
///
|
||
/// # Errors
|
||
///
|
||
/// Returns an error if the network request fails.
|
||
pub async fn load_chats(&mut self, limit: i32) -> Result<(), String> {
|
||
self.chat_manager.load_chats(limit).await
|
||
}
|
||
|
||
/// Loads chats from a specific folder.
|
||
///
|
||
/// # Arguments
|
||
///
|
||
/// * `folder_id` - Folder ID (1-9 for user folders)
|
||
/// * `limit` - Maximum number of chats to load
|
||
///
|
||
/// # Errors
|
||
///
|
||
/// Returns an error if the folder doesn't exist or network request fails.
|
||
pub async fn load_folder_chats(&mut self, folder_id: i32, limit: i32) -> Result<(), String> {
|
||
self.chat_manager.load_folder_chats(folder_id, limit).await
|
||
}
|
||
|
||
/// Leaves a group or channel.
|
||
///
|
||
/// # Arguments
|
||
///
|
||
/// * `chat_id` - ID of the chat to leave
|
||
///
|
||
/// # Errors
|
||
///
|
||
/// Returns an error if the user is not a member or network request fails.
|
||
pub async fn leave_chat(&self, chat_id: ChatId) -> Result<(), String> {
|
||
self.chat_manager.leave_chat(chat_id).await
|
||
}
|
||
|
||
/// Gets profile information for a chat.
|
||
///
|
||
/// Fetches detailed information including bio, username, member count, etc.
|
||
///
|
||
/// # Arguments
|
||
///
|
||
/// * `chat_id` - ID of the chat
|
||
///
|
||
/// # Returns
|
||
///
|
||
/// `ProfileInfo` with chat details
|
||
///
|
||
/// # Errors
|
||
///
|
||
/// Returns an error if the chat doesn't exist or network request fails.
|
||
pub async fn get_profile_info(&self, chat_id: ChatId) -> Result<ProfileInfo, String> {
|
||
self.chat_manager.get_profile_info(chat_id).await
|
||
}
|
||
|
||
pub async fn send_chat_action(&self, chat_id: ChatId, action: tdlib_rs::enums::ChatAction) {
|
||
self.chat_manager.send_chat_action(chat_id, action).await
|
||
}
|
||
|
||
pub fn clear_stale_typing_status(&mut self) -> bool {
|
||
self.chat_manager.clear_stale_typing_status()
|
||
}
|
||
|
||
// Делегирование к message_manager
|
||
pub async fn get_chat_history(
|
||
&mut self,
|
||
chat_id: ChatId,
|
||
limit: i32,
|
||
) -> Result<Vec<MessageInfo>, String> {
|
||
self.message_manager.get_chat_history(chat_id, limit).await
|
||
}
|
||
|
||
pub async fn load_older_messages(
|
||
&mut self,
|
||
chat_id: ChatId,
|
||
from_message_id: MessageId,
|
||
) -> Result<Vec<MessageInfo>, String> {
|
||
self.message_manager
|
||
.load_older_messages(chat_id, from_message_id)
|
||
.await
|
||
}
|
||
|
||
pub async fn get_pinned_messages(&mut self, chat_id: ChatId) -> Result<Vec<MessageInfo>, String> {
|
||
self.message_manager.get_pinned_messages(chat_id).await
|
||
}
|
||
|
||
pub async fn load_current_pinned_message(&mut self, chat_id: ChatId) {
|
||
self.message_manager.load_current_pinned_message(chat_id).await
|
||
}
|
||
|
||
pub async fn search_messages(
|
||
&self,
|
||
chat_id: ChatId,
|
||
query: &str,
|
||
) -> Result<Vec<MessageInfo>, String> {
|
||
self.message_manager.search_messages(chat_id, query).await
|
||
}
|
||
|
||
pub async fn send_message(
|
||
&self,
|
||
chat_id: ChatId,
|
||
text: String,
|
||
reply_to_message_id: Option<MessageId>,
|
||
reply_info: Option<super::types::ReplyInfo>,
|
||
) -> Result<MessageInfo, String> {
|
||
self.message_manager
|
||
.send_message(chat_id, text, reply_to_message_id, reply_info)
|
||
.await
|
||
}
|
||
|
||
pub async fn edit_message(
|
||
&self,
|
||
chat_id: ChatId,
|
||
message_id: MessageId,
|
||
text: String,
|
||
) -> Result<MessageInfo, String> {
|
||
self.message_manager
|
||
.edit_message(chat_id, message_id, text)
|
||
.await
|
||
}
|
||
|
||
pub async fn delete_messages(
|
||
&self,
|
||
chat_id: ChatId,
|
||
message_ids: Vec<MessageId>,
|
||
revoke: bool,
|
||
) -> Result<(), String> {
|
||
self.message_manager
|
||
.delete_messages(chat_id, message_ids, revoke)
|
||
.await
|
||
}
|
||
|
||
pub async fn forward_messages(
|
||
&self,
|
||
to_chat_id: ChatId,
|
||
from_chat_id: ChatId,
|
||
message_ids: Vec<MessageId>,
|
||
) -> Result<(), String> {
|
||
self.message_manager
|
||
.forward_messages(to_chat_id, from_chat_id, message_ids)
|
||
.await
|
||
}
|
||
|
||
pub async fn set_draft_message(&self, chat_id: ChatId, text: String) -> Result<(), String> {
|
||
self.message_manager.set_draft_message(chat_id, text).await
|
||
}
|
||
|
||
pub fn push_message(&mut self, msg: MessageInfo) {
|
||
self.message_manager.push_message(msg)
|
||
}
|
||
|
||
pub async fn fetch_missing_reply_info(&mut self) {
|
||
self.message_manager.fetch_missing_reply_info().await
|
||
}
|
||
|
||
pub async fn process_pending_view_messages(&mut self) {
|
||
self.message_manager.process_pending_view_messages().await
|
||
}
|
||
|
||
// Делегирование к user_cache
|
||
pub fn get_user_status_by_chat_id(&self, chat_id: ChatId) -> Option<&UserOnlineStatus> {
|
||
self.user_cache.get_status_by_chat_id(chat_id)
|
||
}
|
||
|
||
pub async fn process_pending_user_ids(&mut self) {
|
||
self.user_cache.process_pending_user_ids().await
|
||
}
|
||
|
||
// Делегирование к reaction_manager
|
||
pub async fn get_message_available_reactions(
|
||
&self,
|
||
chat_id: ChatId,
|
||
message_id: MessageId,
|
||
) -> Result<Vec<String>, String> {
|
||
self.reaction_manager
|
||
.get_message_available_reactions(chat_id, message_id)
|
||
.await
|
||
}
|
||
|
||
pub async fn toggle_reaction(
|
||
&self,
|
||
chat_id: ChatId,
|
||
message_id: MessageId,
|
||
emoji: String,
|
||
) -> Result<(), String> {
|
||
self.reaction_manager
|
||
.toggle_reaction(chat_id, message_id, emoji)
|
||
.await
|
||
}
|
||
|
||
// Вспомогательные методы
|
||
pub fn client_id(&self) -> i32 {
|
||
self.client_id
|
||
}
|
||
|
||
pub async fn get_me(&self) -> Result<i64, String> {
|
||
match functions::get_me(self.client_id).await {
|
||
Ok(tdlib_rs::enums::User::User(user)) => Ok(user.id),
|
||
Err(e) => Err(format!("Ошибка получения текущего пользователя: {:?}", e)),
|
||
}
|
||
}
|
||
|
||
// Accessor methods для обратной совместимости
|
||
pub fn auth_state(&self) -> &AuthState {
|
||
&self.auth.state
|
||
}
|
||
|
||
pub fn chats(&self) -> &[ChatInfo] {
|
||
&self.chat_manager.chats
|
||
}
|
||
|
||
pub fn chats_mut(&mut self) -> &mut Vec<ChatInfo> {
|
||
&mut self.chat_manager.chats
|
||
}
|
||
|
||
pub fn folders(&self) -> &[FolderInfo] {
|
||
&self.chat_manager.folders
|
||
}
|
||
|
||
pub fn folders_mut(&mut self) -> &mut Vec<FolderInfo> {
|
||
&mut self.chat_manager.folders
|
||
}
|
||
|
||
pub fn current_chat_messages(&self) -> &[MessageInfo] {
|
||
&self.message_manager.current_chat_messages
|
||
}
|
||
|
||
pub fn current_chat_messages_mut(&mut self) -> &mut Vec<MessageInfo> {
|
||
&mut self.message_manager.current_chat_messages
|
||
}
|
||
|
||
pub fn current_chat_id(&self) -> Option<ChatId> {
|
||
self.message_manager.current_chat_id
|
||
}
|
||
|
||
pub fn set_current_chat_id(&mut self, chat_id: Option<ChatId>) {
|
||
self.message_manager.current_chat_id = chat_id;
|
||
}
|
||
|
||
pub fn current_pinned_message(&self) -> Option<&MessageInfo> {
|
||
self.message_manager.current_pinned_message.as_ref()
|
||
}
|
||
|
||
pub fn set_current_pinned_message(&mut self, msg: Option<MessageInfo>) {
|
||
self.message_manager.current_pinned_message = msg;
|
||
}
|
||
|
||
pub fn typing_status(&self) -> Option<&(crate::types::UserId, String, std::time::Instant)> {
|
||
self.chat_manager.typing_status.as_ref()
|
||
}
|
||
|
||
pub fn set_typing_status(&mut self, status: Option<(crate::types::UserId, String, std::time::Instant)>) {
|
||
self.chat_manager.typing_status = status;
|
||
}
|
||
|
||
pub fn pending_view_messages(&self) -> &[(crate::types::ChatId, Vec<crate::types::MessageId>)] {
|
||
&self.message_manager.pending_view_messages
|
||
}
|
||
|
||
pub fn pending_view_messages_mut(&mut self) -> &mut Vec<(crate::types::ChatId, Vec<crate::types::MessageId>)> {
|
||
&mut self.message_manager.pending_view_messages
|
||
}
|
||
|
||
pub fn pending_user_ids(&self) -> &[crate::types::UserId] {
|
||
&self.user_cache.pending_user_ids
|
||
}
|
||
|
||
pub fn pending_user_ids_mut(&mut self) -> &mut Vec<crate::types::UserId> {
|
||
&mut self.user_cache.pending_user_ids
|
||
}
|
||
|
||
pub fn main_chat_list_position(&self) -> i32 {
|
||
self.chat_manager.main_chat_list_position
|
||
}
|
||
|
||
pub fn set_main_chat_list_position(&mut self, position: i32) {
|
||
self.chat_manager.main_chat_list_position = position;
|
||
}
|
||
|
||
// User cache accessors
|
||
pub fn user_cache(&self) -> &UserCache {
|
||
&self.user_cache
|
||
}
|
||
|
||
pub fn user_cache_mut(&mut self) -> &mut UserCache {
|
||
&mut self.user_cache
|
||
}
|
||
|
||
// ==================== Helper методы для упрощения обработки updates ====================
|
||
|
||
/// Находит мутабельную ссылку на чат по ID.
|
||
///
|
||
/// Упрощает повторяющийся паттерн `self.chats_mut().iter_mut().find(...)`.
|
||
///
|
||
/// # Arguments
|
||
///
|
||
/// * `chat_id` - ID чата для поиска
|
||
///
|
||
/// # Returns
|
||
///
|
||
/// * `Some(&mut ChatInfo)` - если чат найден
|
||
/// * `None` - если чат не найден
|
||
|
||
/// Обрабатываем одно обновление от TDLib
|
||
pub fn handle_update(&mut self, update: Update) {
|
||
match update {
|
||
Update::AuthorizationState(state) => {
|
||
crate::tdlib::update_handlers::handle_auth_state(self, state.authorization_state);
|
||
}
|
||
Update::NewChat(new_chat) => {
|
||
// new_chat.chat is already a Chat struct, wrap it in TdChat enum
|
||
let td_chat = TdChat::Chat(new_chat.chat.clone());
|
||
crate::tdlib::chat_helpers::add_or_update_chat(self, &td_chat);
|
||
}
|
||
Update::ChatLastMessage(update) => {
|
||
let chat_id = ChatId::new(update.chat_id);
|
||
let (last_message_text, last_message_date) = update
|
||
.last_message
|
||
.as_ref()
|
||
.map(|msg| (Self::extract_message_text_static(msg).0, msg.date))
|
||
.unwrap_or_default();
|
||
|
||
crate::tdlib::chat_helpers::update_chat(self, chat_id, |chat| {
|
||
chat.last_message = last_message_text;
|
||
chat.last_message_date = last_message_date;
|
||
});
|
||
|
||
// Обновляем позиции если они пришли
|
||
for pos in update.positions.iter().filter(|p| matches!(p.list, ChatList::Main)) {
|
||
crate::tdlib::chat_helpers::update_chat(self, chat_id, |chat| {
|
||
chat.order = pos.order;
|
||
chat.is_pinned = pos.is_pinned;
|
||
});
|
||
}
|
||
|
||
// Пересортируем по order
|
||
self.chats_mut().sort_by(|a, b| b.order.cmp(&a.order));
|
||
}
|
||
Update::ChatReadInbox(update) => {
|
||
crate::tdlib::chat_helpers::update_chat(self, ChatId::new(update.chat_id), |chat| {
|
||
chat.unread_count = update.unread_count;
|
||
});
|
||
}
|
||
Update::ChatUnreadMentionCount(update) => {
|
||
crate::tdlib::chat_helpers::update_chat(self, ChatId::new(update.chat_id), |chat| {
|
||
chat.unread_mention_count = update.unread_mention_count;
|
||
});
|
||
}
|
||
Update::ChatNotificationSettings(update) => {
|
||
crate::tdlib::chat_helpers::update_chat(self, ChatId::new(update.chat_id), |chat| {
|
||
// mute_for > 0 означает что чат замьючен
|
||
chat.is_muted = update.notification_settings.mute_for > 0;
|
||
});
|
||
}
|
||
Update::ChatReadOutbox(update) => {
|
||
// Обновляем last_read_outbox_message_id когда собеседник прочитал сообщения
|
||
let last_read_msg_id = MessageId::new(update.last_read_outbox_message_id);
|
||
crate::tdlib::chat_helpers::update_chat(self, ChatId::new(update.chat_id), |chat| {
|
||
chat.last_read_outbox_message_id = last_read_msg_id;
|
||
});
|
||
// Если это текущий открытый чат — обновляем is_read у сообщений
|
||
if Some(ChatId::new(update.chat_id)) == self.current_chat_id() {
|
||
for msg in self.current_chat_messages_mut().iter_mut() {
|
||
if msg.is_outgoing() && msg.id() <= last_read_msg_id {
|
||
msg.state.is_read = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
Update::ChatPosition(update) => {
|
||
crate::tdlib::update_handlers::handle_chat_position_update(self, update);
|
||
}
|
||
Update::NewMessage(new_msg) => {
|
||
crate::tdlib::update_handlers::handle_new_message_update(self, new_msg);
|
||
}
|
||
Update::User(update) => {
|
||
crate::tdlib::update_handlers::handle_user_update(self, update);
|
||
}
|
||
Update::ChatFolders(update) => {
|
||
// Обновляем список папок
|
||
*self.folders_mut() = update
|
||
.chat_folders
|
||
.into_iter()
|
||
.map(|f| FolderInfo { id: f.id, name: f.title })
|
||
.collect();
|
||
self.set_main_chat_list_position(update.main_chat_list_position);
|
||
}
|
||
Update::UserStatus(update) => {
|
||
// Обновляем онлайн-статус пользователя
|
||
let status = match update.status {
|
||
UserStatus::Online(_) => UserOnlineStatus::Online,
|
||
UserStatus::Offline(offline) => UserOnlineStatus::Offline(offline.was_online),
|
||
UserStatus::Recently(_) => UserOnlineStatus::Recently,
|
||
UserStatus::LastWeek(_) => UserOnlineStatus::LastWeek,
|
||
UserStatus::LastMonth(_) => UserOnlineStatus::LastMonth,
|
||
UserStatus::Empty => UserOnlineStatus::LongTimeAgo,
|
||
};
|
||
self.user_cache.user_statuses.insert(UserId::new(update.user_id), status);
|
||
}
|
||
Update::ConnectionState(update) => {
|
||
// Обновляем состояние сетевого соединения
|
||
self.network_state = match update.state {
|
||
ConnectionState::WaitingForNetwork => NetworkState::WaitingForNetwork,
|
||
ConnectionState::ConnectingToProxy => NetworkState::ConnectingToProxy,
|
||
ConnectionState::Connecting => NetworkState::Connecting,
|
||
ConnectionState::Updating => NetworkState::Updating,
|
||
ConnectionState::Ready => NetworkState::Ready,
|
||
};
|
||
}
|
||
Update::ChatAction(update) => {
|
||
crate::tdlib::update_handlers::handle_chat_action_update(self, update);
|
||
}
|
||
Update::ChatDraftMessage(update) => {
|
||
crate::tdlib::update_handlers::handle_chat_draft_message_update(self, update);
|
||
}
|
||
Update::MessageInteractionInfo(update) => {
|
||
crate::tdlib::update_handlers::handle_message_interaction_info_update(self, update);
|
||
}
|
||
Update::MessageSendSucceeded(update) => {
|
||
crate::tdlib::update_handlers::handle_message_send_succeeded_update(self, update);
|
||
}
|
||
_ => {}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
// Helper functions
|
||
pub fn extract_message_text_static(message: &TdMessage) -> (String, Vec<tdlib_rs::types::TextEntity>) {
|
||
use tdlib_rs::enums::MessageContent;
|
||
match &message.content {
|
||
MessageContent::MessageText(text) => (text.text.text.clone(), text.text.entities.clone()),
|
||
_ => (String::new(), Vec::new()),
|
||
}
|
||
}
|
||
|
||
pub fn extract_content_text(content: &tdlib_rs::enums::MessageContent) -> String {
|
||
use tdlib_rs::enums::MessageContent;
|
||
match content {
|
||
MessageContent::MessageText(text) => text.text.text.clone(),
|
||
_ => String::new(),
|
||
}
|
||
}
|
||
}
|