Files
Solo_bot/database/notifications.py
T
2025-05-24 19:30:23 +03:00

137 lines
4.5 KiB
Python

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 []