use crate::{ errors::HandlerUsed, markups::deletion_markup, models::{DecryptedAccount, User}, utils::delete_optional, MainDialogue, }; use futures::TryStreamExt; use itertools::Itertools; use sea_orm::prelude::*; use std::fmt::Write; use teloxide::{adaptors::Throttle, net::Download, prelude::*, types::Document}; use tokio::task::spawn_blocking; use trim_in_place::TrimInPlace; #[inline] async fn download_file(bot: &Throttle, document: &Document) -> crate::Result> { let path = bot.get_file(document.file.id.as_str()).await?.path; let mut data = Vec::with_capacity(document.file.size as usize); bot.download_file_stream(&path) .try_for_each(|bytes| { data.extend(bytes); async { Ok(()) } }) .await?; Ok(data) } #[inline] fn process_accounts(accounts: &mut [DecryptedAccount]) -> crate::Result> { for account in accounts.iter_mut() { account.name.trim_in_place(); account.login.trim_in_place(); account.password.trim_in_place(); } accounts.sort_unstable_by(|a, b| a.name.cmp(&b.name)); let mut duplicates = Vec::new(); let mut invalid = Vec::new(); accounts .iter() .dedup_by_with_count(|a, b| a.name == b.name) .for_each(|(count, account)| { if count != 1 { duplicates.push(account.name.as_str()); } else if !account.validate() { invalid.push(account.name.as_str()); } }); if duplicates.is_empty() && invalid.is_empty() { return Ok(Ok(())); } let mut error_text = "Your accounts have the following problems:".to_owned(); if !duplicates.is_empty() { write!( error_text, "\n\nDuplicate names:\n{:?}", duplicates.into_iter().format("\n") )? } if !invalid.is_empty() { write!( error_text, "\n\nInvalid account fields:\n{:?}", invalid.into_iter().format("\n") )? } error_text.push_str("\n\nFix these problems and send the file again"); Ok(Err(error_text)) } #[inline] fn user_from_vec(vector: Vec) -> crate::Result> { let mut user: User = serde_json::from_slice(&vector)?; drop(vector); match process_accounts(&mut user.accounts)? { Ok(()) => Ok(Ok(user)), Err(error_text) => Ok(Err(error_text)), } } /// Function to handle GetUser state. It doesn't actually validate anything pub async fn get_user( bot: Throttle, msg: Message, db: DatabaseConnection, dialogue: MainDialogue, next: super::PackagedHandler, ) -> crate::Result<()> { let mut handler = next.lock().await; delete_optional(&bot, handler.previous.as_ref()).await; if let Some("/cancel") = msg.text().map(str::trim_end) { dialogue.exit().await?; bot.send_message(msg.chat.id, "Successfully cancelled") .reply_markup(deletion_markup()) .await?; return Ok(()); } if handler.func.is_none() { let _ = dialogue.exit().await; return Err(HandlerUsed.into()); } let document = match msg.document() { Some(document) => document, None => { let msg = bot .send_message(msg.chat.id, "You didn't send a file. Try again") .await?; handler.previous = Some(msg); return Ok(()); } }; match document.file_name.as_deref() { Some(name) if name.trim_end().ends_with(".json") => (), _ => { let msg = bot .send_message( msg.chat.id, "Invalid file name. You need to send a json file. Try again", ) .await?; handler.previous = Some(msg); return Ok(()); } } let data = download_file(&bot, document).await?; let user = match spawn_blocking(move || user_from_vec(data)).await? { Ok(Ok(user)) => user, Ok(Err(error_text)) => { let msg = bot.send_message(msg.chat.id, error_text).await?; handler.previous = Some(msg); return Ok(()); } Err(_) => { let msg = bot .send_message(msg.chat.id, "Error parsing the json file. Try again") .await?; handler.previous = Some(msg); return Ok(()); } }; let func = handler.func.take().unwrap(); drop(handler); if let Err(err) = func(bot, msg, db, dialogue.clone(), user).await { let _ = dialogue.exit().await; return Err(err); } Ok(()) }