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}; use tokio::task::spawn_blocking; #[inline] pub fn menu_markup_sync( command: &str, names: impl IntoIterator, ) -> InlineKeyboardMarkup { let names = names .into_iter() .map(|name| { let hash = ::digest(name.as_bytes()); let mut data = command.to_owned(); data.reserve(44); data.push(' '); B64_ENGINE.encode_string(hash, &mut data); InlineKeyboardButton::callback(name, data) }) .chunks(3); InlineKeyboardMarkup::new(&names) } #[inline] pub async fn menu_markup( command: impl Into + Send, user_id: u64, db: &Pool, ) -> crate::Result { let command: String = command.into(); let names: Vec = Account::get_names(user_id, db).try_collect().await?; spawn_blocking(move || menu_markup_sync(&command, names)) .await .map_err(Into::into) } #[inline] fn make_button(text: &str, command: &str, param: &str) -> InlineKeyboardButton { let mut data = command.to_owned(); data.reserve(param.len() + 1); data.push(' '); data.push_str(param); InlineKeyboardButton::callback(text, data) } #[inline] pub fn account_markup(name: &str, is_encrypted: bool, locale: LocaleRef) -> InlineKeyboardMarkup { let mut hash = [0; 43]; B64_ENGINE .encode_slice(::digest(name), &mut hash) .unwrap(); let hash = std::str::from_utf8(&hash).unwrap(); let encryption_button = if is_encrypted { (locale.decrypt_button.as_ref(), "decrypt") } else { (locale.hide_button.as_ref(), "get") }; let main_buttons = [ (locale.change_name_button.as_ref(), "an"), (locale.change_login_button.as_ref(), "al"), (locale.change_password_button.as_ref(), "ap"), (locale.delete_account_button.as_ref(), "delete0"), encryption_button, ] .into_iter() .map(|(text, command)| make_button(text, command, hash)) .chunks(2); let menu_button = InlineKeyboardButton::callback(locale.menu_button.as_ref(), "get_menu"); InlineKeyboardMarkup::new(&main_buttons).append_row([menu_button]) } #[inline] pub fn language_markup() -> InlineKeyboardMarkup { let languages = [("🇷🇺 Русский", "ru"), ("🇬🇧 English", "en")] .into_iter() .map(|(text, param)| make_button(text, "change_locale", param)) .chunks(2); InlineKeyboardMarkup::new(&languages) } /// 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(locale: LocaleRef) -> InlineKeyboardMarkup { let button = InlineKeyboardButton::callback(locale.delete_message_button.as_ref(), "delete_message"); InlineKeyboardMarkup::new([[button]]) }