Files

139 lines
4.3 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.
"""
API-клиент для MAX Platform (Мой Мир / VK).
Основные методы:
- send_message: отправка сообщений (с логированием outgoing BotMessage)
- subscribe_webhook: регистрация webhook-а
- answer_callback: ответ на callback-кнопки
"""
import logging
import httpx
from typing import Optional
from app.config import settings
logger = logging.getLogger(__name__)
TEST_USER_THRESHOLD = 999999000
class MaxAPI:
"""Клиент MAX Platform API с поддержкой mock-реплик для тестов."""
def __init__(self):
self.base_url = settings.max_api_base
self.token = settings.max_token
self.client = httpx.AsyncClient(timeout=30.0)
self.mock_replies: list = []
def _headers(self) -> dict:
return {
"Authorization": self.token,
"Content-Type": "application/json",
}
async def get_me(self) -> dict:
r = await self.client.get(f"{self.base_url}/me", headers=self._headers())
r.raise_for_status()
return r.json()
async def subscribe_webhook(self) -> dict:
payload = {
"url": settings.webhook_url,
"update_types": settings.update_types,
"secret": settings.webhook_secret,
}
r = await self.client.post(
f"{self.base_url}/subscriptions",
headers=self._headers(),
json=payload,
)
r.raise_for_status()
return r.json()
async def unsubscribe_webhook(self) -> dict:
r = await self.client.delete(
f"{self.base_url}/subscriptions",
headers=self._headers(),
)
r.raise_for_status()
return r.json()
async def get_subscriptions(self) -> dict:
r = await self.client.get(
f"{self.base_url}/subscriptions",
headers=self._headers(),
)
r.raise_for_status()
return r.json()
async def send_message(
self,
user_id: int,
text: str,
attachments: Optional[list] = None,
format: Optional[str] = None,
notify: bool = True,
conversation_id: int = None,
) -> dict:
"""Отправка сообщения. Если conversation_id — сохраняет outgoing BotMessage."""
if user_id >= TEST_USER_THRESHOLD:
logger.info(f"MOCK send_message to test user {user_id}: {text[:50]}")
self.mock_replies.append(text)
return {"ok": True, "mock": True, "text": text}
payload = {"text": text, "notify": notify}
if attachments:
payload["attachments"] = attachments
if format:
payload["format"] = format
r = await self.client.post(
f"{self.base_url}/messages?user_id={user_id}",
headers=self._headers(),
json=payload,
)
r.raise_for_status()
if conversation_id:
from app.database import async_session
from app.models import BotMessage
from app.handlers.greeting import moscow_now
async with async_session() as db:
db.add(BotMessage(
conversation_id=conversation_id,
direction="outgoing",
text=text,
created_at=moscow_now(),
))
await db.commit()
return r.json()
async def answer_callback(
self,
callback_id: str,
message: Optional[dict] = None,
notification: Optional[str] = None,
) -> dict:
payload = {}
if message is not None:
payload["message"] = message
if notification is not None:
payload["notification"] = notification
r = await self.client.post(
f"{self.base_url}/answers?callback_id={callback_id}",
headers=self._headers(),
json=payload,
)
r.raise_for_status()
return r.json()
def pop_mock_replies(self) -> list:
replies = list(self.mock_replies)
self.mock_replies.clear()
return replies
def is_test_user(self, user_id: int) -> bool:
return user_id >= TEST_USER_THRESHOLD
async def close(self):
await self.client.aclose()
max_api = MaxAPI()