from datetime import datetime, timedelta from sqlalchemy import and_, delete, func, select from sqlalchemy.dialects.postgresql import insert from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.ext.asyncio import AsyncSession from database.models import Key, Notification, User from logger import logger async def add_notification(session: AsyncSession, tg_id: int, notification_type: str): try: stmt = ( insert(Notification) .values( tg_id=tg_id, notification_type=notification_type, last_notification_time=datetime.utcnow(), ) .on_conflict_do_update( index_elements=[Notification.tg_id, Notification.notification_type], set_={"last_notification_time": datetime.utcnow()}, ) ) await session.execute(stmt) await session.commit() logger.info( f"✅ Добавлено уведомление {notification_type} для пользователя {tg_id}" ) except SQLAlchemyError as e: logger.error(f"❌ Ошибка при добавлении уведомления: {e}") await session.rollback() async def delete_notification( session: AsyncSession, tg_id: int, notification_type: str ): await session.execute( delete(Notification).where( Notification.tg_id == tg_id, Notification.notification_type == notification_type, ) ) await session.commit() logger.info(f"🗑 Уведомление {notification_type} для пользователя {tg_id} удалено") async def check_notification_time( session: AsyncSession, tg_id: int, notification_type: str, hours: int = 12 ) -> bool: stmt = select(Notification.last_notification_time).where( Notification.tg_id == tg_id, Notification.notification_type == notification_type ) result = await session.execute(stmt) last_time = result.scalar_one_or_none() if not last_time: return True return datetime.utcnow() - last_time > timedelta(hours=hours) async def get_last_notification_time( session: AsyncSession, tg_id: int, notification_type: str ) -> int | None: stmt = select(Notification.last_notification_time).where( Notification.tg_id == tg_id, Notification.notification_type == notification_type ) result = await session.execute(stmt) ts = result.scalar_one_or_none() if ts: return int(ts.timestamp() * 1000) return None async def check_notifications_bulk( session: AsyncSession, notification_type: str, hours: int, tg_ids: list[int] = None, emails: list[str] = None, ) -> list[dict]: try: stmt = ( select( User.tg_id, Key.email, User.username, User.first_name, User.last_name, func.max(Notification.last_notification_time).label( "last_notification_time" ), ) .outerjoin(Key, User.tg_id == Key.tg_id) .outerjoin( Notification, and_( User.tg_id == Notification.tg_id, Notification.notification_type == notification_type, ), ) .group_by( User.tg_id, Key.email, User.username, User.first_name, User.last_name ) ) if tg_ids: stmt = stmt.where(User.tg_id.in_(tg_ids)) if emails: stmt = stmt.where(Key.email.in_(emails)) result = await session.execute(stmt) now = datetime.utcnow() users = [] for row in result: last_time = row.last_notification_time can_notify = not last_time or (now - last_time > timedelta(hours=hours)) if can_notify: users.append( { "tg_id": row.tg_id, "email": row.email, "username": row.username, "first_name": row.first_name, "last_name": row.last_name, "last_notification_time": ( int(last_time.timestamp() * 1000) if last_time else None ), } ) return users except SQLAlchemyError as e: logger.error(f"Ошибка при массовой проверке уведомлений: {e}") return []