459 lines
13 KiB
Rust
459 lines
13 KiB
Rust
use super::{
|
|
DeletedMessages, EditedMessage, FakeTdClient, ForwardedMessages, SearchQuery, SentMessage,
|
|
TdUpdate,
|
|
};
|
|
use tele_tui::tdlib::types::ReactionInfo;
|
|
use tele_tui::tdlib::{ChatInfo, MessageInfo, ProfileInfo, ReplyInfo};
|
|
use tele_tui::types::{ChatId, MessageId, UserId};
|
|
|
|
#[allow(dead_code)]
|
|
impl FakeTdClient {
|
|
pub async fn load_chats(&self, limit: usize) -> Result<Vec<ChatInfo>, String> {
|
|
if self.should_fail() {
|
|
return Err("Failed to load chats".to_string());
|
|
}
|
|
|
|
if self.simulate_delays {
|
|
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
|
|
}
|
|
|
|
let chats = self
|
|
.chats
|
|
.lock()
|
|
.unwrap()
|
|
.iter()
|
|
.take(limit)
|
|
.cloned()
|
|
.collect();
|
|
Ok(chats)
|
|
}
|
|
|
|
pub async fn open_chat(&self, chat_id: ChatId) -> Result<(), String> {
|
|
if self.should_fail() {
|
|
return Err("Failed to open chat".to_string());
|
|
}
|
|
|
|
*self.current_chat_id.lock().unwrap() = Some(chat_id.as_i64());
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_chat_history(
|
|
&self,
|
|
chat_id: ChatId,
|
|
limit: i32,
|
|
) -> Result<Vec<MessageInfo>, String> {
|
|
if self.should_fail() {
|
|
return Err("Failed to load history".to_string());
|
|
}
|
|
|
|
if self.simulate_delays {
|
|
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
|
}
|
|
|
|
let messages = self
|
|
.messages
|
|
.lock()
|
|
.unwrap()
|
|
.get(&chat_id.as_i64())
|
|
.map(|msgs| msgs.iter().take(limit as usize).cloned().collect())
|
|
.unwrap_or_default();
|
|
|
|
Ok(messages)
|
|
}
|
|
|
|
pub async fn load_older_messages(
|
|
&self,
|
|
chat_id: ChatId,
|
|
from_message_id: MessageId,
|
|
) -> Result<Vec<MessageInfo>, String> {
|
|
if self.should_fail() {
|
|
return Err("Failed to load older messages".to_string());
|
|
}
|
|
|
|
let messages = self.messages.lock().unwrap();
|
|
let chat_messages = messages.get(&chat_id.as_i64()).ok_or("Chat not found")?;
|
|
|
|
if let Some(idx) = chat_messages.iter().position(|m| m.id() == from_message_id) {
|
|
let older = chat_messages.iter().take(idx).cloned().collect();
|
|
Ok(older)
|
|
} else {
|
|
Ok(vec![])
|
|
}
|
|
}
|
|
|
|
pub async fn send_message(
|
|
&self,
|
|
chat_id: ChatId,
|
|
text: String,
|
|
reply_to: Option<MessageId>,
|
|
reply_info: Option<ReplyInfo>,
|
|
) -> Result<MessageInfo, String> {
|
|
if self.should_fail() {
|
|
return Err("Failed to send message".to_string());
|
|
}
|
|
|
|
if self.simulate_delays {
|
|
tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
|
|
}
|
|
|
|
let message_id = MessageId::new((self.sent_messages.lock().unwrap().len() as i64) + 1000);
|
|
|
|
self.sent_messages.lock().unwrap().push(SentMessage {
|
|
chat_id: chat_id.as_i64(),
|
|
text: text.clone(),
|
|
reply_to,
|
|
reply_info: reply_info.clone(),
|
|
});
|
|
|
|
let message = MessageInfo::new(
|
|
message_id,
|
|
"You".to_string(),
|
|
true,
|
|
text,
|
|
vec![],
|
|
chrono::Utc::now().timestamp() as i32,
|
|
0,
|
|
false,
|
|
true,
|
|
true,
|
|
true,
|
|
reply_info,
|
|
None,
|
|
vec![],
|
|
);
|
|
|
|
self.messages
|
|
.lock()
|
|
.unwrap()
|
|
.entry(chat_id.as_i64())
|
|
.or_default()
|
|
.push(message.clone());
|
|
|
|
self.send_update(TdUpdate::NewMessage { chat_id, message: Box::new(message.clone()) });
|
|
|
|
Ok(message)
|
|
}
|
|
|
|
pub async fn edit_message(
|
|
&self,
|
|
chat_id: ChatId,
|
|
message_id: MessageId,
|
|
new_text: String,
|
|
) -> Result<MessageInfo, String> {
|
|
if self.should_fail() {
|
|
return Err("Failed to edit message".to_string());
|
|
}
|
|
|
|
if self.simulate_delays {
|
|
tokio::time::sleep(tokio::time::Duration::from_millis(150)).await;
|
|
}
|
|
|
|
self.edited_messages.lock().unwrap().push(EditedMessage {
|
|
chat_id: chat_id.as_i64(),
|
|
message_id,
|
|
new_text: new_text.clone(),
|
|
});
|
|
|
|
let mut messages = self.messages.lock().unwrap();
|
|
if let Some(chat_msgs) = messages.get_mut(&chat_id.as_i64()) {
|
|
if let Some(msg) = chat_msgs.iter_mut().find(|m| m.id() == message_id) {
|
|
msg.content.text = new_text.clone();
|
|
msg.metadata.edit_date = msg.metadata.date + 60;
|
|
|
|
let updated = msg.clone();
|
|
drop(messages);
|
|
|
|
self.send_update(TdUpdate::MessageContent { chat_id, message_id, new_text });
|
|
|
|
return Ok(updated);
|
|
}
|
|
}
|
|
|
|
Err("Message not found".to_string())
|
|
}
|
|
|
|
pub async fn delete_messages(
|
|
&self,
|
|
chat_id: ChatId,
|
|
message_ids: Vec<MessageId>,
|
|
revoke: bool,
|
|
) -> Result<(), String> {
|
|
if self.should_fail() {
|
|
return Err("Failed to delete messages".to_string());
|
|
}
|
|
|
|
if self.simulate_delays {
|
|
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
|
}
|
|
|
|
self.deleted_messages.lock().unwrap().push(DeletedMessages {
|
|
chat_id: chat_id.as_i64(),
|
|
message_ids: message_ids.clone(),
|
|
revoke,
|
|
});
|
|
|
|
let mut messages = self.messages.lock().unwrap();
|
|
if let Some(chat_msgs) = messages.get_mut(&chat_id.as_i64()) {
|
|
chat_msgs.retain(|m| !message_ids.contains(&m.id()));
|
|
}
|
|
drop(messages);
|
|
|
|
self.send_update(TdUpdate::DeleteMessages { chat_id, message_ids });
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn forward_messages(
|
|
&self,
|
|
to_chat_id: ChatId,
|
|
from_chat_id: ChatId,
|
|
message_ids: Vec<MessageId>,
|
|
) -> Result<(), String> {
|
|
if self.should_fail() {
|
|
return Err("Failed to forward messages".to_string());
|
|
}
|
|
|
|
if self.simulate_delays {
|
|
tokio::time::sleep(tokio::time::Duration::from_millis(150)).await;
|
|
}
|
|
|
|
self.forwarded_messages
|
|
.lock()
|
|
.unwrap()
|
|
.push(ForwardedMessages {
|
|
from_chat_id: from_chat_id.as_i64(),
|
|
to_chat_id: to_chat_id.as_i64(),
|
|
message_ids,
|
|
});
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn search_messages(
|
|
&self,
|
|
chat_id: ChatId,
|
|
query: &str,
|
|
) -> Result<Vec<MessageInfo>, String> {
|
|
if self.should_fail() {
|
|
return Err("Failed to search messages".to_string());
|
|
}
|
|
|
|
let messages = self.messages.lock().unwrap();
|
|
let results: Vec<_> = messages
|
|
.get(&chat_id.as_i64())
|
|
.map(|msgs| {
|
|
msgs.iter()
|
|
.filter(|m| m.text().to_lowercase().contains(&query.to_lowercase()))
|
|
.cloned()
|
|
.collect()
|
|
})
|
|
.unwrap_or_default();
|
|
|
|
self.searched_queries.lock().unwrap().push(SearchQuery {
|
|
chat_id: chat_id.as_i64(),
|
|
query: query.to_string(),
|
|
results_count: results.len(),
|
|
});
|
|
|
|
Ok(results)
|
|
}
|
|
|
|
pub async fn set_draft_message(&self, chat_id: ChatId, text: String) -> Result<(), String> {
|
|
if text.is_empty() {
|
|
self.drafts.lock().unwrap().remove(&chat_id.as_i64());
|
|
} else {
|
|
self.drafts
|
|
.lock()
|
|
.unwrap()
|
|
.insert(chat_id.as_i64(), text.clone());
|
|
}
|
|
|
|
self.send_update(TdUpdate::ChatDraftMessage {
|
|
chat_id,
|
|
draft_text: if text.is_empty() { None } else { Some(text) },
|
|
});
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn send_chat_action(&self, chat_id: ChatId, action: String) {
|
|
self.chat_actions
|
|
.lock()
|
|
.unwrap()
|
|
.push((chat_id.as_i64(), action.clone()));
|
|
|
|
if action == "Typing" {
|
|
*self.typing_chat_id.lock().unwrap() = Some(chat_id.as_i64());
|
|
} else if action == "Cancel" {
|
|
*self.typing_chat_id.lock().unwrap() = None;
|
|
}
|
|
}
|
|
|
|
pub async fn get_message_available_reactions(
|
|
&self,
|
|
_chat_id: ChatId,
|
|
_message_id: MessageId,
|
|
) -> Result<Vec<String>, String> {
|
|
if self.should_fail() {
|
|
return Err("Failed to get available reactions".to_string());
|
|
}
|
|
|
|
Ok(self.available_reactions.lock().unwrap().clone())
|
|
}
|
|
|
|
pub async fn toggle_reaction(
|
|
&self,
|
|
chat_id: ChatId,
|
|
message_id: MessageId,
|
|
emoji: String,
|
|
) -> Result<(), String> {
|
|
if self.should_fail() {
|
|
return Err("Failed to toggle reaction".to_string());
|
|
}
|
|
|
|
let mut messages = self.messages.lock().unwrap();
|
|
if let Some(chat_msgs) = messages.get_mut(&chat_id.as_i64()) {
|
|
if let Some(msg) = chat_msgs.iter_mut().find(|m| m.id() == message_id) {
|
|
let reactions = &mut msg.interactions.reactions;
|
|
|
|
if let Some(pos) = reactions
|
|
.iter()
|
|
.position(|reaction| reaction.emoji == emoji && reaction.is_chosen)
|
|
{
|
|
reactions.remove(pos);
|
|
} else if let Some(reaction) = reactions
|
|
.iter_mut()
|
|
.find(|reaction| reaction.emoji == emoji)
|
|
{
|
|
reaction.is_chosen = true;
|
|
reaction.count += 1;
|
|
} else {
|
|
reactions.push(ReactionInfo {
|
|
emoji: emoji.clone(),
|
|
count: 1,
|
|
is_chosen: true,
|
|
});
|
|
}
|
|
|
|
let updated_reactions = reactions.clone();
|
|
drop(messages);
|
|
|
|
self.send_update(TdUpdate::MessageInteractionInfo {
|
|
chat_id,
|
|
message_id,
|
|
reactions: updated_reactions,
|
|
});
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn download_file(&self, file_id: i32) -> Result<String, String> {
|
|
if self.should_fail() {
|
|
return Err("Failed to download file".to_string());
|
|
}
|
|
|
|
self.downloaded_files
|
|
.lock()
|
|
.unwrap()
|
|
.get(&file_id)
|
|
.cloned()
|
|
.ok_or_else(|| format!("File {} not found", file_id))
|
|
}
|
|
|
|
pub async fn get_profile_info(&self, chat_id: ChatId) -> Result<ProfileInfo, String> {
|
|
if self.should_fail() {
|
|
return Err("Failed to get profile info".to_string());
|
|
}
|
|
|
|
self.profiles
|
|
.lock()
|
|
.unwrap()
|
|
.get(&chat_id.as_i64())
|
|
.cloned()
|
|
.ok_or_else(|| "Profile not found".to_string())
|
|
}
|
|
|
|
pub async fn view_messages(&self, chat_id: ChatId, message_ids: Vec<MessageId>) {
|
|
self.viewed_messages
|
|
.lock()
|
|
.unwrap()
|
|
.push((chat_id.as_i64(), message_ids.iter().map(|id| id.as_i64()).collect()));
|
|
}
|
|
|
|
pub async fn load_folder_chats(&self, _folder_id: i32, _limit: usize) -> Result<(), String> {
|
|
if self.should_fail() {
|
|
return Err("Failed to load folder chats".to_string());
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn send_update(&self, update: TdUpdate) {
|
|
if let Some(tx) = self.update_tx.lock().unwrap().as_ref() {
|
|
let _ = tx.send(update);
|
|
}
|
|
}
|
|
|
|
fn should_fail(&self) -> bool {
|
|
let mut fail = self.fail_next_operation.lock().unwrap();
|
|
if *fail {
|
|
*fail = false;
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
pub fn fail_next(&self) {
|
|
*self.fail_next_operation.lock().unwrap() = true;
|
|
}
|
|
|
|
pub fn simulate_incoming_message(&self, chat_id: ChatId, text: String, sender_name: &str) {
|
|
let message_id = MessageId::new(9000 + chrono::Utc::now().timestamp());
|
|
|
|
let message = MessageInfo::new(
|
|
message_id,
|
|
sender_name.to_string(),
|
|
false,
|
|
text,
|
|
vec![],
|
|
chrono::Utc::now().timestamp() as i32,
|
|
0,
|
|
false,
|
|
false,
|
|
false,
|
|
true,
|
|
None,
|
|
None,
|
|
vec![],
|
|
);
|
|
|
|
self.messages
|
|
.lock()
|
|
.unwrap()
|
|
.entry(chat_id.as_i64())
|
|
.or_default()
|
|
.push(message.clone());
|
|
|
|
self.send_update(TdUpdate::NewMessage { chat_id, message: Box::new(message) });
|
|
}
|
|
|
|
pub fn simulate_typing(&self, chat_id: ChatId, user_id: UserId) {
|
|
self.send_update(TdUpdate::ChatAction { chat_id, user_id, action: "Typing".to_string() });
|
|
}
|
|
|
|
pub fn simulate_network_change(&self, state: tele_tui::tdlib::NetworkState) {
|
|
*self.network_state.lock().unwrap() = state.clone();
|
|
self.send_update(TdUpdate::ConnectionState { state });
|
|
}
|
|
|
|
pub fn simulate_read_outbox(&self, chat_id: ChatId, last_read_message_id: MessageId) {
|
|
self.send_update(TdUpdate::ChatReadOutbox {
|
|
chat_id,
|
|
last_read_outbox_message_id: last_read_message_id,
|
|
});
|
|
}
|
|
}
|