Files
md2vk/app/vk_client.py
T

108 lines
3.7 KiB
Python
Raw 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.
"""VK API клиент — вызов wall.post с format_data."""
from __future__ import annotations
import json
from typing import Optional
import httpx
from app.config import settings
from app.converters.markdown_to_vk import FormatItem, format_data_json
class VkApiError(Exception):
"""Ошибка VK API. Содержит код ошибки и описание."""
def __init__(self, error_code: int, error_msg: str):
self.error_code = error_code
self.error_msg = error_msg
super().__init__(f"VK API error #{error_code}: {error_msg}")
class VkClient:
"""HTTP-клиент для VK API."""
BASE_URL = "https://api.vk.com/method"
def __init__(self, access_token: str):
self.access_token = access_token
self._client = httpx.AsyncClient(timeout=30.0)
async def close(self):
await self._client.aclose()
async def wall_post(
self,
message: str,
owner_id: Optional[int] = None,
from_group: bool = False,
friends_only: bool = False,
publish_date: Optional[int] = None,
attachments: Optional[str] = None,
signed: Optional[bool] = None,
format_data: Optional[list[FormatItem]] = None,
) -> dict:
"""Публикует запись на стене через wall.post.
Возвращает ответ VK API с полями post_id, owner_id.
"""
params = {
"access_token": self.access_token,
"v": settings.vk_api_version,
"message": message,
}
if owner_id is not None:
params["owner_id"] = owner_id
if from_group:
params["from_group"] = 1
if friends_only:
params["friends_only"] = 1
if publish_date is not None:
params["publish_date"] = publish_date
if attachments is not None:
params["attachments"] = attachments
if signed is not None:
params["signed"] = 1 if signed else 0
if format_data:
params["format_data"] = format_data_json(format_data)
response = await self._client.post(f"{self.BASE_URL}/wall.post", data=params)
data = response.json()
if "error" in data:
err = data["error"]
raise VkApiError(
error_code=err.get("error_code", 0),
error_msg=err.get("error_msg", "Unknown error"),
)
return data.get("response", {})
async def users_get(self, user_ids: str) -> list[dict]:
"""Получает информацию о пользователе (для проверки токена/имени)."""
params = {
"access_token": self.access_token,
"v": settings.vk_api_version,
"user_ids": user_ids,
}
response = await self._client.post(f"{self.BASE_URL}/users.get", data=params)
data = response.json()
if "error" in data:
err = data["error"]
raise VkApiError(
error_code=err.get("error_code", 0),
error_msg=err.get("error_msg", "Unknown error"),
)
return data.get("response", [])
async def check_token(self) -> tuple[bool, str]:
"""Проверяет валидность токена. Возвращает (is_valid, display_name)."""
try:
users = await self.users_get("")
if users:
name = f"{users[0].get('first_name', '')} {users[0].get('last_name', '')}".strip()
return True, name or "Unknown"
return False, "Token invalid"
except VkApiError:
return False, "Token invalid or expired"