"""Handlers for managing reminders (view, edit, delete).""" from aiogram import Router, F from aiogram.types import Message, CallbackQuery from aiogram.fsm.context import FSMContext from sqlalchemy.ext.asyncio import AsyncSession from bot.states.reminder_states import EditReminderStates from bot.keyboards.pagination import get_reminders_list_keyboard, PaginationCallback from bot.keyboards.reminders import ( get_reminder_details_keyboard, get_edit_menu_keyboard, get_interval_selection_keyboard, get_confirmation_keyboard, ReminderActionCallback, ReminderEditCallback, ReminderIntervalCallback, ConfirmCallback, ) from bot.keyboards.main_menu import get_main_menu_keyboard from bot.services.user_service import UserService from bot.services.reminders_service import RemindersService from bot.utils.formatting import format_datetime, format_interval_days from bot.utils.validators import validate_time_format, validate_days_interval from bot.logging_config import get_logger logger = get_logger(__name__) router = Router(name="reminders_manage") reminders_service = RemindersService() @router.message(F.text == "πŸ“‹ Мои напоминания") async def show_reminders_list( message: Message, session: AsyncSession, ) -> None: """ Show user's reminders list. Args: message: Telegram message session: Database session """ user = await UserService.ensure_user_exists(session, message.from_user) reminders = await reminders_service.get_user_all_reminders(session, user.id) if not reminders: await message.answer( "Π£ тСбя ΠΏΠΎΠΊΠ° Π½Π΅Ρ‚ Π½Π°ΠΏΠΎΠΌΠΈΠ½Π°Π½ΠΈΠΉ.\n\n" "НаТми Β«βž• НовоС Π½Π°ΠΏΠΎΠΌΠΈΠ½Π°Π½ΠΈΠ΅Β», Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ ΠΏΠ΅Ρ€Π²ΠΎΠ΅!", reply_markup=get_main_menu_keyboard(), ) return await message.answer( f"πŸ“‹ Π’Π²ΠΎΠΈ напоминания ({len(reminders)}):", reply_markup=get_reminders_list_keyboard(reminders, page=0), ) @router.callback_query(PaginationCallback.filter(F.action.in_(["prev", "next"]))) async def paginate_reminders( callback: CallbackQuery, callback_data: PaginationCallback, session: AsyncSession, ) -> None: """ Handle pagination for reminders list. Args: callback: Callback query callback_data: Parsed callback data session: Database session """ user = await UserService.ensure_user_exists(session, callback.from_user) reminders = await reminders_service.get_user_all_reminders(session, user.id) await callback.message.edit_reply_markup( reply_markup=get_reminders_list_keyboard(reminders, page=callback_data.page) ) await callback.answer() @router.callback_query(PaginationCallback.filter(F.action == "select")) async def show_reminder_details( callback: CallbackQuery, callback_data: PaginationCallback, session: AsyncSession, ) -> None: """ Show reminder details. Args: callback: Callback query callback_data: Parsed callback data session: Database session """ reminder = await reminders_service.get_reminder(session, callback_data.reminder_id) if not reminder: await callback.answer("НапоминаниС Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½ΠΎ", show_alert=True) return status = "Активно βœ…" if reminder.is_active else "На ΠΏΠ°ΡƒΠ·Π΅ ⏸" last_done = ( format_datetime(reminder.last_done_at) if reminder.last_done_at else "Π•Ρ‰Ρ‘ Π½Π΅ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΠ»ΠΎΡΡŒ" ) details_text = ( f"πŸ“ НапоминаниС #{reminder.id}\n\n" f"ВСкст: {reminder.text}\n\n" f"ΠŸΠ΅Ρ€ΠΈΠΎΠ΄ΠΈΡ‡Π½ΠΎΡΡ‚ΡŒ: {format_interval_days(reminder.days_interval)}\n" f"ВрСмя: {reminder.time_of_day.strftime('%H:%M')}\n" f"Бтатус: {status}\n\n" f"Π‘Π»Π΅Π΄ΡƒΡŽΡ‰Π΅Π΅ Π½Π°ΠΏΠΎΠΌΠΈΠ½Π°Π½ΠΈΠ΅: {format_datetime(reminder.next_run_at)}\n" f"ПослСднСС Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅: {last_done}\n" f"Π’Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ Ρ€Π°Π·: {reminder.total_done_count}" ) await callback.message.edit_text( details_text, reply_markup=get_reminder_details_keyboard(reminder.id, reminder.is_active), parse_mode="HTML", ) await callback.answer() @router.callback_query(ReminderActionCallback.filter(F.action == "back_to_list")) async def back_to_reminders_list( callback: CallbackQuery, session: AsyncSession, ) -> None: """ Return to reminders list from details view. Args: callback: Callback query session: Database session """ user = await UserService.ensure_user_exists(session, callback.from_user) reminders = await reminders_service.get_user_all_reminders(session, user.id) if not reminders: await callback.message.edit_text( "Π£ тСбя ΠΏΠΎΠΊΠ° Π½Π΅Ρ‚ Π½Π°ΠΏΠΎΠΌΠΈΠ½Π°Π½ΠΈΠΉ.\n\n" "НаТми Β«βž• НовоС Π½Π°ΠΏΠΎΠΌΠΈΠ½Π°Π½ΠΈΠ΅Β», Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ ΠΏΠ΅Ρ€Π²ΠΎΠ΅!" ) await callback.answer() return await callback.message.edit_text( f"πŸ“‹ Π’Π²ΠΎΠΈ напоминания ({len(reminders)}):", reply_markup=get_reminders_list_keyboard(reminders, page=0), ) await callback.answer() # ==================== Edit Reminder Flow ==================== @router.callback_query(ReminderActionCallback.filter(F.action == "edit")) async def start_edit_reminder( callback: CallbackQuery, callback_data: ReminderActionCallback, state: FSMContext, ) -> None: """ Start editing reminder. Args: callback: Callback query callback_data: Parsed callback data state: FSM state context """ await state.update_data(reminder_id=callback_data.reminder_id) await state.set_state(EditReminderStates.selecting_field) await callback.message.edit_text( "Π§Ρ‚ΠΎ ΠΈΠ·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ?", reply_markup=get_edit_menu_keyboard(callback_data.reminder_id), ) await callback.answer() @router.callback_query( EditReminderStates.selecting_field, ReminderEditCallback.filter(F.action == "back") ) async def cancel_edit( callback: CallbackQuery, callback_data: ReminderEditCallback, state: FSMContext, session: AsyncSession, ) -> None: """ Cancel editing and return to reminder details. Args: callback: Callback query callback_data: Parsed callback data state: FSM state context session: Database session """ await state.clear() # Show reminder details again reminder = await reminders_service.get_reminder(session, callback_data.reminder_id) if not reminder: await callback.answer("НапоминаниС Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½ΠΎ", show_alert=True) return status = "Активно βœ…" if reminder.is_active else "На ΠΏΠ°ΡƒΠ·Π΅ ⏸" last_done = ( format_datetime(reminder.last_done_at) if reminder.last_done_at else "Π•Ρ‰Ρ‘ Π½Π΅ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΠ»ΠΎΡΡŒ" ) details_text = ( f"πŸ“ НапоминаниС #{reminder.id}\n\n" f"ВСкст: {reminder.text}\n\n" f"ΠŸΠ΅Ρ€ΠΈΠΎΠ΄ΠΈΡ‡Π½ΠΎΡΡ‚ΡŒ: {format_interval_days(reminder.days_interval)}\n" f"ВрСмя: {reminder.time_of_day.strftime('%H:%M')}\n" f"Бтатус: {status}\n\n" f"Π‘Π»Π΅Π΄ΡƒΡŽΡ‰Π΅Π΅ Π½Π°ΠΏΠΎΠΌΠΈΠ½Π°Π½ΠΈΠ΅: {format_datetime(reminder.next_run_at)}\n" f"ПослСднСС Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅: {last_done}" ) await callback.message.edit_text( details_text, reply_markup=get_reminder_details_keyboard(reminder.id, reminder.is_active), parse_mode="HTML", ) await callback.answer() @router.callback_query( EditReminderStates.selecting_field, ReminderEditCallback.filter(F.action == "text") ) async def edit_text_start( callback: CallbackQuery, callback_data: ReminderEditCallback, state: FSMContext, ) -> None: """ Start editing reminder text. Args: callback: Callback query callback_data: Parsed callback data state: FSM state context """ await state.set_state(EditReminderStates.editing_text) await callback.message.edit_text("Π’Π²Π΅Π΄ΠΈ Π½ΠΎΠ²Ρ‹ΠΉ тСкст напоминания:") await callback.answer() @router.message(EditReminderStates.editing_text) async def edit_text_process( message: Message, state: FSMContext, session: AsyncSession, ) -> None: """ Process new reminder text. Args: message: Telegram message state: FSM state context session: Database session """ text = message.text.strip() if not text or len(text) > 1000: await message.answer("ВСкст Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ ΠΎΡ‚ 1 Π΄ΠΎ 1000 символов. ΠŸΠΎΠΏΡ€ΠΎΠ±ΡƒΠΉ Π΅Ρ‰Ρ‘ Ρ€Π°Π·:") return data = await state.get_data() reminder_id = data["reminder_id"] reminder = await reminders_service.update_reminder_text(session, reminder_id, text) if not reminder: await message.answer("НапоминаниС Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½ΠΎ") await state.clear() return await state.clear() await message.answer( f"βœ… ВСкст ΠΎΠ±Π½ΠΎΠ²Π»Ρ‘Π½!\n\nНовый тСкст: {text}", reply_markup=get_main_menu_keyboard(), ) @router.callback_query( EditReminderStates.selecting_field, ReminderEditCallback.filter(F.action == "interval") ) async def edit_interval_start( callback: CallbackQuery, callback_data: ReminderEditCallback, state: FSMContext, ) -> None: """ Start editing reminder interval. Args: callback: Callback query callback_data: Parsed callback data state: FSM state context """ await state.set_state(EditReminderStates.editing_interval) await callback.message.edit_text( "Π’Ρ‹Π±Π΅Ρ€ΠΈ Π½ΠΎΠ²Ρ‹ΠΉ ΠΏΠ΅Ρ€ΠΈΠΎΠ΄ ΠΈΠ»ΠΈ Π²Π²Π΅Π΄ΠΈ количСство Π΄Π½Π΅ΠΉ:", reply_markup=get_interval_selection_keyboard(), ) await callback.answer() @router.callback_query( EditReminderStates.editing_interval, ReminderIntervalCallback.filter() ) async def edit_interval_button( callback: CallbackQuery, callback_data: ReminderIntervalCallback, state: FSMContext, session: AsyncSession, ) -> None: """ Process interval selection via button. Args: callback: Callback query callback_data: Parsed callback data state: FSM state context session: Database session """ if callback_data.days == 0: await callback.message.edit_text("Π’Π²Π΅Π΄ΠΈ количСство Π΄Π½Π΅ΠΉ (Ρ†Π΅Π»ΠΎΠ΅ ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ число):") await callback.answer() return data = await state.get_data() reminder_id = data["reminder_id"] reminder = await reminders_service.update_reminder_interval( session, reminder_id, callback_data.days ) if not reminder: await callback.answer("НапоминаниС Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½ΠΎ", show_alert=True) await state.clear() return await state.clear() await callback.message.edit_text( f"βœ… ΠŸΠ΅Ρ€ΠΈΠΎΠ΄ΠΈΡ‡Π½ΠΎΡΡ‚ΡŒ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½Π°!\n\nΠ’Π΅ΠΏΠ΅Ρ€ΡŒ: {format_interval_days(callback_data.days)}" ) await callback.message.answer( "Π’Ρ‹Π±Π΅Ρ€ΠΈ дСйствиС ΠΈΠ· мСню:", reply_markup=get_main_menu_keyboard(), ) await callback.answer("ОбновлСно!") @router.message(EditReminderStates.editing_interval) async def edit_interval_text( message: Message, state: FSMContext, session: AsyncSession, ) -> None: """ Process interval input as text. Args: message: Telegram message state: FSM state context session: Database session """ days = validate_days_interval(message.text) if days is None: await message.answer("НСкоррСктноС Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅. Π’Π²Π΅Π΄ΠΈ Ρ†Π΅Π»ΠΎΠ΅ ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ число Π΄Π½Π΅ΠΉ:") return data = await state.get_data() reminder_id = data["reminder_id"] reminder = await reminders_service.update_reminder_interval(session, reminder_id, days) if not reminder: await message.answer("НапоминаниС Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½ΠΎ") await state.clear() return await state.clear() await message.answer( f"βœ… ΠŸΠ΅Ρ€ΠΈΠΎΠ΄ΠΈΡ‡Π½ΠΎΡΡ‚ΡŒ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½Π°!\n\nΠ’Π΅ΠΏΠ΅Ρ€ΡŒ: {format_interval_days(days)}", reply_markup=get_main_menu_keyboard(), ) @router.callback_query( EditReminderStates.selecting_field, ReminderEditCallback.filter(F.action == "time") ) async def edit_time_start( callback: CallbackQuery, callback_data: ReminderEditCallback, state: FSMContext, ) -> None: """ Start editing reminder time. Args: callback: Callback query callback_data: Parsed callback data state: FSM state context """ await state.set_state(EditReminderStates.editing_time) await callback.message.edit_text( "Π’Π²Π΅Π΄ΠΈ Π½ΠΎΠ²ΠΎΠ΅ врСмя Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ Π§Π§:ММ (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, 09:00):" ) await callback.answer() @router.message(EditReminderStates.editing_time) async def edit_time_process( message: Message, state: FSMContext, session: AsyncSession, ) -> None: """ Process new reminder time. Args: message: Telegram message state: FSM state context session: Database session """ time_of_day = validate_time_format(message.text) if time_of_day is None: await message.answer( "НСкоррСктный Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ. Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ Π§Π§:ММ (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, 09:00):" ) return data = await state.get_data() reminder_id = data["reminder_id"] reminder = await reminders_service.update_reminder_time(session, reminder_id, time_of_day) if not reminder: await message.answer("НапоминаниС Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½ΠΎ") await state.clear() return await state.clear() await message.answer( f"βœ… ВрСмя ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΎ!\n\nНовоС врСмя: {time_of_day.strftime('%H:%M')}", reply_markup=get_main_menu_keyboard(), )