from __future__ import annotations import asyncio from datetime import timedelta from sqlalchemy import select from sqlalchemy.ext.asyncio import async_sessionmaker from ..config import settings from ..models import ReminderDelivery from .datetime_utils import now_in_config_timezone from .inventory import get_expiring_products, list_active_users from .telegram import send_message def seconds_until_next_run() -> float: now = now_in_config_timezone() target = now.replace(hour=settings.reminder_hour, minute=settings.reminder_minute, second=0, microsecond=0) if target <= now: target = target + timedelta(days=1) return (target - now).total_seconds() def render_reminder(records: list[dict]) -> str: if not records: return f"Recordatorio diario: no hay productos por vencer en los proximos {settings.expiry_warning_days} dias." lines = [ f"- {item['producto']}: vence {item['fechaCaducidad']}, stock {item['stockActual']} {item['unidad']}" for item in records ] return "Recordatorio diario de vencimientos:\n" + "\n".join(lines) async def run_daily_reminders(session_factory: async_sessionmaker) -> None: async with session_factory() as session: users = await list_active_users(session) if not users: return records = await get_expiring_products(session, settings.expiry_warning_days) message = render_reminder(records) reminder_date = now_in_config_timezone().date().isoformat() for user in users: try: delivery_id = f"{user.chat_id}:{reminder_date}" exists = await session.get(ReminderDelivery, delivery_id) if exists is not None: continue await send_message(user.chat_id, message) session.add( ReminderDelivery( delivery_id=delivery_id, chat_id=str(user.chat_id), reminder_date=reminder_date, created_at=now_in_config_timezone().replace(microsecond=0).isoformat(), ) ) await session.commit() except Exception as exc: print(f"No se pudo enviar recordatorio a {user.chat_id}: {exc}") async def reminder_loop(session_factory: async_sessionmaker) -> None: while True: await asyncio.sleep(seconds_until_next_run()) await run_daily_reminders(session_factory)