225 lines
8.3 KiB
Python
225 lines
8.3 KiB
Python
from aiogram import Router, types
|
||
from aiogram.filters import Command
|
||
from aiogram.types import Message, CallbackQuery
|
||
from aiogram.enums.parse_mode import ParseMode
|
||
import logging
|
||
import aiohttp
|
||
|
||
from instences.config import BASE_URL_FASTAPI
|
||
from keyboard.keyboards import main_keyboard
|
||
from .referrals import _build_referral_text
|
||
|
||
router = Router()
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
async def call_api(method: str, endpoint: str, data=None):
|
||
"""
|
||
Выполняет HTTP-запрос к FastAPI.
|
||
Возвращает:
|
||
- dict при 200/201
|
||
- None при 404
|
||
- "ERROR" при остальных ошибках
|
||
"""
|
||
url = f"{BASE_URL_FASTAPI}{endpoint}"
|
||
logger.info(f"[start] Запрос: {method} {url} с данными {data}")
|
||
|
||
try:
|
||
async with aiohttp.ClientSession() as session:
|
||
async with session.request(method, url, json=data) as response:
|
||
logger.info(
|
||
f"[start] Ответ от {url}: статус {response.status}"
|
||
)
|
||
|
||
if response.status in {200, 201}:
|
||
result = await response.json()
|
||
logger.debug(f"[start] Ответ JSON: {result}")
|
||
return result
|
||
if response.status == 404:
|
||
logger.debug("[start] Получен 404, возвращаю None")
|
||
return None
|
||
|
||
logger.error(
|
||
f"[start] Ошибка в запросе: статус {response.status}, "
|
||
f"причина {response.reason}"
|
||
)
|
||
return "ERROR"
|
||
except Exception as e:
|
||
logger.exception(f"[start] Исключение при запросе к {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_<telegram_id> из /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 c обработкой реферального параметра.
|
||
"""
|
||
user_id = message.from_user.id
|
||
username = message.from_user.username
|
||
referrer_id = _parse_referrer_id(message)
|
||
|
||
logger.info(
|
||
f"[start] Команда /start от {user_id} (@{username}), "
|
||
f"text={message.text!r}, referrer_id={referrer_id}"
|
||
)
|
||
|
||
try:
|
||
# 1. Проверяем, есть ли пользователь в БД
|
||
existing = await call_api("GET", f"/user/{user_id}")
|
||
user_exists = existing not in (None, "ERROR")
|
||
|
||
# 2. Если пользователя нет — создаём
|
||
if not user_exists:
|
||
logger.debug(f"[start] Пользователь {user_id} не найден, создаю.")
|
||
create_result = await call_api(
|
||
"POST",
|
||
"/user/create",
|
||
{"telegram_id": user_id},
|
||
)
|
||
if create_result == "ERROR":
|
||
logger.error(
|
||
f"[start] Не удалось создать пользователя {user_id} в БД"
|
||
)
|
||
|
||
# 3. Обработка рефералки, если параметр есть
|
||
if referrer_id is not None:
|
||
# 3.1. Самореферал
|
||
if referrer_id == user_id:
|
||
logger.info(
|
||
f"[start] Пользователь {user_id} попытался зайти "
|
||
f"по своей реферальной ссылке."
|
||
)
|
||
await message.answer(
|
||
"Нельзя переходить по своей же реферальной ссылке."
|
||
)
|
||
|
||
# 3.2. Пользователь уже зарегистрирован в боте
|
||
elif user_exists:
|
||
logger.info(
|
||
f"[start] Пользователь {user_id} уже есть в БД, "
|
||
f"реферальная ссылка {referrer_id} не сработает."
|
||
)
|
||
await message.answer(
|
||
"Вы уже зарегистрированы в боте, "
|
||
"реферальная ссылка не сработает."
|
||
)
|
||
|
||
# 3.3. Новый пользователь + чужая рефералка → регистрируем реферал
|
||
else:
|
||
payload = {
|
||
"referrer_id": referrer_id,
|
||
"telegram_id": user_id,
|
||
}
|
||
logger.info(
|
||
f"[start] Фиксирую реферала в бекенде: {payload}"
|
||
)
|
||
result = await call_api(
|
||
"POST",
|
||
"/user/referrals/track",
|
||
payload,
|
||
)
|
||
if result == "ERROR":
|
||
logger.error(
|
||
f"[start] Ошибка при фиксации реферала: {payload}"
|
||
)
|
||
await message.answer(
|
||
"Вы вошли по реферальной ссылке."
|
||
)
|
||
|
||
# 4. В любом случае показываем главное меню
|
||
await message.answer(
|
||
_welcome_text(username),
|
||
reply_markup=main_keyboard(),
|
||
)
|
||
logger.info(f"[start] Главное меню отправлено пользователю {user_id}.")
|
||
|
||
except Exception as e:
|
||
logger.exception(
|
||
f"[start] Ошибка при обработке /start для пользователя {user_id}: {e}"
|
||
)
|
||
await message.answer("Произошла ошибка. Попробуйте позже.")
|
||
|
||
|
||
@router.message(Command("referrals"))
|
||
async def referrals_menu_command(message: Message):
|
||
"""
|
||
Команда /referrals из бокового меню Telegram.
|
||
Показывает текст реферальной программы.
|
||
"""
|
||
logger.info(
|
||
f"[start] Команда /referrals от {message.from_user.id} "
|
||
f"(@{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("[start] Реферальная программа отправлена пользователю.")
|
||
except Exception as e:
|
||
logger.exception(
|
||
f"[start] Ошибка при обработке /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 с data="base" — возврат в главное меню.
|
||
"""
|
||
try:
|
||
user_id = callback.from_user.id
|
||
username = callback.from_user.username
|
||
logger.info(f"[start] callback 'base' от {user_id} (@{username})")
|
||
|
||
user_data = await call_api("GET", f"/user/{user_id}")
|
||
if user_data in (None, "ERROR"):
|
||
await call_api(
|
||
"POST",
|
||
"/user/create",
|
||
{"telegram_id": user_id},
|
||
)
|
||
|
||
await callback.message.edit_text(
|
||
_welcome_text(username),
|
||
reply_markup=main_keyboard(),
|
||
)
|
||
except Exception as e:
|
||
logger.exception(f"[start] Ошибка при обработке callback 'base': {e}")
|
||
await callback.message.answer("Произошла ошибка. Попробуйте позже.")
|
||
finally:
|
||
await callback.answer()
|