finance-telegram-bot/app/scheduler.py
2026-07-14 12:21:12 +07:00

80 lines
3.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
import asyncio
import logging
from contextlib import suppress
from datetime import datetime, time
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
from aiogram import Bot
from aiogram.exceptions import TelegramForbiddenError
from app.database import Database
from app.keyboards import main_keyboard
from app.messages import daily_report_text
from app.periods import day_bounds, month_bounds
logger = logging.getLogger(__name__)
async def daily_report_loop(bot: Bot, database: Database) -> None:
"""Check report schedules periodically; the database makes delivery idempotent."""
while True:
try:
await _send_due_reports(bot, database)
except asyncio.CancelledError:
raise
except Exception:
logger.exception("Ошибка цикла ежедневных отчётов")
await asyncio.sleep(30)
async def _send_due_reports(bot: Bot, database: Database) -> None:
users = await database.users_for_reports()
for user in users:
try:
timezone = ZoneInfo(user["timezone"])
report_at = time.fromisoformat(user["report_time"])
except (ZoneInfoNotFoundError, ValueError):
logger.error("Некорректные настройки времени у пользователя %s", user["user_id"])
continue
now = datetime.now(timezone)
if now.time().replace(tzinfo=None) < report_at:
continue
local_date = now.date().isoformat()
day_start, day_end = day_bounds(now)
month_start, month_end = month_bounds(now)
day = await database.totals(user["user_id"], day_start, day_end)
month = await database.totals(user["user_id"], month_start, month_end)
debt = await database.family_debt(user["user_id"])
report_text = daily_report_text(day, month, debt)
if not await database.report_was_sent(user["user_id"], local_date):
try:
await bot.send_message(
chat_id=user["chat_id"],
text=report_text,
reply_markup=main_keyboard(),
)
except TelegramForbiddenError:
logger.info("Пользователь %s заблокировал бота", user["user_id"])
await database.mark_report_sent(user["user_id"], local_date, now)
family_target = await database.family_report_target(user["user_id"])
if family_target is None:
continue
household_id, family_chat_id = family_target
if await database.family_report_was_sent(household_id, local_date):
continue
try:
await bot.send_message(chat_id=family_chat_id, text=report_text)
except TelegramForbiddenError:
logger.info("Бот не может писать в семейный чат %s", family_chat_id)
await database.mark_family_report_sent(household_id, local_date, now)
async def stop_task(task: asyncio.Task) -> None:
task.cancel()
with suppress(asyncio.CancelledError):
await task