diff --git a/api/v1/routes/management.py b/api/v1/routes/management.py index 83a15929..cf7b2e5e 100644 --- a/api/v1/routes/management.py +++ b/api/v1/routes/management.py @@ -15,8 +15,8 @@ from sqlalchemy import distinct, exists, func, select, update from sqlalchemy.ext.asyncio import AsyncSession from api.depends import get_session, verify_admin_token, verify_admin_token_short -from database import async_session_maker from config import API_TOKEN, BOT_SERVICE +from database import async_session_maker, save_blocked_user_ids from core.bootstrap import MANAGEMENT_CONFIG from core.executor import run_io from core.settings.management_config import update_management_config @@ -212,6 +212,13 @@ async def launch_broadcast( rate = max(1, min(int(payload.messages_per_second or 35), 60)) broadcast_service = BroadcastService(bot=bot, session=None, messages_per_second=rate) stats = await broadcast_service.broadcast(messages, workers=workers) + blocked_ids = stats.get("blocked_user_ids") or [] + if blocked_ids: + async with async_session_maker() as session: + try: + await save_blocked_user_ids(session, blocked_ids) + except Exception: + pass return { "success": True, "message": "Broadcast completed", diff --git a/api/v2/routes/management.py b/api/v2/routes/management.py index a2459b34..2e524036 100644 --- a/api/v2/routes/management.py +++ b/api/v2/routes/management.py @@ -15,8 +15,8 @@ from sqlalchemy import distinct, exists, func, select, update from sqlalchemy.ext.asyncio import AsyncSession from api.depends import get_session, verify_identity_admin, verify_identity_admin_short -from database import async_session_maker from config import API_TOKEN, BOT_SERVICE +from database import async_session_maker, save_blocked_user_ids from core.bootstrap import MANAGEMENT_CONFIG from core.executor import run_io from core.settings.management_config import update_management_config @@ -201,6 +201,13 @@ async def launch_broadcast( rate = max(1, min(int(payload.messages_per_second or 35), 60)) broadcast_service = BroadcastService(bot=bot, session=None, messages_per_second=rate) stats = await broadcast_service.broadcast(messages, workers=workers) + blocked_ids = stats.get("blocked_user_ids") or [] + if blocked_ids: + async with async_session_maker() as session: + try: + await save_blocked_user_ids(session, blocked_ids) + except Exception: + pass return { "success": True, "message": "Broadcast completed", diff --git a/database/bans.py b/database/bans.py index 876ab675..ad68a2f5 100644 --- a/database/bans.py +++ b/database/bans.py @@ -2,9 +2,21 @@ from sqlalchemy.dialects.postgresql import insert from sqlalchemy.ext.asyncio import AsyncSession from database.models import BlockedUser +from logger import logger async def create_blocked_user(session: AsyncSession, tg_id: int): stmt = insert(BlockedUser).values(tg_id=tg_id).on_conflict_do_nothing(index_elements=[BlockedUser.tg_id]) await session.execute(stmt) await session.commit() + + +async def save_blocked_user_ids(session: AsyncSession, tg_ids: list[int]) -> None: + """Вставка списка tg_id в таблицу BlockedUser. Вызывать только из основного event loop.""" + if not tg_ids: + return + values = [{"tg_id": tg_id} for tg_id in tg_ids] + stmt = insert(BlockedUser).values(values).on_conflict_do_nothing(index_elements=[BlockedUser.tg_id]) + await session.execute(stmt) + await session.commit() + logger.info(f"📝 Добавлено {len(tg_ids)} пользователей в blocked_users") diff --git a/handlers/admin/sender/sender_handler.py b/handlers/admin/sender/sender_handler.py index d83c6438..e75d2b3d 100644 --- a/handlers/admin/sender/sender_handler.py +++ b/handlers/admin/sender/sender_handler.py @@ -9,6 +9,7 @@ from sqlalchemy.ext.asyncio import AsyncSession from config import API_TOKEN from core.executor import run_io, should_run_heavy_tasks_separately +from database import save_blocked_user_ids from database.models import Server from filters.admin import IsAdminFilter from logger import logger @@ -230,6 +231,11 @@ async def handle_broadcast_confirm(callback_query: CallbackQuery, state: FSMCont state_keyboard_data, progress_cb, ) + if stats.get("blocked_user_ids"): + try: + await save_blocked_user_ids(session, stats["blocked_user_ids"]) + except Exception as e: + logger.error(f"❌ Ошибка при сохранении заблокированных пользователей: {e}") else: messages = [] for tg_id in tg_ids: diff --git a/handlers/admin/sender/sender_service.py b/handlers/admin/sender/sender_service.py index 7093676a..e75d4d24 100644 --- a/handlers/admin/sender/sender_service.py +++ b/handlers/admin/sender/sender_service.py @@ -12,7 +12,7 @@ from aiogram.exceptions import TelegramBadRequest, TelegramForbiddenError, Teleg from aiogram.types import InlineKeyboardMarkup from sqlalchemy.ext.asyncio import AsyncSession -from database import async_session_maker +from database import async_session_maker, save_blocked_user_ids from logger import logger @@ -208,24 +208,12 @@ class BroadcastService: async def _save_blocked_users(self) -> None: if not self.blocked_users: return - - async def _do_save(session: AsyncSession) -> None: - from sqlalchemy.dialects.postgresql import insert - - from database.models import BlockedUser - - values = [{"tg_id": tg_id} for tg_id in self.blocked_users] - stmt = insert(BlockedUser).values(values).on_conflict_do_nothing(index_elements=[BlockedUser.tg_id]) - await session.execute(stmt) - await session.commit() - logger.info(f"📝 Добавлено {len(self.blocked_users)} пользователей в blocked_users") - try: if self._session is not None: - await _do_save(self._session) + await save_blocked_user_ids(self._session, list(self.blocked_users)) else: async with async_session_maker() as session: - await _do_save(session) + await save_blocked_user_ids(session, list(self.blocked_users)) except Exception as e: logger.error(f"❌ Ошибка при сохранении заблокированных пользователей: {e}") if self._session is not None: @@ -316,7 +304,8 @@ class BroadcastService: await asyncio.gather(*worker_tasks, delayed_task, return_exceptions=True) - await self._save_blocked_users() + if self._session is not None: + await self._save_blocked_users() end_time = time.time() total_duration = end_time - self.start_time @@ -331,6 +320,7 @@ class BroadcastService: "avg_speed": avg_speed, "total_messages": len(messages), "blocked_users": len(self.blocked_users), + "blocked_user_ids": list(self.blocked_users), } logger.info(