import asyncio import logging from aiogram.exceptions import ( TelegramAPIError, TelegramForbiddenError, TelegramRetryAfter, ) from aiogram_dialog import DialogManager from sqlalchemy import select from sqlalchemy.exc import SQLAlchemyError from shopbot.bot.database.db import async_session from shopbot.bot.database.models import UserModel async def _send_message_with_retry( bot, user_id: int, message_text: str, max_retries=3, delay=1 ) -> bool: for attempt in range(max_retries): try: await bot.send_message(user_id, message_text) return True except TelegramRetryAfter as e: logging.warning( f"Retry after {e.retry_after} for user {user_id}, attempt {attempt + 1}" ) await asyncio.sleep(e.retry_after) except TelegramForbiddenError: logging.warning(f"User {user_id} blocked bot") return False except TelegramAPIError as e: logging.error(f"Error sending message to user {user_id}: {e}") return False logging.error(f"Max retries exceeded for user {user_id}") return False async def broadcast_message(dialog_manager: DialogManager, **kwargs): message_text = dialog_manager.find("message_input").get_value() bot = dialog_manager.event.bot try: async with async_session() as session: async with session.begin(): result = await session.execute(select(UserModel.tg_id)) user_ids = [row[0] for row in result] total_users = len(user_ids) tasks = [ _send_message_with_retry(bot, user_id, message_text) for user_id in user_ids ] results = await asyncio.gather(*tasks) total_sent = sum(results) total_failed = total_users - total_sent except SQLAlchemyError as e: logging.error(f"Database error: {e}") return {"progress": f"❌ Ошибка при работе с базой данных: {e}"} except Exception as e: logging.error(f"An unexpected error occurred: {e}") return {"progress": f"❌ Непредвиденная ошибка: {e}"} return { "progress": f"✅ Рассылка завершена!\n" f"📨 Всего отправлено: {total_sent}/{total_users}\n" f"❌ Ошибок: {total_failed}" }