Sepparated the code out into 3 more library crates: cryptography, entity and pass_manager
This commit is contained in:
110
src/commands/add_account.rs
Normal file
110
src/commands/add_account.rs
Normal file
@ -0,0 +1,110 @@
|
||||
use crate::{
|
||||
errors::NoUserInfo, markups::deletion_markup, models::DecryptedAccount, state::NameCheckKind,
|
||||
Handler, MainDialogue, State,
|
||||
};
|
||||
use sea_orm::prelude::*;
|
||||
use teloxide::{adaptors::Throttle, prelude::*};
|
||||
use tokio::task::spawn_blocking;
|
||||
|
||||
/// Gets the name of the master password, encryptes the account and adds it to the DB
|
||||
async fn get_master_pass(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
db: DatabaseConnection,
|
||||
dialogue: MainDialogue,
|
||||
account: DecryptedAccount,
|
||||
master_pass: String,
|
||||
) -> crate::Result<()> {
|
||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
||||
dialogue.exit().await?;
|
||||
let account = spawn_blocking(move || account.into_account(user_id, &master_pass)).await??;
|
||||
account.insert(&db).await?;
|
||||
bot.send_message(msg.chat.id, "Success")
|
||||
.reply_markup(deletion_markup())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets the password of the account to add
|
||||
async fn get_password(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
_: DatabaseConnection,
|
||||
dialogue: MainDialogue,
|
||||
name: String,
|
||||
login: String,
|
||||
password: String,
|
||||
) -> crate::Result<()> {
|
||||
let previous = bot
|
||||
.send_message(msg.chat.id, "Send master password")
|
||||
.await?;
|
||||
let account = DecryptedAccount {
|
||||
name,
|
||||
login,
|
||||
password,
|
||||
};
|
||||
dialogue
|
||||
.update(State::GetMasterPass(Handler::new(
|
||||
move |bot, msg, db, dialogue, master_pass| {
|
||||
get_master_pass(bot, msg, db, dialogue, account, master_pass)
|
||||
},
|
||||
previous,
|
||||
)))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets the login of the account to add
|
||||
async fn get_login(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
_: DatabaseConnection,
|
||||
dialogue: MainDialogue,
|
||||
name: String,
|
||||
login: String,
|
||||
) -> crate::Result<()> {
|
||||
let previous = bot.send_message(msg.chat.id, "Send password").await?;
|
||||
dialogue
|
||||
.update(State::GetPassword(Handler::new(
|
||||
move |bot, msg, db, dialogue, password| {
|
||||
get_password(bot, msg, db, dialogue, name, login, password)
|
||||
},
|
||||
previous,
|
||||
)))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets the name of the account to add and checks that there're no accounts with the same name
|
||||
async fn get_account_name(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
_: DatabaseConnection,
|
||||
dialogue: MainDialogue,
|
||||
name: String,
|
||||
) -> crate::Result<()> {
|
||||
let previous = bot.send_message(msg.chat.id, "Send login").await?;
|
||||
dialogue
|
||||
.update(State::GetLogin(Handler::new(
|
||||
move |bot, msg, db, dialogue, login| get_login(bot, msg, db, dialogue, name, login),
|
||||
previous,
|
||||
)))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handles /add_account
|
||||
pub async fn add_account(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
dialogue: MainDialogue,
|
||||
) -> crate::Result<()> {
|
||||
let previous = bot.send_message(msg.chat.id, "Send account name").await?;
|
||||
dialogue
|
||||
.update(State::GetAccountName(
|
||||
Handler::new(get_account_name, previous),
|
||||
NameCheckKind::NewAccountName,
|
||||
))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
10
src/commands/cancel.rs
Normal file
10
src/commands/cancel.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use crate::markups::deletion_markup;
|
||||
use teloxide::{adaptors::Throttle, prelude::*};
|
||||
|
||||
/// Handles /cancel command when there's no active state
|
||||
pub async fn cancel(bot: Throttle<Bot>, msg: Message) -> crate::Result<()> {
|
||||
bot.send_message(msg.chat.id, "Nothing to cancel")
|
||||
.reply_markup(deletion_markup())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
69
src/commands/delete.rs
Normal file
69
src/commands/delete.rs
Normal file
@ -0,0 +1,69 @@
|
||||
use crate::{
|
||||
errors::NoUserInfo,
|
||||
markups::{self, deletion_markup},
|
||||
state::NameCheckKind,
|
||||
Handler, MainDialogue, State,
|
||||
};
|
||||
use entity::prelude::*;
|
||||
use sea_orm::prelude::*;
|
||||
use teloxide::{adaptors::Throttle, prelude::*};
|
||||
|
||||
/// Gets the master password and deletes the account.
|
||||
/// Although it doesn't use the master password, we get it to be sure that it's the user who used that command
|
||||
async fn get_master_pass(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
db: DatabaseConnection,
|
||||
dialogue: MainDialogue,
|
||||
name: String,
|
||||
) -> crate::Result<()> {
|
||||
dialogue.exit().await?;
|
||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
||||
Account::delete_by_id((user_id, name)).exec(&db).await?;
|
||||
bot.send_message(msg.chat.id, "The account is successfully deleted")
|
||||
.reply_markup(deletion_markup())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets the name of the account to delete
|
||||
async fn get_account_name(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
_: DatabaseConnection,
|
||||
dialogue: MainDialogue,
|
||||
name: String,
|
||||
) -> crate::Result<()> {
|
||||
let previous = bot
|
||||
.send_message(msg.chat.id, "Send master password. Once you send correct master password the account is unrecoverable")
|
||||
.await?;
|
||||
dialogue
|
||||
.update(State::GetMasterPass(Handler::new(
|
||||
move |bot, msg, db, dialogue, _| get_master_pass(bot, msg, db, dialogue, name),
|
||||
previous,
|
||||
)))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handles /delete command
|
||||
pub async fn delete(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
dialogue: MainDialogue,
|
||||
db: DatabaseConnection,
|
||||
) -> crate::Result<()> {
|
||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
||||
let markup = markups::account_markup(user_id, &db).await?;
|
||||
let previous = bot
|
||||
.send_message(msg.chat.id, "Send account name")
|
||||
.reply_markup(markup)
|
||||
.await?;
|
||||
dialogue
|
||||
.update(State::GetAccountName(
|
||||
Handler::new(get_account_name, previous),
|
||||
NameCheckKind::MustExist,
|
||||
))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
50
src/commands/delete_all.rs
Normal file
50
src/commands/delete_all.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use crate::{errors::NoUserInfo, markups::deletion_markup, Handler, MainDialogue, State};
|
||||
use entity::prelude::*;
|
||||
use sea_orm::DatabaseConnection;
|
||||
use teloxide::{adaptors::Throttle, prelude::*};
|
||||
use tokio::join;
|
||||
|
||||
/// Gets the master password, deletes the accounts and the master password from DB.
|
||||
/// Although it doesn't use the master password, we get it to be sure that it's the user who used that command
|
||||
async fn get_master_pass(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
db: DatabaseConnection,
|
||||
dialogue: MainDialogue,
|
||||
_: String,
|
||||
) -> crate::Result<()> {
|
||||
dialogue.exit().await?;
|
||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
||||
match join!(
|
||||
Account::delete_all(user_id, &db),
|
||||
MasterPass::remove(user_id, &db),
|
||||
) {
|
||||
(Ok(_), Ok(_)) => (),
|
||||
(Err(err), _) | (Ok(_), Err(err)) => return Err(err.into()),
|
||||
};
|
||||
bot.send_message(msg.chat.id, "Everything was deleted")
|
||||
.reply_markup(deletion_markup())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handles /delete_all command
|
||||
pub async fn delete_all(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
dialogue: MainDialogue,
|
||||
) -> crate::Result<()> {
|
||||
let previous = bot
|
||||
.send_message(
|
||||
msg.chat.id,
|
||||
"Send master password to delete EVERYTHING.\nTHIS ACTION IS IRREVERSIBLE",
|
||||
)
|
||||
.await?;
|
||||
dialogue
|
||||
.update(State::GetMasterPass(Handler::new(
|
||||
get_master_pass,
|
||||
previous,
|
||||
)))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
68
src/commands/export.rs
Normal file
68
src/commands/export.rs
Normal file
@ -0,0 +1,68 @@
|
||||
use crate::{
|
||||
errors::NoUserInfo,
|
||||
markups::deletion_markup,
|
||||
models::{DecryptedAccount, User},
|
||||
Handler, MainDialogue, State,
|
||||
};
|
||||
use entity::prelude::*;
|
||||
use futures::TryStreamExt;
|
||||
use sea_orm::DatabaseConnection;
|
||||
use serde_json::to_vec_pretty;
|
||||
use std::sync::Arc;
|
||||
use teloxide::{adaptors::Throttle, prelude::*, types::InputFile};
|
||||
use tokio::{sync::Mutex, task::spawn_blocking};
|
||||
|
||||
/// Gets the master password, decryptes the account and sends the json file to the user
|
||||
async fn get_master_pass(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
db: DatabaseConnection,
|
||||
dialogue: MainDialogue,
|
||||
master_pass: String,
|
||||
) -> crate::Result<()> {
|
||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
||||
let mut accounts = Vec::new();
|
||||
{
|
||||
let accounts = &Mutex::new(&mut accounts);
|
||||
let master_pass: Arc<str> = master_pass.into();
|
||||
Account::get_all(user_id, &db)
|
||||
.await?
|
||||
.err_into::<crate::Error>()
|
||||
.try_for_each_concurrent(None, |account| {
|
||||
let master_pass = Arc::clone(&master_pass);
|
||||
async move {
|
||||
let account = spawn_blocking(move || {
|
||||
DecryptedAccount::from_account(account, &master_pass)
|
||||
})
|
||||
.await??;
|
||||
accounts.lock().await.push(account);
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
accounts.sort_unstable_by(|this, other| this.name.cmp(&other.name));
|
||||
let json = to_vec_pretty(&User { accounts })?;
|
||||
let file = InputFile::memory(json).file_name("accounts.json");
|
||||
bot.send_document(msg.chat.id, file)
|
||||
.reply_markup(deletion_markup())
|
||||
.await?;
|
||||
dialogue.exit().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handles /export command
|
||||
pub async fn export(bot: Throttle<Bot>, msg: Message, dialogue: MainDialogue) -> crate::Result<()> {
|
||||
let previous = bot
|
||||
.send_message(msg.chat.id, "Send a master password to export the accounts")
|
||||
.await?;
|
||||
dialogue
|
||||
.update(State::GetMasterPass(Handler::new(
|
||||
move |bot, msg, db, dialogue, master_pass| {
|
||||
get_master_pass(bot, msg, db, dialogue, master_pass)
|
||||
},
|
||||
previous,
|
||||
)))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
20
src/commands/gen_password.rs
Normal file
20
src/commands/gen_password.rs
Normal file
@ -0,0 +1,20 @@
|
||||
use crate::markups::deletion_markup;
|
||||
use arrayvec::ArrayString;
|
||||
use cryptography::password_generation::generate_passwords;
|
||||
use std::fmt::Write;
|
||||
use teloxide::{adaptors::Throttle, prelude::*, types::ParseMode};
|
||||
use tokio::task::spawn_blocking;
|
||||
|
||||
/// Handles /gen_password command by generating 10 copyable passwords and sending them to the user
|
||||
pub async fn gen_password(bot: Throttle<Bot>, msg: Message) -> crate::Result<()> {
|
||||
let mut message: ArrayString<{ 10 + 35 * 10 }> = "Passwords:".try_into().unwrap();
|
||||
let passwords = spawn_blocking(generate_passwords).await?;
|
||||
for password in passwords {
|
||||
write!(message, "\n`{password}`").unwrap();
|
||||
}
|
||||
bot.send_message(msg.chat.id, message.as_str())
|
||||
.parse_mode(ParseMode::MarkdownV2)
|
||||
.reply_markup(deletion_markup())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
84
src/commands/get_account.rs
Normal file
84
src/commands/get_account.rs
Normal file
@ -0,0 +1,84 @@
|
||||
use crate::{
|
||||
errors::NoUserInfo,
|
||||
markups::{self, deletion_markup},
|
||||
state::NameCheckKind,
|
||||
Handler, MainDialogue, State,
|
||||
};
|
||||
use cryptography::prelude::*;
|
||||
use entity::prelude::*;
|
||||
use sea_orm::DatabaseConnection;
|
||||
use teloxide::{adaptors::Throttle, prelude::*, types::ParseMode};
|
||||
use tokio::task::spawn_blocking;
|
||||
|
||||
/// Gets the master password, decryptes the account and sends it to the user with copyable fields
|
||||
async fn get_master_pass(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
db: DatabaseConnection,
|
||||
dialogue: MainDialogue,
|
||||
name: String,
|
||||
master_pass: String,
|
||||
) -> crate::Result<()> {
|
||||
dialogue.exit().await?;
|
||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
||||
let account = match Account::get(user_id, &name, &db).await? {
|
||||
Some(account) => account,
|
||||
None => {
|
||||
bot.send_message(msg.chat.id, "Account not found")
|
||||
.reply_markup(deletion_markup())
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let (login, password) = spawn_blocking(move || account.decrypt(&master_pass)).await??;
|
||||
let message = format!("Name:\n`{name}`\nLogin:\n`{login}`\nPassword:\n`{password}`");
|
||||
bot.send_message(msg.chat.id, message)
|
||||
.reply_markup(deletion_markup())
|
||||
.parse_mode(ParseMode::MarkdownV2)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets the account name to get
|
||||
async fn get_account_name(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
_: DatabaseConnection,
|
||||
dialogue: MainDialogue,
|
||||
name: String,
|
||||
) -> crate::Result<()> {
|
||||
let previous = bot
|
||||
.send_message(msg.chat.id, "Send master password")
|
||||
.await?;
|
||||
dialogue
|
||||
.update(State::GetMasterPass(Handler::new(
|
||||
move |bot, msg, db, dialogue, master_pass| {
|
||||
get_master_pass(bot, msg, db, dialogue, name, master_pass)
|
||||
},
|
||||
previous,
|
||||
)))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handles /get_account command
|
||||
pub async fn get_account(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
dialogue: MainDialogue,
|
||||
db: DatabaseConnection,
|
||||
) -> crate::Result<()> {
|
||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
||||
let markup = markups::account_markup(user_id, &db).await?;
|
||||
let previous = bot
|
||||
.send_message(msg.chat.id, "Send account name")
|
||||
.reply_markup(markup)
|
||||
.await?;
|
||||
dialogue
|
||||
.update(State::GetAccountName(
|
||||
Handler::new(get_account_name, previous),
|
||||
NameCheckKind::MustExist,
|
||||
))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
36
src/commands/get_accounts.rs
Normal file
36
src/commands/get_accounts.rs
Normal file
@ -0,0 +1,36 @@
|
||||
use crate::{errors::NoUserInfo, markups::deletion_markup};
|
||||
use entity::prelude::*;
|
||||
use futures::TryStreamExt;
|
||||
use sea_orm::DatabaseConnection;
|
||||
use std::fmt::Write;
|
||||
use teloxide::{adaptors::Throttle, prelude::*, types::ParseMode};
|
||||
|
||||
/// Handles /get_accounts command by sending the list of copyable account names to the user
|
||||
pub async fn get_accounts(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
db: DatabaseConnection,
|
||||
) -> crate::Result<()> {
|
||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
||||
let mut account_names = Account::get_names(user_id, &db, true).await?;
|
||||
let mut result = match account_names.try_next().await? {
|
||||
Some(name) => format!("Accounts:\n`{name}`"),
|
||||
None => {
|
||||
bot.send_message(msg.chat.id, "No accounts found")
|
||||
.reply_markup(deletion_markup())
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
account_names
|
||||
.try_for_each(|name| {
|
||||
write!(result, "\n`{name}`").unwrap();
|
||||
async { Ok(()) }
|
||||
})
|
||||
.await?;
|
||||
bot.send_message(msg.chat.id, result)
|
||||
.parse_mode(ParseMode::MarkdownV2)
|
||||
.reply_markup(deletion_markup())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
10
src/commands/help.rs
Normal file
10
src/commands/help.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use crate::{markups::deletion_markup, Command};
|
||||
use teloxide::{adaptors::Throttle, prelude::*, utils::command::BotCommands};
|
||||
|
||||
/// Handles the help command by sending the passwords descryptions
|
||||
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())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
102
src/commands/import.rs
Normal file
102
src/commands/import.rs
Normal file
@ -0,0 +1,102 @@
|
||||
use crate::{
|
||||
errors::NoUserInfo, markups::deletion_markup, models::User, Handler, MainDialogue, State,
|
||||
};
|
||||
use futures::{future, stream::FuturesUnordered, StreamExt};
|
||||
use itertools::Itertools;
|
||||
use sea_orm::prelude::*;
|
||||
use std::sync::Arc;
|
||||
use teloxide::{adaptors::Throttle, prelude::*};
|
||||
use tokio::task::spawn_blocking;
|
||||
|
||||
/// Gets the master password, encryptes and adds the accounts to the DB
|
||||
async fn get_master_pass(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
db: DatabaseConnection,
|
||||
dialogue: MainDialogue,
|
||||
master_pass: String,
|
||||
user: User,
|
||||
) -> crate::Result<()> {
|
||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
||||
let accounts = user.accounts;
|
||||
|
||||
let failed: Vec<String> = {
|
||||
let master_pass: Arc<str> = master_pass.into();
|
||||
let db = &db;
|
||||
|
||||
let futures: FuturesUnordered<_> = accounts
|
||||
.into_iter()
|
||||
.map(|account| {
|
||||
let master_pass = Arc::clone(&master_pass);
|
||||
async move {
|
||||
if !account.validate() {
|
||||
return Err(account.name);
|
||||
}
|
||||
let name = account.name.clone();
|
||||
match spawn_blocking(move || account.into_account(user_id, &master_pass)).await
|
||||
{
|
||||
Ok(Ok(account)) => match account.insert(db).await {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err(name),
|
||||
},
|
||||
_ => Err(name),
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
futures
|
||||
.filter_map(|result| future::ready(result.err()))
|
||||
.collect()
|
||||
.await
|
||||
};
|
||||
let message = if failed.is_empty() {
|
||||
"Success".to_owned()
|
||||
} else {
|
||||
format!(
|
||||
"Failed to create the following accounts:\n{}",
|
||||
failed.into_iter().format("\n")
|
||||
)
|
||||
};
|
||||
bot.send_message(msg.chat.id, message)
|
||||
.reply_markup(deletion_markup())
|
||||
.await?;
|
||||
dialogue.exit().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Downloads and parses the json and asks for the master password
|
||||
async fn get_document(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
_: DatabaseConnection,
|
||||
dialogue: MainDialogue,
|
||||
user: User,
|
||||
) -> crate::Result<()> {
|
||||
let previous = bot
|
||||
.send_message(msg.chat.id, "Send master password")
|
||||
.await?;
|
||||
dialogue
|
||||
.update(State::GetMasterPass(Handler::new(
|
||||
move |bot, msg, db, dialogue, master_pass| {
|
||||
get_master_pass(bot, msg, db, dialogue, master_pass, user)
|
||||
},
|
||||
previous,
|
||||
)))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handles /import command
|
||||
pub async fn import(bot: Throttle<Bot>, msg: Message, dialogue: MainDialogue) -> crate::Result<()> {
|
||||
let previous = bot
|
||||
.send_message(
|
||||
msg.chat.id,
|
||||
"Send a json document with the same format as created by /export",
|
||||
)
|
||||
.await?;
|
||||
dialogue
|
||||
.update(State::GetUser(Handler::new(get_document, previous)))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
60
src/commands/mod.rs
Normal file
60
src/commands/mod.rs
Normal file
@ -0,0 +1,60 @@
|
||||
//! This module consists of endpoints to handle commands
|
||||
|
||||
mod add_account;
|
||||
mod cancel;
|
||||
mod delete;
|
||||
mod delete_all;
|
||||
mod export;
|
||||
mod gen_password;
|
||||
mod get_account;
|
||||
mod get_accounts;
|
||||
mod help;
|
||||
mod import;
|
||||
mod set_master_pass;
|
||||
mod start;
|
||||
|
||||
pub use add_account::add_account;
|
||||
pub use cancel::cancel;
|
||||
pub use delete::delete;
|
||||
pub use delete_all::delete_all;
|
||||
pub use export::export;
|
||||
pub use gen_password::gen_password;
|
||||
pub use get_account::get_account;
|
||||
pub use get_accounts::get_accounts;
|
||||
pub use help::help;
|
||||
pub use import::import;
|
||||
pub use set_master_pass::set_master_pass;
|
||||
pub use start::start;
|
||||
use teloxide::macros::BotCommands;
|
||||
|
||||
#[derive(BotCommands, Clone, Copy)]
|
||||
#[command(
|
||||
rename_rule = "snake_case",
|
||||
description = "These commands are supported:"
|
||||
)]
|
||||
pub enum Command {
|
||||
#[command(description = "displays the welcome message")]
|
||||
Start,
|
||||
#[command(description = "displays this text")]
|
||||
Help,
|
||||
#[command(description = "sets the master password")]
|
||||
SetMasterPass,
|
||||
#[command(description = "adds the account")]
|
||||
AddAccount,
|
||||
#[command(description = "gets the account")]
|
||||
GetAccount,
|
||||
#[command(description = "gets a list of accounts")]
|
||||
GetAccounts,
|
||||
#[command(description = "deletes the account")]
|
||||
Delete,
|
||||
#[command(description = "deletes all the accounts and the master password")]
|
||||
DeleteAll,
|
||||
#[command(description = "exports all the accounts in a json file")]
|
||||
Export,
|
||||
#[command(description = "loads the accounts from a json file")]
|
||||
Import,
|
||||
#[command(description = "generates 10 secure passwords")]
|
||||
GenPassword,
|
||||
#[command(description = "cancels the current action")]
|
||||
Cancel,
|
||||
}
|
50
src/commands/set_master_pass.rs
Normal file
50
src/commands/set_master_pass.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use crate::{errors::NoUserInfo, markups::deletion_markup, Handler, MainDialogue, State};
|
||||
use cryptography::prelude::*;
|
||||
use entity::prelude::*;
|
||||
use sea_orm::prelude::*;
|
||||
use teloxide::{adaptors::Throttle, prelude::*};
|
||||
use tokio::task;
|
||||
|
||||
/// Actually sets the master password
|
||||
async fn get_master_pass(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
db: DatabaseConnection,
|
||||
dialogue: MainDialogue,
|
||||
master_password: String,
|
||||
) -> crate::Result<()> {
|
||||
dialogue.exit().await?;
|
||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
||||
let model = task::spawn_blocking(move || {
|
||||
master_pass::ActiveModel::from_unencrypted(user_id, &master_password)
|
||||
})
|
||||
.await?;
|
||||
model.insert(&db).await?;
|
||||
bot.send_message(msg.chat.id, "Success")
|
||||
.reply_markup(deletion_markup())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handles /set_master_pass command
|
||||
pub async fn set_master_pass(
|
||||
bot: Throttle<Bot>,
|
||||
msg: Message,
|
||||
dialogue: MainDialogue,
|
||||
db: DatabaseConnection,
|
||||
) -> 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())
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
let previous = bot
|
||||
.send_message(msg.chat.id, "Send new master password")
|
||||
.await?;
|
||||
dialogue
|
||||
.update(State::GetPassword(Handler::new(get_master_pass, previous)))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
12
src/commands/start.rs
Normal file
12
src/commands/start.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use teloxide::{adaptors::Throttle, prelude::*};
|
||||
|
||||
/// Handles /start command by sending the greeting message
|
||||
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?;
|
||||
Ok(())
|
||||
}
|
Reference in New Issue
Block a user