use std::sync::Arc; use teloxide::dispatching::UpdateHandler; use teloxide::prelude::*; use teloxide::types::{InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, KeyboardMarkup}; use teloxide::utils::command::BotCommands; use crate::app_state::AppState; use crate::db; type HandlerResult = Result<(), Box>; #[derive(BotCommands, Clone)] #[command(rename_rule = "lowercase")] pub enum Command { #[command(description = "Запуск бота")] Start, #[command(description = "Справка")] Help, #[command(description = "Настройки частоты цитат")] Settings, } /// Постоянная клавиатура с кнопкой "⚙️ Настройки" fn settings_keyboard() -> KeyboardMarkup { KeyboardMarkup::new(vec![vec![KeyboardButton::new("⚙️ Настройки")]]).resize_keyboard() } /// Inline-клавиатура выбора частоты fn frequency_keyboard() -> InlineKeyboardMarkup { InlineKeyboardMarkup::new(vec![ vec![ InlineKeyboardButton::callback("1 час", "frequency_1"), InlineKeyboardButton::callback("3 часа", "frequency_3"), ], vec![ InlineKeyboardButton::callback("5 часов", "frequency_5"), InlineKeyboardButton::callback("7 часов", "frequency_7"), ], vec![ InlineKeyboardButton::callback("9 часов", "frequency_9"), InlineKeyboardButton::callback("12 часов", "frequency_12"), ], ]) } /// Обработка команд /start, /help, /settings async fn command_handler( bot: Bot, msg: Message, cmd: Command, state: Arc, ) -> HandlerResult { match cmd { Command::Start => { if let Some(user) = msg.from { let fio = if let Some(ref last) = user.last_name { format!("{} {}", user.first_name, last) } else { user.first_name.clone() }; db::upsert_user( &state.pool, user.id.0 as i64, Some(&fio), user.username.as_deref(), ) .await; } bot.send_message( msg.chat.id, "Приветствую тебя, мой дорогой друг. Я бот, который будет писать тебе мотивирующие цитаты. Сейчас цитаты буду приходит один раз в час, в настройках можно изменить это время.", ) .reply_markup(settings_keyboard()) .await?; } Command::Help => { bot.send_message( msg.chat.id, "Я буду присылать тебе мотивирующие цитаты. Используй меню для настроек.", ) .reply_markup(settings_keyboard()) .await?; } Command::Settings => { bot.send_message(msg.chat.id, "Выберите частоту получения цитат:") .reply_markup(frequency_keyboard()) .await?; } } Ok(()) } /// Обработка текстового сообщения "⚙️ Настройки" от reply-кнопки async fn text_message_handler(bot: Bot, msg: Message) -> HandlerResult { if let Some(text) = msg.text() { if text == "⚙️ Настройки" { bot.send_message(msg.chat.id, "Выберите частоту получения цитат:") .reply_markup(frequency_keyboard()) .await?; } } Ok(()) } /// Обработка callback-запроса frequency_N async fn callback_handler(bot: Bot, q: CallbackQuery, state: Arc) -> HandlerResult { if let Some(ref data) = q.data { if let Some(hours_str) = data.strip_prefix("frequency_") { if let Ok(hours) = hours_str.parse::() { let telegram_id = q.from.id.0 as i64; db::update_frequency(&state.pool, telegram_id, hours).await; bot.answer_callback_query(&q.id).await?; if let Some(msg) = q.regular_message() { bot.edit_message_text( msg.chat.id, msg.id, format!( "Отлично! Теперь я буду присылать цитаты каждые {} ч.", hours ), ) .await?; } } } } Ok(()) } /// Собрать dptree-обработчик для Dispatcher pub fn build_handler() -> UpdateHandler> { dptree::entry() .branch( Update::filter_message() .branch( dptree::entry() .filter_command::() .endpoint(command_handler), ) .branch(dptree::endpoint(text_message_handler)), ) .branch(Update::filter_callback_query().endpoint(callback_handler)) }