diff --git a/app/handlers/start.py b/app/handlers/start.py index 2df57ece..80ec4da7 100644 --- a/app/handlers/start.py +++ b/app/handlers/start.py @@ -222,12 +222,28 @@ async def _continue_registration_after_language( async def cmd_start(message: types.Message, state: FSMContext, db: AsyncSession, db_user=None): logger.info(f"🚀 START: Обработка /start от {message.from_user.id}") - + + data = await state.get_data() or {} + pending_start_payload = data.pop("pending_start_payload", None) + referral_code = None campaign = None start_args = message.text.split() + start_parameter = None + if len(start_args) > 1: start_parameter = start_args[1] + elif pending_start_payload: + start_parameter = pending_start_payload + logger.info( + "📦 START: Используем сохраненный payload '%s'", + pending_start_payload, + ) + + if pending_start_payload is not None: + await state.set_data(data) + + if start_parameter: campaign = await get_campaign_by_start_parameter( db, start_parameter, @@ -1409,6 +1425,41 @@ async def required_sub_channel_check( try: state_data = await state.get_data() or {} + pending_start_payload = state_data.pop("pending_start_payload", None) + state_updated = pending_start_payload is not None + + if pending_start_payload: + logger.info( + "📦 CHANNEL CHECK: Найден сохраненный payload '%s'", + pending_start_payload, + ) + + if "campaign_id" not in state_data and "referral_code" not in state_data: + campaign = await get_campaign_by_start_parameter( + db, + pending_start_payload, + only_active=True, + ) + + if campaign: + state_data["campaign_id"] = campaign.id + logger.info( + "📣 CHANNEL CHECK: Кампания %s восстановлена из payload", + campaign.id, + ) + else: + state_data["referral_code"] = pending_start_payload + logger.info( + "🎯 CHANNEL CHECK: Payload интерпретирован как реферальный код", + ) + else: + logger.debug( + "ℹ️ CHANNEL CHECK: Payload уже обработан ранее, пропускаем восстановление", + ) + + if state_updated: + await state.set_data(state_data) + user = db_user if not user: user = await get_user_by_telegram_id(db, query.from_user.id) diff --git a/app/middlewares/channel_checker.py b/app/middlewares/channel_checker.py index 65026540..b2469563 100644 --- a/app/middlewares/channel_checker.py +++ b/app/middlewares/channel_checker.py @@ -1,5 +1,5 @@ import logging -from typing import Callable, Dict, Any, Awaitable +from typing import Callable, Dict, Any, Awaitable, Optional from aiogram import BaseMiddleware, Bot from aiogram.exceptions import TelegramForbiddenError, TelegramBadRequest from aiogram.fsm.context import FSMContext @@ -86,16 +86,16 @@ class ChannelCheckerMiddleware(BaseMiddleware): return await handler(event, data) is_required = settings.CHANNEL_IS_REQUIRED_SUB - + if not is_required: logger.debug("⚠️ Обязательная подписка отключена, пропускаем проверку") return await handler(event, data) channel_link = settings.CHANNEL_LINK - + try: member = await bot.get_chat_member(chat_id=channel_id, user_id=telegram_id) - + if member.status in self.GOOD_MEMBER_STATUS: return await handler(event, data) elif member.status in self.BAD_MEMBER_STATUS: @@ -104,6 +104,8 @@ class ChannelCheckerMiddleware(BaseMiddleware): if telegram_id: await self._deactivate_trial_subscription(telegram_id) + await self._capture_start_payload(state, event) + if isinstance(event, CallbackQuery) and event.data == "sub_channel_check": await event.answer("❌ Вы еще не подписались на канал! Подпишитесь и попробуйте снова.", show_alert=True) return @@ -111,10 +113,12 @@ class ChannelCheckerMiddleware(BaseMiddleware): return await self._deny_message(event, bot, channel_link) else: logger.warning(f"⚠️ Неожиданный статус пользователя {telegram_id}: {member.status}") + await self._capture_start_payload(state, event) return await self._deny_message(event, bot, channel_link) - + except TelegramForbiddenError as e: logger.error(f"❌ Бот заблокирован в канале {channel_id}: {e}") + await self._capture_start_payload(state, event) return await self._deny_message(event, bot, channel_link) except TelegramBadRequest as e: if "chat not found" in str(e).lower(): @@ -123,11 +127,45 @@ class ChannelCheckerMiddleware(BaseMiddleware): logger.error(f"❌ Пользователь {telegram_id} не найден: {e}") else: logger.error(f"❌ Ошибка запроса к каналу {channel_id}: {e}") + await self._capture_start_payload(state, event) return await self._deny_message(event, bot, channel_link) except Exception as e: logger.error(f"❌ Неожиданная ошибка при проверке подписки: {e}") return await handler(event, data) + async def _capture_start_payload(self, state: Optional[FSMContext], event: TelegramObject) -> None: + if not state: + return + + message: Optional[Message] = None + if isinstance(event, Message): + message = event + elif isinstance(event, CallbackQuery): + message = event.message + elif isinstance(event, Update): + message = event.message + + if not message or not message.text: + return + + text = message.text.strip() + if not text.startswith("/start"): + return + + parts = text.split(maxsplit=1) + if len(parts) < 2 or not parts[1]: + return + + payload = parts[1] + + data = await state.get_data() or {} + if data.get("pending_start_payload") == payload: + return + + data["pending_start_payload"] = payload + await state.set_data(data) + logger.debug("💾 Сохранен start payload %s для последующей обработки", payload) + async def _deactivate_trial_subscription(self, telegram_id: int) -> None: async for db in get_db(): try: