added get endpoint, updated state_management

This commit is contained in:
StNicolay 2023-07-26 00:52:12 +03:00
parent 96c7d2e37e
commit 22c754a256
Signed by: StNicolay
GPG Key ID: 9693D04DCD962B0D
27 changed files with 278 additions and 149 deletions

View File

@ -1,7 +1,7 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3
use futures::Stream; use futures::Stream;
use sea_orm::{entity::prelude::*, QueryOrder, QuerySelect}; use sea_orm::{entity::prelude::*, QueryOrder, QuerySelect, Statement};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "account")] #[sea_orm(table_name = "account")]
@ -86,4 +86,20 @@ impl Entity {
.await?; .await?;
Ok(()) Ok(())
} }
/// Gets a name by a hex of a SHA256 hash of the name
pub async fn get_name_by_hash(
user_id: u64,
hash: String,
db: &DatabaseConnection,
) -> crate::Result<Option<String>> {
db.query_one(Statement::from_sql_and_values(
sea_orm::DatabaseBackend::MySql,
"SELECT `name` FROM `account` WHERE SHA2(`name`, 256) = ? AND `user_id` = ?;",
[hash.into(), user_id.into()],
))
.await?
.map(|result| result.try_get_by_index(0))
.transpose()
}
} }

View File

@ -1,5 +1,4 @@
use crate::prelude::*; use crate::prelude::*;
use teloxide::{dispatching::DpHandlerDescription, dptree::Handler};
/// Deletes the message from the callback /// Deletes the message from the callback
pub async fn delete_message(bot: Throttle<Bot>, q: CallbackQuery) -> crate::Result<()> { pub async fn delete_message(bot: Throttle<Bot>, q: CallbackQuery) -> crate::Result<()> {

35
src/callbacks/get.rs Normal file
View File

@ -0,0 +1,35 @@
use crate::prelude::*;
use teloxide::types::ParseMode;
pub async fn get(
bot: Throttle<Bot>,
q: CallbackQuery,
db: DatabaseConnection,
hash: super::NameHash,
) -> crate::Result<()> {
let user_id = q.from.id.0;
let msg = q.message.as_ref().unwrap();
let hash = hex::encode(hash);
let name = match Account::get_name_by_hash(user_id, hash, &db).await? {
Some(name) => name,
None => {
bot.edit_message_text(
msg.chat.id,
msg.id,
"Account wan't found. Select another one",
)
.reply_markup(menu_markup(user_id, &db).await?)
.await?;
return Ok(());
}
};
let text = format!("Name:\n`{name}`\nLogin:\n***\nPassword:\n***");
bot.send_message(msg.chat.id, text)
.reply_markup(account_markup(&name, true))
.parse_mode(ParseMode::MarkdownV2)
.await?;
Ok(())
}

View File

@ -20,7 +20,7 @@ pub async fn get_menu(
.await?; .await?;
} }
let markup = spawn_blocking(|| menu_markup(names)).await?; let markup = spawn_blocking(|| menu_markup_sync(names)).await?;
bot.edit_message_text(msg.chat.id, msg.id, "Choose your account") bot.edit_message_text(msg.chat.id, msg.id, "Choose your account")
.reply_markup(markup) .reply_markup(markup)
.await?; .await?;

View File

@ -1,26 +1,28 @@
//! This module consists of endpoints to handle callbacks //! This module consists of endpoints to handle callbacks
mod delete_message; mod delete_message;
mod get;
mod get_menu; mod get_menu;
pub use delete_message::delete_message; pub use delete_message::delete_message;
pub use get::get;
pub use get_menu::get_menu; pub use get_menu::get_menu;
use crate::errors::InvalidCommand; use crate::errors::InvalidCommand;
use base64::{engine::general_purpose::STANDARD_NO_PAD as B64_ENGINE, Engine as _}; use base64::{engine::general_purpose::STANDARD_NO_PAD as B64_ENGINE, Engine as _};
use std::str::FromStr; use std::str::FromStr;
use teloxide::types::{CallbackQuery, Message}; use teloxide::types::CallbackQuery;
type NameHash = [u8; 32]; type NameHash = Vec<u8>;
#[derive(Clone, Copy)] #[derive(Clone, Copy, Debug)]
pub enum AlterableField { pub enum AlterableField {
Name, Name,
Login, Login,
Pass, Pass,
} }
#[derive(Clone, Copy)] #[derive(Clone, Debug)]
pub enum CallbackCommand { pub enum CallbackCommand {
DeleteMessage, DeleteMessage,
Get(NameHash), Get(NameHash),
@ -34,7 +36,7 @@ pub enum CallbackCommand {
impl CallbackCommand { impl CallbackCommand {
pub fn from_query(q: CallbackQuery) -> Option<Self> { pub fn from_query(q: CallbackQuery) -> Option<Self> {
q.message.as_ref()?; q.message.as_ref()?;
q.data.and_then(|text| text.parse().ok()) q.data.and_then(|text| dbg!(text.parse()).ok())
} }
} }
@ -45,6 +47,8 @@ impl FromStr for CallbackCommand {
use AlterableField::*; use AlterableField::*;
use CallbackCommand::*; use CallbackCommand::*;
println!("{s}");
match s { match s {
"delete_message" => return Ok(DeleteMessage), "delete_message" => return Ok(DeleteMessage),
"get_menu" => return Ok(GetMenu), "get_menu" => return Ok(GetMenu),
@ -56,20 +60,19 @@ impl FromStr for CallbackCommand {
(Some(command), Some(name), None) => (command, name), (Some(command), Some(name), None) => (command, name),
_ => return Err(InvalidCommand::InvalidParams), _ => return Err(InvalidCommand::InvalidParams),
}; };
let name_hash = B64_ENGINE.decode(name)?;
let mut name_arr = [0; 32]; if name_hash.len() != 32 {
if B64_ENGINE.decode_slice(name, &mut name_arr)? != 32 { return Err(InvalidCommand::InvalidOutputLength);
return Err(InvalidCommand::OutputTooShort); }
};
match command { match command {
"get" => Ok(Get(name_arr)), "get" => Ok(Get(name_hash)),
"decrypt" => Ok(Decrypt(name_arr)), "decrypt" => Ok(Decrypt(name_hash)),
"hide" => Ok(Hide(name_arr)), "hide" => Ok(Hide(name_hash)),
"an" => Ok(Alter(name_arr, Name)), "an" => Ok(Alter(name_hash, Name)),
"al" => Ok(Alter(name_arr, Login)), "al" => Ok(Alter(name_hash, Login)),
"ap" => Ok(Alter(name_arr, Pass)), "ap" => Ok(Alter(name_hash, Pass)),
"delete" => Ok(DeleteAccount(name_arr)), "delete" => Ok(DeleteAccount(name_hash)),
_ => Err(InvalidCommand::InvalidParams), _ => Err(InvalidCommand::InvalidParams),
} }
} }

View File

@ -8,11 +8,13 @@ async fn get_master_pass(
msg: Message, msg: Message,
db: DatabaseConnection, db: DatabaseConnection,
dialogue: MainDialogue, dialogue: MainDialogue,
ids: MessageIds,
name: String, name: String,
login: String, login: String,
password: String, password: String,
master_pass: String, master_pass: String,
) -> crate::Result<()> { ) -> crate::Result<()> {
ids.delete(&bot).await;
let user_id = msg.from().ok_or(NoUserInfo)?.id.0; let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
dialogue.exit().await?; dialogue.exit().await?;
let account = spawn_blocking(move || { let account = spawn_blocking(move || {
@ -38,4 +40,9 @@ handler!(get_login(name: String, login: String),
get_password 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);
handler!(pub add_account(), "Send account name", State::GetNewName, get_account_name); first_handler!(
add_account,
"Send account name",
State::GetNewName,
get_account_name
);

View File

@ -7,10 +7,13 @@ async fn get_master_pass(
msg: Message, msg: Message,
db: DatabaseConnection, db: DatabaseConnection,
dialogue: MainDialogue, dialogue: MainDialogue,
ids: MessageIds,
name: String, name: String,
_: String, _: String,
) -> crate::Result<()> { ) -> crate::Result<()> {
ids.delete(&bot).await;
dialogue.exit().await?; dialogue.exit().await?;
let user_id = msg.from().ok_or(NoUserInfo)?.id.0; let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
Account::delete_by_id((user_id, name)).exec(&db).await?; Account::delete_by_id((user_id, name)).exec(&db).await?;
bot.send_message(msg.chat.id, "The account is successfully deleted") bot.send_message(msg.chat.id, "The account is successfully deleted")
@ -25,4 +28,8 @@ handler!(
State::GetMasterPass, State::GetMasterPass,
get_master_pass get_master_pass
); );
ask_name_handler!(pub delete(), "Send the name of the account to delete", get_account_name); ask_name_handler!(
delete,
"Send the name of the account to delete",
get_account_name
);

View File

@ -10,8 +10,10 @@ async fn get_master_pass(
msg: Message, msg: Message,
db: DatabaseConnection, db: DatabaseConnection,
dialogue: MainDialogue, dialogue: MainDialogue,
ids: MessageIds,
_: String, _: String,
) -> crate::Result<()> { ) -> crate::Result<()> {
ids.delete(&bot).await;
dialogue.exit().await?; dialogue.exit().await?;
let user_id = msg.from().ok_or(NoUserInfo)?.id.0; let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
let txn = db.begin().await?; let txn = db.begin().await?;
@ -36,8 +38,8 @@ async fn get_master_pass(
Ok(()) Ok(())
} }
handler!( first_handler!(
pub delete_all(), delete_all,
"Send master password to delete EVERYTHING.\nTHIS ACTION IS IRREVERSIBLE", "Send master password to delete EVERYTHING.\nTHIS ACTION IS IRREVERSIBLE",
State::GetMasterPass, State::GetMasterPass,
get_master_pass get_master_pass

View File

@ -23,8 +23,10 @@ async fn get_master_pass(
msg: Message, msg: Message,
db: DatabaseConnection, db: DatabaseConnection,
dialogue: MainDialogue, dialogue: MainDialogue,
ids: MessageIds,
master_pass: String, master_pass: String,
) -> crate::Result<()> { ) -> crate::Result<()> {
ids.delete(&bot).await;
let user_id = msg.from().ok_or(NoUserInfo)?.id.0; let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
let mut accounts = Vec::new(); let mut accounts = Vec::new();
@ -53,4 +55,9 @@ async fn get_master_pass(
Ok(()) Ok(())
} }
handler!(pub export(), "Send the master password to export your accounts", State::GetMasterPass, get_master_pass); first_handler!(
export,
"Send the master password to export your accounts",
State::GetMasterPass,
get_master_pass
);

View File

@ -8,10 +8,13 @@ async fn get_master_pass(
msg: Message, msg: Message,
db: DatabaseConnection, db: DatabaseConnection,
dialogue: MainDialogue, dialogue: MainDialogue,
ids: MessageIds,
name: String, name: String,
master_pass: String, master_pass: String,
) -> crate::Result<()> { ) -> crate::Result<()> {
ids.delete(&bot).await;
dialogue.exit().await?; dialogue.exit().await?;
let user_id = msg.from().ok_or(NoUserInfo)?.id.0; let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
let account = match Account::get(user_id, &name, &db).await? { let account = match Account::get(user_id, &name, &db).await? {
Some(account) => account, Some(account) => account,
@ -32,4 +35,8 @@ async fn get_master_pass(
} }
handler!(get_account_name(name:String), "Send master password", State::GetMasterPass, get_master_pass); handler!(get_account_name(name:String), "Send master password", State::GetMasterPass, get_master_pass);
ask_name_handler!(pub get_account(), "Send the name of the account to get", get_account_name); ask_name_handler!(
get_account,
"Send the name of the account to get",
get_account_name
);

View File

@ -31,9 +31,11 @@ async fn get_master_pass(
msg: Message, msg: Message,
db: DatabaseConnection, db: DatabaseConnection,
dialogue: MainDialogue, dialogue: MainDialogue,
ids: MessageIds,
user: User, user: User,
master_pass: String, master_pass: String,
) -> crate::Result<()> { ) -> crate::Result<()> {
ids.delete(&bot).await;
let user_id = msg.from().ok_or(NoUserInfo)?.id.0; let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
let mut failed = Vec::new(); let mut failed = Vec::new();
@ -64,4 +66,9 @@ async fn get_master_pass(
} }
handler!(get_user(user: User), "Send master password", State::GetMasterPass, get_master_pass); handler!(get_user(user: User), "Send master password", State::GetMasterPass, get_master_pass);
handler!(pub import(), "Send a json document with the same format as created by /export", State::GetUser, get_user); first_handler!(
import,
"Send a json document with the same format as created by /export",
State::GetUser,
get_user
);

View File

@ -15,7 +15,7 @@ pub async fn menu(bot: Throttle<Bot>, msg: Message, db: DatabaseConnection) -> c
.await?; .await?;
} }
let markup = spawn_blocking(|| menu_markup(names)).await?; let markup = spawn_blocking(|| menu_markup_sync(names)).await?;
bot.send_message(msg.chat.id, "Choose your account") bot.send_message(msg.chat.id, "Choose your account")
.reply_markup(markup) .reply_markup(markup)
.await?; .await?;

View File

@ -7,9 +7,12 @@ async fn get_master_pass(
msg: Message, msg: Message,
db: DatabaseConnection, db: DatabaseConnection,
dialogue: MainDialogue, dialogue: MainDialogue,
ids: MessageIds,
master_pass: String, master_pass: String,
) -> crate::Result<()> { ) -> crate::Result<()> {
ids.delete(&bot).await;
dialogue.exit().await?; dialogue.exit().await?;
let user_id = msg.from().ok_or(NoUserInfo)?.id.0; let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
let model = let model =
spawn_blocking(move || master_pass::ActiveModel::from_unencrypted(user_id, &master_pass)) spawn_blocking(move || master_pass::ActiveModel::from_unencrypted(user_id, &master_pass))
@ -40,10 +43,10 @@ pub async fn set_master_pass(
.await?; .await?;
dialogue dialogue
.update(State::GetNewMasterPass(Handler::new( .update(State::GetNewMasterPass(Handler::new(
|bot, msg, db, dialogue, master_pass| { |bot, msg, db, dialogue, ids, master_pass| {
Box::pin(get_master_pass(bot, msg, db, dialogue, master_pass)) Box::pin(get_master_pass(bot, msg, db, dialogue, ids, master_pass))
}, },
&previous, MessageIds::from(&previous),
))) )))
.await?; .await?;
Ok(()) Ok(())

View File

@ -11,7 +11,7 @@ pub enum InvalidCommand {
#[error("Invalid params")] #[error("Invalid params")]
InvalidParams, InvalidParams,
#[error("Not enough bytes in the name's hash")] #[error("Not enough bytes in the name's hash")]
OutputTooShort, InvalidOutputLength,
#[error("Error decoding the values: {0}")] #[error("Error decoding the values: {0}")]
NameDecodingError(#[from] base64::DecodeSliceError), NameDecodingError(#[from] base64::DecodeError),
} }

View File

@ -1,39 +1,66 @@
#[macro_export]
macro_rules! change_state {
($dialogue: expr, $previous: expr, ($($param: ident),*), $message: literal, $next_state: expr, $next_func: ident) => {{
$dialogue
.update($next_state(Handler::new(
move |bot, msg, db, dialogue, ids, param| Box::pin($next_func(bot, msg, db, dialogue, ids, $($param,)* param)),
&$previous,
)))
.await?;
Ok(())
}};
}
#[macro_export]
macro_rules! first_handler {
($function_name: ident, $message: expr, $next_state: expr, $next_func: ident) => {
#[inline]
pub async fn $function_name(
bot: Throttle<Bot>,
msg: Message,
dialogue: MainDialogue,
) -> $crate::Result<()> {
let previous = bot.send_message(msg.chat.id, $message).await?;
$crate::change_state!(dialogue, previous, (), $message, $next_state, $next_func)
}
};
}
#[macro_export] #[macro_export]
macro_rules! handler { macro_rules! handler {
($v: vis $function_name: ident ($($param: ident: $type: ty),*), $message: literal, $next_state: expr, $next_func: ident) => { ($function_name: ident ($($param: ident: $type: ty),*), $message: literal, $next_state: expr, $next_func: ident) => {
#[allow(clippy::too_many_arguments)]
#[inline] #[inline]
$v async fn $function_name( async fn $function_name(
bot: Throttle<Bot>, bot: Throttle<Bot>,
msg: Message, msg: Message,
_: DatabaseConnection, _: DatabaseConnection,
dialogue: MainDialogue, dialogue: MainDialogue,
ids: MessageIds,
$($param: $type),* $($param: $type),*
) -> $crate::Result<()> { ) -> $crate::Result<()> {
ids.delete(&bot).await;
let previous = bot.send_message(msg.chat.id, $message).await?; let previous = bot.send_message(msg.chat.id, $message).await?;
dialogue
.update($next_state(Handler::new( $crate::change_state!(dialogue, previous, ($($param),*), $message, $next_state, $next_func)
move |bot, msg, db, dialogue, param| Box::pin($next_func(bot, msg, db, dialogue, $($param,)* param)),
&previous,
)))
.await?;
Ok(())
} }
}; };
} }
#[macro_export] #[macro_export]
macro_rules! ask_name_handler { macro_rules! ask_name_handler {
($v: vis $function_name: ident ($($param: ident: $type: ty),*), $message: literal, $next_func: ident) => { ($function_name: ident, $message: literal, $next_func: ident) => {
#[inline] #[inline]
$v async fn $function_name( pub async fn $function_name(
bot: Throttle<Bot>, bot: Throttle<Bot>,
msg: Message, msg: Message,
dialogue: MainDialogue, dialogue: MainDialogue,
db: DatabaseConnection, db: DatabaseConnection,
$($param: $type),*
) -> $crate::Result<()> { ) -> $crate::Result<()> {
let user_id = msg.from().ok_or(NoUserInfo)?.id.0; let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
let markup = account_markup(user_id, &db).await?; let markup = account_list_markup(user_id, &db).await?;
if markup.keyboard.is_empty() { if markup.keyboard.is_empty() {
bot.send_message(msg.chat.id, "No accounts found") bot.send_message(msg.chat.id, "No accounts found")
.reply_markup(deletion_markup()) .reply_markup(deletion_markup())
@ -44,13 +71,15 @@ macro_rules! ask_name_handler {
.send_message(msg.chat.id, $message) .send_message(msg.chat.id, $message)
.reply_markup(markup) .reply_markup(markup)
.await?; .await?;
dialogue
.update(State::GetExistingName(Handler::new( $crate::change_state!(
move |bot, msg, db, dialogue, param| Box::pin($next_func(bot, msg, db, dialogue, $($param,)* param)), dialogue,
&previous, previous,
))) (),
.await?; $message,
Ok(()) State::GetExistingName,
$next_func
)
} }
}; };
} }
@ -71,7 +100,7 @@ macro_rules! simple_state_handler {
msg, msg,
db, db,
dialogue, dialogue,
|bot, msg, db, param| Box::pin($check(bot, msg, db, param)), |msg, db, param| Box::pin($check(msg, db, param)),
$no_text_message, $no_text_message,
next, next,
) )

View File

@ -63,7 +63,8 @@ fn get_dispatcher(
let callback_handler = Update::filter_callback_query() let callback_handler = Update::filter_callback_query()
.filter_map(CallbackCommand::from_query) .filter_map(CallbackCommand::from_query)
.branch(case![CallbackCommand::GetMenu].endpoint(callbacks::get_menu)) .branch(case![CallbackCommand::GetMenu].endpoint(callbacks::get_menu))
.branch(case![CallbackCommand::DeleteMessage].endpoint(callbacks::delete_message)); .branch(case![CallbackCommand::DeleteMessage].endpoint(callbacks::delete_message))
.branch(case![CallbackCommand::Get(hash)].endpoint(callbacks::get));
let handler = dptree::entry() let handler = dptree::entry()
.branch(message_handler) .branch(message_handler)

View File

@ -1,13 +1,13 @@
use crate::prelude::*; use crate::prelude::*;
use base64::{engine::general_purpose::STANDARD as B64_ENGINE, Engine as _}; use base64::{engine::general_purpose::STANDARD_NO_PAD as B64_ENGINE, Engine as _};
use futures::future;
use itertools::Itertools; use itertools::Itertools;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use teloxide::types::{InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, KeyboardMarkup}; use teloxide::types::{InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, KeyboardMarkup};
use tokio::task::spawn_blocking;
/// Creates a markup of all user's account names /// Creates a markup of all user's account names
#[inline] #[inline]
pub async fn account_markup( pub async fn account_list_markup(
user_id: u64, user_id: u64,
db: &DatabaseConnection, db: &DatabaseConnection,
) -> crate::Result<KeyboardMarkup> { ) -> crate::Result<KeyboardMarkup> {
@ -25,7 +25,7 @@ pub async fn account_markup(
} }
#[inline] #[inline]
pub fn menu_markup(names: impl IntoIterator<Item = String>) -> InlineKeyboardMarkup { pub fn menu_markup_sync(names: impl IntoIterator<Item = String>) -> InlineKeyboardMarkup {
let names = names let names = names
.into_iter() .into_iter()
.map(|name| { .map(|name| {
@ -40,6 +40,57 @@ pub fn menu_markup(names: impl IntoIterator<Item = String>) -> InlineKeyboardMar
InlineKeyboardMarkup::new(&names) InlineKeyboardMarkup::new(&names)
} }
#[inline]
pub async fn menu_markup(
user_id: u64,
db: &DatabaseConnection,
) -> crate::Result<InlineKeyboardMarkup> {
let names: Vec<String> = Account::get_names(user_id, db).await?.try_collect().await?;
spawn_blocking(|| menu_markup_sync(names))
.await
.map_err(Into::into)
}
#[inline]
fn make_button(text: &str, command: &str, hash: &str) -> InlineKeyboardButton {
let mut data = command.to_owned();
data.reserve(44);
data.push(' ');
data.push_str(hash);
InlineKeyboardButton::callback(text, data)
}
#[inline]
pub fn account_markup(name: &str, is_encrypted: bool) -> InlineKeyboardMarkup {
let mut hash = [0; 43];
B64_ENGINE
.encode_slice(<Sha256 as Digest>::digest(name), &mut hash)
.unwrap();
let hash = std::str::from_utf8(&hash).unwrap();
let alter_buttons = [
("Alter name", "an"),
("Alter login", "al"),
("Alter password", "ap"),
]
.map(|(text, command)| make_button(text, command, hash));
let mut second_raw = Vec::new();
if is_encrypted {
second_raw.push(make_button("Decrypt", "decrypt", hash))
} else {
second_raw.push(make_button("Hide", "hide", hash));
}
second_raw.push(make_button("Delete account", "delete", hash));
let menu_button = InlineKeyboardButton::callback("Back to the menu", "get_menu");
InlineKeyboardMarkup::new([alter_buttons])
.append_row(second_raw)
.append_row([menu_button])
}
/// Creates a markup with a "Delete message" button. /// Creates a markup with a "Delete message" button.
/// This markup should be added for all messages that won't be deleted afterwards /// This markup should be added for all messages that won't be deleted afterwards
#[inline] #[inline]

View File

@ -2,7 +2,7 @@ pub(crate) use crate::{
ask_name_handler, ask_name_handler,
commands::Command, commands::Command,
errors::*, errors::*,
handler, first_handler, handler,
markups::*, markups::*,
models::*, models::*,
state::State, state::State,

View File

@ -14,11 +14,10 @@ pub async fn generic<F>(
) -> crate::Result<()> ) -> crate::Result<()>
where where
for<'a> F: FnOnce( for<'a> F: FnOnce(
&'a Throttle<Bot>,
&'a Message, &'a Message,
&'a DatabaseConnection, &'a DatabaseConnection,
&'a str, &'a str,
) -> BoxFuture<'a, crate::Result<Option<Message>>>, ) -> BoxFuture<'a, crate::Result<Option<String>>>,
{ {
let mut handler = next.lock().await; let mut handler = next.lock().await;
if handler.func.is_none() { if handler.func.is_none() {
@ -26,8 +25,6 @@ where
return Err(HandlerUsed.into()); return Err(HandlerUsed.into());
} }
handler.previous.delete(&bot).await;
let text = match msg.text() { let text = match msg.text() {
Some(text) => text.trim(), Some(text) => text.trim(),
None => { None => {
@ -45,16 +42,18 @@ where
return Ok(()); return Ok(());
} }
if let Some(failure_message) = check(&bot, &msg, &db, text).await? { if let Some(text) = check(&msg, &db, text).await? {
let failure_message = bot.send_message(msg.chat.id, text).await?;
handler.previous = MessageIds::from(&failure_message); handler.previous = MessageIds::from(&failure_message);
return Ok(()); return Ok(());
} }
let previous = handler.previous;
let func = handler.func.take().unwrap(); let func = handler.func.take().unwrap();
drop(handler); drop(handler);
let text = text.to_owned(); let text = text.to_owned();
if let Err(err) = func(bot, msg, db, dialogue.clone(), text).await { if let Err(err) = func(bot, msg, db, dialogue.clone(), previous, text).await {
let _ = dialogue.exit().await; let _ = dialogue.exit().await;
return Err(err); return Err(err);
} }

View File

@ -1,24 +1,5 @@
use crate::prelude::*; use crate::prelude::*;
/// Checks that the account with that name exists
#[inline]
async fn check_name(
bot: &Throttle<Bot>,
msg: &Message,
db: &DatabaseConnection,
name: &str,
user_id: u64,
) -> crate::Result<Option<Message>> {
if !Account::exists(user_id, name, db).await? {
let msg = bot
.send_message(msg.chat.id, "Account doesn't exists. Try again")
.reply_markup(account_markup(user_id, db).await?)
.await?;
return Ok(Some(msg));
}
Ok(None)
}
/// Function to handle GetExistingName state /// Function to handle GetExistingName state
pub async fn get_existing_name( pub async fn get_existing_name(
bot: Throttle<Bot>, bot: Throttle<Bot>,
@ -35,8 +16,6 @@ pub async fn get_existing_name(
return Err(HandlerUsed.into()); return Err(HandlerUsed.into());
} }
handler.previous.delete(&bot).await;
let text = match msg.text() { let text = match msg.text() {
Some(text) => text.trim(), Some(text) => text.trim(),
None => { None => {
@ -45,7 +24,7 @@ pub async fn get_existing_name(
msg.chat.id, msg.chat.id,
"Couldn't get the text of the message. Send the name again", "Couldn't get the text of the message. Send the name again",
) )
.reply_markup(account_markup(user_id, &db).await?) .reply_markup(account_list_markup(user_id, &db).await?)
.await?; .await?;
handler.previous = MessageIds::from(&msg); handler.previous = MessageIds::from(&msg);
return Ok(()); return Ok(());
@ -60,16 +39,21 @@ pub async fn get_existing_name(
return Ok(()); return Ok(());
} }
if let Some(failure_message) = check_name(&bot, &msg, &db, text, user_id).await? { if !Account::exists(user_id, text, &db).await? {
handler.previous = MessageIds::from(&failure_message); let msg = bot
.send_message(msg.chat.id, "Account doesn't exists. Try again")
.reply_markup(account_list_markup(user_id, &db).await?)
.await?;
handler.previous = MessageIds::from(&msg);
return Ok(()); return Ok(());
} }
let previous = handler.previous;
let func = handler.func.take().unwrap(); let func = handler.func.take().unwrap();
drop(handler); drop(handler);
let text = text.to_owned(); let text = text.to_owned();
if let Err(err) = func(bot, msg, db, dialogue.clone(), text).await { if let Err(err) = func(bot, msg, db, dialogue.clone(), previous, text).await {
let _ = dialogue.exit().await; let _ = dialogue.exit().await;
return Err(err); return Err(err);
} }

View File

@ -2,17 +2,13 @@ use crate::prelude::*;
#[inline] #[inline]
async fn check_login( async fn check_login(
bot: &Throttle<Bot>, _: &Message,
msg: &Message,
_: &DatabaseConnection, _: &DatabaseConnection,
login: &str, login: &str,
) -> crate::Result<Option<Message>> { ) -> crate::Result<Option<String>> {
let is_valid = validate_field(login); let is_valid = validate_field(login);
if !is_valid { if !is_valid {
let msg = bot return Ok(Some("Invalid login. Try again".to_owned()));
.send_message(msg.chat.id, "Invalid login. Try again")
.await?;
return Ok(Some(msg));
} }
Ok(None) Ok(None)
} }

View File

@ -5,11 +5,10 @@ use tokio::task::spawn_blocking;
/// Returns true if the provided master password is valid /// Returns true if the provided master password is valid
#[inline] #[inline]
async fn check_master_pass( async fn check_master_pass(
bot: &Throttle<Bot>,
msg: &Message, msg: &Message,
db: &DatabaseConnection, db: &DatabaseConnection,
master_pass: &str, master_pass: &str,
) -> crate::Result<Option<Message>> { ) -> crate::Result<Option<String>> {
let user_id = msg.from().ok_or(NoUserInfo)?.id.0; let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
let model = MasterPass::get(user_id, db).await?; let model = MasterPass::get(user_id, db).await?;
@ -20,21 +19,15 @@ async fn check_master_pass(
} }
None => { None => {
error!("User was put into the GetMasterPass state with no master password set"); error!("User was put into the GetMasterPass state with no master password set");
let msg = bot return Ok(Some(
.send_message( "No master password set. Use /cancel and set it by using /set_master_pass"
msg.chat.id, .to_owned(),
"No master password set. Use /cancel and set it by using /set_master_pass", ));
)
.await?;
return Ok(Some(msg));
} }
}; };
if !is_valid { if !is_valid {
let msg = bot return Ok(Some("Wrong master password. Try again".to_owned()));
.send_message(msg.chat.id, "Wrong master password. Try again")
.await?;
return Ok(Some(msg));
} }
Ok(None) Ok(None)
} }

View File

@ -33,20 +33,13 @@ fn process_validity(validity: PasswordValidity) -> Result<(), String> {
/// Checks that the account with that name exists /// Checks that the account with that name exists
#[inline] #[inline]
async fn check_new_master_pass( async fn check_new_master_pass(
bot: &Throttle<Bot>, _: &Message,
msg: &Message,
_: &DatabaseConnection, _: &DatabaseConnection,
password: &str, password: &str,
) -> crate::Result<Option<Message>> { ) -> crate::Result<Option<String>> {
let validity = check_master_pass(password); let validity = check_master_pass(password);
match process_validity(validity) { Ok(process_validity(validity).err())
Ok(()) => Ok(None),
Err(error_text) => {
let msg = bot.send_message(msg.chat.id, error_text).await?;
Ok(Some(msg))
}
}
} }
crate::simple_state_handler!( crate::simple_state_handler!(

View File

@ -3,26 +3,19 @@ use crate::prelude::*;
/// Validates a new account /// Validates a new account
#[inline] #[inline]
async fn check_new_account_name( async fn check_new_account_name(
bot: &Throttle<Bot>,
msg: &Message, msg: &Message,
db: &DatabaseConnection, db: &DatabaseConnection,
name: &str, name: &str,
) -> crate::Result<Option<Message>> { ) -> crate::Result<Option<String>> {
let user_id = msg.from().ok_or(NoUserInfo)?.id.0; let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
if Account::exists(user_id, name, db).await? {
let msg = bot
.send_message(msg.chat.id, "Account already exists")
.await?;
return Ok(Some(msg));
}
if !validate_field(name) {
let msg = bot
.send_message(msg.chat.id, "Invalid account name. Try again")
.await?;
return Ok(Some(msg));
}
Ok(None) if Account::exists(user_id, name, db).await? {
Ok(Some("Account already exists".to_owned()))
} else if !validate_field(name) {
Ok(Some("Invalid account name. Try again".to_owned()))
} else {
Ok(None)
}
} }
crate::simple_state_handler!( crate::simple_state_handler!(

View File

@ -2,17 +2,13 @@ use crate::prelude::*;
#[inline] #[inline]
async fn check_password( async fn check_password(
bot: &Throttle<Bot>, _: &Message,
msg: &Message,
_: &DatabaseConnection, _: &DatabaseConnection,
login: &str, password: &str,
) -> crate::Result<Option<Message>> { ) -> crate::Result<Option<String>> {
let is_valid = validate_field(login); let is_valid = validate_field(password);
if !is_valid { if !is_valid {
let msg = bot return Ok(Some("Invalid password. Try again".to_owned()));
.send_message(msg.chat.id, "Invalid password. Try again")
.await?;
return Ok(Some(msg));
} }
Ok(None) Ok(None)
} }

View File

@ -145,8 +145,6 @@ pub async fn get_user(
return Err(HandlerUsed.into()); return Err(HandlerUsed.into());
} }
handler.previous.delete(&bot).await;
if let Some("/cancel") = msg.text().map(str::trim) { if let Some("/cancel") = msg.text().map(str::trim) {
dialogue.exit().await?; dialogue.exit().await?;
bot.send_message(msg.chat.id, "Successfully cancelled") bot.send_message(msg.chat.id, "Successfully cancelled")
@ -190,10 +188,11 @@ pub async fn get_user(
} }
}; };
let previous = handler.previous;
let func = handler.func.take().unwrap(); let func = handler.func.take().unwrap();
drop(handler); drop(handler);
if let Err(err) = func(bot, msg, db, dialogue.clone(), user).await { if let Err(err) = func(bot, msg, db, dialogue.clone(), previous, user).await {
let _ = dialogue.exit().await; let _ = dialogue.exit().await;
return Err(err); return Err(err);
} }

View File

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use futures::future::BoxFuture; use futures::future::BoxFuture;
use std::sync::Arc; use std::sync::Arc;
use teloxide::types::MessageId; use teloxide::types::{InlineKeyboardMarkup, MessageId};
use tokio::sync::Mutex; use tokio::sync::Mutex;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -27,6 +27,7 @@ type DynHanlder<T> = Box<
Message, Message,
DatabaseConnection, DatabaseConnection,
MainDialogue, MainDialogue,
MessageIds,
T, T,
) -> BoxFuture<'static, crate::Result<()>> ) -> BoxFuture<'static, crate::Result<()>>
+ Send, + Send,
@ -50,6 +51,7 @@ impl<T> Handler<T> {
Message, Message,
DatabaseConnection, DatabaseConnection,
MainDialogue, MainDialogue,
MessageIds,
T, T,
) -> BoxFuture<'static, crate::Result<()>> ) -> BoxFuture<'static, crate::Result<()>>
+ Send + Send