reminder-bot/bot/handlers/callbacks.py

251 lines
8.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""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(),
)