191 lines
6.7 KiB
Python
191 lines
6.7 KiB
Python
from aiogram import Router, types
|
||
from aiogram.types import Message, CallbackQuery
|
||
from aiogram.filters import Command
|
||
from aiogram.enums.parse_mode import ParseMode
|
||
import logging
|
||
import aiohttp
|
||
|
||
from instences.config import BASE_URL_FASTAPI
|
||
from keyboard.keyboards import main_keyboard
|
||
|
||
router = Router()
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
async def call_api(method: str, endpoint: str, data: dict | None = None):
|
||
"""
|
||
Мини-обёртка для запросов к backend API.
|
||
"""
|
||
url = f"{BASE_URL_FASTAPI}{endpoint}"
|
||
logger.info(f"API {method} {url} data={data}")
|
||
|
||
try:
|
||
async with aiohttp.ClientSession() as session:
|
||
async with session.request(method, url, json=data) as resp:
|
||
logger.info(f"API response {resp.status} {resp.reason}")
|
||
if resp.status in {200, 201}:
|
||
return await resp.json()
|
||
if resp.status in {400, 404}:
|
||
try:
|
||
return await resp.json()
|
||
except Exception:
|
||
return None
|
||
logger.error(f"Unexpected status {resp.status}: {await resp.text()}")
|
||
return "ERROR"
|
||
except Exception as e:
|
||
logger.exception(f"API exception {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 обработкой реферального параметра.
|
||
|
||
Логика:
|
||
- проверяем, есть ли пользователь в БД по telegram_id;
|
||
- если нет — создаём /user/create;
|
||
- если есть корректный ref_ и пользователь новый — вызываем
|
||
/user/{referrer_id}/add_referral с invited_id = telegram_id.
|
||
"""
|
||
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:
|
||
existing = await call_api("GET", f"/user/{user_id}")
|
||
user_exists = existing not in (None, "ERROR")
|
||
|
||
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} в БД"
|
||
)
|
||
|
||
if referrer_id is not None:
|
||
if referrer_id == user_id:
|
||
logger.info(
|
||
f"[start] Пользователь {user_id} попытался зайти "
|
||
f"по своей реферальной ссылке."
|
||
)
|
||
await message.answer(
|
||
"Нельзя переходить по своей же реферальной ссылке."
|
||
)
|
||
|
||
elif user_exists:
|
||
logger.info(
|
||
f"[start] Пользователь {user_id} уже есть в БД, "
|
||
f"реферальная ссылка {referrer_id} не сработает."
|
||
)
|
||
await message.answer(
|
||
"Вы уже зарегистрированы в боте, "
|
||
"реферальная ссылка не сработает."
|
||
)
|
||
|
||
else:
|
||
payload = {
|
||
"invited_id": user_id,
|
||
}
|
||
logger.info(
|
||
f"[start] Фиксирую реферала в бекенде: "
|
||
f"referrer_id={referrer_id}, payload={payload}"
|
||
)
|
||
result = await call_api(
|
||
"POST",
|
||
f"/user/{referrer_id}/add_referral",
|
||
payload,
|
||
)
|
||
if result == "ERROR":
|
||
logger.error(
|
||
f"[start] Ошибка при фиксации реферала через "
|
||
f"/user/{referrer_id}/add_referral: referrer={referrer_id}, "
|
||
f"invited={user_id}"
|
||
)
|
||
await message.answer("Вы вошли по реферальной ссылке.")
|
||
|
||
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.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()
|