use crate::handlers::markups::deletion_markup; use arrayvec::{ArrayString, ArrayVec}; use rand::{rngs::OsRng, seq::SliceRandom}; use std::{iter, str::from_utf8_unchecked}; use teloxide::{adaptors::Throttle, prelude::*, types::ParseMode}; use tokio::task::JoinSet; const CHARS: &[u8] = br##"!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}~"##; /// Returns true if the generated master password is valid. /// It checks that it has at least one lowercase, one lowercase and one punctuation char #[inline] fn check_generated_password(password: &[u8]) -> bool { let mut flags: u8 = 0; for &byte in password { match byte { b'a'..=b'z' => flags |= 0b1, b'A'..=b'Z' => flags |= 0b10, b'0'..=b'9' => flags |= 0b100, b'!'..=b'/' | b':'..=b'@' | b'['..=b'`' | b'{'..=b'~' => flags |= 0b1000, _ => (), } if flags == 0b1111 { return true; } } false } /// Continuously generates the password until it passes the checks fn generete_password() -> ArrayString<34> { loop { let password: ArrayVec = iter::repeat_with(|| *CHARS.choose(&mut OsRng).unwrap()) .take(32) .collect(); if check_generated_password(&password) { let mut string = ArrayString::<34>::new_const(); string.push('`'); unsafe { string.push_str(from_utf8_unchecked(&password)) }; string.push('`'); return string; } } } /// Handles /gen_password command by generating 10 copyable passwords and sending them to the user pub async fn gen_password(bot: Throttle, msg: Message) -> crate::Result<()> { let mut message = ArrayString::<{ 11 + 35 * 10 }>::new(); message.push_str("Passwords:\n"); let mut join_set = JoinSet::new(); for _ in 0..10 { join_set.spawn_blocking(generete_password); } while let Some(password) = join_set.join_next().await { message.push_str(password?.as_str()); message.push('\n') } bot.send_message(msg.chat.id, message.as_str()) .parse_mode(ParseMode::MarkdownV2) .reply_markup(deletion_markup()) .await?; Ok(()) }