"""Handlers for inline button callbacks (done, snooze, pause, delete).""" from aiogram import Router, F from aiogram.types import CallbackQuery, Message from sqlalchemy.ext.asyncio import AsyncSession from bot.keyboards.reminders import ( ReminderActionCallback, SnoozeDelayCallback, ConfirmCallback, get_snooze_delay_keyboard, get_confirmation_keyboard, ) from bot.keyboards.main_menu import get_main_menu_keyboard from bot.services.reminders_service import RemindersService from bot.services.time_service import get_time_service from bot.logging_config import get_logger logger = get_logger(__name__) router = Router(name="callbacks") reminders_service = RemindersService() async def _verify_owner( session: AsyncSession, reminder_id: int, tg_user_id: int, ) -> bool: """Fetch reminder and verify it belongs to tg_user_id.""" from sqlalchemy.orm import selectinload from sqlalchemy import select from bot.db.models import Reminder result = await session.execute( select(Reminder) .options(selectinload(Reminder.user)) .where(Reminder.id == reminder_id) ) reminder = result.scalar_one_or_none() if not reminder: return False if reminder.user.tg_user_id != tg_user_id: return False return True @router.callback_query(F.data == "noop") async def handle_noop(callback: CallbackQuery) -> None: """Handle noop callback (page counter button).""" await callback.answer() @router.callback_query(ReminderActionCallback.filter(F.action == "done")) async def handle_done( callback: CallbackQuery, callback_data: ReminderActionCallback, session: AsyncSession, ) -> None: """Handle 'Done' button - mark reminder as completed.""" owner_check = await _verify_owner(session, callback_data.reminder_id, callback.from_user.id) if not owner_check: await callback.answer("Напоминание не найдено", show_alert=True) return reminder = await reminders_service.mark_as_done(session, callback_data.reminder_id) if not reminder: await callback.answer("Напоминание не найдено", show_alert=True) return next_run_str = get_time_service().format_next_run(reminder.next_run_at) await callback.message.edit_text( f"✅ Отлично! Отметил как выполненное.\n\n" f"Следующее напоминание будет {next_run_str}." ) await callback.answer("Выполнено!") logger.info(f"Reminder {reminder.id} marked as done by user {callback.from_user.id}") @router.callback_query(ReminderActionCallback.filter(F.action == "snooze")) async def handle_snooze_select( callback: CallbackQuery, callback_data: ReminderActionCallback, session: AsyncSession, ) -> None: """Handle 'Snooze' button - show delay options.""" owner_check = await _verify_owner(session, callback_data.reminder_id, callback.from_user.id) if not owner_check: await callback.answer("Напоминание не найдено", show_alert=True) return await callback.message.edit_text( "На сколько отложить напоминание?", reply_markup=get_snooze_delay_keyboard(callback_data.reminder_id), ) await callback.answer() @router.callback_query(SnoozeDelayCallback.filter()) async def handle_snooze_delay( callback: CallbackQuery, callback_data: SnoozeDelayCallback, session: AsyncSession, ) -> None: """Handle snooze delay selection.""" owner_check = await _verify_owner(session, callback_data.reminder_id, callback.from_user.id) if not owner_check: await callback.answer("Напоминание не найдено", show_alert=True) return reminder = await reminders_service.snooze( session, callback_data.reminder_id, callback_data.hours ) if not reminder: await callback.answer("Напоминание не найдено", show_alert=True) return next_run_str = get_time_service().format_next_run(reminder.next_run_at) await callback.message.edit_text( f"⏰ Напоминание отложено.\n\n" f"Напомню {next_run_str}." ) await callback.answer("Отложено!") logger.info( f"Reminder {reminder.id} snoozed for {callback_data.hours}h " f"by user {callback.from_user.id}" ) @router.callback_query(ReminderActionCallback.filter(F.action == "pause")) async def handle_pause( callback: CallbackQuery, callback_data: ReminderActionCallback, session: AsyncSession, ) -> None: """Handle 'Pause' button - deactivate reminder.""" owner_check = await _verify_owner(session, callback_data.reminder_id, callback.from_user.id) if not owner_check: await callback.answer("Напоминание не найдено", show_alert=True) return reminder = await reminders_service.pause_reminder(session, callback_data.reminder_id) if not reminder: await callback.answer("Напоминание не найдено", show_alert=True) return await callback.message.edit_text( "⏸ Напоминание поставлено на паузу.\n\n" "Можешь возобновить его в любое время через «📋 Мои напоминания»." ) await callback.answer("Поставлено на паузу") logger.info(f"Reminder {reminder.id} paused by user {callback.from_user.id}") @router.callback_query(ReminderActionCallback.filter(F.action == "resume")) async def handle_resume( callback: CallbackQuery, callback_data: ReminderActionCallback, session: AsyncSession, ) -> None: """Handle 'Resume' button - reactivate reminder.""" owner_check = await _verify_owner(session, callback_data.reminder_id, callback.from_user.id) if not owner_check: await callback.answer("Напоминание не найдено", show_alert=True) return reminder = await reminders_service.resume_reminder(session, callback_data.reminder_id) if not reminder: await callback.answer("Напоминание не найдено", show_alert=True) return next_run_str = get_time_service().format_next_run(reminder.next_run_at) await callback.message.edit_text( f"▶️ Напоминание возобновлено!\n\n" f"Следующее напоминание будет {next_run_str}." ) await callback.answer("Возобновлено!") logger.info(f"Reminder {reminder.id} resumed by user {callback.from_user.id}") @router.callback_query(ReminderActionCallback.filter(F.action == "delete")) async def handle_delete_confirm( callback: CallbackQuery, callback_data: ReminderActionCallback, session: AsyncSession, ) -> None: """Handle 'Delete' button - ask for confirmation.""" owner_check = await _verify_owner(session, callback_data.reminder_id, callback.from_user.id) if not owner_check: await callback.answer("Напоминание не найдено", show_alert=True) return await callback.message.edit_text( "Точно удалить напоминание?", reply_markup=get_confirmation_keyboard( entity="delete", entity_id=callback_data.reminder_id ), ) await callback.answer() @router.callback_query(ConfirmCallback.filter(F.entity == "delete")) async def handle_delete_execute( callback: CallbackQuery, callback_data: ConfirmCallback, session: AsyncSession, ) -> None: """Execute reminder deletion after confirmation.""" if callback_data.action == "no": await callback.message.edit_text("Удаление отменено.") await callback.answer() return deleted = await reminders_service.delete_reminder_by_id( session, callback_data.entity_id ) if not deleted: await callback.answer("Напоминание не найдено", show_alert=True) return await callback.message.edit_text("🗑 Напоминание удалено.") await callback.answer("Удалено") logger.info( f"Reminder {callback_data.entity_id} deleted by user {callback.from_user.id}" ) @router.message(F.text == "⚙️ Настройки") async def handle_settings(message: Message) -> None: """Handle settings button (placeholder).""" await message.answer( "⚙️ Настройки\n\n" "Функционал настроек будет добавлен в следующих версиях.\n\n" "Пока доступны основные функции:\n" "• Создание напоминаний\n" "• Просмотр и управление\n" "• Редактирование параметров", reply_markup=get_main_menu_keyboard(), )