135 lines
4.8 KiB
Python
135 lines
4.8 KiB
Python
import logging
|
||
from typing import Any
|
||
|
||
import aiohttp
|
||
|
||
from config import settings
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class APIClient:
|
||
"""HTTP client for backend API communication."""
|
||
|
||
def __init__(self):
|
||
self.base_url = settings.API_URL
|
||
self._session: aiohttp.ClientSession | None = None
|
||
logger.info(f"[APIClient] Initialized with base_url: {self.base_url}")
|
||
|
||
async def _get_session(self) -> aiohttp.ClientSession:
|
||
if self._session is None or self._session.closed:
|
||
logger.info("[APIClient] Creating new aiohttp session")
|
||
self._session = aiohttp.ClientSession()
|
||
return self._session
|
||
|
||
async def _request(
|
||
self,
|
||
method: str,
|
||
endpoint: str,
|
||
**kwargs
|
||
) -> dict[str, Any] | None:
|
||
"""Make HTTP request to backend API."""
|
||
session = await self._get_session()
|
||
url = f"{self.base_url}/api/v1{endpoint}"
|
||
|
||
# Add bot secret header for authentication
|
||
headers = kwargs.pop("headers", {})
|
||
if settings.BOT_API_SECRET:
|
||
headers["X-Bot-Secret"] = settings.BOT_API_SECRET
|
||
|
||
logger.info(f"[APIClient] {method} {url}")
|
||
if 'json' in kwargs:
|
||
logger.info(f"[APIClient] Request body: {kwargs['json']}")
|
||
if 'params' in kwargs:
|
||
logger.info(f"[APIClient] Request params: {kwargs['params']}")
|
||
|
||
try:
|
||
async with session.request(method, url, headers=headers, **kwargs) as response:
|
||
logger.info(f"[APIClient] Response status: {response.status}")
|
||
response_text = await response.text()
|
||
logger.info(f"[APIClient] Response body: {response_text[:500]}")
|
||
|
||
if response.status == 200:
|
||
import json
|
||
return json.loads(response_text)
|
||
elif response.status == 404:
|
||
logger.warning(f"[APIClient] 404 Not Found")
|
||
return None
|
||
else:
|
||
logger.error(f"[APIClient] API error {response.status}: {response_text}")
|
||
return {"error": response_text}
|
||
except aiohttp.ClientError as e:
|
||
logger.error(f"[APIClient] Request failed: {e}")
|
||
return {"error": str(e)}
|
||
except Exception as e:
|
||
logger.error(f"[APIClient] Unexpected error: {e}")
|
||
return {"error": str(e)}
|
||
|
||
async def confirm_telegram_link(
|
||
self,
|
||
token: str,
|
||
telegram_id: int,
|
||
telegram_username: str | None,
|
||
telegram_first_name: str | None = None,
|
||
telegram_last_name: str | None = None,
|
||
telegram_avatar_url: str | None = None
|
||
) -> dict[str, Any]:
|
||
"""Confirm Telegram account linking."""
|
||
result = await self._request(
|
||
"POST",
|
||
"/telegram/confirm-link",
|
||
json={
|
||
"token": token,
|
||
"telegram_id": telegram_id,
|
||
"telegram_username": telegram_username,
|
||
"telegram_first_name": telegram_first_name,
|
||
"telegram_last_name": telegram_last_name,
|
||
"telegram_avatar_url": telegram_avatar_url
|
||
}
|
||
)
|
||
return result or {"error": "Не удалось связаться с сервером"}
|
||
|
||
async def get_user_by_telegram_id(self, telegram_id: int) -> dict[str, Any] | None:
|
||
"""Get user by Telegram ID."""
|
||
return await self._request("GET", f"/telegram/user/{telegram_id}")
|
||
|
||
async def unlink_telegram(self, telegram_id: int) -> dict[str, Any]:
|
||
"""Unlink Telegram account."""
|
||
result = await self._request(
|
||
"POST",
|
||
f"/telegram/unlink/{telegram_id}"
|
||
)
|
||
return result or {"error": "Не удалось связаться с сервером"}
|
||
|
||
async def get_user_marathons(self, telegram_id: int) -> list[dict[str, Any]]:
|
||
"""Get user's marathons."""
|
||
result = await self._request("GET", f"/telegram/marathons/{telegram_id}")
|
||
if isinstance(result, list):
|
||
return result
|
||
return result.get("marathons", []) if result else []
|
||
|
||
async def get_marathon_details(
|
||
self,
|
||
marathon_id: int,
|
||
telegram_id: int
|
||
) -> dict[str, Any] | None:
|
||
"""Get marathon details for user."""
|
||
return await self._request(
|
||
"GET",
|
||
f"/telegram/marathon/{marathon_id}",
|
||
params={"telegram_id": telegram_id}
|
||
)
|
||
|
||
async def get_user_stats(self, telegram_id: int) -> dict[str, Any] | None:
|
||
"""Get user's overall statistics."""
|
||
return await self._request("GET", f"/telegram/stats/{telegram_id}")
|
||
|
||
async def close(self):
|
||
"""Close the HTTP session."""
|
||
if self._session and not self._session.closed:
|
||
await self._session.close()
|
||
|
||
|
||
# Global API client instance
|
||
api_client = APIClient()
|