Cleaned up warnings by removing unused code: - Removed unused format_timestamp() function from utils.rs - Removed unused len() method from LruCache - Removed unused date field from ForwardInfo struct - Removed unnecessary #[allow(dead_code)] attributes from: * AuthState enum (actually used) * ChatInfo struct (actually used) * TdClient impl block (actually used) This reduces code noise and makes real warnings more visible. Changes: - 20 lines removed - 1 line added - 6 files changed Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
549 lines
16 KiB
Rust
549 lines
16 KiB
Rust
use tdlib_rs::types::TextEntity;
|
||
|
||
use crate::types::{ChatId, MessageId};
|
||
|
||
#[derive(Debug, Clone)]
|
||
pub struct ChatInfo {
|
||
pub id: ChatId,
|
||
pub title: String,
|
||
pub username: Option<String>,
|
||
pub last_message: String,
|
||
pub last_message_date: i32,
|
||
pub unread_count: i32,
|
||
/// Количество непрочитанных упоминаний (@)
|
||
pub unread_mention_count: i32,
|
||
pub is_pinned: bool,
|
||
pub order: i64,
|
||
/// ID последнего прочитанного исходящего сообщения (для галочек)
|
||
pub last_read_outbox_message_id: MessageId,
|
||
/// ID папок, в которых находится чат
|
||
pub folder_ids: Vec<i32>,
|
||
/// Чат замьючен (уведомления отключены)
|
||
pub is_muted: bool,
|
||
/// Черновик сообщения
|
||
pub draft_text: Option<String>,
|
||
}
|
||
|
||
/// Информация о сообщении, на которое отвечают
|
||
#[derive(Debug, Clone)]
|
||
pub struct ReplyInfo {
|
||
/// ID сообщения, на которое отвечают
|
||
pub message_id: MessageId,
|
||
/// Имя отправителя оригинального сообщения
|
||
pub sender_name: String,
|
||
/// Текст оригинального сообщения (превью)
|
||
pub text: String,
|
||
}
|
||
|
||
/// Информация о пересланном сообщении
|
||
#[derive(Debug, Clone)]
|
||
pub struct ForwardInfo {
|
||
/// Имя оригинального отправителя
|
||
pub sender_name: String,
|
||
}
|
||
|
||
/// Информация о реакции на сообщение
|
||
#[derive(Debug, Clone)]
|
||
pub struct ReactionInfo {
|
||
/// Эмодзи реакции (например, "👍")
|
||
pub emoji: String,
|
||
/// Количество людей, поставивших эту реакцию
|
||
pub count: i32,
|
||
/// Поставил ли текущий пользователь эту реакцию
|
||
pub is_chosen: bool,
|
||
}
|
||
|
||
/// Метаданные сообщения (ID, отправитель, время)
|
||
#[derive(Debug, Clone)]
|
||
pub struct MessageMetadata {
|
||
pub id: MessageId,
|
||
pub sender_name: String,
|
||
pub date: i32,
|
||
/// Дата редактирования (0 если не редактировалось)
|
||
pub edit_date: i32,
|
||
}
|
||
|
||
/// Контент сообщения (текст и форматирование)
|
||
#[derive(Debug, Clone, PartialEq)]
|
||
pub struct MessageContent {
|
||
pub text: String,
|
||
/// Сущности форматирования (bold, italic, code и т.д.)
|
||
pub entities: Vec<TextEntity>,
|
||
}
|
||
|
||
/// Состояние и права доступа к сообщению
|
||
#[derive(Debug, Clone)]
|
||
pub struct MessageState {
|
||
pub is_outgoing: bool,
|
||
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,
|
||
}
|
||
|
||
/// Взаимодействия с сообщением (reply, forward, reactions)
|
||
#[derive(Debug, Clone)]
|
||
pub struct MessageInteractions {
|
||
/// Информация о reply (если это ответ на сообщение)
|
||
pub reply_to: Option<ReplyInfo>,
|
||
/// Информация о forward (если сообщение переслано)
|
||
pub forward_from: Option<ForwardInfo>,
|
||
/// Реакции на сообщение
|
||
pub reactions: Vec<ReactionInfo>,
|
||
}
|
||
|
||
#[derive(Debug, Clone)]
|
||
pub struct MessageInfo {
|
||
pub metadata: MessageMetadata,
|
||
pub content: MessageContent,
|
||
pub state: MessageState,
|
||
pub interactions: MessageInteractions,
|
||
}
|
||
|
||
impl MessageInfo {
|
||
/// Создать новое сообщение
|
||
pub fn new(
|
||
id: MessageId,
|
||
sender_name: String,
|
||
is_outgoing: bool,
|
||
content: String,
|
||
entities: Vec<TextEntity>,
|
||
date: i32,
|
||
edit_date: i32,
|
||
is_read: bool,
|
||
can_be_edited: bool,
|
||
can_be_deleted_only_for_self: bool,
|
||
can_be_deleted_for_all_users: bool,
|
||
reply_to: Option<ReplyInfo>,
|
||
forward_from: Option<ForwardInfo>,
|
||
reactions: Vec<ReactionInfo>,
|
||
) -> Self {
|
||
Self {
|
||
metadata: MessageMetadata {
|
||
id,
|
||
sender_name,
|
||
date,
|
||
edit_date,
|
||
},
|
||
content: MessageContent {
|
||
text: content,
|
||
entities,
|
||
},
|
||
state: MessageState {
|
||
is_outgoing,
|
||
is_read,
|
||
can_be_edited,
|
||
can_be_deleted_only_for_self,
|
||
can_be_deleted_for_all_users,
|
||
},
|
||
interactions: MessageInteractions {
|
||
reply_to,
|
||
forward_from,
|
||
reactions,
|
||
},
|
||
}
|
||
}
|
||
|
||
// Удобные getter'ы для частых операций
|
||
pub fn id(&self) -> MessageId {
|
||
self.metadata.id
|
||
}
|
||
|
||
pub fn sender_name(&self) -> &str {
|
||
&self.metadata.sender_name
|
||
}
|
||
|
||
pub fn date(&self) -> i32 {
|
||
self.metadata.date
|
||
}
|
||
|
||
pub fn edit_date(&self) -> i32 {
|
||
self.metadata.edit_date
|
||
}
|
||
|
||
pub fn is_edited(&self) -> bool {
|
||
self.metadata.edit_date > 0
|
||
}
|
||
|
||
pub fn text(&self) -> &str {
|
||
&self.content.text
|
||
}
|
||
|
||
pub fn entities(&self) -> &[TextEntity] {
|
||
&self.content.entities
|
||
}
|
||
|
||
pub fn is_outgoing(&self) -> bool {
|
||
self.state.is_outgoing
|
||
}
|
||
|
||
pub fn is_read(&self) -> bool {
|
||
self.state.is_read
|
||
}
|
||
|
||
pub fn can_be_edited(&self) -> bool {
|
||
self.state.can_be_edited
|
||
}
|
||
|
||
pub fn can_be_deleted_only_for_self(&self) -> bool {
|
||
self.state.can_be_deleted_only_for_self
|
||
}
|
||
|
||
pub fn can_be_deleted_for_all_users(&self) -> bool {
|
||
self.state.can_be_deleted_for_all_users
|
||
}
|
||
|
||
pub fn reply_to(&self) -> Option<&ReplyInfo> {
|
||
self.interactions.reply_to.as_ref()
|
||
}
|
||
|
||
pub fn forward_from(&self) -> Option<&ForwardInfo> {
|
||
self.interactions.forward_from.as_ref()
|
||
}
|
||
|
||
pub fn reactions(&self) -> &[ReactionInfo] {
|
||
&self.interactions.reactions
|
||
}
|
||
}
|
||
|
||
/// Builder для удобного создания MessageInfo с fluent API
|
||
///
|
||
/// # Примеры
|
||
///
|
||
/// ```
|
||
/// use tele_tui::tdlib::MessageBuilder;
|
||
/// use tele_tui::types::MessageId;
|
||
///
|
||
/// let message = MessageBuilder::new(MessageId::new(123))
|
||
/// .sender_name("Alice")
|
||
/// .text("Hello, world!")
|
||
/// .outgoing()
|
||
/// .date(1640000000)
|
||
/// .build();
|
||
/// ```
|
||
pub struct MessageBuilder {
|
||
id: MessageId,
|
||
sender_name: String,
|
||
is_outgoing: bool,
|
||
text: String,
|
||
entities: Vec<TextEntity>,
|
||
date: i32,
|
||
edit_date: i32,
|
||
is_read: bool,
|
||
can_be_edited: bool,
|
||
can_be_deleted_only_for_self: bool,
|
||
can_be_deleted_for_all_users: bool,
|
||
reply_to: Option<ReplyInfo>,
|
||
forward_from: Option<ForwardInfo>,
|
||
reactions: Vec<ReactionInfo>,
|
||
}
|
||
|
||
impl MessageBuilder {
|
||
/// Создать новый builder с обязательным ID сообщения
|
||
pub fn new(id: MessageId) -> Self {
|
||
Self {
|
||
id,
|
||
sender_name: String::new(),
|
||
is_outgoing: false,
|
||
text: String::new(),
|
||
entities: Vec::new(),
|
||
date: 0,
|
||
edit_date: 0,
|
||
is_read: false,
|
||
can_be_edited: false,
|
||
can_be_deleted_only_for_self: true,
|
||
can_be_deleted_for_all_users: false,
|
||
reply_to: None,
|
||
forward_from: None,
|
||
reactions: Vec::new(),
|
||
}
|
||
}
|
||
|
||
/// Установить имя отправителя
|
||
pub fn sender_name(mut self, name: impl Into<String>) -> Self {
|
||
self.sender_name = name.into();
|
||
self
|
||
}
|
||
|
||
/// Пометить сообщение как исходящее
|
||
pub fn outgoing(mut self) -> Self {
|
||
self.is_outgoing = true;
|
||
self.can_be_edited = true;
|
||
self.can_be_deleted_for_all_users = true;
|
||
self
|
||
}
|
||
|
||
/// Пометить сообщение как входящее
|
||
pub fn incoming(mut self) -> Self {
|
||
self.is_outgoing = false;
|
||
self.can_be_edited = false;
|
||
self.can_be_deleted_for_all_users = false;
|
||
self
|
||
}
|
||
|
||
/// Установить текст сообщения
|
||
pub fn text(mut self, text: impl Into<String>) -> Self {
|
||
self.text = text.into();
|
||
self
|
||
}
|
||
|
||
/// Установить entities для форматирования
|
||
pub fn entities(mut self, entities: Vec<TextEntity>) -> Self {
|
||
self.entities = entities;
|
||
self
|
||
}
|
||
|
||
/// Установить дату сообщения (unix timestamp)
|
||
pub fn date(mut self, date: i32) -> Self {
|
||
self.date = date;
|
||
self
|
||
}
|
||
|
||
/// Установить дату редактирования (unix timestamp)
|
||
pub fn edit_date(mut self, edit_date: i32) -> Self {
|
||
self.edit_date = edit_date;
|
||
self
|
||
}
|
||
|
||
/// Пометить сообщение как отредактированное (edit_date = date + 60)
|
||
pub fn edited(mut self) -> Self {
|
||
self.edit_date = self.date + 60;
|
||
self
|
||
}
|
||
|
||
/// Пометить сообщение как прочитанное
|
||
pub fn read(mut self) -> Self {
|
||
self.is_read = true;
|
||
self
|
||
}
|
||
|
||
/// Пометить сообщение как непрочитанное
|
||
pub fn unread(mut self) -> Self {
|
||
self.is_read = false;
|
||
self
|
||
}
|
||
|
||
/// Разрешить редактирование
|
||
pub fn editable(mut self) -> Self {
|
||
self.can_be_edited = true;
|
||
self
|
||
}
|
||
|
||
/// Разрешить удаление только для себя
|
||
pub fn deletable_for_self(mut self) -> Self {
|
||
self.can_be_deleted_only_for_self = true;
|
||
self
|
||
}
|
||
|
||
/// Разрешить удаление для всех
|
||
pub fn deletable_for_all(mut self) -> Self {
|
||
self.can_be_deleted_for_all_users = true;
|
||
self
|
||
}
|
||
|
||
/// Установить информацию об ответе
|
||
pub fn reply_to(mut self, reply: ReplyInfo) -> Self {
|
||
self.reply_to = Some(reply);
|
||
self
|
||
}
|
||
|
||
/// Установить информацию о пересылке
|
||
pub fn forward_from(mut self, forward: ForwardInfo) -> Self {
|
||
self.forward_from = Some(forward);
|
||
self
|
||
}
|
||
|
||
/// Установить реакции
|
||
pub fn reactions(mut self, reactions: Vec<ReactionInfo>) -> Self {
|
||
self.reactions = reactions;
|
||
self
|
||
}
|
||
|
||
/// Добавить одну реакцию
|
||
pub fn add_reaction(mut self, reaction: ReactionInfo) -> Self {
|
||
self.reactions.push(reaction);
|
||
self
|
||
}
|
||
|
||
/// Построить MessageInfo из данных builder'а
|
||
pub fn build(self) -> MessageInfo {
|
||
MessageInfo::new(
|
||
self.id,
|
||
self.sender_name,
|
||
self.is_outgoing,
|
||
self.text,
|
||
self.entities,
|
||
self.date,
|
||
self.edit_date,
|
||
self.is_read,
|
||
self.can_be_edited,
|
||
self.can_be_deleted_only_for_self,
|
||
self.can_be_deleted_for_all_users,
|
||
self.reply_to,
|
||
self.forward_from,
|
||
self.reactions,
|
||
)
|
||
}
|
||
}
|
||
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
use crate::types::MessageId;
|
||
|
||
#[test]
|
||
fn test_message_builder_basic() {
|
||
let message = MessageBuilder::new(MessageId::new(123))
|
||
.sender_name("Alice")
|
||
.text("Hello, world!")
|
||
.date(1640000000)
|
||
.build();
|
||
|
||
assert_eq!(message.id(), MessageId::new(123));
|
||
assert_eq!(message.sender_name(), "Alice");
|
||
assert_eq!(message.text(), "Hello, world!");
|
||
assert_eq!(message.date(), 1640000000);
|
||
assert!(!message.is_outgoing());
|
||
}
|
||
|
||
#[test]
|
||
fn test_message_builder_outgoing() {
|
||
let message = MessageBuilder::new(MessageId::new(456))
|
||
.sender_name("Me")
|
||
.text("Test message")
|
||
.outgoing()
|
||
.read()
|
||
.build();
|
||
|
||
assert!(message.is_outgoing());
|
||
assert!(message.is_read());
|
||
assert!(message.can_be_edited());
|
||
assert!(message.can_be_deleted_for_all_users());
|
||
}
|
||
|
||
#[test]
|
||
fn test_message_builder_edited() {
|
||
let message = MessageBuilder::new(MessageId::new(789))
|
||
.text("Original text")
|
||
.date(1640000000)
|
||
.edited()
|
||
.build();
|
||
|
||
assert!(message.is_edited());
|
||
assert_eq!(message.edit_date(), 1640000060);
|
||
}
|
||
|
||
#[test]
|
||
fn test_message_builder_with_reply() {
|
||
let reply = ReplyInfo {
|
||
message_id: MessageId::new(100),
|
||
sender_name: "Bob".to_string(),
|
||
text: "Original message".to_string(),
|
||
};
|
||
|
||
let message = MessageBuilder::new(MessageId::new(200))
|
||
.text("Reply text")
|
||
.reply_to(reply)
|
||
.build();
|
||
|
||
assert!(message.reply_to().is_some());
|
||
assert_eq!(message.reply_to().unwrap().sender_name, "Bob");
|
||
}
|
||
|
||
#[test]
|
||
fn test_message_builder_with_reactions() {
|
||
let reaction = ReactionInfo {
|
||
emoji: "👍".to_string(),
|
||
count: 5,
|
||
is_chosen: true,
|
||
};
|
||
|
||
let message = MessageBuilder::new(MessageId::new(300))
|
||
.text("Cool message")
|
||
.add_reaction(reaction.clone())
|
||
.build();
|
||
|
||
assert_eq!(message.reactions().len(), 1);
|
||
assert_eq!(message.reactions()[0].emoji, "👍");
|
||
assert_eq!(message.reactions()[0].count, 5);
|
||
}
|
||
|
||
#[test]
|
||
fn test_message_builder_fluent_api() {
|
||
let message = MessageBuilder::new(MessageId::new(999))
|
||
.sender_name("Charlie")
|
||
.text("Complex message")
|
||
.date(1640000000)
|
||
.outgoing()
|
||
.read()
|
||
.editable()
|
||
.deletable_for_all()
|
||
.build();
|
||
|
||
assert_eq!(message.sender_name(), "Charlie");
|
||
assert_eq!(message.text(), "Complex message");
|
||
assert!(message.is_outgoing());
|
||
assert!(message.is_read());
|
||
assert!(message.can_be_edited());
|
||
assert!(message.can_be_deleted_for_all_users());
|
||
}
|
||
}
|
||
|
||
#[derive(Debug, Clone)]
|
||
pub struct FolderInfo {
|
||
pub id: i32,
|
||
pub name: String,
|
||
}
|
||
|
||
/// Информация о профиле чата/пользователя
|
||
#[derive(Debug, Clone)]
|
||
pub struct ProfileInfo {
|
||
pub chat_id: ChatId,
|
||
pub title: String,
|
||
pub username: Option<String>,
|
||
pub bio: Option<String>,
|
||
pub phone_number: Option<String>,
|
||
pub chat_type: String, // "Личный чат", "Группа", "Канал"
|
||
pub member_count: Option<i32>,
|
||
pub description: Option<String>,
|
||
pub invite_link: Option<String>,
|
||
pub is_group: bool,
|
||
pub online_status: Option<String>,
|
||
}
|
||
|
||
/// Состояние сетевого соединения
|
||
#[derive(Debug, Clone, PartialEq)]
|
||
pub enum NetworkState {
|
||
/// Ожидание подключения к сети
|
||
WaitingForNetwork,
|
||
/// Подключение к прокси
|
||
ConnectingToProxy,
|
||
/// Подключение к серверам Telegram
|
||
Connecting,
|
||
/// Обновление данных
|
||
Updating,
|
||
/// Подключено
|
||
Ready,
|
||
}
|
||
|
||
/// Онлайн-статус пользователя
|
||
#[derive(Debug, Clone, PartialEq)]
|
||
pub enum UserOnlineStatus {
|
||
/// Онлайн
|
||
Online,
|
||
/// Был недавно (менее часа назад)
|
||
Recently,
|
||
/// Был на этой неделе
|
||
LastWeek,
|
||
/// Был в этом месяце
|
||
LastMonth,
|
||
/// Давно не был
|
||
LongTimeAgo,
|
||
/// Оффлайн с указанием времени (unix timestamp)
|
||
Offline(i32),
|
||
}
|