Files
drivers_bot/app/services/notifications.py
VPN SaaS Dev 83ad880b9d
Some checks failed
ci / test (push) Has been cancelled
Mechanic's work place
2026-05-16 10:04:56 +09:00

71 lines
2.6 KiB
Python

from datetime import UTC, datetime, timedelta
import httpx
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.config import settings
from app.models.car import ServiceNotification
from app.models.user import User
MODERATOR_ROLES = {"admin", "verifier", "moderator"}
async def notify_user(user: User, text: str) -> bool:
if not settings.bot_token or settings.app_env == "test":
return False
try:
async with httpx.AsyncClient(timeout=5) as client:
response = await client.post(
f"https://api.telegram.org/bot{settings.bot_token}/sendMessage",
data={"chat_id": str(user.telegram_id), "text": text},
)
return response.status_code < 400
except Exception:
return False
async def notify_platform_moderators(session: AsyncSession, text: str) -> None:
result = await session.execute(select(User).where(User.platform_role.in_(MODERATOR_ROLES)))
for user in result.scalars():
await notify_user(user, text)
async def retry_failed_notifications(session: AsyncSession, *, limit: int = 50) -> int:
return await process_notification_queue(session, limit=limit)
async def process_notification_queue(session: AsyncSession, *, limit: int = 50) -> int:
now = datetime.now(UTC)
result = await session.execute(
select(ServiceNotification)
.where(
ServiceNotification.status.in_(["pending", "failed", "retrying"]),
ServiceNotification.retry_count < 5,
)
.order_by(ServiceNotification.created_at.asc())
.limit(limit)
)
delivered = 0
for notification in result.scalars():
if notification.status == "retrying" and notification.created_at > now - timedelta(seconds=30):
continue
notification.status = "processing"
user = await session.get(User, notification.recipient_user_id)
if user is None:
notification.status = "abandoned"
notification.last_error = "recipient_not_found"
continue
ok = await notify_user(user, f"{notification.title}\n{notification.body}" if notification.body else notification.title)
notification.retry_count += 1
if ok:
notification.status = "sent"
notification.sent_at = datetime.now(UTC)
notification.last_error = None
delivered += 1
else:
notification.status = "abandoned" if notification.retry_count >= 5 else "retrying"
notification.last_error = "telegram_delivery_failed"
await session.commit()
return delivered