commit
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
use std::env;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::time::Instant;
|
||||
use tdlib_rs::enums::{AuthorizationState, ChatAction, ChatList, ChatType, ConnectionState, MessageContent, MessageSender, SearchMessagesFilter, Update, User, UserStatus};
|
||||
use tdlib_rs::enums::{
|
||||
AuthorizationState, ChatAction, ChatList, ChatType, ConnectionState, MessageContent,
|
||||
MessageSender, SearchMessagesFilter, Update, User, UserStatus,
|
||||
};
|
||||
use tdlib_rs::types::TextEntity;
|
||||
|
||||
/// Максимальный размер кэшей пользователей
|
||||
@@ -311,7 +314,11 @@ impl TdClient {
|
||||
/// Если сообщение с таким id уже есть — заменяет его (сохраняя reply_to)
|
||||
pub fn push_message(&mut self, msg: MessageInfo) {
|
||||
// Проверяем, есть ли уже сообщение с таким id
|
||||
if let Some(idx) = self.current_chat_messages.iter().position(|m| m.id == msg.id) {
|
||||
if let Some(idx) = self
|
||||
.current_chat_messages
|
||||
.iter()
|
||||
.position(|m| m.id == msg.id)
|
||||
{
|
||||
// Если новое сообщение имеет reply_to, или старое не имеет — заменяем
|
||||
if msg.reply_to.is_some() || self.current_chat_messages[idx].reply_to.is_none() {
|
||||
self.current_chat_messages[idx] = msg;
|
||||
@@ -350,7 +357,8 @@ impl TdClient {
|
||||
/// Например: "Вася печатает..."
|
||||
pub fn get_typing_text(&self) -> Option<String> {
|
||||
self.typing_status.as_ref().map(|(user_id, action, _)| {
|
||||
let name = self.user_names
|
||||
let name = self
|
||||
.user_names
|
||||
.peek(user_id)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| "Кто-то".to_string());
|
||||
@@ -361,20 +369,20 @@ impl TdClient {
|
||||
/// Инициализация TDLib с параметрами
|
||||
pub async fn init(&mut self) -> Result<(), String> {
|
||||
let result = functions::set_tdlib_parameters(
|
||||
false, // use_test_dc
|
||||
"tdlib_data".to_string(), // database_directory
|
||||
"".to_string(), // files_directory
|
||||
"".to_string(), // database_encryption_key
|
||||
true, // use_file_database
|
||||
true, // use_chat_info_database
|
||||
true, // use_message_database
|
||||
false, // use_secret_chats
|
||||
self.api_id, // api_id
|
||||
self.api_hash.clone(), // api_hash
|
||||
"en".to_string(), // system_language_code
|
||||
"Desktop".to_string(), // device_model
|
||||
"".to_string(), // system_version
|
||||
env!("CARGO_PKG_VERSION").to_string(), // application_version
|
||||
false, // use_test_dc
|
||||
"tdlib_data".to_string(), // database_directory
|
||||
"".to_string(), // files_directory
|
||||
"".to_string(), // database_encryption_key
|
||||
true, // use_file_database
|
||||
true, // use_chat_info_database
|
||||
true, // use_message_database
|
||||
false, // use_secret_chats
|
||||
self.api_id, // api_id
|
||||
self.api_hash.clone(), // api_hash
|
||||
"en".to_string(), // system_language_code
|
||||
"Desktop".to_string(), // device_model
|
||||
"".to_string(), // system_version
|
||||
env!("CARGO_PKG_VERSION").to_string(), // application_version
|
||||
self.client_id,
|
||||
)
|
||||
.await;
|
||||
@@ -457,7 +465,9 @@ impl TdClient {
|
||||
if update.position.order == 0 {
|
||||
// Чат больше не в Main (перемещён в архив и т.д.)
|
||||
self.chats.retain(|c| c.id != update.chat_id);
|
||||
} else if let Some(chat) = self.chats.iter_mut().find(|c| c.id == update.chat_id) {
|
||||
} else if let Some(chat) =
|
||||
self.chats.iter_mut().find(|c| c.id == update.chat_id)
|
||||
{
|
||||
// Обновляем позицию существующего чата
|
||||
chat.order = update.position.order;
|
||||
chat.is_pinned = update.position.is_pinned;
|
||||
@@ -493,7 +503,10 @@ impl TdClient {
|
||||
let is_incoming = !msg_info.is_outgoing;
|
||||
|
||||
// Проверяем, есть ли уже сообщение с таким id
|
||||
let existing_idx = self.current_chat_messages.iter().position(|m| m.id == msg_info.id);
|
||||
let existing_idx = self
|
||||
.current_chat_messages
|
||||
.iter()
|
||||
.position(|m| m.id == msg_info.id);
|
||||
|
||||
match existing_idx {
|
||||
Some(idx) => {
|
||||
@@ -505,8 +518,10 @@ impl TdClient {
|
||||
// но сохраняем reply_to (добавленный при отправке)
|
||||
let existing = &mut self.current_chat_messages[idx];
|
||||
existing.can_be_edited = msg_info.can_be_edited;
|
||||
existing.can_be_deleted_only_for_self = msg_info.can_be_deleted_only_for_self;
|
||||
existing.can_be_deleted_for_all_users = msg_info.can_be_deleted_for_all_users;
|
||||
existing.can_be_deleted_only_for_self =
|
||||
msg_info.can_be_deleted_only_for_self;
|
||||
existing.can_be_deleted_for_all_users =
|
||||
msg_info.can_be_deleted_for_all_users;
|
||||
existing.is_read = msg_info.is_read;
|
||||
}
|
||||
}
|
||||
@@ -529,9 +544,8 @@ impl TdClient {
|
||||
if user.first_name.is_empty() && user.last_name.is_empty() {
|
||||
// Удаляем чаты с этим пользователем из списка
|
||||
let user_id = user.id;
|
||||
self.chats.retain(|c| {
|
||||
self.chat_user_ids.get(&c.id) != Some(&user_id)
|
||||
});
|
||||
self.chats
|
||||
.retain(|c| self.chat_user_ids.get(&c.id) != Some(&user_id));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -550,7 +564,8 @@ impl TdClient {
|
||||
// Обновляем username в чатах, связанных с этим пользователем
|
||||
for (&chat_id, &user_id) in &self.chat_user_ids.clone() {
|
||||
if user_id == user.id {
|
||||
if let Some(chat) = self.chats.iter_mut().find(|c| c.id == chat_id) {
|
||||
if let Some(chat) = self.chats.iter_mut().find(|c| c.id == chat_id)
|
||||
{
|
||||
chat.username = Some(format!("@{}", username));
|
||||
}
|
||||
}
|
||||
@@ -564,10 +579,7 @@ impl TdClient {
|
||||
self.folders = update
|
||||
.chat_folders
|
||||
.into_iter()
|
||||
.map(|f| FolderInfo {
|
||||
id: f.id,
|
||||
name: f.title,
|
||||
})
|
||||
.map(|f| FolderInfo { id: f.id, name: f.title })
|
||||
.collect();
|
||||
self.main_chat_list_position = update.main_chat_list_position;
|
||||
}
|
||||
@@ -607,14 +619,26 @@ impl TdClient {
|
||||
let action_text = match update.action {
|
||||
ChatAction::Typing => Some("печатает...".to_string()),
|
||||
ChatAction::RecordingVideo => Some("записывает видео...".to_string()),
|
||||
ChatAction::UploadingVideo(_) => Some("отправляет видео...".to_string()),
|
||||
ChatAction::RecordingVoiceNote => Some("записывает голосовое...".to_string()),
|
||||
ChatAction::UploadingVoiceNote(_) => Some("отправляет голосовое...".to_string()),
|
||||
ChatAction::UploadingVideo(_) => {
|
||||
Some("отправляет видео...".to_string())
|
||||
}
|
||||
ChatAction::RecordingVoiceNote => {
|
||||
Some("записывает голосовое...".to_string())
|
||||
}
|
||||
ChatAction::UploadingVoiceNote(_) => {
|
||||
Some("отправляет голосовое...".to_string())
|
||||
}
|
||||
ChatAction::UploadingPhoto(_) => Some("отправляет фото...".to_string()),
|
||||
ChatAction::UploadingDocument(_) => Some("отправляет файл...".to_string()),
|
||||
ChatAction::UploadingDocument(_) => {
|
||||
Some("отправляет файл...".to_string())
|
||||
}
|
||||
ChatAction::ChoosingSticker => Some("выбирает стикер...".to_string()),
|
||||
ChatAction::RecordingVideoNote => Some("записывает видеосообщение...".to_string()),
|
||||
ChatAction::UploadingVideoNote(_) => Some("отправляет видеосообщение...".to_string()),
|
||||
ChatAction::RecordingVideoNote => {
|
||||
Some("записывает видеосообщение...".to_string())
|
||||
}
|
||||
ChatAction::UploadingVideoNote(_) => {
|
||||
Some("отправляет видеосообщение...".to_string())
|
||||
}
|
||||
ChatAction::Cancel => None, // Отмена — сбрасываем статус
|
||||
_ => None,
|
||||
};
|
||||
@@ -633,7 +657,9 @@ impl TdClient {
|
||||
if let Some(chat) = self.chats.iter_mut().find(|c| c.id == update.chat_id) {
|
||||
chat.draft_text = update.draft_message.as_ref().and_then(|draft| {
|
||||
// Извлекаем текст из InputMessageText
|
||||
if let tdlib_rs::enums::InputMessageContent::InputMessageText(text_msg) = &draft.input_message_text {
|
||||
if let tdlib_rs::enums::InputMessageContent::InputMessageText(text_msg) =
|
||||
&draft.input_message_text
|
||||
{
|
||||
Some(text_msg.text.text.clone())
|
||||
} else {
|
||||
None
|
||||
@@ -644,7 +670,11 @@ impl TdClient {
|
||||
Update::MessageInteractionInfo(update) => {
|
||||
// Обновляем реакции в текущем открытом чате
|
||||
if Some(update.chat_id) == self.current_chat_id {
|
||||
if let Some(msg) = self.current_chat_messages.iter_mut().find(|m| m.id == update.message_id) {
|
||||
if let Some(msg) = self
|
||||
.current_chat_messages
|
||||
.iter_mut()
|
||||
.find(|m| m.id == update.message_id)
|
||||
{
|
||||
// Извлекаем реакции из interaction_info
|
||||
msg.reactions = update
|
||||
.interaction_info
|
||||
@@ -656,8 +686,12 @@ impl TdClient {
|
||||
.iter()
|
||||
.filter_map(|reaction| {
|
||||
let emoji = match &reaction.r#type {
|
||||
tdlib_rs::enums::ReactionType::Emoji(e) => e.emoji.clone(),
|
||||
tdlib_rs::enums::ReactionType::CustomEmoji(_) => return None,
|
||||
tdlib_rs::enums::ReactionType::Emoji(e) => {
|
||||
e.emoji.clone()
|
||||
}
|
||||
tdlib_rs::enums::ReactionType::CustomEmoji(_) => {
|
||||
return None
|
||||
}
|
||||
};
|
||||
|
||||
Some(ReactionInfo {
|
||||
@@ -697,9 +731,10 @@ impl TdClient {
|
||||
}
|
||||
|
||||
// Ищем позицию в Main списке (если есть)
|
||||
let main_position = td_chat.positions.iter().find(|pos| {
|
||||
matches!(pos.list, ChatList::Main)
|
||||
});
|
||||
let main_position = td_chat
|
||||
.positions
|
||||
.iter()
|
||||
.find(|pos| matches!(pos.list, ChatList::Main));
|
||||
|
||||
// Получаем order и is_pinned из позиции, или используем значения по умолчанию
|
||||
let (order, is_pinned) = main_position
|
||||
@@ -716,7 +751,9 @@ impl TdClient {
|
||||
let username = match &td_chat.r#type {
|
||||
ChatType::Private(private) => {
|
||||
// Ограничиваем размер chat_user_ids
|
||||
if self.chat_user_ids.len() >= MAX_CHAT_USER_IDS && !self.chat_user_ids.contains_key(&td_chat.id) {
|
||||
if self.chat_user_ids.len() >= MAX_CHAT_USER_IDS
|
||||
&& !self.chat_user_ids.contains_key(&td_chat.id)
|
||||
{
|
||||
// Удаляем случайную запись (первую найденную)
|
||||
if let Some(&key) = self.chat_user_ids.keys().next() {
|
||||
self.chat_user_ids.remove(&key);
|
||||
@@ -724,7 +761,9 @@ impl TdClient {
|
||||
}
|
||||
self.chat_user_ids.insert(td_chat.id, private.user_id);
|
||||
// Проверяем, есть ли уже username в кэше (peek не обновляет LRU)
|
||||
self.user_usernames.peek(&private.user_id).map(|u| format!("@{}", u))
|
||||
self.user_usernames
|
||||
.peek(&private.user_id)
|
||||
.map(|u| format!("@{}", u))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
@@ -784,7 +823,13 @@ impl TdClient {
|
||||
// Ограничиваем количество чатов
|
||||
if self.chats.len() > MAX_CHATS {
|
||||
// Удаляем чат с наименьшим order (наименее активный)
|
||||
if let Some(min_idx) = self.chats.iter().enumerate().min_by_key(|(_, c)| c.order).map(|(i, _)| i) {
|
||||
if let Some(min_idx) = self
|
||||
.chats
|
||||
.iter()
|
||||
.enumerate()
|
||||
.min_by_key(|(_, c)| c.order)
|
||||
.map(|(i, _)| i)
|
||||
{
|
||||
self.chats.remove(min_idx);
|
||||
}
|
||||
}
|
||||
@@ -891,11 +936,7 @@ impl TdClient {
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
Some(ReplyInfo {
|
||||
message_id: reply.message_id,
|
||||
sender_name,
|
||||
text,
|
||||
})
|
||||
Some(ReplyInfo { message_id: reply.message_id, sender_name, text })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
@@ -905,10 +946,7 @@ impl TdClient {
|
||||
fn extract_forward_info(&self, message: &TdMessage) -> Option<ForwardInfo> {
|
||||
message.forward_info.as_ref().map(|info| {
|
||||
let sender_name = self.get_origin_sender_name(&info.origin);
|
||||
ForwardInfo {
|
||||
sender_name,
|
||||
date: info.date,
|
||||
}
|
||||
ForwardInfo { sender_name, date: info.date }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -944,24 +982,24 @@ impl TdClient {
|
||||
fn get_origin_sender_name(&self, origin: &tdlib_rs::enums::MessageOrigin) -> String {
|
||||
use tdlib_rs::enums::MessageOrigin;
|
||||
match origin {
|
||||
MessageOrigin::User(u) => {
|
||||
self.user_names.peek(&u.sender_user_id)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| format!("User_{}", u.sender_user_id))
|
||||
}
|
||||
MessageOrigin::Chat(c) => {
|
||||
self.chats.iter()
|
||||
.find(|chat| chat.id == c.sender_chat_id)
|
||||
.map(|chat| chat.title.clone())
|
||||
.unwrap_or_else(|| "Чат".to_string())
|
||||
}
|
||||
MessageOrigin::User(u) => self
|
||||
.user_names
|
||||
.peek(&u.sender_user_id)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| format!("User_{}", u.sender_user_id)),
|
||||
MessageOrigin::Chat(c) => self
|
||||
.chats
|
||||
.iter()
|
||||
.find(|chat| chat.id == c.sender_chat_id)
|
||||
.map(|chat| chat.title.clone())
|
||||
.unwrap_or_else(|| "Чат".to_string()),
|
||||
MessageOrigin::HiddenUser(h) => h.sender_name.clone(),
|
||||
MessageOrigin::Channel(c) => {
|
||||
self.chats.iter()
|
||||
.find(|chat| chat.id == c.chat_id)
|
||||
.map(|chat| chat.title.clone())
|
||||
.unwrap_or_else(|| "Канал".to_string())
|
||||
}
|
||||
MessageOrigin::Channel(c) => self
|
||||
.chats
|
||||
.iter()
|
||||
.find(|chat| chat.id == c.chat_id)
|
||||
.map(|chat| chat.title.clone())
|
||||
.unwrap_or_else(|| "Канал".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1032,19 +1070,17 @@ impl TdClient {
|
||||
functions::get_message(chat_id, msg_id, self.client_id).await
|
||||
{
|
||||
let sender_name = match &msg.sender_id {
|
||||
tdlib_rs::enums::MessageSender::User(user) => {
|
||||
self.user_names
|
||||
.get(&user.user_id)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| format!("User_{}", user.user_id))
|
||||
}
|
||||
tdlib_rs::enums::MessageSender::Chat(chat) => {
|
||||
self.chats
|
||||
.iter()
|
||||
.find(|c| c.id == chat.chat_id)
|
||||
.map(|c| c.title.clone())
|
||||
.unwrap_or_else(|| "Чат".to_string())
|
||||
}
|
||||
tdlib_rs::enums::MessageSender::User(user) => self
|
||||
.user_names
|
||||
.get(&user.user_id)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| format!("User_{}", user.user_id)),
|
||||
tdlib_rs::enums::MessageSender::Chat(chat) => self
|
||||
.chats
|
||||
.iter()
|
||||
.find(|c| c.id == chat.chat_id)
|
||||
.map(|c| c.title.clone())
|
||||
.unwrap_or_else(|| "Чат".to_string()),
|
||||
};
|
||||
let (content, _) = extract_message_text_static(&msg);
|
||||
reply_cache.insert(msg_id, (sender_name, content));
|
||||
@@ -1068,12 +1104,7 @@ impl TdClient {
|
||||
|
||||
/// Отправка номера телефона
|
||||
pub async fn send_phone_number(&mut self, phone: String) -> Result<(), String> {
|
||||
let result = functions::set_authentication_phone_number(
|
||||
phone,
|
||||
None,
|
||||
self.client_id,
|
||||
)
|
||||
.await;
|
||||
let result = functions::set_authentication_phone_number(phone, None, self.client_id).await;
|
||||
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
@@ -1103,12 +1134,7 @@ impl TdClient {
|
||||
|
||||
/// Загрузка списка чатов
|
||||
pub async fn load_chats(&mut self, limit: i32) -> Result<(), String> {
|
||||
let result = functions::load_chats(
|
||||
Some(ChatList::Main),
|
||||
limit,
|
||||
self.client_id,
|
||||
)
|
||||
.await;
|
||||
let result = functions::load_chats(Some(ChatList::Main), limit, self.client_id).await;
|
||||
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
@@ -1118,16 +1144,10 @@ impl TdClient {
|
||||
|
||||
/// Загрузка чатов для конкретной папки
|
||||
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 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;
|
||||
let result = functions::load_chats(Some(chat_list), limit, self.client_id).await;
|
||||
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
@@ -1155,9 +1175,9 @@ impl TdClient {
|
||||
let result = functions::get_chat_history(
|
||||
chat_id,
|
||||
from_message_id,
|
||||
0, // offset
|
||||
0, // offset
|
||||
limit,
|
||||
false, // only_local - загружаем с сервера!
|
||||
false, // only_local - загружаем с сервера!
|
||||
self.client_id,
|
||||
)
|
||||
.await;
|
||||
@@ -1209,8 +1229,8 @@ impl TdClient {
|
||||
let _ = functions::view_messages(
|
||||
chat_id,
|
||||
message_ids,
|
||||
None, // source
|
||||
true, // force_read
|
||||
None, // source
|
||||
true, // force_read
|
||||
self.client_id,
|
||||
)
|
||||
.await;
|
||||
@@ -1223,14 +1243,14 @@ impl TdClient {
|
||||
pub async fn get_pinned_messages(&mut self, chat_id: i64) -> Result<Vec<MessageInfo>, String> {
|
||||
let result = functions::search_chat_messages(
|
||||
chat_id,
|
||||
"".to_string(), // query
|
||||
None, // sender_id
|
||||
0, // from_message_id
|
||||
0, // offset
|
||||
100, // limit
|
||||
Some(SearchMessagesFilter::Pinned), // filter
|
||||
0, // message_thread_id
|
||||
0, // saved_messages_topic_id
|
||||
"".to_string(), // query
|
||||
None, // sender_id
|
||||
0, // from_message_id
|
||||
0, // offset
|
||||
100, // limit
|
||||
Some(SearchMessagesFilter::Pinned), // filter
|
||||
0, // message_thread_id
|
||||
0, // saved_messages_topic_id
|
||||
self.client_id,
|
||||
)
|
||||
.await;
|
||||
@@ -1279,7 +1299,11 @@ impl TdClient {
|
||||
}
|
||||
|
||||
/// Поиск сообщений в чате по тексту
|
||||
pub async fn search_messages(&mut self, chat_id: i64, query: &str) -> Result<Vec<MessageInfo>, String> {
|
||||
pub async fn search_messages(
|
||||
&mut self,
|
||||
chat_id: i64,
|
||||
query: &str,
|
||||
) -> Result<Vec<MessageInfo>, String> {
|
||||
if query.trim().is_empty() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
@@ -1287,13 +1311,13 @@ impl TdClient {
|
||||
let result = functions::search_chat_messages(
|
||||
chat_id,
|
||||
query.to_string(),
|
||||
None, // sender_id
|
||||
0, // from_message_id
|
||||
0, // offset
|
||||
50, // limit
|
||||
None, // filter (no filter = search by text)
|
||||
0, // message_thread_id
|
||||
0, // saved_messages_topic_id
|
||||
None, // sender_id
|
||||
0, // from_message_id
|
||||
0, // offset
|
||||
50, // limit
|
||||
None, // filter (no filter = search by text)
|
||||
0, // message_thread_id
|
||||
0, // saved_messages_topic_id
|
||||
self.client_id,
|
||||
)
|
||||
.await;
|
||||
@@ -1359,8 +1383,12 @@ impl TdClient {
|
||||
profile.online_status = Some(match user.status {
|
||||
tdlib_rs::enums::UserStatus::Online(_) => "Онлайн".to_string(),
|
||||
tdlib_rs::enums::UserStatus::Recently(_) => "Был(а) недавно".to_string(),
|
||||
tdlib_rs::enums::UserStatus::LastWeek(_) => "Был(а) на этой неделе".to_string(),
|
||||
tdlib_rs::enums::UserStatus::LastMonth(_) => "Был(а) в этом месяце".to_string(),
|
||||
tdlib_rs::enums::UserStatus::LastWeek(_) => {
|
||||
"Был(а) на этой неделе".to_string()
|
||||
}
|
||||
tdlib_rs::enums::UserStatus::LastMonth(_) => {
|
||||
"Был(а) в этом месяце".to_string()
|
||||
}
|
||||
tdlib_rs::enums::UserStatus::Offline(offline) => {
|
||||
crate::utils::format_was_online(offline.was_online)
|
||||
}
|
||||
@@ -1369,8 +1397,10 @@ impl TdClient {
|
||||
}
|
||||
|
||||
// Bio (getUserFullInfo)
|
||||
let full_info_result = functions::get_user_full_info(private_chat.user_id, self.client_id).await;
|
||||
if let Ok(tdlib_rs::enums::UserFullInfo::UserFullInfo(full_info)) = full_info_result {
|
||||
let full_info_result =
|
||||
functions::get_user_full_info(private_chat.user_id, self.client_id).await;
|
||||
if let Ok(tdlib_rs::enums::UserFullInfo::UserFullInfo(full_info)) = full_info_result
|
||||
{
|
||||
if let Some(bio_obj) = full_info.bio {
|
||||
profile.bio = Some(bio_obj.text);
|
||||
}
|
||||
@@ -1381,14 +1411,21 @@ impl TdClient {
|
||||
profile.is_group = true;
|
||||
|
||||
// Получаем информацию о группе
|
||||
let group_result = functions::get_basic_group(basic_group.basic_group_id, self.client_id).await;
|
||||
let group_result =
|
||||
functions::get_basic_group(basic_group.basic_group_id, self.client_id).await;
|
||||
if let Ok(tdlib_rs::enums::BasicGroup::BasicGroup(group)) = group_result {
|
||||
profile.member_count = Some(group.member_count);
|
||||
}
|
||||
|
||||
// Полная информация о группе
|
||||
let full_info_result = functions::get_basic_group_full_info(basic_group.basic_group_id, self.client_id).await;
|
||||
if let Ok(tdlib_rs::enums::BasicGroupFullInfo::BasicGroupFullInfo(full_info)) = full_info_result {
|
||||
let full_info_result = functions::get_basic_group_full_info(
|
||||
basic_group.basic_group_id,
|
||||
self.client_id,
|
||||
)
|
||||
.await;
|
||||
if let Ok(tdlib_rs::enums::BasicGroupFullInfo::BasicGroupFullInfo(full_info)) =
|
||||
full_info_result
|
||||
{
|
||||
if !full_info.description.is_empty() {
|
||||
profile.description = Some(full_info.description);
|
||||
}
|
||||
@@ -1399,9 +1436,14 @@ impl TdClient {
|
||||
}
|
||||
ChatType::Supergroup(supergroup) => {
|
||||
// Получаем информацию о супергруппе
|
||||
let sg_result = functions::get_supergroup(supergroup.supergroup_id, self.client_id).await;
|
||||
let sg_result =
|
||||
functions::get_supergroup(supergroup.supergroup_id, self.client_id).await;
|
||||
if let Ok(tdlib_rs::enums::Supergroup::Supergroup(sg)) = sg_result {
|
||||
profile.chat_type = if sg.is_channel { "Канал".to_string() } else { "Супергруппа".to_string() };
|
||||
profile.chat_type = if sg.is_channel {
|
||||
"Канал".to_string()
|
||||
} else {
|
||||
"Супергруппа".to_string()
|
||||
};
|
||||
profile.is_group = !sg.is_channel;
|
||||
profile.member_count = Some(sg.member_count);
|
||||
|
||||
@@ -1414,8 +1456,12 @@ impl TdClient {
|
||||
}
|
||||
|
||||
// Полная информация о супергруппе
|
||||
let full_info_result = functions::get_supergroup_full_info(supergroup.supergroup_id, self.client_id).await;
|
||||
if let Ok(tdlib_rs::enums::SupergroupFullInfo::SupergroupFullInfo(full_info)) = full_info_result {
|
||||
let full_info_result =
|
||||
functions::get_supergroup_full_info(supergroup.supergroup_id, self.client_id)
|
||||
.await;
|
||||
if let Ok(tdlib_rs::enums::SupergroupFullInfo::SupergroupFullInfo(full_info)) =
|
||||
full_info_result
|
||||
{
|
||||
if !full_info.description.is_empty() {
|
||||
profile.description = Some(full_info.description);
|
||||
}
|
||||
@@ -1451,9 +1497,9 @@ impl TdClient {
|
||||
let result = functions::get_chat_history(
|
||||
chat_id,
|
||||
from_message_id,
|
||||
0, // offset
|
||||
0, // offset
|
||||
limit,
|
||||
false, // only_local
|
||||
false, // only_local
|
||||
self.client_id,
|
||||
)
|
||||
.await;
|
||||
@@ -1497,11 +1543,9 @@ impl TdClient {
|
||||
/// Получение моего user_id
|
||||
pub async fn get_me(&self) -> Result<i64, String> {
|
||||
match functions::get_me(self.client_id).await {
|
||||
Ok(user) => {
|
||||
match user {
|
||||
User::User(u) => Ok(u.id),
|
||||
}
|
||||
}
|
||||
Ok(user) => match user {
|
||||
User::User(u) => Ok(u.id),
|
||||
},
|
||||
Err(e) => Err(format!("Ошибка получения профиля: {:?}", e)),
|
||||
}
|
||||
}
|
||||
@@ -1513,30 +1557,37 @@ impl TdClient {
|
||||
0, // message_thread_id
|
||||
Some(action),
|
||||
self.client_id,
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Отправка текстового сообщения с поддержкой Markdown и reply
|
||||
pub async fn send_message(&self, chat_id: i64, text: String, reply_to_message_id: Option<i64>, reply_info: Option<ReplyInfo>) -> Result<MessageInfo, String> {
|
||||
use tdlib_rs::types::{FormattedText, InputMessageText, TextParseModeMarkdown, InputMessageReplyToMessage};
|
||||
use tdlib_rs::enums::{InputMessageContent, TextParseMode, InputMessageReplyTo};
|
||||
pub async fn send_message(
|
||||
&self,
|
||||
chat_id: i64,
|
||||
text: String,
|
||||
reply_to_message_id: Option<i64>,
|
||||
reply_info: Option<ReplyInfo>,
|
||||
) -> Result<MessageInfo, String> {
|
||||
use tdlib_rs::enums::{InputMessageContent, InputMessageReplyTo, TextParseMode};
|
||||
use tdlib_rs::types::{
|
||||
FormattedText, InputMessageReplyToMessage, InputMessageText, TextParseModeMarkdown,
|
||||
};
|
||||
|
||||
// Парсим markdown в тексте
|
||||
let formatted_text = match functions::parse_text_entities(
|
||||
text.clone(),
|
||||
TextParseMode::Markdown(TextParseModeMarkdown { version: 2 }),
|
||||
self.client_id,
|
||||
).await {
|
||||
Ok(tdlib_rs::enums::FormattedText::FormattedText(ft)) => FormattedText {
|
||||
text: ft.text,
|
||||
entities: ft.entities,
|
||||
},
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(tdlib_rs::enums::FormattedText::FormattedText(ft)) => {
|
||||
FormattedText { text: ft.text, entities: ft.entities }
|
||||
}
|
||||
Err(_) => {
|
||||
// Если парсинг не удался, отправляем как plain text
|
||||
FormattedText {
|
||||
text: text.clone(),
|
||||
entities: vec![],
|
||||
}
|
||||
FormattedText { text: text.clone(), entities: vec![] }
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1558,9 +1609,9 @@ impl TdClient {
|
||||
|
||||
let result = functions::send_message(
|
||||
chat_id,
|
||||
0, // message_thread_id
|
||||
0, // message_thread_id
|
||||
reply_to,
|
||||
None, // options
|
||||
None, // options
|
||||
content,
|
||||
self.client_id,
|
||||
)
|
||||
@@ -1592,7 +1643,6 @@ impl TdClient {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Получить доступные реакции для сообщения
|
||||
pub async fn get_message_available_reactions(
|
||||
&mut self,
|
||||
@@ -1653,9 +1703,9 @@ impl TdClient {
|
||||
message_id: i64,
|
||||
emoji: String,
|
||||
) -> Result<(), String> {
|
||||
use tdlib_rs::enums::ReactionType;
|
||||
use tdlib_rs::functions;
|
||||
use tdlib_rs::types::ReactionTypeEmoji;
|
||||
use tdlib_rs::enums::ReactionType;
|
||||
|
||||
let reaction_type = ReactionType::Emoji(ReactionTypeEmoji { emoji });
|
||||
|
||||
@@ -1678,17 +1728,18 @@ impl TdClient {
|
||||
/// Редактирование текстового сообщения с поддержкой Markdown
|
||||
/// Устанавливает черновик для чата через TDLib API
|
||||
pub async fn set_draft_message(&self, chat_id: i64, text: String) -> Result<(), String> {
|
||||
use tdlib_rs::types::{FormattedText, InputMessageText, DraftMessage};
|
||||
use tdlib_rs::enums::InputMessageContent;
|
||||
use tdlib_rs::types::{DraftMessage, FormattedText, InputMessageText};
|
||||
|
||||
if text.is_empty() {
|
||||
// Очищаем черновик
|
||||
let result = functions::set_chat_draft_message(
|
||||
chat_id,
|
||||
0, // message_thread_id
|
||||
0, // message_thread_id
|
||||
None, // draft_message (None = очистить)
|
||||
self.client_id,
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
@@ -1696,10 +1747,7 @@ impl TdClient {
|
||||
}
|
||||
} else {
|
||||
// Создаём черновик
|
||||
let formatted_text = FormattedText {
|
||||
text: text.clone(),
|
||||
entities: vec![],
|
||||
};
|
||||
let formatted_text = FormattedText { text: text.clone(), entities: vec![] };
|
||||
|
||||
let input_message = InputMessageContent::InputMessageText(InputMessageText {
|
||||
text: formatted_text,
|
||||
@@ -1718,7 +1766,8 @@ impl TdClient {
|
||||
0, // message_thread_id
|
||||
Some(draft),
|
||||
self.client_id,
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
@@ -1727,26 +1776,29 @@ impl TdClient {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn edit_message(&self, chat_id: i64, message_id: i64, text: String) -> Result<MessageInfo, String> {
|
||||
use tdlib_rs::types::{FormattedText, InputMessageText, TextParseModeMarkdown};
|
||||
pub async fn edit_message(
|
||||
&self,
|
||||
chat_id: i64,
|
||||
message_id: i64,
|
||||
text: String,
|
||||
) -> Result<MessageInfo, String> {
|
||||
use tdlib_rs::enums::{InputMessageContent, TextParseMode};
|
||||
use tdlib_rs::types::{FormattedText, InputMessageText, TextParseModeMarkdown};
|
||||
|
||||
// Парсим markdown в тексте
|
||||
let formatted_text = match functions::parse_text_entities(
|
||||
text.clone(),
|
||||
TextParseMode::Markdown(TextParseModeMarkdown { version: 2 }),
|
||||
self.client_id,
|
||||
).await {
|
||||
Ok(tdlib_rs::enums::FormattedText::FormattedText(ft)) => FormattedText {
|
||||
text: ft.text,
|
||||
entities: ft.entities,
|
||||
},
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(tdlib_rs::enums::FormattedText::FormattedText(ft)) => {
|
||||
FormattedText { text: ft.text, entities: ft.entities }
|
||||
}
|
||||
Err(_) => {
|
||||
// Если парсинг не удался, отправляем как plain text
|
||||
FormattedText {
|
||||
text: text.clone(),
|
||||
entities: vec![],
|
||||
}
|
||||
FormattedText { text: text.clone(), entities: vec![] }
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1756,13 +1808,8 @@ impl TdClient {
|
||||
clear_draft: true,
|
||||
});
|
||||
|
||||
let result = functions::edit_message_text(
|
||||
chat_id,
|
||||
message_id,
|
||||
content,
|
||||
self.client_id,
|
||||
)
|
||||
.await;
|
||||
let result =
|
||||
functions::edit_message_text(chat_id, message_id, content, self.client_id).await;
|
||||
|
||||
match result {
|
||||
Ok(tdlib_rs::enums::Message::Message(msg)) => {
|
||||
@@ -1790,14 +1837,13 @@ impl TdClient {
|
||||
|
||||
/// Удаление сообщений
|
||||
/// revoke = true удаляет для всех, false только для себя
|
||||
pub async fn delete_messages(&self, chat_id: i64, message_ids: Vec<i64>, revoke: bool) -> Result<(), String> {
|
||||
let result = functions::delete_messages(
|
||||
chat_id,
|
||||
message_ids,
|
||||
revoke,
|
||||
self.client_id,
|
||||
)
|
||||
.await;
|
||||
pub async fn delete_messages(
|
||||
&self,
|
||||
chat_id: i64,
|
||||
message_ids: Vec<i64>,
|
||||
revoke: bool,
|
||||
) -> Result<(), String> {
|
||||
let result = functions::delete_messages(chat_id, message_ids, revoke, self.client_id).await;
|
||||
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
@@ -1806,13 +1852,18 @@ impl TdClient {
|
||||
}
|
||||
|
||||
/// Пересылка сообщений
|
||||
pub async fn forward_messages(&self, to_chat_id: i64, from_chat_id: i64, message_ids: Vec<i64>) -> Result<(), String> {
|
||||
pub async fn forward_messages(
|
||||
&self,
|
||||
to_chat_id: i64,
|
||||
from_chat_id: i64,
|
||||
message_ids: Vec<i64>,
|
||||
) -> Result<(), String> {
|
||||
let result = functions::forward_messages(
|
||||
to_chat_id,
|
||||
0, // message_thread_id
|
||||
from_chat_id,
|
||||
message_ids,
|
||||
None, // options
|
||||
None, // options
|
||||
false, // send_copy
|
||||
false, // remove_caption
|
||||
self.client_id,
|
||||
@@ -1832,8 +1883,8 @@ impl TdClient {
|
||||
let _ = functions::view_messages(
|
||||
chat_id,
|
||||
message_ids,
|
||||
None, // source
|
||||
true, // force_read
|
||||
None, // source
|
||||
true, // force_read
|
||||
self.client_id,
|
||||
)
|
||||
.await;
|
||||
@@ -1847,7 +1898,8 @@ impl TdClient {
|
||||
const BATCH_SIZE: usize = 5;
|
||||
|
||||
// Убираем дубликаты и уже загруженные
|
||||
self.pending_user_ids.retain(|id| !self.user_names.contains_key(id));
|
||||
self.pending_user_ids
|
||||
.retain(|id| !self.user_names.contains_key(id));
|
||||
self.pending_user_ids.dedup();
|
||||
|
||||
// Берём последние BATCH_SIZE элементов
|
||||
@@ -1885,16 +1937,17 @@ impl TdClient {
|
||||
/// Статическая функция для извлечения текста и entities сообщения (без &self)
|
||||
fn extract_message_text_static(message: &TdMessage) -> (String, Vec<TextEntity>) {
|
||||
match &message.content {
|
||||
MessageContent::MessageText(text) => {
|
||||
(text.text.text.clone(), text.text.entities.clone())
|
||||
}
|
||||
MessageContent::MessageText(text) => (text.text.text.clone(), text.text.entities.clone()),
|
||||
MessageContent::MessagePhoto(photo) => {
|
||||
if photo.caption.text.is_empty() {
|
||||
("[Фото]".to_string(), vec![])
|
||||
} else {
|
||||
// Добавляем смещение для "[Фото] " к entities
|
||||
let prefix_len = "[Фото] ".chars().count() as i32;
|
||||
let adjusted_entities: Vec<TextEntity> = photo.caption.entities.iter()
|
||||
let adjusted_entities: Vec<TextEntity> = photo
|
||||
.caption
|
||||
.entities
|
||||
.iter()
|
||||
.map(|e| TextEntity {
|
||||
offset: e.offset + prefix_len,
|
||||
length: e.length,
|
||||
@@ -1909,7 +1962,10 @@ fn extract_message_text_static(message: &TdMessage) -> (String, Vec<TextEntity>)
|
||||
("[Видео]".to_string(), vec![])
|
||||
} else {
|
||||
let prefix_len = "[Видео] ".chars().count() as i32;
|
||||
let adjusted_entities: Vec<TextEntity> = video.caption.entities.iter()
|
||||
let adjusted_entities: Vec<TextEntity> = video
|
||||
.caption
|
||||
.entities
|
||||
.iter()
|
||||
.map(|e| TextEntity {
|
||||
offset: e.offset + prefix_len,
|
||||
length: e.length,
|
||||
@@ -1932,7 +1988,10 @@ fn extract_message_text_static(message: &TdMessage) -> (String, Vec<TextEntity>)
|
||||
("[GIF]".to_string(), vec![])
|
||||
} else {
|
||||
let prefix_len = "[GIF] ".chars().count() as i32;
|
||||
let adjusted_entities: Vec<TextEntity> = anim.caption.entities.iter()
|
||||
let adjusted_entities: Vec<TextEntity> = anim
|
||||
.caption
|
||||
.entities
|
||||
.iter()
|
||||
.map(|e| TextEntity {
|
||||
offset: e.offset + prefix_len,
|
||||
length: e.length,
|
||||
@@ -1942,9 +2001,7 @@ fn extract_message_text_static(message: &TdMessage) -> (String, Vec<TextEntity>)
|
||||
(format!("[GIF] {}", anim.caption.text), adjusted_entities)
|
||||
}
|
||||
}
|
||||
MessageContent::MessageAudio(audio) => {
|
||||
(format!("[Аудио: {}]", audio.audio.title), vec![])
|
||||
}
|
||||
MessageContent::MessageAudio(audio) => (format!("[Аудио: {}]", audio.audio.title), vec![]),
|
||||
MessageContent::MessageCall(_) => ("[Звонок]".to_string(), vec![]),
|
||||
MessageContent::MessagePoll(poll) => {
|
||||
(format!("[Опрос: {}]", poll.poll.question.text), vec![])
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
pub mod client;
|
||||
|
||||
pub use client::TdClient;
|
||||
pub use client::UserOnlineStatus;
|
||||
pub use client::ChatInfo;
|
||||
pub use client::FolderInfo;
|
||||
pub use client::ForwardInfo;
|
||||
pub use client::MessageInfo;
|
||||
pub use client::NetworkState;
|
||||
pub use client::ProfileInfo;
|
||||
pub use client::ChatInfo;
|
||||
pub use client::MessageInfo;
|
||||
pub use client::ReactionInfo;
|
||||
pub use client::ReplyInfo;
|
||||
pub use client::ForwardInfo;
|
||||
pub use client::FolderInfo;
|
||||
pub use client::TdClient;
|
||||
pub use client::UserOnlineStatus;
|
||||
pub use tdlib_rs::enums::ChatAction;
|
||||
|
||||
Reference in New Issue
Block a user