pass_manager/src/state/get_user.rs

233 lines
6.5 KiB
Rust
Raw Normal View History

use crate::prelude::*;
use futures::TryFutureExt;
use itertools::Itertools;
use std::{borrow::Cow, fmt::Write, path::Path};
use teloxide::{
net::Download,
types::{Document, FileMeta},
};
use tokio::{task::spawn_blocking, try_join};
use trim_in_place::TrimInPlace;
2024-04-16 13:02:48 +00:00
#[derive(Clone, Copy)]
enum InvalidDocument {
NoFileSend,
FileTooLarge,
CouldntGetFileName,
InvalidFileName,
}
impl InvalidDocument {
const fn into_str(self, locale: LocaleRef) -> &'static str {
match self {
Self::NoFileSend => &locale.no_file_send,
Self::FileTooLarge => &locale.file_too_large,
Self::CouldntGetFileName => &locale.couldnt_get_file_name,
Self::InvalidFileName => &locale.invalid_file_name,
}
}
}
#[inline]
2024-04-16 13:02:48 +00:00
fn validate_document(document: Option<&Document>) -> Result<&Document, InvalidDocument> {
2023-11-16 18:51:46 +00:00
let Some(document) = document else {
2024-04-16 13:02:48 +00:00
return Err(InvalidDocument::NoFileSend);
};
2023-07-20 20:42:40 +00:00
if document.file.size > 1024 * 1024 * 200 {
2024-04-16 13:02:48 +00:00
return Err(InvalidDocument::FileTooLarge);
2023-07-20 20:42:40 +00:00
}
let name = match document.file_name.as_deref() {
Some(name) => Path::new(name.trim_end()),
2024-04-16 13:02:48 +00:00
None => return Err(InvalidDocument::CouldntGetFileName),
};
match name.extension() {
Some(ext) if ext.eq_ignore_ascii_case("json") => Ok(document),
2024-04-16 13:02:48 +00:00
_ => Err(InvalidDocument::InvalidFileName),
}
}
#[inline]
2024-02-22 09:44:02 +00:00
async fn download_file(bot: &Throttle<Bot>, file: &FileMeta) -> crate::Result<Box<[u8]>> {
let path = bot.get_file(&file.id).await?.path;
let mut data = Vec::with_capacity(file.size as usize);
2024-02-22 09:44:02 +00:00
let mut stream = bot.download_file_stream(&path);
while let Some(bytes) = stream.try_next().await? {
data.extend_from_slice(&bytes);
}
Ok(data.into_boxed_slice())
}
#[inline]
fn process_accounts(
accounts: &mut [DecryptedAccount],
2023-09-04 18:25:43 +00:00
existing_names: ahash::HashSet<String>,
2024-04-16 13:02:48 +00:00
locale: LocaleRef,
) -> 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 existing = Vec::new();
let mut invalid = Vec::new();
accounts
.iter()
.dedup_by_with_count(|a, b| a.name == b.name)
.for_each(|(count, account)| {
let duplicate = count != 1;
let exists = existing_names.contains(&account.name);
if duplicate {
duplicates.push(account.name.as_str());
}
if exists {
2023-11-16 18:51:46 +00:00
existing.push(account.name.as_str());
}
// If it already exists or if it is a duplicate there's no need to check the account's validity
if !duplicate && !exists && !account.validate() {
invalid.push(account.name.as_str());
}
});
drop(existing_names);
if duplicates.is_empty() && invalid.is_empty() && existing.is_empty() {
return Ok(Ok(()));
}
2024-04-16 13:02:48 +00:00
let mut error_text = locale.following_accounts_have_problems.as_ref().to_owned();
if !duplicates.is_empty() {
write!(
error_text,
2024-04-16 13:02:48 +00:00
"\n\n{}:\n{:?}",
locale.duplicate_names,
duplicates.into_iter().format("\n")
2023-11-16 18:51:46 +00:00
)?;
}
if !existing.is_empty() {
write!(
error_text,
2024-04-16 13:02:48 +00:00
"\n\n{}:\n{:?}",
locale.accounts_already_in_db,
existing.into_iter().format("\n")
2023-11-16 18:51:46 +00:00
)?;
}
if !invalid.is_empty() {
write!(
error_text,
2024-04-16 13:02:48 +00:00
"\n\n{}:\n{:?}",
locale.invalid_fields,
invalid.into_iter().format("\n")
2023-11-16 18:51:46 +00:00
)?;
}
2024-04-16 13:02:48 +00:00
error_text.push_str(&locale.fix_that_and_send_again);
Ok(Err(error_text))
}
#[inline]
2024-02-22 09:44:02 +00:00
fn user_from_bytes(
bytes: impl AsRef<[u8]>,
2023-09-04 18:25:43 +00:00
existing_names: ahash::HashSet<String>,
2024-04-16 13:02:48 +00:00
locale: LocaleRef,
) -> crate::Result<Result<User, String>> {
2024-02-22 09:44:02 +00:00
let mut user: User = serde_json::from_slice(bytes.as_ref())?;
drop(bytes);
2024-04-16 13:02:48 +00:00
match process_accounts(&mut user.accounts, existing_names, locale)? {
Ok(()) => Ok(Ok(user)),
Err(error_text) => Ok(Err(error_text)),
}
}
2023-05-27 23:21:50 +00:00
#[inline]
async fn user_from_document(
bot: &Throttle<Bot>,
db: &Pool,
document: Option<&Document>,
user_id: u64,
2024-04-16 13:02:48 +00:00
locale: LocaleRef,
) -> Result<User, Cow<'static, str>> {
let (data, existing_names) = {
2024-04-16 13:02:48 +00:00
let file = &validate_document(document)
.map_err(|err| err.into_str(locale))?
.file;
let data = download_file(bot, file).map_err(|_| locale.error_downloading_file.as_ref());
let existing_names = Account::get_names(user_id, db)
.try_collect()
2024-04-16 13:02:48 +00:00
.map_err(|_| locale.error_getting_account_names.as_ref());
try_join!(data, existing_names)?
};
2024-04-16 13:02:48 +00:00
match spawn_blocking(|| user_from_bytes(data, existing_names, locale)).await {
2024-02-22 09:44:02 +00:00
Ok(Ok(user)) => user.map_err(Cow::Owned),
2024-04-16 13:02:48 +00:00
_ => Err(Cow::Borrowed(&locale.error_parsing_json_file)),
}
}
2023-11-16 18:51:46 +00:00
/// Function to handle `GetUser` state. It doesn't actually validate anything
#[inline]
2023-05-27 23:21:50 +00:00
pub async fn get_user(
bot: Throttle<Bot>,
msg: Message,
2024-02-03 13:23:39 +00:00
db: Pool,
2023-05-27 23:21:50 +00:00
dialogue: MainDialogue,
2024-04-16 13:02:48 +00:00
locale: LocaleRef,
2023-11-16 18:51:46 +00:00
handler: PackagedHandler<User>,
2023-05-27 23:21:50 +00:00
) -> crate::Result<()> {
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
2023-11-16 18:51:46 +00:00
let mut handler = handler.lock().await;
if handler.func.is_none() {
let _ = dialogue.exit().await;
return Err(HandlerUsed.into());
}
2023-11-16 18:51:46 +00:00
if msg.text().map(str::trim) == Some("/cancel") {
2023-05-27 23:21:50 +00:00
dialogue.exit().await?;
2023-07-29 07:19:22 +00:00
handler
.previous
2024-04-16 13:02:48 +00:00
.alter_message(
&bot,
locale.successfully_canceled.as_ref(),
deletion_markup(locale),
None,
)
2023-05-27 23:21:50 +00:00
.await?;
return Ok(());
}
2024-04-16 13:02:48 +00:00
let user = match user_from_document(&bot, &db, msg.document(), user_id, locale).await {
Ok(user) => user,
Err(error_text) => {
2023-07-29 07:19:22 +00:00
handler
.previous
.alter_message(&bot, error_text, None, None)
.await?;
return Ok(());
}
2023-05-27 23:21:50 +00:00
};
let previous = handler.previous;
2023-05-27 23:21:50 +00:00
let func = handler.func.take().unwrap();
drop(handler);
2024-04-16 13:02:48 +00:00
if let Err(err) = func(bot, msg, db, dialogue.clone(), previous, locale, user).await {
2023-05-27 23:21:50 +00:00
let _ = dialogue.exit().await;
return Err(err);
}
Ok(())
}