initial commit
This commit is contained in:
252
handlers/favorites.py
Normal file
252
handlers/favorites.py
Normal file
@@ -0,0 +1,252 @@
|
||||
from aiogram import Router, F
|
||||
from aiogram.types import CallbackQuery, InputMediaPhoto
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, text, delete
|
||||
from database.models import Book, Favorite, User
|
||||
from database.connection import async_session_maker
|
||||
from keyboards.inline import get_book_keyboard, get_main_menu
|
||||
from handlers.books import format_book_message, is_book_favorite
|
||||
import logging
|
||||
|
||||
router = Router()
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith("add_fav:"))
|
||||
async def add_to_favorites(callback: CallbackQuery):
|
||||
"""Добавить книгу в избранное"""
|
||||
book_id = int(callback.data.split(":")[1])
|
||||
telegram_id = callback.from_user.id
|
||||
|
||||
try:
|
||||
async with async_session_maker() as session:
|
||||
# Получаем пользователя
|
||||
result = await session.execute(
|
||||
select(User).where(User.telegram_id == telegram_id)
|
||||
)
|
||||
user = result.scalar_one_or_none()
|
||||
|
||||
if not user:
|
||||
await callback.answer("⛔ Пользователь не найден", show_alert=True)
|
||||
return
|
||||
|
||||
# Проверяем, существует ли книга
|
||||
result = await session.execute(
|
||||
select(Book).where(Book.id == book_id, Book.deleted_at.is_(None))
|
||||
)
|
||||
book = result.scalar_one_or_none()
|
||||
|
||||
if not book:
|
||||
await callback.answer("❌ Книга не найдена", show_alert=True)
|
||||
return
|
||||
|
||||
# Проверяем, не добавлена ли уже
|
||||
result = await session.execute(
|
||||
select(Favorite).where(
|
||||
Favorite.user_id == user.id,
|
||||
Favorite.book_id == book_id
|
||||
)
|
||||
)
|
||||
existing_favorite = result.scalar_one_or_none()
|
||||
|
||||
if existing_favorite:
|
||||
await callback.answer("ℹ️ Книга уже в избранном", show_alert=True)
|
||||
return
|
||||
|
||||
# Добавляем в избранное
|
||||
new_favorite = Favorite(
|
||||
user_id=user.id,
|
||||
book_id=book_id
|
||||
)
|
||||
session.add(new_favorite)
|
||||
await session.commit()
|
||||
|
||||
# Обновляем клавиатуру
|
||||
try:
|
||||
current_markup = callback.message.reply_markup
|
||||
# Извлекаем номер страницы из существующей клавиатуры
|
||||
page = 0
|
||||
total_pages = 1
|
||||
|
||||
if current_markup and current_markup.inline_keyboard:
|
||||
for row in current_markup.inline_keyboard:
|
||||
for button in row:
|
||||
if button.callback_data and "books_page:" in button.callback_data:
|
||||
parts = button.callback_data.split(":")
|
||||
if len(parts) > 1:
|
||||
page = int(parts[1])
|
||||
elif button.text and "/" in button.text:
|
||||
try:
|
||||
pages_info = button.text.split("/")
|
||||
page = int(pages_info[0]) - 1
|
||||
total_pages = int(pages_info[1])
|
||||
except:
|
||||
pass
|
||||
|
||||
new_keyboard = get_book_keyboard(book_id, True, page, total_pages)
|
||||
await callback.message.edit_reply_markup(reply_markup=new_keyboard)
|
||||
except Exception as e:
|
||||
logger.warning(f"Не удалось обновить клавиатуру: {e}")
|
||||
|
||||
await callback.answer("✅ Книга добавлена в избранное!")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при добавлении в избранное: {e}")
|
||||
await callback.answer("❌ Произошла ошибка", show_alert=True)
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith("remove_fav:"))
|
||||
async def remove_from_favorites(callback: CallbackQuery):
|
||||
"""Удалить книгу из избранного"""
|
||||
book_id = int(callback.data.split(":")[1])
|
||||
telegram_id = callback.from_user.id
|
||||
|
||||
try:
|
||||
async with async_session_maker() as session:
|
||||
# Получаем пользователя
|
||||
result = await session.execute(
|
||||
select(User).where(User.telegram_id == telegram_id)
|
||||
)
|
||||
user = result.scalar_one_or_none()
|
||||
|
||||
if not user:
|
||||
await callback.answer("⛔ Пользователь не найден", show_alert=True)
|
||||
return
|
||||
|
||||
# Удаляем из избранного
|
||||
await session.execute(
|
||||
delete(Favorite).where(
|
||||
Favorite.user_id == user.id,
|
||||
Favorite.book_id == book_id
|
||||
)
|
||||
)
|
||||
await session.commit()
|
||||
|
||||
# Обновляем клавиатуру
|
||||
try:
|
||||
current_markup = callback.message.reply_markup
|
||||
page = 0
|
||||
total_pages = 1
|
||||
|
||||
if current_markup and current_markup.inline_keyboard:
|
||||
for row in current_markup.inline_keyboard:
|
||||
for button in row:
|
||||
if button.callback_data and "books_page:" in button.callback_data:
|
||||
parts = button.callback_data.split(":")
|
||||
if len(parts) > 1:
|
||||
page = int(parts[1])
|
||||
elif button.text and "/" in button.text:
|
||||
try:
|
||||
pages_info = button.text.split("/")
|
||||
page = int(pages_info[0]) - 1
|
||||
total_pages = int(pages_info[1])
|
||||
except:
|
||||
pass
|
||||
|
||||
new_keyboard = get_book_keyboard(book_id, False, page, total_pages)
|
||||
await callback.message.edit_reply_markup(reply_markup=new_keyboard)
|
||||
except Exception as e:
|
||||
logger.warning(f"Не удалось обновить клавиатуру: {e}")
|
||||
|
||||
await callback.answer("❌ Книга удалена из избранного")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при удалении из избранного: {e}")
|
||||
await callback.answer("❌ Произошла ошибка", show_alert=True)
|
||||
|
||||
|
||||
@router.callback_query(F.data == "favorites")
|
||||
async def show_favorites(callback: CallbackQuery):
|
||||
"""Показать избранные книги"""
|
||||
telegram_id = callback.from_user.id
|
||||
|
||||
try:
|
||||
async with async_session_maker() as session:
|
||||
# Получаем пользователя
|
||||
result = await session.execute(
|
||||
select(User).where(User.telegram_id == telegram_id)
|
||||
)
|
||||
user = result.scalar_one_or_none()
|
||||
|
||||
if not user:
|
||||
await callback.answer("⛔ Пользователь не найден", show_alert=True)
|
||||
return
|
||||
|
||||
# Получаем избранные книги с полной информацией
|
||||
query = text("""
|
||||
SELECT
|
||||
b.id, b.title, b.description, b.cover_url,
|
||||
b.average_rating, b.rating_count,
|
||||
STRING_AGG(DISTINCT a.name, ', ') as authors,
|
||||
STRING_AGG(DISTINCT g.name, ', ') as genres
|
||||
FROM books_favorite f
|
||||
JOIN books_book b ON f.book_id = b.id
|
||||
LEFT JOIN books_book_authors ba ON b.id = ba.book_id
|
||||
LEFT JOIN books_author a ON ba.author_id = a.id
|
||||
LEFT JOIN books_book_genres bg ON b.id = bg.book_id
|
||||
LEFT JOIN books_genre g ON bg.genre_id = g.id
|
||||
WHERE f.user_id = :user_id AND b.deleted_at IS NULL
|
||||
GROUP BY b.id, f.created_at
|
||||
ORDER BY f.created_at DESC
|
||||
LIMIT 1
|
||||
""")
|
||||
|
||||
result = await session.execute(query, {"user_id": user.id})
|
||||
favorites = result.mappings().all()
|
||||
|
||||
if not favorites:
|
||||
await callback.message.edit_text(
|
||||
"⭐ У вас пока нет избранных книг.\n\n"
|
||||
"Добавьте книги в избранное, чтобы быстро находить их здесь!",
|
||||
reply_markup=get_main_menu()
|
||||
)
|
||||
await callback.answer()
|
||||
return
|
||||
|
||||
# Показываем первую избранную книгу
|
||||
book = favorites[0]
|
||||
message_text = format_book_message(book, is_favorite=True)
|
||||
|
||||
# Создаем клавиатуру (всегда с кнопкой удаления из избранного)
|
||||
keyboard = get_book_keyboard(book['id'], True, 0, 1)
|
||||
|
||||
# Отправляем с картинкой если есть
|
||||
if book['cover_url']:
|
||||
try:
|
||||
if callback.message.photo:
|
||||
await callback.message.edit_media(
|
||||
media=InputMediaPhoto(
|
||||
media=book['cover_url'],
|
||||
caption=message_text,
|
||||
parse_mode="HTML"
|
||||
),
|
||||
reply_markup=keyboard
|
||||
)
|
||||
else:
|
||||
await callback.message.delete()
|
||||
await callback.message.answer_photo(
|
||||
photo=book['cover_url'],
|
||||
caption=message_text,
|
||||
parse_mode="HTML",
|
||||
reply_markup=keyboard
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Не удалось загрузить обложку: {e}")
|
||||
await callback.message.edit_text(
|
||||
"🖼 [Обложка недоступна]\n\n" + message_text,
|
||||
parse_mode="HTML",
|
||||
reply_markup=keyboard
|
||||
)
|
||||
else:
|
||||
await callback.message.edit_text(
|
||||
message_text,
|
||||
parse_mode="HTML",
|
||||
reply_markup=keyboard
|
||||
)
|
||||
|
||||
await callback.answer()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка в show_favorites: {e}")
|
||||
await callback.answer("❌ Произошла ошибка", show_alert=True)
|
||||
|
||||
Reference in New Issue
Block a user