fixes
This commit is contained in:
@@ -1,9 +1,82 @@
|
||||
use std::env;
|
||||
use std::collections::HashMap;
|
||||
use tdlib_rs::enums::{AuthorizationState, ChatList, ChatType, ConnectionState, MessageContent, Update, User, UserStatus};
|
||||
use tdlib_rs::types::TextEntity;
|
||||
|
||||
/// Максимальный размер кэшей пользователей
|
||||
const MAX_USER_CACHE_SIZE: usize = 500;
|
||||
/// Максимальное количество сообщений в текущем чате
|
||||
const MAX_MESSAGES_IN_CHAT: usize = 500;
|
||||
/// Максимальное количество чатов
|
||||
const MAX_CHATS: usize = 200;
|
||||
/// Максимальный размер кэша chat_user_ids
|
||||
const MAX_CHAT_USER_IDS: usize = 500;
|
||||
|
||||
/// Простой LRU-кэш на основе HashMap + Vec для отслеживания порядка
|
||||
pub struct LruCache<V> {
|
||||
map: HashMap<i64, V>,
|
||||
/// Порядок доступа: последний элемент — самый недавно использованный
|
||||
order: Vec<i64>,
|
||||
capacity: usize,
|
||||
}
|
||||
|
||||
impl<V: Clone> LruCache<V> {
|
||||
pub fn new(capacity: usize) -> Self {
|
||||
Self {
|
||||
map: HashMap::with_capacity(capacity),
|
||||
order: Vec::with_capacity(capacity),
|
||||
capacity,
|
||||
}
|
||||
}
|
||||
|
||||
/// Получить значение и обновить порядок доступа
|
||||
pub fn get(&mut self, key: &i64) -> Option<&V> {
|
||||
if self.map.contains_key(key) {
|
||||
// Перемещаем ключ в конец (самый недавно использованный)
|
||||
self.order.retain(|k| k != key);
|
||||
self.order.push(*key);
|
||||
self.map.get(key)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Получить значение без обновления порядка (для read-only доступа)
|
||||
pub fn peek(&self, key: &i64) -> Option<&V> {
|
||||
self.map.get(key)
|
||||
}
|
||||
|
||||
/// Вставить значение
|
||||
pub fn insert(&mut self, key: i64, value: V) {
|
||||
if self.map.contains_key(&key) {
|
||||
// Обновляем существующее значение
|
||||
self.map.insert(key, value);
|
||||
self.order.retain(|k| *k != key);
|
||||
self.order.push(key);
|
||||
} else {
|
||||
// Если кэш полон, удаляем самый старый элемент
|
||||
if self.map.len() >= self.capacity {
|
||||
if let Some(oldest) = self.order.first().copied() {
|
||||
self.order.remove(0);
|
||||
self.map.remove(&oldest);
|
||||
}
|
||||
}
|
||||
self.map.insert(key, value);
|
||||
self.order.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// Проверить наличие ключа
|
||||
pub fn contains_key(&self, key: &i64) -> bool {
|
||||
self.map.contains_key(key)
|
||||
}
|
||||
|
||||
/// Количество элементов
|
||||
#[allow(dead_code)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.map.len()
|
||||
}
|
||||
}
|
||||
use tdlib_rs::functions;
|
||||
use tdlib_rs::types::{Chat as TdChat, Message as TdMessage};
|
||||
|
||||
@@ -46,8 +119,18 @@ pub struct MessageInfo {
|
||||
pub sender_name: String,
|
||||
pub is_outgoing: bool,
|
||||
pub content: String,
|
||||
/// Сущности форматирования (bold, italic, code и т.д.)
|
||||
pub entities: Vec<TextEntity>,
|
||||
pub date: i32,
|
||||
/// Дата редактирования (0 если не редактировалось)
|
||||
pub edit_date: i32,
|
||||
pub is_read: bool,
|
||||
/// Можно ли редактировать сообщение
|
||||
pub can_be_edited: bool,
|
||||
/// Можно ли удалить только для себя
|
||||
pub can_be_deleted_only_for_self: bool,
|
||||
/// Можно ли удалить для всех
|
||||
pub can_be_deleted_for_all_users: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -97,10 +180,10 @@ pub struct TdClient {
|
||||
pub current_chat_messages: Vec<MessageInfo>,
|
||||
/// ID текущего открытого чата (для получения новых сообщений)
|
||||
pub current_chat_id: Option<i64>,
|
||||
/// Кэш usernames: user_id -> username
|
||||
user_usernames: HashMap<i64, String>,
|
||||
/// Кэш имён: user_id -> display_name (first_name + last_name)
|
||||
user_names: HashMap<i64, String>,
|
||||
/// LRU-кэш usernames: user_id -> username
|
||||
user_usernames: LruCache<String>,
|
||||
/// LRU-кэш имён: user_id -> display_name (first_name + last_name)
|
||||
user_names: LruCache<String>,
|
||||
/// Связь chat_id -> user_id для приватных чатов
|
||||
chat_user_ids: HashMap<i64, i64>,
|
||||
/// Очередь сообщений для отметки как прочитанных: (chat_id, message_ids)
|
||||
@@ -111,8 +194,8 @@ pub struct TdClient {
|
||||
pub folders: Vec<FolderInfo>,
|
||||
/// Позиция основного списка среди папок
|
||||
pub main_chat_list_position: i32,
|
||||
/// Онлайн-статусы пользователей: user_id -> status
|
||||
user_statuses: HashMap<i64, UserOnlineStatus>,
|
||||
/// LRU-кэш онлайн-статусов пользователей: user_id -> status
|
||||
user_statuses: LruCache<UserOnlineStatus>,
|
||||
/// Состояние сетевого соединения
|
||||
pub network_state: NetworkState,
|
||||
}
|
||||
@@ -136,14 +219,14 @@ impl TdClient {
|
||||
chats: Vec::new(),
|
||||
current_chat_messages: Vec::new(),
|
||||
current_chat_id: None,
|
||||
user_usernames: HashMap::new(),
|
||||
user_names: HashMap::new(),
|
||||
user_usernames: LruCache::new(MAX_USER_CACHE_SIZE),
|
||||
user_names: LruCache::new(MAX_USER_CACHE_SIZE),
|
||||
chat_user_ids: HashMap::new(),
|
||||
pending_view_messages: Vec::new(),
|
||||
pending_user_ids: Vec::new(),
|
||||
folders: Vec::new(),
|
||||
main_chat_list_position: 0,
|
||||
user_statuses: HashMap::new(),
|
||||
user_statuses: LruCache::new(MAX_USER_CACHE_SIZE),
|
||||
network_state: NetworkState::Connecting,
|
||||
}
|
||||
}
|
||||
@@ -156,23 +239,21 @@ impl TdClient {
|
||||
self.client_id
|
||||
}
|
||||
|
||||
/// Очистка кэшей если они превышают лимит
|
||||
fn trim_caches(&mut self) {
|
||||
if self.user_names.len() > MAX_USER_CACHE_SIZE {
|
||||
// Оставляем только пользователей из текущих чатов
|
||||
let active_user_ids: std::collections::HashSet<i64> =
|
||||
self.chat_user_ids.values().copied().collect();
|
||||
self.user_names.retain(|id, _| active_user_ids.contains(id));
|
||||
self.user_usernames.retain(|id, _| active_user_ids.contains(id));
|
||||
self.user_statuses.retain(|id, _| active_user_ids.contains(id));
|
||||
/// Добавляет сообщение в текущий чат с соблюдением лимита
|
||||
pub fn push_message(&mut self, msg: MessageInfo) {
|
||||
self.current_chat_messages.push(msg);
|
||||
// Ограничиваем количество сообщений (удаляем старые)
|
||||
if self.current_chat_messages.len() > MAX_MESSAGES_IN_CHAT {
|
||||
self.current_chat_messages.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Получение онлайн-статуса пользователя по chat_id (для приватных чатов)
|
||||
/// Использует peek для read-only доступа (не обновляет LRU порядок)
|
||||
pub fn get_user_status_by_chat_id(&self, chat_id: i64) -> Option<&UserOnlineStatus> {
|
||||
self.chat_user_ids
|
||||
.get(&chat_id)
|
||||
.and_then(|user_id| self.user_statuses.get(user_id))
|
||||
.and_then(|user_id| self.user_statuses.peek(user_id))
|
||||
}
|
||||
|
||||
/// Инициализация TDLib с параметрами
|
||||
@@ -216,7 +297,7 @@ impl TdClient {
|
||||
let (last_message_text, last_message_date) = update
|
||||
.last_message
|
||||
.as_ref()
|
||||
.map(|msg| (extract_message_text_static(msg), msg.date))
|
||||
.map(|msg| (extract_message_text_static(msg).0, msg.date))
|
||||
.unwrap_or_default();
|
||||
|
||||
if let Some(chat) = self.chats.iter_mut().find(|c| c.id == chat_id) {
|
||||
@@ -310,7 +391,7 @@ impl TdClient {
|
||||
let is_incoming = !msg_info.is_outgoing;
|
||||
// Проверяем, что сообщение ещё не добавлено (по id)
|
||||
if !self.current_chat_messages.iter().any(|m| m.id == msg_info.id) {
|
||||
self.current_chat_messages.push(msg_info);
|
||||
self.push_message(msg_info);
|
||||
// Если это входящее сообщение — добавляем в очередь для отметки как прочитанное
|
||||
if is_incoming {
|
||||
self.pending_view_messages.push((chat_id, vec![msg_id]));
|
||||
@@ -354,9 +435,7 @@ impl TdClient {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Периодически очищаем кэши
|
||||
self.trim_caches();
|
||||
// LRU-кэш автоматически удаляет старые записи при вставке
|
||||
}
|
||||
Update::ChatFolders(update) => {
|
||||
// Обновляем список папок
|
||||
@@ -429,15 +508,22 @@ impl TdClient {
|
||||
let (last_message, last_message_date) = td_chat
|
||||
.last_message
|
||||
.as_ref()
|
||||
.map(|m| (extract_message_text_static(m), m.date))
|
||||
.map(|m| (extract_message_text_static(m).0, m.date))
|
||||
.unwrap_or_default();
|
||||
|
||||
// Извлекаем user_id для приватных чатов и сохраняем связь
|
||||
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 let Some(&key) = self.chat_user_ids.keys().next() {
|
||||
self.chat_user_ids.remove(&key);
|
||||
}
|
||||
}
|
||||
self.chat_user_ids.insert(td_chat.id, private.user_id);
|
||||
// Проверяем, есть ли уже username в кэше
|
||||
self.user_usernames.get(&private.user_id).map(|u| format!("@{}", u))
|
||||
// Проверяем, есть ли уже username в кэше (peek не обновляет LRU)
|
||||
self.user_usernames.peek(&private.user_id).map(|u| format!("@{}", u))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
@@ -493,6 +579,13 @@ impl TdClient {
|
||||
}
|
||||
} else {
|
||||
self.chats.push(chat_info);
|
||||
// Ограничиваем количество чатов
|
||||
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) {
|
||||
self.chats.remove(min_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Сортируем чаты по order (TDLib order учитывает pinned и время)
|
||||
@@ -502,9 +595,9 @@ impl TdClient {
|
||||
fn convert_message(&mut self, message: &TdMessage, chat_id: i64) -> MessageInfo {
|
||||
let sender_name = match &message.sender_id {
|
||||
tdlib_rs::enums::MessageSender::User(user) => {
|
||||
// Пробуем получить имя из кеша
|
||||
if let Some(name) = self.user_names.get(&user.user_id) {
|
||||
name.clone()
|
||||
// Пробуем получить имя из кеша (get обновляет LRU порядок)
|
||||
if let Some(name) = self.user_names.get(&user.user_id).cloned() {
|
||||
name
|
||||
} else {
|
||||
// Добавляем в очередь для загрузки
|
||||
if !self.pending_user_ids.contains(&user.user_id) {
|
||||
@@ -535,13 +628,20 @@ impl TdClient {
|
||||
true // Входящие сообщения не показывают галочки
|
||||
};
|
||||
|
||||
let (content, entities) = extract_message_text_static(message);
|
||||
|
||||
MessageInfo {
|
||||
id: message.id,
|
||||
sender_name,
|
||||
is_outgoing: message.is_outgoing,
|
||||
content: extract_message_text_static(message),
|
||||
content,
|
||||
entities,
|
||||
date: message.date,
|
||||
edit_date: message.edit_date,
|
||||
is_read,
|
||||
can_be_edited: message.can_be_edited,
|
||||
can_be_deleted_only_for_self: message.can_be_deleted_only_for_self,
|
||||
can_be_deleted_for_all_users: message.can_be_deleted_for_all_users,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -760,16 +860,32 @@ impl TdClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// Отправка текстового сообщения
|
||||
/// Отправка текстового сообщения с поддержкой Markdown
|
||||
pub async fn send_message(&self, chat_id: i64, text: String) -> Result<MessageInfo, String> {
|
||||
use tdlib_rs::types::{FormattedText, InputMessageText};
|
||||
use tdlib_rs::enums::InputMessageContent;
|
||||
use tdlib_rs::types::{FormattedText, InputMessageText, TextParseModeMarkdown};
|
||||
use tdlib_rs::enums::{InputMessageContent, TextParseMode};
|
||||
|
||||
// Парсим 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,
|
||||
},
|
||||
Err(_) => {
|
||||
// Если парсинг не удался, отправляем как plain text
|
||||
FormattedText {
|
||||
text: text.clone(),
|
||||
entities: vec![],
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let content = InputMessageContent::InputMessageText(InputMessageText {
|
||||
text: FormattedText {
|
||||
text: text.clone(),
|
||||
entities: vec![],
|
||||
},
|
||||
text: formatted_text,
|
||||
link_preview_options: None,
|
||||
clear_draft: true,
|
||||
});
|
||||
@@ -786,20 +902,102 @@ impl TdClient {
|
||||
|
||||
match result {
|
||||
Ok(tdlib_rs::enums::Message::Message(msg)) => {
|
||||
// Конвертируем отправленное сообщение в MessageInfo
|
||||
// Извлекаем текст и entities из отправленного сообщения
|
||||
let (content, entities) = extract_message_text_static(&msg);
|
||||
Ok(MessageInfo {
|
||||
id: msg.id,
|
||||
sender_name: "You".to_string(),
|
||||
sender_name: "Вы".to_string(),
|
||||
is_outgoing: true,
|
||||
content: text,
|
||||
content,
|
||||
entities,
|
||||
date: msg.date,
|
||||
edit_date: msg.edit_date,
|
||||
is_read: false,
|
||||
can_be_edited: msg.can_be_edited,
|
||||
can_be_deleted_only_for_self: msg.can_be_deleted_only_for_self,
|
||||
can_be_deleted_for_all_users: msg.can_be_deleted_for_all_users,
|
||||
})
|
||||
}
|
||||
Err(e) => Err(format!("Ошибка отправки сообщения: {:?}", e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Редактирование текстового сообщения с поддержкой Markdown
|
||||
pub async fn edit_message(&self, chat_id: i64, message_id: i64, text: String) -> Result<MessageInfo, String> {
|
||||
use tdlib_rs::types::{FormattedText, InputMessageText, TextParseModeMarkdown};
|
||||
use tdlib_rs::enums::{InputMessageContent, TextParseMode};
|
||||
|
||||
// Парсим 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,
|
||||
},
|
||||
Err(_) => {
|
||||
// Если парсинг не удался, отправляем как plain text
|
||||
FormattedText {
|
||||
text: text.clone(),
|
||||
entities: vec![],
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let content = InputMessageContent::InputMessageText(InputMessageText {
|
||||
text: formatted_text,
|
||||
link_preview_options: None,
|
||||
clear_draft: true,
|
||||
});
|
||||
|
||||
let result = functions::edit_message_text(
|
||||
chat_id,
|
||||
message_id,
|
||||
content,
|
||||
self.client_id,
|
||||
)
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(tdlib_rs::enums::Message::Message(msg)) => {
|
||||
let (content, entities) = extract_message_text_static(&msg);
|
||||
Ok(MessageInfo {
|
||||
id: msg.id,
|
||||
sender_name: "Вы".to_string(),
|
||||
is_outgoing: true,
|
||||
content,
|
||||
entities,
|
||||
date: msg.date,
|
||||
edit_date: msg.edit_date,
|
||||
is_read: true,
|
||||
can_be_edited: msg.can_be_edited,
|
||||
can_be_deleted_only_for_self: msg.can_be_deleted_only_for_self,
|
||||
can_be_deleted_for_all_users: msg.can_be_deleted_for_all_users,
|
||||
})
|
||||
}
|
||||
Err(e) => Err(format!("Ошибка редактирования сообщения: {:?}", e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Удаление сообщений
|
||||
/// 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;
|
||||
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(format!("Ошибка удаления сообщения: {:?}", e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Обработка очереди сообщений для отметки как прочитанных
|
||||
pub async fn process_pending_view_messages(&mut self) {
|
||||
let pending = std::mem::take(&mut self.pending_view_messages);
|
||||
@@ -815,14 +1013,21 @@ impl TdClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// Обработка очереди user_id для загрузки имён
|
||||
/// Обработка очереди user_id для загрузки имён (lazy loading)
|
||||
/// Загружает только последние 5 запросов за цикл для снижения нагрузки
|
||||
pub async fn process_pending_user_ids(&mut self) {
|
||||
let pending = std::mem::take(&mut self.pending_user_ids);
|
||||
for user_id in pending {
|
||||
// Пропускаем если имя уже есть
|
||||
if self.user_names.contains_key(&user_id) {
|
||||
continue;
|
||||
}
|
||||
// Берём только последние запросы (они актуальнее — от недавних сообщений)
|
||||
const BATCH_SIZE: usize = 5;
|
||||
|
||||
// Убираем дубликаты и уже загруженные
|
||||
self.pending_user_ids.retain(|id| !self.user_names.contains_key(id));
|
||||
self.pending_user_ids.dedup();
|
||||
|
||||
// Берём последние BATCH_SIZE элементов
|
||||
let start = self.pending_user_ids.len().saturating_sub(BATCH_SIZE);
|
||||
let batch: Vec<i64> = self.pending_user_ids.drain(start..).collect();
|
||||
|
||||
for user_id in batch {
|
||||
// Загружаем информацию о пользователе
|
||||
if let Ok(User::User(user)) = functions::get_user(user_id, self.client_id).await {
|
||||
let display_name = if user.last_name.is_empty() {
|
||||
@@ -840,37 +1045,83 @@ impl TdClient {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ограничиваем размер очереди (старые запросы отбрасываем)
|
||||
const MAX_QUEUE_SIZE: usize = 50;
|
||||
if self.pending_user_ids.len() > MAX_QUEUE_SIZE {
|
||||
let excess = self.pending_user_ids.len() - MAX_QUEUE_SIZE;
|
||||
self.pending_user_ids.drain(0..excess);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Статическая функция для извлечения текста сообщения (без &self)
|
||||
fn extract_message_text_static(message: &TdMessage) -> String {
|
||||
/// Статическая функция для извлечения текста и entities сообщения (без &self)
|
||||
fn extract_message_text_static(message: &TdMessage) -> (String, Vec<TextEntity>) {
|
||||
match &message.content {
|
||||
MessageContent::MessageText(text) => text.text.text.clone(),
|
||||
MessageContent::MessageText(text) => {
|
||||
(text.text.text.clone(), text.text.entities.clone())
|
||||
}
|
||||
MessageContent::MessagePhoto(photo) => {
|
||||
if photo.caption.text.is_empty() {
|
||||
"[Фото]".to_string()
|
||||
("[Фото]".to_string(), vec![])
|
||||
} else {
|
||||
format!("[Фото] {}", photo.caption.text)
|
||||
// Добавляем смещение для "[Фото] " к entities
|
||||
let prefix_len = "[Фото] ".chars().count() as i32;
|
||||
let adjusted_entities: Vec<TextEntity> = photo.caption.entities.iter()
|
||||
.map(|e| TextEntity {
|
||||
offset: e.offset + prefix_len,
|
||||
length: e.length,
|
||||
r#type: e.r#type.clone(),
|
||||
})
|
||||
.collect();
|
||||
(format!("[Фото] {}", photo.caption.text), adjusted_entities)
|
||||
}
|
||||
}
|
||||
MessageContent::MessageVideo(video) => {
|
||||
if video.caption.text.is_empty() {
|
||||
("[Видео]".to_string(), vec![])
|
||||
} else {
|
||||
let prefix_len = "[Видео] ".chars().count() as i32;
|
||||
let adjusted_entities: Vec<TextEntity> = video.caption.entities.iter()
|
||||
.map(|e| TextEntity {
|
||||
offset: e.offset + prefix_len,
|
||||
length: e.length,
|
||||
r#type: e.r#type.clone(),
|
||||
})
|
||||
.collect();
|
||||
(format!("[Видео] {}", video.caption.text), adjusted_entities)
|
||||
}
|
||||
}
|
||||
MessageContent::MessageVideo(_) => "[Видео]".to_string(),
|
||||
MessageContent::MessageDocument(doc) => {
|
||||
format!("[Файл: {}]", doc.document.file_name)
|
||||
(format!("[Файл: {}]", doc.document.file_name), vec![])
|
||||
}
|
||||
MessageContent::MessageVoiceNote(_) => "[Голосовое сообщение]".to_string(),
|
||||
MessageContent::MessageVideoNote(_) => "[Видеосообщение]".to_string(),
|
||||
MessageContent::MessageVoiceNote(_) => ("[Голосовое сообщение]".to_string(), vec![]),
|
||||
MessageContent::MessageVideoNote(_) => ("[Видеосообщение]".to_string(), vec![]),
|
||||
MessageContent::MessageSticker(sticker) => {
|
||||
format!("[Стикер: {}]", sticker.sticker.emoji)
|
||||
(format!("[Стикер: {}]", sticker.sticker.emoji), vec![])
|
||||
}
|
||||
MessageContent::MessageAnimation(anim) => {
|
||||
if anim.caption.text.is_empty() {
|
||||
("[GIF]".to_string(), vec![])
|
||||
} else {
|
||||
let prefix_len = "[GIF] ".chars().count() as i32;
|
||||
let adjusted_entities: Vec<TextEntity> = anim.caption.entities.iter()
|
||||
.map(|e| TextEntity {
|
||||
offset: e.offset + prefix_len,
|
||||
length: e.length,
|
||||
r#type: e.r#type.clone(),
|
||||
})
|
||||
.collect();
|
||||
(format!("[GIF] {}", anim.caption.text), adjusted_entities)
|
||||
}
|
||||
}
|
||||
MessageContent::MessageAnimation(_) => "[GIF]".to_string(),
|
||||
MessageContent::MessageAudio(audio) => {
|
||||
format!("[Аудио: {}]", audio.audio.title)
|
||||
(format!("[Аудио: {}]", audio.audio.title), vec![])
|
||||
}
|
||||
MessageContent::MessageCall(_) => "[Звонок]".to_string(),
|
||||
MessageContent::MessageCall(_) => ("[Звонок]".to_string(), vec![]),
|
||||
MessageContent::MessagePoll(poll) => {
|
||||
format!("[Опрос: {}]", poll.poll.question.text)
|
||||
(format!("[Опрос: {}]", poll.poll.question.text), vec![])
|
||||
}
|
||||
_ => "[Сообщение]".to_string(),
|
||||
_ => ("[Сообщение]".to_string(), vec![]),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user