70 lines
2.4 KiB
Python
70 lines
2.4 KiB
Python
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}"
|
||
}
|