Updated GetUsers to provide better inforamtion about invalid accounts to a user
This commit is contained in:
		@@ -45,9 +45,13 @@ impl DecryptedAccount {
 | 
			
		||||
    /// Returns true if the account's fields are valid
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn validate(&self) -> bool {
 | 
			
		||||
        [&self.name, &self.login, &self.password]
 | 
			
		||||
            .into_iter()
 | 
			
		||||
            .all(|field| validate_field(field))
 | 
			
		||||
        [
 | 
			
		||||
            self.name.as_str(),
 | 
			
		||||
            self.login.as_str(),
 | 
			
		||||
            self.password.as_str(),
 | 
			
		||||
        ]
 | 
			
		||||
        .into_iter()
 | 
			
		||||
        .all(validate_field)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,91 @@
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::HandlerUsed, markups::deletion_markup, models::User, utils::delete_optional,
 | 
			
		||||
    errors::HandlerUsed,
 | 
			
		||||
    markups::deletion_markup,
 | 
			
		||||
    models::{DecryptedAccount, User},
 | 
			
		||||
    utils::delete_optional,
 | 
			
		||||
    MainDialogue,
 | 
			
		||||
};
 | 
			
		||||
use futures::TryStreamExt;
 | 
			
		||||
use itertools::Itertools;
 | 
			
		||||
use sea_orm::prelude::*;
 | 
			
		||||
use teloxide::{adaptors::Throttle, net::Download, 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<Bot>, document: &Document) -> crate::Result<Vec<u8>> {
 | 
			
		||||
    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<Result<(), String>> {
 | 
			
		||||
    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<u8>) -> crate::Result<Result<User, String>> {
 | 
			
		||||
    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(
 | 
			
		||||
@@ -56,17 +136,15 @@ pub async fn get_user(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let file = bot.get_file(&document.file.id).await?;
 | 
			
		||||
    let mut data = Vec::with_capacity(document.file.size as usize);
 | 
			
		||||
    bot.download_file_stream(&file.path)
 | 
			
		||||
        .try_for_each(|bytes| {
 | 
			
		||||
            data.extend(bytes);
 | 
			
		||||
            async { Ok(()) }
 | 
			
		||||
        })
 | 
			
		||||
        .await?;
 | 
			
		||||
    let data = download_file(&bot, document).await?;
 | 
			
		||||
 | 
			
		||||
    let user: User = match spawn_blocking(move || serde_json::from_slice(&data)).await? {
 | 
			
		||||
        Ok(user) => user,
 | 
			
		||||
    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")
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,10 @@ pub async fn delete_optional(bot: &Throttle<Bot>, msg: Option<&Message>) {
 | 
			
		||||
/// Returns true if the field is valid
 | 
			
		||||
#[inline]
 | 
			
		||||
pub fn validate_field(field: &str) -> bool {
 | 
			
		||||
    if field.is_empty() {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    field
 | 
			
		||||
        .chars()
 | 
			
		||||
        .all(|char| char != '`' && char != '\\' && char != '\n')
 | 
			
		||||
        .all(|char| !['`', '\\', '\n', '\t'].contains(&char))
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user