shopbot/bot/services/broadcast.py
2024-12-25 04:30:33 +03:00

70 lines
2.4 KiB
Python
Raw Permalink 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.

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}"
}