Added state persistance
This commit is contained in:
parent
ef0ccfb457
commit
40f7194cbe
@ -1,7 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
entity::prelude::Account,
|
|
||||||
errors::NoUserInfo,
|
errors::NoUserInfo,
|
||||||
handlers::{markups::deletion_markup, Handler, MainDialogue, State},
|
handlers::{markups::deletion_markup, state::NameCheckKind, Handler, MainDialogue, State},
|
||||||
models::DecryptedAccount,
|
models::DecryptedAccount,
|
||||||
};
|
};
|
||||||
use sea_orm::prelude::*;
|
use sea_orm::prelude::*;
|
||||||
@ -81,18 +80,10 @@ async fn get_login(
|
|||||||
async fn get_account_name(
|
async fn get_account_name(
|
||||||
bot: Throttle<Bot>,
|
bot: Throttle<Bot>,
|
||||||
msg: Message,
|
msg: Message,
|
||||||
db: DatabaseConnection,
|
_: DatabaseConnection,
|
||||||
dialogue: MainDialogue,
|
dialogue: MainDialogue,
|
||||||
name: String,
|
name: String,
|
||||||
) -> crate::Result<()> {
|
) -> crate::Result<()> {
|
||||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
|
||||||
if Account::exists(user_id, &name, &db).await? {
|
|
||||||
dialogue.exit().await?;
|
|
||||||
bot.send_message(msg.chat.id, "Account alreay exists")
|
|
||||||
.reply_markup(deletion_markup())
|
|
||||||
.await?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let previous = bot.send_message(msg.chat.id, "Send login").await?;
|
let previous = bot.send_message(msg.chat.id, "Send login").await?;
|
||||||
dialogue
|
dialogue
|
||||||
.update(State::GetLogin(Handler::new(
|
.update(State::GetLogin(Handler::new(
|
||||||
@ -111,10 +102,10 @@ pub async fn add_account(
|
|||||||
) -> crate::Result<()> {
|
) -> crate::Result<()> {
|
||||||
let previous = bot.send_message(msg.chat.id, "Send account name").await?;
|
let previous = bot.send_message(msg.chat.id, "Send account name").await?;
|
||||||
dialogue
|
dialogue
|
||||||
.update(State::GetAccountName(Handler::new(
|
.update(State::GetAccountName(
|
||||||
get_account_name,
|
Handler::new(get_account_name, previous),
|
||||||
previous,
|
NameCheckKind::NewAccountName,
|
||||||
)))
|
))
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ use crate::{
|
|||||||
errors::NoUserInfo,
|
errors::NoUserInfo,
|
||||||
handlers::{
|
handlers::{
|
||||||
markups::{self, deletion_markup},
|
markups::{self, deletion_markup},
|
||||||
|
state::NameCheckKind,
|
||||||
Handler, MainDialogue, State,
|
Handler, MainDialogue, State,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -31,18 +32,10 @@ async fn get_master_pass(
|
|||||||
async fn get_account_name(
|
async fn get_account_name(
|
||||||
bot: Throttle<Bot>,
|
bot: Throttle<Bot>,
|
||||||
msg: Message,
|
msg: Message,
|
||||||
db: DatabaseConnection,
|
_: DatabaseConnection,
|
||||||
dialogue: MainDialogue,
|
dialogue: MainDialogue,
|
||||||
name: String,
|
name: String,
|
||||||
) -> crate::Result<()> {
|
) -> crate::Result<()> {
|
||||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
|
||||||
if !Account::exists(user_id, &name, &db).await? {
|
|
||||||
dialogue.exit().await?;
|
|
||||||
bot.send_message(msg.chat.id, "Account doesn't exists")
|
|
||||||
.reply_markup(deletion_markup())
|
|
||||||
.await?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let previous = bot
|
let previous = bot
|
||||||
.send_message(msg.chat.id, "Send master password. Once you send correct master password the account is unrecoverable")
|
.send_message(msg.chat.id, "Send master password. Once you send correct master password the account is unrecoverable")
|
||||||
.await?;
|
.await?;
|
||||||
@ -69,10 +62,10 @@ pub async fn delete(
|
|||||||
.reply_markup(markup)
|
.reply_markup(markup)
|
||||||
.await?;
|
.await?;
|
||||||
dialogue
|
dialogue
|
||||||
.update(State::GetAccountName(Handler::new(
|
.update(State::GetAccountName(
|
||||||
get_account_name,
|
Handler::new(get_account_name, previous),
|
||||||
previous,
|
NameCheckKind::MustExist,
|
||||||
)))
|
))
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ use crate::{
|
|||||||
errors::NoUserInfo,
|
errors::NoUserInfo,
|
||||||
handlers::{
|
handlers::{
|
||||||
markups::{self, deletion_markup},
|
markups::{self, deletion_markup},
|
||||||
|
state::NameCheckKind,
|
||||||
Handler, MainDialogue, State,
|
Handler, MainDialogue, State,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -43,18 +44,10 @@ async fn get_master_pass(
|
|||||||
async fn get_account_name(
|
async fn get_account_name(
|
||||||
bot: Throttle<Bot>,
|
bot: Throttle<Bot>,
|
||||||
msg: Message,
|
msg: Message,
|
||||||
db: DatabaseConnection,
|
_: DatabaseConnection,
|
||||||
dialogue: MainDialogue,
|
dialogue: MainDialogue,
|
||||||
name: String,
|
name: String,
|
||||||
) -> crate::Result<()> {
|
) -> crate::Result<()> {
|
||||||
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
|
||||||
if !Account::exists(user_id, &name, &db).await? {
|
|
||||||
dialogue.exit().await?;
|
|
||||||
bot.send_message(msg.chat.id, "Account doesn't exists")
|
|
||||||
.reply_markup(deletion_markup())
|
|
||||||
.await?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let previous = bot
|
let previous = bot
|
||||||
.send_message(msg.chat.id, "Send master password")
|
.send_message(msg.chat.id, "Send master password")
|
||||||
.await?;
|
.await?;
|
||||||
@ -83,10 +76,10 @@ pub async fn get_account(
|
|||||||
.reply_markup(markup)
|
.reply_markup(markup)
|
||||||
.await?;
|
.await?;
|
||||||
dialogue
|
dialogue
|
||||||
.update(State::GetAccountName(Handler::new(
|
.update(State::GetAccountName(
|
||||||
get_account_name,
|
Handler::new(get_account_name, previous),
|
||||||
previous,
|
NameCheckKind::MustExist,
|
||||||
)))
|
))
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
errors::NoUserInfo,
|
errors::NoUserInfo,
|
||||||
handlers::{markups::deletion_markup, Handler, MainDialogue, State},
|
handlers::{markups::deletion_markup, Handler, MainDialogue, State},
|
||||||
models::{DecryptedAccount, User},
|
models::User,
|
||||||
};
|
};
|
||||||
use futures::{future, stream::FuturesUnordered, StreamExt, TryStreamExt};
|
use futures::{future, stream::FuturesUnordered, StreamExt};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use sea_orm::prelude::*;
|
use sea_orm::prelude::*;
|
||||||
use serde_json::from_slice;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use teloxide::{adaptors::Throttle, net::Download, prelude::*};
|
use teloxide::{adaptors::Throttle, prelude::*};
|
||||||
use tokio::task::spawn_blocking;
|
use tokio::task::spawn_blocking;
|
||||||
|
|
||||||
/// Gets the master password, encryptes and adds the accounts to the DB
|
/// Gets the master password, encryptes and adds the accounts to the DB
|
||||||
@ -18,12 +17,15 @@ async fn get_master_pass(
|
|||||||
db: DatabaseConnection,
|
db: DatabaseConnection,
|
||||||
dialogue: MainDialogue,
|
dialogue: MainDialogue,
|
||||||
master_pass: String,
|
master_pass: String,
|
||||||
accounts: Vec<DecryptedAccount>,
|
user: User,
|
||||||
) -> 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 accounts = user.accounts;
|
||||||
|
|
||||||
let failed: Vec<String> = {
|
let failed: Vec<String> = {
|
||||||
let master_pass: Arc<str> = master_pass.into();
|
let master_pass: Arc<str> = master_pass.into();
|
||||||
let db = &db;
|
let db = &db;
|
||||||
|
|
||||||
let futures: FuturesUnordered<_> = accounts
|
let futures: FuturesUnordered<_> = accounts
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|account| {
|
.map(|account| {
|
||||||
@ -44,6 +46,7 @@ async fn get_master_pass(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
futures
|
futures
|
||||||
.filter_map(|result| future::ready(result.err()))
|
.filter_map(|result| future::ready(result.err()))
|
||||||
.collect()
|
.collect()
|
||||||
@ -70,44 +73,15 @@ async fn get_document(
|
|||||||
msg: Message,
|
msg: Message,
|
||||||
_: DatabaseConnection,
|
_: DatabaseConnection,
|
||||||
dialogue: MainDialogue,
|
dialogue: MainDialogue,
|
||||||
(): (),
|
user: User,
|
||||||
) -> crate::Result<()> {
|
) -> crate::Result<()> {
|
||||||
let document = msg.document().unwrap();
|
|
||||||
match document.file_name {
|
|
||||||
Some(ref name) if name.trim_end().ends_with(".json") => (),
|
|
||||||
_ => {
|
|
||||||
bot.send_message(msg.chat.id, "Invalid file name")
|
|
||||||
.reply_markup(deletion_markup())
|
|
||||||
.await?;
|
|
||||||
dialogue.exit().await?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut data = Vec::with_capacity(document.file.size as usize);
|
|
||||||
let file = bot.get_file(&document.file.id).await?;
|
|
||||||
bot.download_file_stream(&file.path)
|
|
||||||
.try_for_each(|bytes| {
|
|
||||||
data.extend(bytes);
|
|
||||||
async { Ok(()) }
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
let accounts = match spawn_blocking(move || from_slice::<User>(&data)).await? {
|
|
||||||
Ok(user) => user.accounts,
|
|
||||||
Err(_) => {
|
|
||||||
bot.send_message(msg.chat.id, "Error parsing the json file")
|
|
||||||
.reply_markup(deletion_markup())
|
|
||||||
.await?;
|
|
||||||
dialogue.exit().await?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let previous = bot
|
let previous = bot
|
||||||
.send_message(msg.chat.id, "Send master password")
|
.send_message(msg.chat.id, "Send master password")
|
||||||
.await?;
|
.await?;
|
||||||
dialogue
|
dialogue
|
||||||
.update(State::GetMasterPass(Handler::new(
|
.update(State::GetMasterPass(Handler::new(
|
||||||
move |bot, msg, db, dialogue, master_pass| {
|
move |bot, msg, db, dialogue, master_pass| {
|
||||||
get_master_pass(bot, msg, db, dialogue, master_pass, accounts)
|
get_master_pass(bot, msg, db, dialogue, master_pass, user)
|
||||||
},
|
},
|
||||||
previous,
|
previous,
|
||||||
)))
|
)))
|
||||||
@ -124,7 +98,7 @@ pub async fn import(bot: Throttle<Bot>, msg: Message, dialogue: MainDialogue) ->
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
dialogue
|
dialogue
|
||||||
.update(State::GetDocument(Handler::new(get_document, previous)))
|
.update(State::GetUser(Handler::new(get_document, previous)))
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ mod utils;
|
|||||||
|
|
||||||
use commands::Command;
|
use commands::Command;
|
||||||
use sea_orm::prelude::*;
|
use sea_orm::prelude::*;
|
||||||
use state::{Handler, PackagedHandler};
|
use state::{Handler, MainDialogue, State};
|
||||||
use teloxide::{
|
use teloxide::{
|
||||||
adaptors::{throttle::Limits, Throttle},
|
adaptors::{throttle::Limits, Throttle},
|
||||||
dispatching::dialogue::InMemStorage,
|
dispatching::dialogue::InMemStorage,
|
||||||
@ -16,19 +16,6 @@ use teloxide::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
type MainDialogue = Dialogue<State, InMemStorage<State>>;
|
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
|
||||||
pub enum State {
|
|
||||||
#[default]
|
|
||||||
Start,
|
|
||||||
GetAccountName(PackagedHandler<String>),
|
|
||||||
GetMasterPass(PackagedHandler<String>),
|
|
||||||
GetLogin(PackagedHandler<String>),
|
|
||||||
GetPassword(PackagedHandler<String>),
|
|
||||||
GetDocument(PackagedHandler<()>),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_dispatcher(
|
pub fn get_dispatcher(
|
||||||
token: String,
|
token: String,
|
||||||
db: DatabaseConnection,
|
db: DatabaseConnection,
|
||||||
@ -56,11 +43,11 @@ pub fn get_dispatcher(
|
|||||||
let message_handler = Update::filter_message()
|
let message_handler = Update::filter_message()
|
||||||
.map_async(utils::delete_message)
|
.map_async(utils::delete_message)
|
||||||
.enter_dialogue::<Update, InMemStorage<State>, State>()
|
.enter_dialogue::<Update, InMemStorage<State>, State>()
|
||||||
.branch(case![State::GetAccountName(next)].endpoint(state::get_account_name))
|
.branch(case![State::GetAccountName(next, check)].endpoint(state::get_account_name))
|
||||||
.branch(case![State::GetMasterPass(next)].endpoint(state::get_master_pass))
|
.branch(case![State::GetMasterPass(next)].endpoint(state::get_master_pass))
|
||||||
.branch(case![State::GetLogin(next)].endpoint(state::get_login))
|
.branch(case![State::GetLogin(next)].endpoint(state::get_login))
|
||||||
.branch(case![State::GetPassword(next)].endpoint(state::get_password))
|
.branch(case![State::GetPassword(next)].endpoint(state::get_password))
|
||||||
.branch(case![State::GetDocument(next)].endpoint(state::get_document))
|
.branch(case![State::GetUser(next)].endpoint(state::get_user))
|
||||||
.branch(command_handler)
|
.branch(command_handler)
|
||||||
.endpoint(default::default);
|
.endpoint(default::default);
|
||||||
|
|
||||||
|
@ -22,19 +22,23 @@ where
|
|||||||
&'a Message,
|
&'a Message,
|
||||||
&'a DatabaseConnection,
|
&'a DatabaseConnection,
|
||||||
&'a str,
|
&'a str,
|
||||||
) -> PinnedFuture<'a, crate::Result<bool>>,
|
) -> PinnedFuture<'a, crate::Result<Option<Message>>>,
|
||||||
{
|
{
|
||||||
let handler = next.lock().await.take();
|
let mut handler = next.lock().await;
|
||||||
let previous = handler.as_ref().and_then(|h| h.previous.as_ref());
|
delete_optional(&bot, handler.previous.as_ref()).await;
|
||||||
delete_optional(&bot, previous).await;
|
|
||||||
let text = match msg.text() {
|
let text = match msg.text() {
|
||||||
Some(text) => text.trim_end(),
|
Some(text) => text.trim_end(),
|
||||||
None => {
|
None => {
|
||||||
bot.send_message(msg.chat.id, "Couldn't get the text of the message")
|
bot.send_message(
|
||||||
.await?;
|
msg.chat.id,
|
||||||
|
"Couldn't get the text of the message. Send the message again",
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if text == "/cancel" {
|
if text == "/cancel" {
|
||||||
dialogue.exit().await?;
|
dialogue.exit().await?;
|
||||||
bot.send_message(msg.chat.id, "Successfully cancelled")
|
bot.send_message(msg.chat.id, "Successfully cancelled")
|
||||||
@ -42,26 +46,22 @@ where
|
|||||||
.await?;
|
.await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let handler = match handler {
|
|
||||||
Some(handler) => handler,
|
if handler.func.is_none() {
|
||||||
None => {
|
let _ = dialogue.exit().await;
|
||||||
let _ = dialogue.exit().await;
|
return Err(HandlerUsed.into());
|
||||||
return Err(HandlerUsed.into());
|
}
|
||||||
}
|
|
||||||
};
|
if let Some(failure_message) = check(&bot, &msg, &db, text).await? {
|
||||||
match check(&bot, &msg, &db, text).await {
|
handler.previous = Some(failure_message);
|
||||||
Ok(true) => (),
|
return Ok(());
|
||||||
Ok(false) => {
|
}
|
||||||
dialogue.exit().await?;
|
|
||||||
return Ok(());
|
let func = handler.func.take().unwrap();
|
||||||
}
|
drop(handler);
|
||||||
Err(err) => {
|
|
||||||
let _ = dialogue.exit().await;
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let text = text.to_owned();
|
let text = text.to_owned();
|
||||||
if let Err(err) = (handler.handler)(bot, msg, db, dialogue.clone(), text).await {
|
|
||||||
|
if let Err(err) = func(bot, msg, db, dialogue.clone(), text).await {
|
||||||
let _ = dialogue.exit().await;
|
let _ = dialogue.exit().await;
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,71 @@
|
|||||||
use crate::handlers::{markups::deletion_markup, utils::validate_field, MainDialogue};
|
use crate::{
|
||||||
|
entity::prelude::Account,
|
||||||
|
errors::NoUserInfo,
|
||||||
|
handlers::{markups::account_markup, utils::validate_field, MainDialogue},
|
||||||
|
};
|
||||||
use sea_orm::prelude::*;
|
use sea_orm::prelude::*;
|
||||||
use teloxide::{adaptors::Throttle, prelude::*};
|
use teloxide::{adaptors::Throttle, prelude::*};
|
||||||
|
|
||||||
|
/// Specifies the kind of checks to be run during the account name validation
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum NameCheckKind {
|
||||||
|
NewAccountName,
|
||||||
|
MustExist,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validates the account name
|
||||||
|
#[inline]
|
||||||
|
pub async fn check_name(
|
||||||
|
bot: &Throttle<Bot>,
|
||||||
|
msg: &Message,
|
||||||
|
db: &DatabaseConnection,
|
||||||
|
name: &str,
|
||||||
|
check_kind: NameCheckKind,
|
||||||
|
) -> crate::Result<Option<Message>> {
|
||||||
|
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
|
||||||
|
match check_kind {
|
||||||
|
NameCheckKind::MustExist => {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
NameCheckKind::NewAccountName => {
|
||||||
|
if Account::exists(user_id, name, db).await? {
|
||||||
|
let msg = bot
|
||||||
|
.send_message(msg.chat.id, "Account alreay 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Function to handle GetAccountName state
|
/// Function to handle GetAccountName state
|
||||||
pub async fn get_account_name(
|
pub async fn get_account_name(
|
||||||
bot: Throttle<Bot>,
|
bot: Throttle<Bot>,
|
||||||
msg: Message,
|
msg: Message,
|
||||||
db: DatabaseConnection,
|
db: DatabaseConnection,
|
||||||
dialogue: MainDialogue,
|
dialogue: MainDialogue,
|
||||||
next: super::PackagedHandler<String>,
|
(next, check_kind): (super::PackagedHandler<String>, NameCheckKind),
|
||||||
) -> crate::Result<()> {
|
) -> crate::Result<()> {
|
||||||
super::generic::generic(
|
super::generic::generic(
|
||||||
bot,
|
bot,
|
||||||
msg,
|
msg,
|
||||||
db,
|
db,
|
||||||
dialogue,
|
dialogue,
|
||||||
|bot, msg, _, name| {
|
|bot, msg, db, name| Box::pin(check_name(bot, msg, db, name, check_kind)),
|
||||||
Box::pin(async move {
|
|
||||||
let is_valid = validate_field(name);
|
|
||||||
if !is_valid {
|
|
||||||
bot.send_message(msg.chat.id, "Invalid account name")
|
|
||||||
.reply_markup(deletion_markup())
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
Ok(is_valid)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
next,
|
next,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
errors::HandlerUsed,
|
|
||||||
handlers::{markups::deletion_markup, utils::delete_optional, MainDialogue},
|
|
||||||
};
|
|
||||||
use sea_orm::prelude::*;
|
|
||||||
use teloxide::{adaptors::Throttle, prelude::*};
|
|
||||||
|
|
||||||
/// Function to handle get_document state. It doesn't actually validate anything
|
|
||||||
pub async fn get_document(
|
|
||||||
bot: Throttle<Bot>,
|
|
||||||
msg: Message,
|
|
||||||
db: DatabaseConnection,
|
|
||||||
dialogue: MainDialogue,
|
|
||||||
next: super::PackagedHandler<()>,
|
|
||||||
) -> crate::Result<()> {
|
|
||||||
let handler = next.lock().await.take();
|
|
||||||
let previous = handler.as_ref().and_then(|h| h.previous.as_ref());
|
|
||||||
delete_optional(&bot, previous).await;
|
|
||||||
if let Some("/cancel") = msg.text().map(str::trim_end) {
|
|
||||||
dialogue.exit().await?;
|
|
||||||
bot.send_message(msg.chat.id, "Successfully cancelled")
|
|
||||||
.reply_markup(deletion_markup())
|
|
||||||
.await?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let handler = match handler {
|
|
||||||
Some(handler) => handler,
|
|
||||||
None => {
|
|
||||||
let _ = dialogue.exit().await;
|
|
||||||
return Err(HandlerUsed.into());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if msg.document().is_none() {
|
|
||||||
bot.send_message(msg.chat.id, "You didn't send a file")
|
|
||||||
.reply_markup(deletion_markup())
|
|
||||||
.await?;
|
|
||||||
dialogue.exit().await?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if let Err(err) = (handler.handler)(bot, msg, db, dialogue.clone(), ()).await {
|
|
||||||
let _ = dialogue.exit().await;
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
use crate::handlers::{markups::deletion_markup, utils::validate_field, MainDialogue};
|
use crate::handlers::{utils::validate_field, MainDialogue};
|
||||||
use sea_orm::prelude::*;
|
use sea_orm::prelude::*;
|
||||||
use teloxide::{adaptors::Throttle, prelude::*};
|
use teloxide::{adaptors::Throttle, prelude::*};
|
||||||
|
|
||||||
@ -19,11 +19,12 @@ pub async fn get_login(
|
|||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let is_valid = validate_field(login);
|
let is_valid = validate_field(login);
|
||||||
if !is_valid {
|
if !is_valid {
|
||||||
bot.send_message(msg.chat.id, "Invalid login")
|
let msg = bot
|
||||||
.reply_markup(deletion_markup())
|
.send_message(msg.chat.id, "Invalid login. Try again")
|
||||||
.await?;
|
.await?;
|
||||||
|
return Ok(Some(msg));
|
||||||
}
|
}
|
||||||
Ok(is_valid)
|
Ok(None)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
next,
|
next,
|
||||||
|
@ -1,40 +1,44 @@
|
|||||||
use crate::{
|
use crate::{entity::prelude::MasterPass, errors::NoUserInfo, handlers::MainDialogue};
|
||||||
entity::prelude::MasterPass,
|
use log::error;
|
||||||
errors::NoUserInfo,
|
|
||||||
handlers::{markups::deletion_markup, MainDialogue},
|
|
||||||
};
|
|
||||||
use sea_orm::prelude::*;
|
use sea_orm::prelude::*;
|
||||||
use teloxide::{adaptors::Throttle, prelude::*};
|
use teloxide::{adaptors::Throttle, prelude::*};
|
||||||
use tokio::task::spawn_blocking;
|
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]
|
||||||
pub async fn check_master_pass<'a>(
|
pub async fn check_master_pass(
|
||||||
bot: &'a Throttle<Bot>,
|
bot: &Throttle<Bot>,
|
||||||
msg: &'a Message,
|
msg: &Message,
|
||||||
db: &'a DatabaseConnection,
|
db: &DatabaseConnection,
|
||||||
master_pass: &'a str,
|
master_pass: &str,
|
||||||
) -> crate::Result<bool> {
|
) -> crate::Result<Option<Message>> {
|
||||||
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?;
|
||||||
|
|
||||||
let is_valid = match model {
|
let is_valid = match model {
|
||||||
Some(model) => {
|
Some(model) => {
|
||||||
let master_pass = master_pass.to_owned();
|
let master_pass = master_pass.to_owned();
|
||||||
spawn_blocking(move || model.verify(&master_pass)).await??
|
spawn_blocking(move || model.verify(&master_pass)).await??
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
bot.send_message(msg.chat.id, "No master password set")
|
error!("User was put into the GetMasterPass state with no master password set");
|
||||||
.reply_markup(deletion_markup())
|
let msg = bot
|
||||||
|
.send_message(
|
||||||
|
msg.chat.id,
|
||||||
|
"No master password set. Use /cancel and set it by using /set_master_pass",
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
return Ok(false);
|
return Ok(Some(msg));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if !is_valid {
|
if !is_valid {
|
||||||
bot.send_message(msg.chat.id, "Wrong master password")
|
let msg = bot
|
||||||
.reply_markup(deletion_markup())
|
.send_message(msg.chat.id, "Wrong master password. Try again")
|
||||||
.await?;
|
.await?;
|
||||||
|
return Ok(Some(msg));
|
||||||
}
|
}
|
||||||
Ok(is_valid)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_master_pass(
|
pub async fn get_master_pass(
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use sea_orm::prelude::*;
|
use sea_orm::prelude::*;
|
||||||
use teloxide::{adaptors::Throttle, prelude::*};
|
use teloxide::{adaptors::Throttle, prelude::*};
|
||||||
|
|
||||||
use crate::handlers::{markups::deletion_markup, utils::validate_field, MainDialogue};
|
use crate::handlers::{utils::validate_field, MainDialogue};
|
||||||
|
|
||||||
/// Function to handle GetPassword state
|
/// Function to handle GetPassword state
|
||||||
pub async fn get_password(
|
pub async fn get_password(
|
||||||
@ -20,11 +20,12 @@ pub async fn get_password(
|
|||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let is_valid = validate_field(password);
|
let is_valid = validate_field(password);
|
||||||
if !is_valid {
|
if !is_valid {
|
||||||
bot.send_message(msg.chat.id, "Invalid password")
|
let msg = bot
|
||||||
.reply_markup(deletion_markup())
|
.send_message(msg.chat.id, "Invalid password. Try again")
|
||||||
.await?;
|
.await?;
|
||||||
|
return Ok(Some(msg));
|
||||||
}
|
}
|
||||||
Ok(is_valid)
|
Ok(None)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
next,
|
next,
|
||||||
|
88
src/handlers/state/get_user.rs
Normal file
88
src/handlers/state/get_user.rs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
use crate::{
|
||||||
|
errors::HandlerUsed,
|
||||||
|
handlers::{markups::deletion_markup, utils::delete_optional, MainDialogue},
|
||||||
|
models::User,
|
||||||
|
};
|
||||||
|
use futures::TryStreamExt;
|
||||||
|
use sea_orm::prelude::*;
|
||||||
|
use teloxide::{adaptors::Throttle, net::Download, prelude::*};
|
||||||
|
use tokio::task::spawn_blocking;
|
||||||
|
|
||||||
|
/// Function to handle GetUser state. It doesn't actually validate anything
|
||||||
|
pub async fn get_user(
|
||||||
|
bot: Throttle<Bot>,
|
||||||
|
msg: Message,
|
||||||
|
db: DatabaseConnection,
|
||||||
|
dialogue: MainDialogue,
|
||||||
|
next: super::PackagedHandler<User>,
|
||||||
|
) -> crate::Result<()> {
|
||||||
|
let mut handler = next.lock().await;
|
||||||
|
delete_optional(&bot, handler.previous.as_ref()).await;
|
||||||
|
|
||||||
|
if let Some("/cancel") = msg.text().map(str::trim_end) {
|
||||||
|
dialogue.exit().await?;
|
||||||
|
bot.send_message(msg.chat.id, "Successfully cancelled")
|
||||||
|
.reply_markup(deletion_markup())
|
||||||
|
.await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if handler.func.is_none() {
|
||||||
|
let _ = dialogue.exit().await;
|
||||||
|
return Err(HandlerUsed.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let document = match msg.document() {
|
||||||
|
Some(document) => document,
|
||||||
|
None => {
|
||||||
|
let msg = bot
|
||||||
|
.send_message(msg.chat.id, "You didn't send a file. Try again")
|
||||||
|
.await?;
|
||||||
|
handler.previous = Some(msg);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match document.file_name.as_deref() {
|
||||||
|
Some(name) if name.trim_end().ends_with(".json") => (),
|
||||||
|
_ => {
|
||||||
|
let msg = bot
|
||||||
|
.send_message(
|
||||||
|
msg.chat.id,
|
||||||
|
"Invalid file name. You need to send a json file. Try again",
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
handler.previous = Some(msg);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = bot.get_file(&document.file.id).await?;
|
||||||
|
let mut data = Vec::with_capacity(document.file.size as usize);
|
||||||
|
bot.download_file_stream(&file.path)
|
||||||
|
.try_for_each(|bytes| {
|
||||||
|
data.extend(bytes);
|
||||||
|
async { Ok(()) }
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let user: User = match spawn_blocking(move || serde_json::from_slice(&data)).await? {
|
||||||
|
Ok(user) => user,
|
||||||
|
Err(_) => {
|
||||||
|
let msg = bot
|
||||||
|
.send_message(msg.chat.id, "Error parsing the json file. Try again")
|
||||||
|
.await?;
|
||||||
|
handler.previous = Some(msg);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let func = handler.func.take().unwrap();
|
||||||
|
drop(handler);
|
||||||
|
|
||||||
|
if let Err(err) = func(bot, msg, db, dialogue.clone(), user).await {
|
||||||
|
let _ = dialogue.exit().await;
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -4,24 +4,25 @@ use std::{future::Future, sync::Arc};
|
|||||||
use teloxide::{adaptors::Throttle, prelude::*};
|
use teloxide::{adaptors::Throttle, prelude::*};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
type DynHanlder<T> = Box<
|
||||||
|
dyn FnOnce(
|
||||||
|
Throttle<Bot>,
|
||||||
|
Message,
|
||||||
|
DatabaseConnection,
|
||||||
|
MainDialogue,
|
||||||
|
T,
|
||||||
|
) -> crate::PinnedFuture<'static, crate::Result<()>>
|
||||||
|
+ Send
|
||||||
|
+ Sync,
|
||||||
|
>;
|
||||||
|
|
||||||
pub struct Handler<T> {
|
pub struct Handler<T> {
|
||||||
pub handler: Box<
|
pub func: Option<DynHanlder<T>>,
|
||||||
dyn FnOnce(
|
|
||||||
Throttle<Bot>,
|
|
||||||
Message,
|
|
||||||
DatabaseConnection,
|
|
||||||
MainDialogue,
|
|
||||||
T,
|
|
||||||
) -> crate::PinnedFuture<'static, crate::Result<()>>
|
|
||||||
+ Send
|
|
||||||
+ Sync,
|
|
||||||
>,
|
|
||||||
|
|
||||||
pub previous: Option<Message>,
|
pub previous: Option<Message>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type PackagedHandler<T> = Arc<Mutex<Option<Handler<T>>>>;
|
pub type PackagedHandler<T> = Arc<Mutex<Handler<T>>>;
|
||||||
|
|
||||||
impl<T> Handler<T> {
|
impl<T> Handler<T> {
|
||||||
/// Convinience method to convert a simple async function and a previous message into PackagedHandler
|
/// Convinience method to convert a simple async function and a previous message into PackagedHandler
|
||||||
@ -35,11 +36,11 @@ impl<T> Handler<T> {
|
|||||||
F: Future<Output = crate::Result<()>> + Send + 'static,
|
F: Future<Output = crate::Result<()>> + Send + 'static,
|
||||||
{
|
{
|
||||||
let handler = Self {
|
let handler = Self {
|
||||||
handler: Box::new(move |bot, msg, db, dialogue, val| {
|
func: Some(Box::new(move |bot, msg, db, dialogue, val| {
|
||||||
Box::pin(f(bot, msg, db, dialogue, val))
|
Box::pin(f(bot, msg, db, dialogue, val))
|
||||||
}),
|
})),
|
||||||
previous: previous.into(),
|
previous: previous.into(),
|
||||||
};
|
};
|
||||||
Arc::new(Mutex::new(Some(handler)))
|
Arc::new(Mutex::new(handler))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,31 @@
|
|||||||
|
|
||||||
mod generic;
|
mod generic;
|
||||||
mod get_account_name;
|
mod get_account_name;
|
||||||
mod get_document;
|
|
||||||
mod get_login;
|
mod get_login;
|
||||||
mod get_master_pass;
|
mod get_master_pass;
|
||||||
mod get_password;
|
mod get_password;
|
||||||
|
mod get_user;
|
||||||
mod handler;
|
mod handler;
|
||||||
|
|
||||||
pub use get_account_name::get_account_name;
|
pub use get_account_name::{get_account_name, NameCheckKind};
|
||||||
pub use get_document::get_document;
|
|
||||||
pub use get_login::get_login;
|
pub use get_login::get_login;
|
||||||
pub use get_master_pass::get_master_pass;
|
pub use get_master_pass::get_master_pass;
|
||||||
pub use get_password::get_password;
|
pub use get_password::get_password;
|
||||||
|
pub use get_user::get_user;
|
||||||
pub use handler::{Handler, PackagedHandler};
|
pub use handler::{Handler, PackagedHandler};
|
||||||
|
|
||||||
|
use crate::models::User;
|
||||||
|
use teloxide::{dispatching::dialogue::InMemStorage, prelude::*};
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub enum State {
|
||||||
|
#[default]
|
||||||
|
Start,
|
||||||
|
GetAccountName(PackagedHandler<String>, NameCheckKind),
|
||||||
|
GetMasterPass(PackagedHandler<String>),
|
||||||
|
GetLogin(PackagedHandler<String>),
|
||||||
|
GetPassword(PackagedHandler<String>),
|
||||||
|
GetUser(PackagedHandler<User>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type MainDialogue = Dialogue<State, InMemStorage<State>>;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user