Made cryptography and entity modules

Coupling was rising so it just makes sense
This commit is contained in:
StNicolay 2024-05-05 18:38:21 +03:00
parent 9af37f78b2
commit 5871943c01
Signed by: StNicolay
GPG Key ID: 9693D04DCD962B0D
23 changed files with 151 additions and 175 deletions

109
Cargo.lock generated
View File

@ -94,9 +94,9 @@ dependencies = [
[[package]]
name = "autocfg"
version = "1.2.0"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "backtrace"
@ -281,6 +281,25 @@ version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]]
name = "crossbeam-deque"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-queue"
version = "0.3.11"
@ -307,24 +326,6 @@ dependencies = [
"typenum",
]
[[package]]
name = "cryptography"
version = "0.1.0"
dependencies = [
"arrayvec",
"bitflags 2.5.0",
"chacha20poly1305",
"entity",
"once_cell",
"pbkdf2",
"rand",
"scrypt",
"serde",
"sha2",
"subtle",
"thiserror",
]
[[package]]
name = "darling"
version = "0.13.4"
@ -429,15 +430,6 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "entity"
version = "0.1.0"
dependencies = [
"futures",
"hex",
"sqlx",
]
[[package]]
name = "equivalent"
version = "1.0.1"
@ -1053,9 +1045,9 @@ dependencies = [
[[package]]
name = "num-iter"
version = "0.1.44"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
"autocfg",
"num-integer",
@ -1064,9 +1056,9 @@ dependencies = [
[[package]]
name = "num-traits"
version = "0.2.18"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
"libm",
@ -1096,6 +1088,9 @@ name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
dependencies = [
"parking_lot_core",
]
[[package]]
name = "opaque-debug"
@ -1140,17 +1135,24 @@ dependencies = [
"anyhow",
"arrayvec",
"base64 0.22.1",
"cryptography",
"bitflags 2.5.0",
"chacha20poly1305",
"derive_more",
"dotenvy",
"entity",
"futures",
"hex",
"itertools 0.12.1",
"once_cell",
"parking_lot",
"pbkdf2",
"rand",
"scrypt",
"serde",
"serde_json",
"serde_yaml",
"sha2",
"sqlx",
"subtle",
"teloxide",
"thiserror",
"tokio",
@ -1184,6 +1186,8 @@ checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
dependencies = [
"digest",
"hmac",
"password-hash",
"rayon",
]
[[package]]
@ -1349,6 +1353,26 @@ dependencies = [
"getrandom",
]
[[package]]
name = "rayon"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "rc-box"
version = "1.2.0"
@ -1872,6 +1896,7 @@ dependencies = [
"sha2",
"sqlx-core",
"sqlx-mysql",
"sqlx-sqlite",
"syn 1.0.109",
"tempfile",
"tokio",
@ -1974,6 +1999,7 @@ dependencies = [
"libsqlite3-sys",
"log",
"percent-encoding",
"serde",
"sqlx-core",
"tracing",
"url",
@ -2244,16 +2270,15 @@ dependencies = [
[[package]]
name = "tokio-util"
version = "0.7.10"
version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
"tracing",
]
[[package]]
@ -2757,18 +2782,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.7.32"
version = "0.7.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
checksum = "087eca3c1eaf8c47b94d02790dd086cd594b912d2043d4de4bfdd466b3befb7c"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.32"
version = "0.7.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
checksum = "6f4b6c273f496d8fd4eaf18853e6b448760225dc030ff2c485a786859aea6393"
dependencies = [
"proc-macro2",
"quote",

View File

@ -10,36 +10,44 @@ debug = 1
lto = true
codegen-units = 1
[workspace]
members = [".", "entity", "cryptography"]
[workspace.lints.clippy]
[lints.clippy]
pedantic = "warn"
all = "warn"
nursery = "warn"
[lints]
workspace = true
[dependencies]
ahash = "0.8"
anyhow = { version = "1", features = ["backtrace"] }
arrayvec = "0.7"
base64 = "0.22"
cryptography = { version = "0.1", path = "cryptography" }
bitflags = "2"
chacha20poly1305 = { version = "0.10", features = ["std"] }
derive_more = { version = "0.99", default-features = false, features = [
"deref",
"display",
] }
dotenvy = "0.15"
entity = { version = "0.1", path = "entity" }
futures = "0.3"
hex = "0.4"
itertools = "0.12"
once_cell = { version = "1", features = ["parking_lot"] }
parking_lot = "0.12"
pbkdf2 = { version = "0.12", features = ["parallel"] }
rand = { version = "0.8", default-features = false, features = [
"std_rng",
"std",
] }
scrypt = { version = "0.11", default-features = false, features = ["std"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_yaml = "0.9"
sha2 = "0.10"
sqlx = { version = "0.7", features = [
"mysql",
"runtime-tokio-rustls",
"macros",
"migrate",
] }
subtle = "2"
teloxide = { version = "0.12", features = [
"macros",
"ctrlc_handler",

View File

@ -1,26 +0,0 @@
[package]
name = "cryptography"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lints]
workspace = true
[dependencies]
sha2 = "0.10"
scrypt = { version = "0.11", default-features = false, features = ["std"] }
pbkdf2 = "0.12"
thiserror = "1"
entity = { version = "0.1", path = "../entity" }
chacha20poly1305 = { version = "0.10", features = ["std"] }
rand = { version = "0.8", default-features = false, features = [
"std_rng",
"std",
] }
bitflags = "2"
arrayvec = "0.7"
subtle = "2"
once_cell = "1"
serde = { version = "1", features = ["derive"] }

View File

@ -1,19 +0,0 @@
use super::hashing::HashedBytes;
use entity::master_pass;
pub trait FromUnencryptedExt {
fn from_unencrypted(user_id: u64, password: &str) -> master_pass::MasterPass;
}
impl FromUnencryptedExt for master_pass::MasterPass {
/// Hashes the password and creates an `ActiveModel` with all fields set to Set variant
#[inline]
fn from_unencrypted(user_id: u64, password: &str) -> Self {
let hash = HashedBytes::new(password.as_bytes());
Self {
user_id,
password_hash: hash.hash.to_vec(),
salt: hash.salt.to_vec(),
}
}
}

View File

@ -1,3 +0,0 @@
pub use crate::{
account::Decrypted as DecryptedAccount, master_pass::FromUnencryptedExt as _, validate_field,
};

View File

@ -1,19 +0,0 @@
[package]
name = "entity"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lints]
workspace = true
[dependencies]
futures = "0.3"
sqlx = { version = "0.7", features = [
"mysql",
"runtime-tokio-rustls",
"macros",
"migrate",
], default-features = false }
hex = "0.4"

View File

@ -1 +0,0 @@
pub use crate::{account::Account, master_pass::MasterPass, Pool};

View File

@ -10,9 +10,11 @@ crate::export_handlers!(
change_locale
);
use crate::{errors::InvalidCommand, handle_error, locales::LocaleTypeExt};
use crate::{
entity::locale::LocaleType, errors::handle_error, errors::InvalidCommand,
locales::LocaleTypeExt,
};
use base64::{engine::general_purpose::STANDARD_NO_PAD as B64_ENGINE, Engine as _};
use entity::locale::LocaleType;
use std::str::FromStr;
use teloxide::types::CallbackQuery;

View File

@ -1,5 +1,5 @@
use crate::entity::account::Account;
use chacha20poly1305::{AeadCore, AeadInPlace, ChaCha20Poly1305, KeyInit};
use entity::account::Account;
use pbkdf2::pbkdf2_hmac_array;
use rand::{rngs::OsRng, RngCore};
use sha2::Sha256;
@ -36,9 +36,9 @@ impl Cipher {
/// Returns an error if the tag doesn't match the ciphertext
#[inline]
#[allow(clippy::missing_panics_doc)]
pub fn decrypt(&self, value: &mut Vec<u8>) -> crate::Result<()> {
pub fn decrypt(&self, value: &mut Vec<u8>) -> super::Result<()> {
if value.len() <= 12 {
return Err(crate::Error::InvalidInputLength);
return Err(super::Error::InvalidInputLength);
}
let nonce: [u8; 12] = value[value.len() - 12..].try_into().unwrap();
value.truncate(value.len() - 12);
@ -63,7 +63,7 @@ impl Decrypted {
///
/// Returns an error if the tag doesn't match the ciphertext or if the decrypted data isn't valid UTF-8
#[inline]
pub fn from_account(mut account: Account, master_pass: &str) -> crate::Result<Self> {
pub fn from_account(mut account: Account, master_pass: &str) -> super::Result<Self> {
let cipher = Cipher::new(master_pass.as_bytes(), &account.salt);
cipher.decrypt(&mut account.enc_login)?;
cipher.decrypt(&mut account.enc_password)?;
@ -125,7 +125,7 @@ mod tests {
});
#[test]
fn cipher_test() -> crate::Result<()> {
fn cipher_test() -> crate::cryptography::Result<()> {
const ORIGINAL: &[u8] = b"Data to protect";
let mut data = ORIGINAL.to_owned();
@ -138,7 +138,7 @@ mod tests {
}
#[test]
fn account_encryption() -> crate::Result<()> {
fn account_encryption() -> crate::cryptography::Result<()> {
let original = Decrypted {
name: "Account Name".to_owned(),
login: "StrongLogin@mail.com".to_owned(),
@ -157,7 +157,7 @@ mod tests {
assert!(matches!(
CIPHER.decrypt(&mut bytes),
Err(crate::Error::InvalidInputLength)
Err(crate::cryptography::Error::InvalidInputLength)
));
}
}

View File

@ -1,4 +1,4 @@
use entity::master_pass::MasterPass;
use crate::entity::master_pass::MasterPass;
use once_cell::sync::Lazy;
use rand::{rngs::OsRng, RngCore};
use scrypt::{scrypt, Params};

View File

@ -2,15 +2,13 @@
pub mod account;
pub mod hashing;
pub mod master_pass;
pub mod passwords;
pub mod prelude;
/// Returns true if the field is valid
#[inline]
#[must_use]
pub fn validate_field(field: &str) -> bool {
if !(1..255).contains(&field.len()) {
if field.len() > 255 {
return false;
}
field
@ -24,7 +22,7 @@ pub enum Error {
InvalidInputLength,
#[error(transparent)]
ChaChaError(#[from] chacha20poly1305::Error),
ChaCha(#[from] chacha20poly1305::Error),
#[error(transparent)]
InvalidUTF8(#[from] std::string::FromUtf8Error),

View File

@ -103,7 +103,7 @@ pub fn check_master_pass(password: &str) -> PasswordValidity {
#[cfg(test)]
mod tests {
use crate::passwords::CHARS;
use super::CHARS;
#[test]
fn chars_must_be_ascii() {

View File

@ -14,7 +14,7 @@ pub struct Account {
impl Account {
// Inserts the account into DB
#[inline]
pub async fn insert(&self, pool: &Pool) -> crate::Result<()> {
pub async fn insert(&self, pool: &Pool) -> super::Result<()> {
query!(
"INSERT INTO account VALUES (?, ?, ?, ?, ?)",
self.user_id,
@ -30,7 +30,7 @@ impl Account {
/// Gets all user's account from DB
#[inline]
pub fn get_all(user_id: u64, pool: &Pool) -> impl Stream<Item = crate::Result<Self>> + '_ {
pub fn get_all(user_id: u64, pool: &Pool) -> impl Stream<Item = super::Result<Self>> + '_ {
query_as("SELECT * FROM account WHERE user_id = ?")
.bind(user_id)
.fetch(pool)
@ -38,7 +38,7 @@ impl Account {
/// Streams the names of the user accounts
#[inline]
pub fn get_names(user_id: u64, pool: &Pool) -> impl Stream<Item = crate::Result<String>> + '_ {
pub fn get_names(user_id: u64, pool: &Pool) -> impl Stream<Item = super::Result<String>> + '_ {
query_as::<_, (String,)>("SELECT name FROM account WHERE user_id = ? ORDER BY name")
.bind(user_id)
.fetch(pool)
@ -47,7 +47,7 @@ impl Account {
/// Checks if the account exists
#[inline]
pub async fn exists(user_id: u64, account_name: &str, pool: &Pool) -> crate::Result<bool> {
pub async fn exists(user_id: u64, account_name: &str, pool: &Pool) -> super::Result<bool> {
query_as::<_, (bool,)>(
"SELECT EXISTS(SELECT * FROM account WHERE user_id = ? AND name = ? LIMIT 1) as value",
)
@ -60,7 +60,7 @@ impl Account {
/// Gets the account from the DB
#[inline]
pub async fn get(user_id: u64, account_name: &str, pool: &Pool) -> crate::Result<Option<Self>> {
pub async fn get(user_id: u64, account_name: &str, pool: &Pool) -> super::Result<Option<Self>> {
query_as("SELECT * FROM account WHERE user_id = ? AND name = ?")
.bind(user_id)
.bind(account_name)
@ -70,7 +70,7 @@ impl Account {
// Deletes the account from DB
#[inline]
pub async fn delete(user_id: u64, name: &str, pool: &Pool) -> crate::Result<()> {
pub async fn delete(user_id: u64, name: &str, pool: &Pool) -> super::Result<()> {
query!(
"DELETE FROM account WHERE user_id = ? AND name = ?",
user_id,
@ -86,7 +86,7 @@ impl Account {
pub async fn delete_all(
user_id: u64,
pool: impl Executor<'_, Database = MySql>,
) -> crate::Result<()> {
) -> super::Result<()> {
query!("DELETE FROM account WHERE user_id = ?", user_id)
.execute(pool)
.await
@ -99,7 +99,7 @@ impl Account {
user_id: u64,
hash: &[u8],
pool: &Pool,
) -> crate::Result<Option<String>> {
) -> super::Result<Option<String>> {
let hash = hex::encode(hash);
let name = query_as::<_, (String,)>(
"SELECT `name` FROM `account` WHERE SHA2(`name`, 256) = ? AND `user_id` = ?;",
@ -113,7 +113,7 @@ impl Account {
}
#[inline]
pub async fn get_salt(user_id: u64, name: &str, pool: &Pool) -> crate::Result<Option<Vec<u8>>> {
pub async fn get_salt(user_id: u64, name: &str, pool: &Pool) -> super::Result<Option<Vec<u8>>> {
let salt =
query_as::<_, (Vec<u8>,)>("SELECT salt FROM account WHERE user_id = ? AND name = ?")
.bind(user_id)
@ -130,7 +130,7 @@ impl Account {
original_name: &str,
new_name: &str,
pool: &Pool,
) -> crate::Result<bool> {
) -> super::Result<bool> {
query!(
"UPDATE account SET name = ? WHERE user_id = ? AND name = ?",
new_name,
@ -148,7 +148,7 @@ impl Account {
name: &str,
login: Vec<u8>,
pool: &Pool,
) -> crate::Result<bool> {
) -> super::Result<bool> {
query!(
"UPDATE account SET enc_login = ? WHERE user_id = ? AND name = ?",
login,
@ -166,7 +166,7 @@ impl Account {
name: &str,
password: Vec<u8>,
pool: &Pool,
) -> crate::Result<bool> {
) -> super::Result<bool> {
query!(
"UPDATE account SET enc_password = ? WHERE user_id = ? AND name = ?",
password,

View File

@ -28,7 +28,7 @@ impl From<LocaleType> for u8 {
}
impl LocaleType {
pub async fn get_from_db(user_id: u64, db: &Pool) -> crate::Result<Option<Self>> {
pub async fn get_from_db(user_id: u64, db: &Pool) -> super::Result<Option<Self>> {
let result: Option<(u8,)> = query_as("SELECT locale FROM master_pass WHERE user_id = ?")
.bind(user_id)
.fetch_optional(db)
@ -36,7 +36,7 @@ impl LocaleType {
Ok(result.and_then(|val| val.0.try_into().ok()))
}
pub async fn update(self, user_id: u64, db: &Pool) -> crate::Result<bool> {
pub async fn update(self, user_id: u64, db: &Pool) -> super::Result<bool> {
let result: QueryResult = query!(
"UPDATE master_pass SET locale = ? WHERE user_id = ?",
u8::from(self),

View File

@ -1,4 +1,4 @@
use crate::{locale::LocaleType, Pool};
use super::{locale::LocaleType, Pool};
use sqlx::{prelude::FromRow, query, query_as, Executor, MySql};
#[derive(Clone, Debug, PartialEq, FromRow, Eq)]
@ -11,7 +11,7 @@ pub struct MasterPass {
impl MasterPass {
// Inserts the master password into DB
#[inline]
pub async fn insert(&self, pool: &Pool, locale: LocaleType) -> crate::Result<()> {
pub async fn insert(&self, pool: &Pool, locale: LocaleType) -> super::Result<()> {
let locale: u8 = locale.into();
query!(
"INSERT INTO master_pass VALUES (?, ?, ?, ?)",
@ -27,7 +27,7 @@ impl MasterPass {
/// Gets the master password from the database
#[inline]
pub async fn get(user_id: u64, pool: &Pool) -> crate::Result<Option<Self>> {
pub async fn get(user_id: u64, pool: &Pool) -> super::Result<Option<Self>> {
query_as("SELECT user_id, salt, password_hash FROM master_pass WHERE user_id = ?")
.bind(user_id)
.fetch_optional(pool)
@ -36,7 +36,7 @@ impl MasterPass {
/// Checks if the master password for the user exists
#[inline]
pub async fn exists(user_id: u64, pool: &Pool) -> crate::Result<bool> {
pub async fn exists(user_id: u64, pool: &Pool) -> super::Result<bool> {
query_as::<_, (bool,)>(
"SELECT EXISTS(SELECT * FROM master_pass WHERE user_id = ? LIMIT 1) as value",
)
@ -50,7 +50,7 @@ impl MasterPass {
pub async fn remove(
user_id: u64,
pool: impl Executor<'_, Database = MySql>,
) -> crate::Result<()> {
) -> super::Result<()> {
query!("DELETE FROM master_pass WHERE user_id = ?", user_id)
.execute(pool)
.await

View File

@ -4,7 +4,6 @@
pub mod account;
pub mod locale;
pub mod master_pass;
pub mod prelude;
pub use sqlx::{mysql::MySqlPool as Pool, Result};

View File

@ -1,7 +1,9 @@
mod callbacks;
mod commands;
mod cryptography;
mod default;
mod delete_mesage_handler;
mod entity;
mod errors;
mod filter_user_info;
mod locales;
@ -13,18 +15,25 @@ mod prelude;
mod state;
use anyhow::{Error, Result};
use dotenvy::dotenv;
use prelude::*;
use std::{env, sync::Arc};
use teloxide::{adaptors::throttle::Limits, dispatching::dialogue::InMemStorage, filter_command};
use crate::callbacks::CallbackCommand;
fn get_dispatcher(
token: String,
db: Pool,
) -> Dispatcher<Throttle<Bot>, crate::Error, teloxide::dispatching::DefaultKey> {
use dptree::{case, deps};
db: prelude::Pool,
) -> teloxide::prelude::Dispatcher<
teloxide::adaptors::Throttle<teloxide::Bot>,
crate::Error,
teloxide::dispatching::DefaultKey,
> {
use callbacks::CallbackCommand;
use commands::Command;
use state::State;
use teloxide::{
adaptors::throttle::Limits,
dispatching::dialogue::InMemStorage,
dptree::{case, deps},
filter_command,
prelude::*,
};
let bot = Bot::new(token).throttle(Limits::default());
@ -73,28 +82,30 @@ fn get_dispatcher(
.branch(case![CallbackCommand::ChangeLocale(locale)].endpoint(callbacks::change_locale));
let handler = dptree::entry()
.map_async(Locale::from_update)
.map_async(locales::Locale::from_update)
.enter_dialogue::<Update, InMemStorage<State>, State>()
.branch(message_handler)
.branch(callback_handler);
Dispatcher::builder(bot, handler)
.dependencies(deps![db, InMemStorage::<State>::new()])
.error_handler(Arc::from(errors::ErrorHandler))
.error_handler(std::sync::Arc::from(errors::ErrorHandler))
.enable_ctrlc_handler()
.build()
}
#[tokio::main]
async fn main() -> Result<()> {
let _ = dotenv();
use std::env;
let _ = dotenvy::dotenv();
errors::init_logger();
locales::LocaleStore::init();
let token = env::var("TOKEN").expect("expected TOKEN in the enviroment");
let database_url = env::var("DATABASE_URL").expect("expected DATABASE_URL in the enviroment");
let pool = Pool::connect(&database_url).await?;
let pool = entity::Pool::connect(&database_url).await?;
entity::migrate(&pool).await?;

View File

@ -1,13 +1,14 @@
pub use crate::{
commands::Command,
pub(crate) use crate::cryptography::{
self, account::Decrypted as DecryptedAccount, validate_field,
};
pub(crate) use crate::entity::{self, account::Account, master_pass::MasterPass, Pool};
pub(crate) use crate::{
errors::{handle_error, NoUserInfo},
first_handler, handler,
locales::{Locale, LocaleRef},
locales::LocaleRef,
markups::*,
models::*,
state::{Handler, MainDialogue, MessageIds, PackagedHandler, State},
};
pub use cryptography::prelude::*;
pub use entity::{prelude::*, Pool};
pub use futures::{StreamExt, TryStreamExt};
pub use teloxide::{adaptors::Throttle, prelude::*};
pub(crate) use futures::{StreamExt, TryStreamExt};
pub(crate) use teloxide::{adaptors::Throttle, prelude::*};