Added support for multiple languages
This commit is contained in:
@@ -10,6 +10,7 @@ async fn get_master_pass(
|
||||
db: Pool,
|
||||
dialogue: MainDialogue,
|
||||
mut ids: MessageIds,
|
||||
locale: LocaleRef,
|
||||
name: String,
|
||||
login: String,
|
||||
password: String,
|
||||
@@ -30,26 +31,26 @@ async fn get_master_pass(
|
||||
.await?;
|
||||
account.insert(&db).await?;
|
||||
|
||||
ids.alter_message(&bot, "Success", deletion_markup(), None)
|
||||
ids.alter_message(&bot, locale.success.as_ref(), deletion_markup(locale), None)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
handler!(
|
||||
get_password(name:String, login: String, password: String),
|
||||
"Send master password",
|
||||
send_master_password,
|
||||
State::GetMasterPass,
|
||||
get_master_pass
|
||||
);
|
||||
handler!(get_login(name: String, login: String),
|
||||
"Send password",
|
||||
send_password,
|
||||
State::GetPassword,
|
||||
get_password
|
||||
);
|
||||
handler!(get_account_name(name: String), "Send login", State::GetLogin, get_login);
|
||||
handler!(get_account_name(name: String), send_login, State::GetLogin, get_login);
|
||||
first_handler!(
|
||||
add_account,
|
||||
"Send account name",
|
||||
send_account_name,
|
||||
State::GetNewName,
|
||||
get_account_name
|
||||
);
|
||||
|
@@ -2,9 +2,9 @@ use crate::prelude::*;
|
||||
|
||||
/// Handles /cancel command when there's no active state
|
||||
#[inline]
|
||||
pub async fn cancel(bot: Throttle<Bot>, msg: Message) -> crate::Result<()> {
|
||||
bot.send_message(msg.chat.id, "Nothing to cancel")
|
||||
.reply_markup(deletion_markup())
|
||||
pub async fn cancel(bot: Throttle<Bot>, msg: Message, locale: LocaleRef) -> crate::Result<()> {
|
||||
bot.send_message(msg.chat.id, locale.nothing_to_cancel.as_ref())
|
||||
.reply_markup(deletion_markup(locale))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
13
src/commands/change_language.rs
Normal file
13
src/commands/change_language.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
#[inline]
|
||||
pub async fn change_language(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
locale: LocaleRef,
|
||||
) -> crate::Result<()> {
|
||||
bot.send_message(msg.chat.id, locale.choose_language.as_ref())
|
||||
.reply_markup(language_markup())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
@@ -1,19 +1,24 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
#[inline]
|
||||
pub async fn delete(bot: Throttle<Bot>, msg: Message, db: Pool) -> crate::Result<()> {
|
||||
pub async fn delete(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
db: Pool,
|
||||
locale: LocaleRef,
|
||||
) -> crate::Result<()> {
|
||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
||||
|
||||
let markup = menu_markup("delete1", user_id, &db).await?;
|
||||
|
||||
if markup.inline_keyboard.is_empty() {
|
||||
bot.send_message(msg.chat.id, "You don't have any accounts")
|
||||
.reply_markup(deletion_markup())
|
||||
bot.send_message(msg.chat.id, locale.no_accounts_found.as_ref())
|
||||
.reply_markup(deletion_markup(locale))
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
bot.send_message(msg.chat.id, "Choose the account to delete")
|
||||
bot.send_message(msg.chat.id, locale.choose_account.as_ref())
|
||||
.reply_markup(markup)
|
||||
.await?;
|
||||
Ok(())
|
||||
|
@@ -10,6 +10,7 @@ async fn get_master_pass(
|
||||
db: Pool,
|
||||
dialogue: MainDialogue,
|
||||
mut ids: MessageIds,
|
||||
locale: LocaleRef,
|
||||
_: String,
|
||||
) -> crate::Result<()> {
|
||||
dialogue.exit().await?;
|
||||
@@ -23,22 +24,22 @@ async fn get_master_pass(
|
||||
let text = match result {
|
||||
(Ok(()), Ok(())) => {
|
||||
txn.commit().await?;
|
||||
"Everything was deleted"
|
||||
locale.everything_was_deleted.as_ref()
|
||||
}
|
||||
(Err(err), _) | (_, Err(err)) => {
|
||||
error!("{}", crate::Error::from(err));
|
||||
txn.rollback().await?;
|
||||
"Something went wrong. Try again later"
|
||||
locale.something_went_wrong.as_ref()
|
||||
}
|
||||
};
|
||||
ids.alter_message(&bot, text, deletion_markup(), None)
|
||||
ids.alter_message(&bot, text, deletion_markup(locale), None)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
first_handler!(
|
||||
delete_all,
|
||||
"Send master password to delete EVERYTHING.\nTHIS ACTION IS IRREVERSIBLE",
|
||||
send_master_pass_to_delete_everything,
|
||||
State::GetMasterPass,
|
||||
get_master_pass
|
||||
);
|
||||
|
@@ -25,6 +25,7 @@ async fn get_master_pass(
|
||||
db: Pool,
|
||||
dialogue: MainDialogue,
|
||||
ids: MessageIds,
|
||||
locale: LocaleRef,
|
||||
master_pass: String,
|
||||
) -> crate::Result<()> {
|
||||
dialogue.exit().await?;
|
||||
@@ -51,14 +52,14 @@ async fn get_master_pass(
|
||||
let file = InputFile::memory(json).file_name("accounts.json");
|
||||
|
||||
bot.send_document(msg.chat.id, file)
|
||||
.reply_markup(deletion_markup())
|
||||
.reply_markup(deletion_markup(locale))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
first_handler!(
|
||||
export,
|
||||
"Send the master password to export your accounts",
|
||||
send_master_password,
|
||||
State::GetMasterPass,
|
||||
get_master_pass
|
||||
);
|
||||
|
@@ -15,7 +15,11 @@ const BUFFER_LENGTH: usize =
|
||||
|
||||
/// Handles /`gen_password` command by generating 10 copyable passwords and sending them to the user
|
||||
#[inline]
|
||||
pub async fn gen_password(bot: Throttle<Bot>, msg: Message) -> crate::Result<()> {
|
||||
pub async fn gen_password(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
locale: LocaleRef,
|
||||
) -> crate::Result<()> {
|
||||
let mut message: ArrayString<BUFFER_LENGTH> = MESSAGE_HEADER.try_into().unwrap();
|
||||
let passwords: PasswordArray = spawn_blocking(generate_passwords).await?;
|
||||
for password in passwords {
|
||||
@@ -23,7 +27,7 @@ pub async fn gen_password(bot: Throttle<Bot>, msg: Message) -> crate::Result<()>
|
||||
}
|
||||
bot.send_message(msg.chat.id, message.as_str())
|
||||
.parse_mode(ParseMode::MarkdownV2)
|
||||
.reply_markup(deletion_markup())
|
||||
.reply_markup(deletion_markup(locale))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@@ -1,19 +1,24 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
#[inline]
|
||||
pub async fn get_account(bot: Throttle<Bot>, msg: Message, db: Pool) -> crate::Result<()> {
|
||||
pub async fn get_account(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
db: Pool,
|
||||
locale: LocaleRef,
|
||||
) -> crate::Result<()> {
|
||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
||||
|
||||
let markup = menu_markup("decrypt", user_id, &db).await?;
|
||||
|
||||
if markup.inline_keyboard.is_empty() {
|
||||
bot.send_message(msg.chat.id, "You don't have any accounts")
|
||||
.reply_markup(deletion_markup())
|
||||
bot.send_message(msg.chat.id, locale.no_accounts_found.as_ref())
|
||||
.reply_markup(deletion_markup(locale))
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
bot.send_message(msg.chat.id, "Choose the account to get")
|
||||
bot.send_message(msg.chat.id, locale.choose_account.as_ref())
|
||||
.reply_markup(markup)
|
||||
.await?;
|
||||
Ok(())
|
||||
|
@@ -4,26 +4,32 @@ use teloxide::types::ParseMode;
|
||||
|
||||
/// Handles /`get_accounts` command by sending the list of copyable account names to the user
|
||||
#[inline]
|
||||
pub async fn get_accounts(bot: Throttle<Bot>, msg: Message, db: Pool) -> crate::Result<()> {
|
||||
pub async fn get_accounts(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
db: Pool,
|
||||
locale: LocaleRef,
|
||||
) -> crate::Result<()> {
|
||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
||||
let mut account_names = Account::get_names(user_id, &db);
|
||||
|
||||
let mut text = if let Some(name) = account_names.try_next().await? {
|
||||
format!("Accounts:\n`{name}`")
|
||||
} else {
|
||||
bot.send_message(msg.chat.id, "No accounts found")
|
||||
.reply_markup(deletion_markup())
|
||||
let Some(mut text) = account_names.try_next().await? else {
|
||||
bot.send_message(msg.chat.id, locale.no_accounts_found.as_ref())
|
||||
.reply_markup(deletion_markup(locale))
|
||||
.await?;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
text.insert(0, '`');
|
||||
text.push('`');
|
||||
|
||||
while let Some(name) = account_names.try_next().await? {
|
||||
write!(text, "\n`{name}`")?;
|
||||
}
|
||||
|
||||
bot.send_message(msg.chat.id, text)
|
||||
.parse_mode(ParseMode::MarkdownV2)
|
||||
.reply_markup(deletion_markup())
|
||||
.reply_markup(deletion_markup(locale))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@@ -1,11 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use teloxide::utils::command::BotCommands;
|
||||
|
||||
/// Handles the help command by sending the passwords descryptions
|
||||
#[inline]
|
||||
pub async fn help(bot: Throttle<Bot>, msg: Message) -> crate::Result<()> {
|
||||
bot.send_message(msg.chat.id, Command::descriptions().to_string())
|
||||
.reply_markup(deletion_markup())
|
||||
pub async fn help(bot: Throttle<Bot>, msg: Message, locale: LocaleRef) -> crate::Result<()> {
|
||||
bot.send_message(msg.chat.id, locale.help_command.as_ref())
|
||||
.reply_markup(deletion_markup(locale))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@@ -27,12 +27,14 @@ async fn encrypt_account(
|
||||
|
||||
/// Gets the master password, encryptes and adds the accounts to the DB
|
||||
#[inline]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn get_master_pass(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
db: Pool,
|
||||
dialogue: MainDialogue,
|
||||
mut ids: MessageIds,
|
||||
locale: LocaleRef,
|
||||
user: User,
|
||||
master_pass: String,
|
||||
) -> crate::Result<()> {
|
||||
@@ -53,22 +55,18 @@ async fn get_master_pass(
|
||||
}
|
||||
|
||||
let text = if failed.is_empty() {
|
||||
"Success".to_owned()
|
||||
locale.success.as_ref().to_owned()
|
||||
} else {
|
||||
format!(
|
||||
"Failed to create the following accounts:\n{}",
|
||||
"{}:\n{}",
|
||||
locale.couldnt_create_following_accounts,
|
||||
failed.into_iter().format("\n")
|
||||
)
|
||||
};
|
||||
ids.alter_message(&bot, text, deletion_markup(), None)
|
||||
ids.alter_message(&bot, text, deletion_markup(locale), None)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
handler!(get_user(user: User), "Send master password", State::GetMasterPass, get_master_pass);
|
||||
first_handler!(
|
||||
import,
|
||||
"Send a json document with the same format as created by /export",
|
||||
State::GetUser,
|
||||
get_user
|
||||
);
|
||||
handler!(get_user(user: User),send_master_password, State::GetMasterPass, get_master_pass);
|
||||
first_handler!(import, send_json_file, State::GetUser, get_user);
|
||||
|
@@ -1,19 +1,24 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
#[inline]
|
||||
pub async fn menu(bot: Throttle<Bot>, msg: Message, db: Pool) -> crate::Result<()> {
|
||||
pub async fn menu(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
db: Pool,
|
||||
locale: LocaleRef,
|
||||
) -> crate::Result<()> {
|
||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
||||
|
||||
let markup = menu_markup("get", user_id, &db).await?;
|
||||
|
||||
if markup.inline_keyboard.is_empty() {
|
||||
bot.send_message(msg.chat.id, "You don't have any accounts")
|
||||
.reply_markup(deletion_markup())
|
||||
bot.send_message(msg.chat.id, locale.no_accounts_found.as_ref())
|
||||
.reply_markup(deletion_markup(locale))
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
bot.send_message(msg.chat.id, "Choose your account")
|
||||
bot.send_message(msg.chat.id, locale.choose_account.as_ref())
|
||||
.reply_markup(markup)
|
||||
.await?;
|
||||
Ok(())
|
||||
|
@@ -1,25 +1,29 @@
|
||||
use crate::{change_state, prelude::*};
|
||||
use crate::{change_state, locales::LocaleTypeExt, prelude::*};
|
||||
use cryptography::hashing::HashedBytes;
|
||||
use entity::locale::LocaleType;
|
||||
use tokio::task::spawn_blocking;
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn get_master_pass2(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
db: Pool,
|
||||
dialogue: MainDialogue,
|
||||
mut ids: MessageIds,
|
||||
locale: LocaleRef,
|
||||
hash: HashedBytes<[u8; 64], [u8; 64]>,
|
||||
master_pass: String,
|
||||
) -> crate::Result<()> {
|
||||
dialogue.exit().await?;
|
||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
||||
let from = msg.from().ok_or(NoUserInfo)?;
|
||||
let user_id = from.id.0;
|
||||
|
||||
if !hash.verify(master_pass.as_bytes()) {
|
||||
ids.alter_message(
|
||||
&bot,
|
||||
"The passwords didn't match. Use the command again",
|
||||
deletion_markup(),
|
||||
locale.master_password_dont_match.as_ref(),
|
||||
deletion_markup(locale),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
@@ -32,9 +36,14 @@ async fn get_master_pass2(
|
||||
password_hash: hash.hash.to_vec(),
|
||||
salt: hash.salt.to_vec(),
|
||||
};
|
||||
model.insert(&db).await?;
|
||||
let locale_type = from
|
||||
.language_code
|
||||
.as_deref()
|
||||
.and_then(LocaleType::from_language_code)
|
||||
.unwrap_or_default();
|
||||
model.insert(&db, locale_type).await?;
|
||||
|
||||
ids.alter_message(&bot, "Success", deletion_markup(), None)
|
||||
ids.alter_message(&bot, locale.success.as_ref(), deletion_markup(locale), None)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
@@ -47,11 +56,13 @@ async fn get_master_pass(
|
||||
_: Pool,
|
||||
dialogue: MainDialogue,
|
||||
mut ids: MessageIds,
|
||||
locale: LocaleRef,
|
||||
master_pass: String,
|
||||
) -> crate::Result<()> {
|
||||
let hash = spawn_blocking(move || HashedBytes::new(master_pass.as_bytes())).await?;
|
||||
|
||||
ids.alter_message(&bot, "Send it again", None, None).await?;
|
||||
ids.alter_message(&bot, locale.send_master_password_again.as_ref(), None, None)
|
||||
.await?;
|
||||
|
||||
change_state!(
|
||||
dialogue,
|
||||
@@ -71,16 +82,17 @@ pub async fn set_master_pass(
|
||||
msg: Message,
|
||||
dialogue: MainDialogue,
|
||||
db: Pool,
|
||||
locale: LocaleRef,
|
||||
) -> crate::Result<()> {
|
||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
||||
if MasterPass::exists(user_id, &db).await? {
|
||||
bot.send_message(msg.chat.id, "Master password already exists")
|
||||
.reply_markup(deletion_markup())
|
||||
bot.send_message(msg.chat.id, locale.master_password_is_set.as_ref())
|
||||
.reply_markup(deletion_markup(locale))
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
let previous = bot
|
||||
.send_message(msg.chat.id, "Send new master password")
|
||||
.send_message(msg.chat.id, locale.send_new_master_password.as_ref())
|
||||
.await?;
|
||||
|
||||
change_state!(
|
||||
|
@@ -2,12 +2,8 @@ use crate::prelude::*;
|
||||
|
||||
/// Handles /start command by sending the greeting message
|
||||
#[inline]
|
||||
pub async fn start(bot: Throttle<Bot>, msg: Message) -> crate::Result<()> {
|
||||
bot.send_message(
|
||||
msg.chat.id,
|
||||
"Hi! This bot can be used to store the passwords securely. \
|
||||
Use /help command to get the list of commands",
|
||||
)
|
||||
.await?;
|
||||
pub async fn start(bot: Throttle<Bot>, msg: Message, locale: LocaleRef) -> crate::Result<()> {
|
||||
bot.send_message(msg.chat.id, locale.start_command.as_ref())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
Reference in New Issue
Block a user