Files
game-marathon/bot/services/api_client.py
2025-12-18 17:15:21 +07:00

135 lines
4.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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()