Added support for multiple languages
This commit is contained in:
@ -9,11 +9,16 @@ pub async fn generic<F>(
|
||||
db: Pool,
|
||||
dialogue: MainDialogue,
|
||||
check: F,
|
||||
no_text_message: impl Into<String> + Send,
|
||||
locale: LocaleRef,
|
||||
handler: PackagedHandler<String>,
|
||||
) -> crate::Result<()>
|
||||
where
|
||||
for<'a> F: FnOnce(&'a Message, &'a Pool, &'a str) -> BoxFuture<'a, crate::Result<Option<String>>>
|
||||
for<'a> F: FnOnce(
|
||||
&'a Message,
|
||||
&'a Pool,
|
||||
LocaleRef,
|
||||
&'a str,
|
||||
) -> BoxFuture<'a, crate::Result<Option<String>>>
|
||||
+ Send,
|
||||
{
|
||||
let mut handler = handler.lock().await;
|
||||
@ -25,7 +30,7 @@ where
|
||||
let Some(text) = msg.text().map(str::trim) else {
|
||||
handler
|
||||
.previous
|
||||
.alter_message(&bot, no_text_message, None, None)
|
||||
.alter_message(&bot, locale.couldnt_get_message_text.as_ref(), None, None)
|
||||
.await?;
|
||||
return Ok(());
|
||||
};
|
||||
@ -34,12 +39,17 @@ where
|
||||
dialogue.exit().await?;
|
||||
handler
|
||||
.previous
|
||||
.alter_message(&bot, "Successfully cancelled", deletion_markup(), None)
|
||||
.alter_message(
|
||||
&bot,
|
||||
locale.successfully_canceled.as_ref(),
|
||||
deletion_markup(locale),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(text) = check(&msg, &db, text).await? {
|
||||
if let Some(text) = check(&msg, &db, locale, text).await? {
|
||||
handler
|
||||
.previous
|
||||
.alter_message(&bot, text, None, None)
|
||||
@ -52,7 +62,7 @@ where
|
||||
drop(handler);
|
||||
let text = text.to_owned();
|
||||
|
||||
if let Err(err) = func(bot, msg, db, dialogue.clone(), previous, text).await {
|
||||
if let Err(err) = func(bot, msg, db, dialogue.clone(), previous, locale, text).await {
|
||||
let _ = dialogue.exit().await;
|
||||
return Err(err);
|
||||
}
|
||||
|
@ -1,16 +1,17 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
#[inline]
|
||||
async fn check_login(_: &Message, _: &Pool, login: &str) -> crate::Result<Option<String>> {
|
||||
async fn check_login(
|
||||
_: &Message,
|
||||
_: &Pool,
|
||||
locale: LocaleRef,
|
||||
login: &str,
|
||||
) -> crate::Result<Option<String>> {
|
||||
let is_valid = validate_field(login);
|
||||
if !is_valid {
|
||||
return Ok(Some("Invalid login. Try again".to_owned()));
|
||||
return Ok(Some(locale.invalid_login.as_ref().into()));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
crate::simple_state_handler!(
|
||||
get_login,
|
||||
check_login,
|
||||
"Couldn't get the text of the message. Send the login again"
|
||||
);
|
||||
crate::simple_state_handler!(get_login, check_login);
|
||||
|
@ -8,14 +8,13 @@ use tokio::task::spawn_blocking;
|
||||
async fn check_master_pass(
|
||||
msg: &Message,
|
||||
db: &Pool,
|
||||
locale: LocaleRef,
|
||||
master_pass: &str,
|
||||
) -> crate::Result<Option<String>> {
|
||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
||||
let Some(model) = MasterPass::get(user_id, db).await? else {
|
||||
error!("User was put into the GetMasterPass state with no master password set");
|
||||
return Ok(Some(
|
||||
"No master password set. Use /cancel and set it by using /set_master_pass".to_owned(),
|
||||
));
|
||||
return Ok(Some(locale.master_password_is_not_set.as_ref().into()));
|
||||
};
|
||||
|
||||
let is_valid = {
|
||||
@ -25,13 +24,9 @@ async fn check_master_pass(
|
||||
};
|
||||
|
||||
if !is_valid {
|
||||
return Ok(Some("Wrong master password. Try again".to_owned()));
|
||||
return Ok(Some(locale.wrong_master_password.as_ref().into()));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
crate::simple_state_handler!(
|
||||
get_master_pass,
|
||||
check_master_pass,
|
||||
"Couldn't get the text of the message. Send the master password again"
|
||||
);
|
||||
crate::simple_state_handler!(get_master_pass, check_master_pass);
|
||||
|
@ -1,31 +1,32 @@
|
||||
use crate::prelude::*;
|
||||
use cryptography::passwords::{check_master_pass, PasswordValidity};
|
||||
use std::fmt::Write as _;
|
||||
|
||||
#[inline]
|
||||
fn process_validity(validity: PasswordValidity) -> Result<(), String> {
|
||||
fn process_validity(validity: PasswordValidity, locale: LocaleRef) -> Result<(), String> {
|
||||
if validity.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut error_text = "Your master password is invalid:\n".to_owned();
|
||||
let mut error_text = locale.master_password_too_weak.as_ref().to_owned();
|
||||
|
||||
if validity.contains(PasswordValidity::NO_LOWERCASE) {
|
||||
error_text.push_str("\n* It doesn't have any lowercase characters");
|
||||
write!(error_text, "\n* {}", locale.no_lowercase).unwrap();
|
||||
}
|
||||
if validity.contains(PasswordValidity::NO_UPPERCASE) {
|
||||
error_text.push_str("\n* It doesn't have any uppercase characters");
|
||||
write!(error_text, "\n* {}", locale.no_uppercase).unwrap();
|
||||
}
|
||||
if validity.contains(PasswordValidity::NO_NUMBER) {
|
||||
error_text.push_str("\n* It doesn't have any numbers");
|
||||
write!(error_text, "\n* {}", locale.no_numbers).unwrap();
|
||||
}
|
||||
if validity.contains(PasswordValidity::NO_SPECIAL_CHARACTER) {
|
||||
error_text.push_str("\n* It doesn't have any special characters");
|
||||
write!(error_text, "\n* {}", locale.no_special_characters).unwrap();
|
||||
}
|
||||
if validity.contains(PasswordValidity::TOO_SHORT) {
|
||||
error_text.push_str("\n* It is shorter than 8 characters");
|
||||
write!(error_text, "\n* {}", locale.master_pass_too_short).unwrap();
|
||||
}
|
||||
|
||||
error_text.push_str("\n\nModify your password and send it again");
|
||||
error_text.push_str(&locale.change_master_password_and_send_again);
|
||||
|
||||
Err(error_text)
|
||||
}
|
||||
@ -35,15 +36,12 @@ fn process_validity(validity: PasswordValidity) -> Result<(), String> {
|
||||
async fn check_new_master_pass(
|
||||
_: &Message,
|
||||
_: &Pool,
|
||||
locale: LocaleRef,
|
||||
password: &str,
|
||||
) -> crate::Result<Option<String>> {
|
||||
let validity = check_master_pass(password);
|
||||
|
||||
Ok(process_validity(validity).err())
|
||||
Ok(process_validity(validity, locale).err())
|
||||
}
|
||||
|
||||
crate::simple_state_handler!(
|
||||
get_new_master_pass,
|
||||
check_new_master_pass,
|
||||
"Couldn't get the text of the message. Send the master password again"
|
||||
);
|
||||
crate::simple_state_handler!(get_new_master_pass, check_new_master_pass);
|
||||
|
@ -5,21 +5,18 @@ use crate::prelude::*;
|
||||
async fn check_new_account_name(
|
||||
msg: &Message,
|
||||
db: &Pool,
|
||||
locale: LocaleRef,
|
||||
name: &str,
|
||||
) -> crate::Result<Option<String>> {
|
||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
||||
|
||||
if Account::exists(user_id, name, db).await? {
|
||||
Ok(Some("Account already exists".to_owned()))
|
||||
Ok(Some(locale.account_already_exists.as_ref().into()))
|
||||
} else if !validate_field(name) {
|
||||
Ok(Some("Invalid account name. Try again".to_owned()))
|
||||
Ok(Some(locale.invalid_name.as_ref().into()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
crate::simple_state_handler!(
|
||||
get_new_name,
|
||||
check_new_account_name,
|
||||
"Couldn't get the text of the message. Send the name of the new account again"
|
||||
);
|
||||
crate::simple_state_handler!(get_new_name, check_new_account_name);
|
||||
|
@ -1,16 +1,17 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
#[inline]
|
||||
async fn check_password(_: &Message, _: &Pool, password: &str) -> crate::Result<Option<String>> {
|
||||
async fn check_password(
|
||||
_: &Message,
|
||||
_: &Pool,
|
||||
locale: LocaleRef,
|
||||
password: &str,
|
||||
) -> crate::Result<Option<String>> {
|
||||
let is_valid = validate_field(password);
|
||||
if !is_valid {
|
||||
return Ok(Some("Invalid password. Try again".to_owned()));
|
||||
return Ok(Some(locale.invalid_password.as_ref().into()));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
crate::simple_state_handler!(
|
||||
get_password,
|
||||
check_password,
|
||||
"Couldn't get the text of the message. Send the password again"
|
||||
);
|
||||
crate::simple_state_handler!(get_password, check_password);
|
||||
|
@ -9,24 +9,43 @@ use teloxide::{
|
||||
use tokio::{task::spawn_blocking, try_join};
|
||||
use trim_in_place::TrimInPlace;
|
||||
|
||||
#[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]
|
||||
fn validate_document(document: Option<&Document>) -> Result<&Document, &'static str> {
|
||||
fn validate_document(document: Option<&Document>) -> Result<&Document, InvalidDocument> {
|
||||
let Some(document) = document else {
|
||||
return Err("You didn't send a file. Try again");
|
||||
return Err(InvalidDocument::NoFileSend);
|
||||
};
|
||||
|
||||
if document.file.size > 1024 * 1024 * 200 {
|
||||
return Err("The file is larger than 200 MiB. Try splitting it into multiple files");
|
||||
return Err(InvalidDocument::FileTooLarge);
|
||||
}
|
||||
|
||||
let name = match document.file_name.as_deref() {
|
||||
Some(name) => Path::new(name.trim_end()),
|
||||
None => return Err("Couldn't get the name of the file. Try sending it again"),
|
||||
None => return Err(InvalidDocument::CouldntGetFileName),
|
||||
};
|
||||
|
||||
match name.extension() {
|
||||
Some(ext) if ext.eq_ignore_ascii_case("json") => Ok(document),
|
||||
_ => Err("Invalid file name. You need to send a json file. Try again"),
|
||||
_ => Err(InvalidDocument::InvalidFileName),
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,6 +64,7 @@ async fn download_file(bot: &Throttle<Bot>, file: &FileMeta) -> crate::Result<Bo
|
||||
fn process_accounts(
|
||||
accounts: &mut [DecryptedAccount],
|
||||
existing_names: ahash::HashSet<String>,
|
||||
locale: LocaleRef,
|
||||
) -> crate::Result<Result<(), String>> {
|
||||
for account in accounts.iter_mut() {
|
||||
account.name.trim_in_place();
|
||||
@ -82,12 +102,13 @@ fn process_accounts(
|
||||
return Ok(Ok(()));
|
||||
}
|
||||
|
||||
let mut error_text = "Your accounts have the following problems:".to_owned();
|
||||
let mut error_text = locale.following_accounts_have_problems.as_ref().to_owned();
|
||||
|
||||
if !duplicates.is_empty() {
|
||||
write!(
|
||||
error_text,
|
||||
"\n\nDuplicate names:\n{:?}",
|
||||
"\n\n{}:\n{:?}",
|
||||
locale.duplicate_names,
|
||||
duplicates.into_iter().format("\n")
|
||||
)?;
|
||||
}
|
||||
@ -95,7 +116,8 @@ fn process_accounts(
|
||||
if !existing.is_empty() {
|
||||
write!(
|
||||
error_text,
|
||||
"\n\nAccounts with these names already exist in the database:\n{:?}",
|
||||
"\n\n{}:\n{:?}",
|
||||
locale.accounts_already_in_db,
|
||||
existing.into_iter().format("\n")
|
||||
)?;
|
||||
}
|
||||
@ -103,12 +125,13 @@ fn process_accounts(
|
||||
if !invalid.is_empty() {
|
||||
write!(
|
||||
error_text,
|
||||
"\n\nInvalid account fields:\n{:?}",
|
||||
"\n\n{}:\n{:?}",
|
||||
locale.invalid_fields,
|
||||
invalid.into_iter().format("\n")
|
||||
)?;
|
||||
}
|
||||
|
||||
error_text.push_str("\n\nFix these problems and send the file again");
|
||||
error_text.push_str(&locale.fix_that_and_send_again);
|
||||
|
||||
Ok(Err(error_text))
|
||||
}
|
||||
@ -117,10 +140,11 @@ fn process_accounts(
|
||||
fn user_from_bytes(
|
||||
bytes: impl AsRef<[u8]>,
|
||||
existing_names: ahash::HashSet<String>,
|
||||
locale: LocaleRef,
|
||||
) -> crate::Result<Result<User, String>> {
|
||||
let mut user: User = serde_json::from_slice(bytes.as_ref())?;
|
||||
drop(bytes);
|
||||
match process_accounts(&mut user.accounts, existing_names)? {
|
||||
match process_accounts(&mut user.accounts, existing_names, locale)? {
|
||||
Ok(()) => Ok(Ok(user)),
|
||||
Err(error_text) => Ok(Err(error_text)),
|
||||
}
|
||||
@ -132,21 +156,24 @@ async fn user_from_document(
|
||||
db: &Pool,
|
||||
document: Option<&Document>,
|
||||
user_id: u64,
|
||||
locale: LocaleRef,
|
||||
) -> Result<User, Cow<'static, str>> {
|
||||
let (data, existing_names) = {
|
||||
let file = &validate_document(document)?.file;
|
||||
let data = download_file(bot, file).map_err(|_| "Error downloading the file. Try again");
|
||||
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()
|
||||
.map_err(|_| "Error getting existing account names. Try again");
|
||||
.map_err(|_| locale.error_getting_account_names.as_ref());
|
||||
|
||||
try_join!(data, existing_names)?
|
||||
};
|
||||
|
||||
match spawn_blocking(|| user_from_bytes(data, existing_names)).await {
|
||||
match spawn_blocking(|| user_from_bytes(data, existing_names, locale)).await {
|
||||
Ok(Ok(user)) => user.map_err(Cow::Owned),
|
||||
_ => Err(Cow::Borrowed("Error parsing the json file. Try again")),
|
||||
_ => Err(Cow::Borrowed(&locale.error_parsing_json_file)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,6 +184,7 @@ pub async fn get_user(
|
||||
msg: Message,
|
||||
db: Pool,
|
||||
dialogue: MainDialogue,
|
||||
locale: LocaleRef,
|
||||
handler: PackagedHandler<User>,
|
||||
) -> crate::Result<()> {
|
||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
||||
@ -171,12 +199,17 @@ pub async fn get_user(
|
||||
dialogue.exit().await?;
|
||||
handler
|
||||
.previous
|
||||
.alter_message(&bot, "Successfully cancelled", deletion_markup(), None)
|
||||
.alter_message(
|
||||
&bot,
|
||||
locale.successfully_canceled.as_ref(),
|
||||
deletion_markup(locale),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let user = match user_from_document(&bot, &db, msg.document(), user_id).await {
|
||||
let user = match user_from_document(&bot, &db, msg.document(), user_id, locale).await {
|
||||
Ok(user) => user,
|
||||
Err(error_text) => {
|
||||
handler
|
||||
@ -191,7 +224,7 @@ pub async fn get_user(
|
||||
let func = handler.func.take().unwrap();
|
||||
drop(handler);
|
||||
|
||||
if let Err(err) = func(bot, msg, db, dialogue.clone(), previous, user).await {
|
||||
if let Err(err) = func(bot, msg, db, dialogue.clone(), previous, locale, user).await {
|
||||
let _ = dialogue.exit().await;
|
||||
return Err(err);
|
||||
}
|
||||
|
@ -68,6 +68,7 @@ type DynHanlder<T> = Box<
|
||||
Pool,
|
||||
MainDialogue,
|
||||
MessageIds,
|
||||
LocaleRef,
|
||||
T,
|
||||
) -> BoxFuture<'static, crate::Result<()>>
|
||||
+ Send,
|
||||
@ -92,6 +93,7 @@ impl<T> Handler<T> {
|
||||
Pool,
|
||||
MainDialogue,
|
||||
MessageIds,
|
||||
LocaleRef,
|
||||
T,
|
||||
) -> BoxFuture<'static, crate::Result<()>>
|
||||
+ Send
|
||||
|
Reference in New Issue
Block a user