feat: add models part 4
This commit is contained in:
199
itd/client.py
199
itd/client.py
@@ -23,11 +23,16 @@ from itd.models.post import Post, NewPost
|
|||||||
from itd.models.clan import Clan
|
from itd.models.clan import Clan
|
||||||
from itd.models.hashtag import Hashtag
|
from itd.models.hashtag import Hashtag
|
||||||
from itd.models.user import User, UserProfileUpdate, UserPrivacy, UserFollower, UserWhoToFollow
|
from itd.models.user import User, UserProfileUpdate, UserPrivacy, UserFollower, UserWhoToFollow
|
||||||
from itd.models.pagination import Pagination
|
from itd.models.pagination import Pagination, PostsPagintaion, LikedPostsPagintaion
|
||||||
from itd.models.verification import Verification, VerificationStatus
|
from itd.models.verification import Verification, VerificationStatus
|
||||||
|
|
||||||
|
from itd.enums import PostsTab
|
||||||
from itd.request import set_cookies
|
from itd.request import set_cookies
|
||||||
from itd.exceptions import NoCookie, NoAuthData, SamePassword, InvalidOldPassword, NotFound, ValidationError, UserBanned, PendingRequestExists, Forbidden, UsernameTaken, CantFollowYourself, Unauthorized
|
from itd.exceptions import (
|
||||||
|
NoCookie, NoAuthData, SamePassword, InvalidOldPassword, NotFound, ValidationError, UserBanned,
|
||||||
|
PendingRequestExists, Forbidden, UsernameTaken, CantFollowYourself, Unauthorized,
|
||||||
|
CantRepostYourPost, AlreadyReposted
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def refresh_on_error(func):
|
def refresh_on_error(func):
|
||||||
@@ -302,15 +307,15 @@ class Client:
|
|||||||
def verify(self, file_url: str) -> Verification:
|
def verify(self, file_url: str) -> Verification:
|
||||||
"""Отправить запрос на верификацию
|
"""Отправить запрос на верификацию
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
file_url (str): Ссылка на видео
|
file_url (str): Ссылка на видео
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
PendingRequestExists: Запрос уже отправлен
|
PendingRequestExists: Запрос уже отправлен
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Verification: Верификация
|
Verification: Верификация
|
||||||
"""
|
"""
|
||||||
res = verify(self.token, file_url)
|
res = verify(self.token, file_url)
|
||||||
if res.json().get('error', {}).get('code') == 'PENDING_REQUEST_EXISTS':
|
if res.json().get('error', {}).get('code') == 'PENDING_REQUEST_EXISTS':
|
||||||
raise PendingRequestExists()
|
raise PendingRequestExists()
|
||||||
@@ -614,6 +619,20 @@ class Client:
|
|||||||
|
|
||||||
@refresh_on_error
|
@refresh_on_error
|
||||||
def create_post(self, content: str, wall_recipient_id: UUID | None = None, attach_ids: list[UUID] = []) -> NewPost:
|
def create_post(self, content: str, wall_recipient_id: UUID | None = None, attach_ids: list[UUID] = []) -> NewPost:
|
||||||
|
"""Создать пост
|
||||||
|
|
||||||
|
Args:
|
||||||
|
content (str): Содержимое
|
||||||
|
wall_recipient_id (UUID | None, optional): UUID пользователя (чтобы создать пост ему на стене). Defaults to None.
|
||||||
|
attach_ids (list[UUID], optional): UUID вложений. Defaults to [].
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotFound: Пользователь не найден
|
||||||
|
ValidationError: Ошибка валидации
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
NewPost: Новый пост
|
||||||
|
"""
|
||||||
res = create_post(self.token, content, wall_recipient_id, attach_ids)
|
res = create_post(self.token, content, wall_recipient_id, attach_ids)
|
||||||
if res.json().get('error', {}).get('code') == 'NOT_FOUND':
|
if res.json().get('error', {}).get('code') == 'NOT_FOUND':
|
||||||
raise NotFound('Wall recipient')
|
raise NotFound('Wall recipient')
|
||||||
@@ -624,36 +643,166 @@ class Client:
|
|||||||
return NewPost.model_validate(res.json())
|
return NewPost.model_validate(res.json())
|
||||||
|
|
||||||
@refresh_on_error
|
@refresh_on_error
|
||||||
def get_posts(self, username: str | None = None, limit: int = 20, cursor: int = 0, sort: str = '', tab: str = ''):
|
def get_posts(self, cursor: int = 0, tab: PostsTab = PostsTab.POPULAR) -> tuple[list[Post], PostsPagintaion]:
|
||||||
return get_posts(self.token, username, limit, cursor, sort, tab)
|
"""Получить список постов
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cursor (int, optional): Страница. Defaults to 0.
|
||||||
|
tab (PostsTab, optional): Вкладка (популярное или подписки). Defaults to PostsTab.POPULAR.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[Post]: Список постов
|
||||||
|
Pagination: Пагинация
|
||||||
|
"""
|
||||||
|
res = get_posts(self.token, cursor, tab)
|
||||||
|
res.raise_for_status()
|
||||||
|
data = res.json()['data']
|
||||||
|
|
||||||
|
return [Post.model_validate(post) for post in data['posts']], PostsPagintaion.model_validate(data['pagination'])
|
||||||
|
|
||||||
@refresh_on_error
|
@refresh_on_error
|
||||||
def get_post(self, id: str):
|
def get_post(self, id: UUID) -> Post:
|
||||||
return get_post(self.token, id)
|
"""Получить пост
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id (UUID): UUID поста
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotFound: Пост не найден
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Post: Пост
|
||||||
|
"""
|
||||||
|
res = get_post(self.token, id)
|
||||||
|
if res.json().get('error', {}).get('code') == 'NOT_FOUND':
|
||||||
|
raise NotFound('Post')
|
||||||
|
res.raise_for_status()
|
||||||
|
|
||||||
|
return Post.model_validate(res.json()['data'])
|
||||||
|
|
||||||
@refresh_on_error
|
@refresh_on_error
|
||||||
def edit_post(self, id: str, content: str):
|
def edit_post(self, id: UUID, content: str) -> str:
|
||||||
return edit_post(self.token, id, content)
|
"""Редактировать пост
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id (UUID): UUID поста
|
||||||
|
content (str): Содержимое
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotFound: Пост не найден
|
||||||
|
Forbidden: Нет доступа
|
||||||
|
ValidationError: Ошибка валидации
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Новое содержимое
|
||||||
|
"""
|
||||||
|
res = edit_post(self.token, id, content)
|
||||||
|
|
||||||
|
if res.json().get('error', {}).get('code') == 'NOT_FOUND':
|
||||||
|
raise NotFound('Post')
|
||||||
|
if res.json().get('error', {}).get('code') == 'FORBIDDEN':
|
||||||
|
raise Forbidden('edit post')
|
||||||
|
if res.status_code == 422 and 'found' in res.json():
|
||||||
|
raise ValidationError(*list(res.json()['found'].items())[0])
|
||||||
|
res.raise_for_status()
|
||||||
|
|
||||||
|
return res.json()['content']
|
||||||
|
|
||||||
@refresh_on_error
|
@refresh_on_error
|
||||||
def delete_post(self, id: str):
|
def delete_post(self, id: UUID) -> None:
|
||||||
return delete_post(self.token, id)
|
"""Удалить пост
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id (UUID): UUID поста
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotFound: Пост не найден
|
||||||
|
Forbidden: Нет доступа
|
||||||
|
"""
|
||||||
|
res = delete_post(self.token, id)
|
||||||
|
if res.status_code == 204:
|
||||||
|
return
|
||||||
|
|
||||||
|
if res.json().get('error', {}).get('code') == 'NOT_FOUND':
|
||||||
|
raise NotFound('Post')
|
||||||
|
if res.json().get('error', {}).get('code') == 'FORBIDDEN':
|
||||||
|
raise Forbidden('delete post')
|
||||||
|
res.raise_for_status()
|
||||||
|
|
||||||
@refresh_on_error
|
@refresh_on_error
|
||||||
def pin_post(self, id: str):
|
def pin_post(self, id: UUID):
|
||||||
return pin_post(self.token, id)
|
"""Закрепить пост
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id (UUID): UUID поста
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotFound: Пост не найден
|
||||||
|
Forbidden: Нет доступа
|
||||||
|
"""
|
||||||
|
res = pin_post(self.token, id)
|
||||||
|
|
||||||
|
if res.json().get('error', {}).get('code') == 'NOT_FOUND':
|
||||||
|
raise NotFound('Post')
|
||||||
|
if res.json().get('error', {}).get('code') == 'FORBIDDEN':
|
||||||
|
raise Forbidden('pin post')
|
||||||
|
res.raise_for_status()
|
||||||
|
|
||||||
@refresh_on_error
|
@refresh_on_error
|
||||||
def repost(self, id: str, content: str | None = None):
|
def repost(self, id: UUID, content: str | None = None) -> NewPost:
|
||||||
return repost(self.token, id, content)
|
"""Репостнуть пост
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id (UUID): UUID поста
|
||||||
|
content (str | None, optional): Содержимое (доп. комментарий). Defaults to None.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotFound: Пост не найден
|
||||||
|
AlreadyReposted: Пост уже репостнут
|
||||||
|
CantRepostYourPost: Нельзя репостить самого себя
|
||||||
|
ValidationError: Ошибка валидации
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
NewPost: Новый пост
|
||||||
|
"""
|
||||||
|
res = repost(self.token, id, content)
|
||||||
|
|
||||||
|
if res.json().get('error', {}).get('code') == 'NOT_FOUND':
|
||||||
|
raise NotFound('Post')
|
||||||
|
if res.json().get('error', {}).get('code') == 'CONFLICT':
|
||||||
|
raise AlreadyReposted()
|
||||||
|
if res.status_code == 422 and res.json().get('message') == 'Cannot repost your own post':
|
||||||
|
raise CantRepostYourPost()
|
||||||
|
if res.status_code == 422 and 'found' in res.json():
|
||||||
|
raise ValidationError(*list(res.json()['found'].items())[0])
|
||||||
|
res.raise_for_status()
|
||||||
|
|
||||||
|
return NewPost.model_validate(res.json())
|
||||||
|
|
||||||
@refresh_on_error
|
@refresh_on_error
|
||||||
def view_post(self, id: str):
|
def view_post(self, id: UUID) -> None:
|
||||||
return view_post(self.token, id)
|
"""Просмотреть пост
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id (UUID): UUID поста
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotFound: Пост не найден
|
||||||
|
"""
|
||||||
|
res = view_post(self.token, id)
|
||||||
|
if res.json().get('error', {}).get('code') == 'NOT_FOUND':
|
||||||
|
raise NotFound('Post')
|
||||||
|
res.raise_for_status()
|
||||||
|
|
||||||
@refresh_on_error
|
@refresh_on_error
|
||||||
def get_liked_posts(self, username: str, limit: int = 20, cursor: int = 0):
|
def get_liked_posts(self, username_or_id: str | UUID, limit: int = 20, cursor: int = 0):
|
||||||
return get_liked_posts(self.token, username, limit, cursor)
|
res = get_liked_posts(self.token, username_or_id, limit, cursor)
|
||||||
|
if res.json().get('error', {}).get('code') == 'NOT_FOUND':
|
||||||
|
raise NotFound('User')
|
||||||
|
res.raise_for_status()
|
||||||
|
data = res.json()['data']
|
||||||
|
|
||||||
|
return [Post.model_validate(post) for post in data['posts']], LikedPostsPagintaion.model_validate(data['pagination'])
|
||||||
|
|
||||||
|
|
||||||
@refresh_on_error
|
@refresh_on_error
|
||||||
|
|||||||
@@ -27,3 +27,7 @@ class ReportTargetReason(Enum):
|
|||||||
class AttachType(Enum):
|
class AttachType(Enum):
|
||||||
AUDIO = 'audio'
|
AUDIO = 'audio'
|
||||||
IMAGE = 'image'
|
IMAGE = 'image'
|
||||||
|
|
||||||
|
class PostsTab(Enum):
|
||||||
|
FOLLOWING = 'following'
|
||||||
|
POPULAR = 'popular'
|
||||||
|
|||||||
@@ -71,5 +71,13 @@ class CantFollowYourself(Exception):
|
|||||||
return 'Cannot follow yourself'
|
return 'Cannot follow yourself'
|
||||||
|
|
||||||
class Unauthorized(Exception):
|
class Unauthorized(Exception):
|
||||||
def __str__(self) -> str:
|
def __str__(self):
|
||||||
return 'Auth required - refresh token'
|
return 'Auth required - refresh token'
|
||||||
|
|
||||||
|
class CantRepostYourPost(Exception):
|
||||||
|
def __str__(self):
|
||||||
|
return 'Cannot repost your own post'
|
||||||
|
|
||||||
|
class AlreadyReposted(Exception):
|
||||||
|
def __str__(self):
|
||||||
|
return 'Post already reposted'
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
@@ -8,3 +9,15 @@ class Pagination(BaseModel):
|
|||||||
total: int | None = None
|
total: int | None = None
|
||||||
has_more: bool = Field(True, alias='hasMore')
|
has_more: bool = Field(True, alias='hasMore')
|
||||||
next_cursor: UUID | None = Field(None, alias='nextCursor')
|
next_cursor: UUID | None = Field(None, alias='nextCursor')
|
||||||
|
|
||||||
|
|
||||||
|
class PostsPagintaion(BaseModel):
|
||||||
|
limit: int = 20
|
||||||
|
next_cursor: int = Field(1, alias='nextCursor')
|
||||||
|
has_more: bool = Field(True, alias='hasMore')
|
||||||
|
|
||||||
|
|
||||||
|
class LikedPostsPagintaion(BaseModel):
|
||||||
|
limit: int = 20
|
||||||
|
next_cursor: datetime | None = Field(None, alias='nextCursor')
|
||||||
|
has_more: bool = Field(True, alias='hasMore')
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ def fetch(token: str, method: str, url: str, params: dict = {}, files: dict[str,
|
|||||||
if res.json().get('error', {}).get('code') == 'UNAUTHORIZED':
|
if res.json().get('error', {}).get('code') == 'UNAUTHORIZED':
|
||||||
raise Unauthorized()
|
raise Unauthorized()
|
||||||
except JSONDecodeError:
|
except JSONDecodeError:
|
||||||
pass
|
pass # todo
|
||||||
|
|
||||||
if not res.ok:
|
if not res.ok:
|
||||||
print(res.text)
|
print(res.text)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from itd.request import fetch
|
from itd.request import fetch
|
||||||
|
from itd.enums import PostsTab
|
||||||
|
|
||||||
def create_post(token: str, content: str, wall_recipient_id: UUID | None = None, attach_ids: list[UUID] = []):
|
def create_post(token: str, content: str, wall_recipient_id: UUID | None = None, attach_ids: list[UUID] = []):
|
||||||
data: dict = {'content': content}
|
data: dict = {'content': content}
|
||||||
@@ -11,16 +12,8 @@ def create_post(token: str, content: str, wall_recipient_id: UUID | None = None,
|
|||||||
|
|
||||||
return fetch(token, 'post', 'posts', data)
|
return fetch(token, 'post', 'posts', data)
|
||||||
|
|
||||||
def get_posts(token: str, username: str | None = None, limit: int = 20, cursor: int = 0, sort: str = '', tab: str = ''):
|
def get_posts(token: str, cursor: int = 0, tab: PostsTab = PostsTab.POPULAR):
|
||||||
data: dict = {'limit': limit, 'cursor': cursor}
|
return fetch(token, 'get', 'posts', {'cursor': cursor, 'tab': tab.value})
|
||||||
if username:
|
|
||||||
data['username'] = username
|
|
||||||
if sort:
|
|
||||||
data['sort'] = sort
|
|
||||||
if tab:
|
|
||||||
data['tab'] = tab
|
|
||||||
|
|
||||||
return fetch(token, 'get', 'posts', data)
|
|
||||||
|
|
||||||
def get_post(token: str, id: UUID):
|
def get_post(token: str, id: UUID):
|
||||||
return fetch(token, 'get', f'posts/{id}')
|
return fetch(token, 'get', f'posts/{id}')
|
||||||
@@ -43,8 +36,8 @@ def repost(token: str, id: UUID, content: str | None = None):
|
|||||||
def view_post(token: str, id: UUID):
|
def view_post(token: str, id: UUID):
|
||||||
return fetch(token, 'post', f'posts/{id}/view')
|
return fetch(token, 'post', f'posts/{id}/view')
|
||||||
|
|
||||||
def get_liked_posts(token: str, username: str, limit: int = 20, cursor: int = 0):
|
def get_liked_posts(token: str, username_or_id: str | UUID, limit: int = 20, cursor: int = 0):
|
||||||
return fetch(token, 'get', f'posts/user/{username}/liked', {'limit': limit, 'cursor': cursor})
|
return fetch(token, 'get', f'posts/user/{username_or_id}/liked', {'limit': limit})
|
||||||
|
|
||||||
# todo post restore
|
# todo post restore
|
||||||
# todo post like
|
# todo post like
|
||||||
Reference in New Issue
Block a user