use crate::prelude::*; use base64::{engine::general_purpose::STANDARD_NO_PAD as B64_ENGINE, Engine as _}; use itertools::Itertools; use sha2::{Digest, Sha256}; use teloxide::types::{InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, KeyboardMarkup}; use tokio::task::spawn_blocking; /// Creates a markup of all user's account names #[inline] pub async fn account_list_markup( user_id: u64, db: &DatabaseConnection, ) -> crate::Result { let account_names: Vec> = Account::get_names(user_id, db) .await? .map_ok(KeyboardButton::new) .try_chunks(3) .map_err(|err| err.1) .try_collect() .await?; let markup = KeyboardMarkup::new(account_names) .resize_keyboard(true) .one_time_keyboard(true); Ok(markup) } #[inline] pub fn menu_markup_sync(names: impl IntoIterator) -> InlineKeyboardMarkup { let names = names .into_iter() .map(|name| { let hash = ::digest(name.as_bytes()); let mut data = "get ".to_owned(); data.reserve(43); B64_ENGINE.encode_string(hash, &mut data); InlineKeyboardButton::callback(name, data) }) .chunks(3); InlineKeyboardMarkup::new(&names) } #[inline] pub async fn menu_markup( user_id: u64, db: &DatabaseConnection, ) -> crate::Result { let names: Vec = 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(::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. /// This markup should be added for all messages that won't be deleted afterwards #[inline] pub fn deletion_markup() -> InlineKeyboardMarkup { let button = InlineKeyboardButton::callback("Delete message", "delete_message"); InlineKeyboardMarkup::new([[button]]) }