from aiogram import Router, types from aiogram.filters import Command from aiogram.types import Message, CallbackQuery import logging from instences.config import BASE_URL_FASTAPI import aiohttp from keyboard.keyboards import main_keyboard from aiogram.enums.parse_mode import ParseMode from .referrals import _build_referral_text router = Router() logger = logging.getLogger(__name__) async def call_api(method, endpoint, data=None): """ Выполняет HTTP-запрос к FastAPI. :param method: HTTP метод (GET, POST, и т.д.) :param endpoint: конечная точка API :param data: тело запроса (если необходимо) :return: JSON-ответ или "ERROR" при неуспехе """ url = f"{BASE_URL_FASTAPI}{endpoint}" logger.info(f"Инициализация запроса: {method} {url} с данными {data}") try: async with aiohttp.ClientSession() as session: async with session.request(method, url, json=data) as response: logger.info( f"Получен ответ от {url}: статус {response.status}" ) if response.status in {200, 201}: result = await response.json() logger.debug(f"Ответ JSON: {result}") return result if response.status == 404: logger.debug(f"Код {response.status}, возвращаю ничего") return None logger.error( f"Ошибка в запросе: статус {response.status}, причина {response.reason}" ) return "ERROR" except Exception as e: logger.exception(f"Исключение при выполнении запроса к {url}: {e}") return "ERROR" def _welcome_text(username: str | None) -> str: """ Текст приветствия в /start и в главном меню. Имя пока не используем — оставляем сигнатуру на будущее. """ return "🥚 Lark Security\n\nВыберите действие из меню ниже." def _parse_referrer_id(message: Message) -> int | None: """ Достаём ref_ из /start. Примеры: /start /start ref_123456789 """ text = message.text or "" parts = text.split(maxsplit=1) if len(parts) < 2: return None arg = parts[1].strip() if not arg.startswith("ref_"): return None raw_id = arg[4:] if not raw_id.isdigit(): return None try: return int(raw_id) except ValueError: return None @router.message(Command("start")) async def start_command(message: Message): """ Обработчик команды /start. Добавлена обработка реферального payload'а ref_. """ logger.info( f"Получена команда /start от пользователя: " f"{message.from_user.id} ({message.from_user.username}) | text='{message.text}'" ) user_id = message.from_user.id referrer_id = _parse_referrer_id(message) try: # 1. Проверяем/создаём пользователя в базе user_data = await call_api("GET", f"/user/{user_id}") if not user_data: logger.debug( "Пользователь не найден в базе, создаем новую запись." ) await call_api( "POST", "/user/create", {"telegram_id": user_id}, ) # 2. Если есть реферер и он не сам пользователь — отправляем инфу в бекенд if referrer_id and referrer_id != user_id: payload = { "referrer_id": referrer_id, "telegram_id": user_id, } logger.info( f"Отправка данных о реферале в бекенд: {payload}" ) result = await call_api( "POST", "/user/referrals/track", payload, ) if result == "ERROR": logger.error( f"Не удалось зафиксировать реферала в бекенде: {payload}" ) elif referrer_id == user_id: logger.info( "Обнаружена попытка самореферала, запрос в бекенд не отправляем." ) # 3. Приветственное сообщение и главное меню logger.debug("Отправка приветственного сообщения пользователю.") await message.answer( _welcome_text(message.from_user.username), reply_markup=main_keyboard(), ) logger.info("Приветственное сообщение отправлено.") except Exception as e: logger.exception( f"Ошибка при обработке команды /start для пользователя " f"{user_id}: {e}" ) await message.answer("Произошла ошибка. Попробуйте позже.") @router.message(Command("referrals")) async def referrals_menu_command(message: Message): """ Команда /referrals из бокового меню Telegram. Показывает текст реферальной программы. """ logger.info( f"Получена команда /referrals от пользователя: " f"{message.from_user.id} ({message.from_user.username})" ) try: text = await _build_referral_text(message.bot, message.from_user.id) await message.answer(text, parse_mode=ParseMode.HTML) logger.info("Реферальная программа отправлена пользователю.") except Exception as e: logger.exception( f"Ошибка при обработке команды /referrals для пользователя " f"{message.from_user.id}: {e}" ) await message.answer("Произошла ошибка. Попробуйте позже.") @router.callback_query(lambda callback: callback.data == "base") async def start_callback_handler(callback: CallbackQuery): """ Обработчик callback_query с data="base". Возвращает пользователя в главное меню. """ try: user_data = await call_api("GET", f"/user/{callback.from_user.id}") if not user_data: await call_api( "POST", "/user/create", {"telegram_id": callback.from_user.id}, ) await callback.message.edit_text( _welcome_text(callback.from_user.username), reply_markup=main_keyboard(), ) except Exception as e: logger.exception(f"Ошибка при обработке callback с data='base': {e}") await callback.message.answer("Произошла ошибка. Попробуйте позже.") finally: await callback.answer()