feat: add models part 3
This commit is contained in:
197
itd/client.py
197
itd/client.py
@@ -2,7 +2,7 @@ from uuid import UUID
|
|||||||
from _io import BufferedReader
|
from _io import BufferedReader
|
||||||
from typing import cast
|
from typing import cast
|
||||||
|
|
||||||
from requests.exceptions import HTTPError
|
from requests.exceptions import HTTPError, ConnectionError
|
||||||
|
|
||||||
from itd.routes.users import get_user, update_profile, follow, unfollow, get_followers, get_following, update_privacy
|
from itd.routes.users import get_user, update_profile, follow, unfollow, get_followers, get_following, update_privacy
|
||||||
from itd.routes.etc import get_top_clans, get_who_to_follow, get_platform_status
|
from itd.routes.etc import get_top_clans, get_who_to_follow, get_platform_status
|
||||||
@@ -17,13 +17,16 @@ from itd.routes.auth import refresh_token, change_password, logout
|
|||||||
from itd.routes.verification import verificate, get_verification_status
|
from itd.routes.verification import verificate, get_verification_status
|
||||||
|
|
||||||
from itd.models.comment import Comment
|
from itd.models.comment import Comment
|
||||||
|
from itd.models.notification import Notification
|
||||||
|
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.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
|
||||||
from itd.models.verification import Verification, VerificationStatus
|
from itd.models.verification import Verification, VerificationStatus
|
||||||
|
|
||||||
from itd.request import set_cookies
|
from itd.request import set_cookies
|
||||||
from itd.exceptions import NoCookie, NoAuthData, SamePassword, InvalidOldPassword, NotFound, ValidationError, UserBanned, PendingRequestExists
|
from itd.exceptions import NoCookie, NoAuthData, SamePassword, InvalidOldPassword, NotFound, ValidationError, UserBanned, PendingRequestExists, Forbidden, UsernameTaken
|
||||||
|
|
||||||
|
|
||||||
def refresh_on_error(func):
|
def refresh_on_error(func):
|
||||||
@@ -35,6 +38,9 @@ def refresh_on_error(func):
|
|||||||
self.refresh_auth()
|
self.refresh_auth()
|
||||||
return func(self, *args, **kwargs)
|
return func(self, *args, **kwargs)
|
||||||
raise e
|
raise e
|
||||||
|
except ConnectionError:
|
||||||
|
self.refresh_auth()
|
||||||
|
return func(self, *args, **kwargs)
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
@@ -166,6 +172,8 @@ class Client:
|
|||||||
res = update_profile(self.token, bio, display_name, username, banner_id)
|
res = update_profile(self.token, bio, display_name, username, banner_id)
|
||||||
if res.status_code == 422 and 'found' in res.json():
|
if res.status_code == 422 and 'found' in res.json():
|
||||||
raise ValidationError(*list(res.json()['found'].items())[0])
|
raise ValidationError(*list(res.json()['found'].items())[0])
|
||||||
|
if res.json().get('error', {}).get('code') == 'USERNAME_TAKEN':
|
||||||
|
raise UsernameTaken()
|
||||||
res.raise_for_status()
|
res.raise_for_status()
|
||||||
|
|
||||||
return UserProfileUpdate.model_validate(res.json())
|
return UserProfileUpdate.model_validate(res.json())
|
||||||
@@ -354,6 +362,7 @@ class Client:
|
|||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValidationError: Ошибка валидации
|
ValidationError: Ошибка валидации
|
||||||
|
NotFound: Пост не найден
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Comment: Комментарий
|
Comment: Комментарий
|
||||||
@@ -379,6 +388,7 @@ class Client:
|
|||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValidationError: Ошибка валидации
|
ValidationError: Ошибка валидации
|
||||||
|
NotFound: Пользователь или комментарий не найден
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Comment: Комментарий
|
Comment: Комментарий
|
||||||
@@ -397,56 +407,191 @@ class Client:
|
|||||||
|
|
||||||
@refresh_on_error
|
@refresh_on_error
|
||||||
def get_comments(self, post_id: UUID, limit: int = 20, cursor: int = 0, sort: str = 'popular') -> tuple[list[Comment], Pagination]:
|
def get_comments(self, post_id: UUID, limit: int = 20, cursor: int = 0, sort: str = 'popular') -> tuple[list[Comment], Pagination]:
|
||||||
|
"""Получить список комментариев
|
||||||
|
|
||||||
|
Args:
|
||||||
|
post_id (UUID): UUID поста
|
||||||
|
limit (int, optional): Лимит. Defaults to 20.
|
||||||
|
cursor (int, optional): Курсор (сколько пропустить). Defaults to 0.
|
||||||
|
sort (str, optional): Сортировка. Defaults to 'popular'.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotFound: Пост не найден
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[Comment]: Список комментариев
|
||||||
|
Pagination: Пагинация
|
||||||
|
"""
|
||||||
res = get_comments(self.token, post_id, limit, cursor, sort)
|
res = get_comments(self.token, post_id, limit, cursor, sort)
|
||||||
if res.json().get('error', {}).get('code') == 'NOT_FOUND':
|
if res.json().get('error', {}).get('code') == 'NOT_FOUND':
|
||||||
raise NotFound('Post')
|
raise NotFound('Post')
|
||||||
res.raise_for_status()
|
res.raise_for_status()
|
||||||
data = res.json()['data']
|
data = res.json()['data']
|
||||||
|
|
||||||
return [Comment.model_validate(comment) for comment in data['comments']], Pagination(page=(cursor // limit) or 1, limit=limit, total=data['total'], hasMore=data['hasMore'])
|
return [Comment.model_validate(comment) for comment in data['comments']], Pagination(page=(cursor // limit) or 1, limit=limit, total=data['total'], hasMore=data['hasMore'], nextCursor=None)
|
||||||
|
|
||||||
@refresh_on_error
|
@refresh_on_error
|
||||||
def like_comment(self, id: UUID):
|
def like_comment(self, id: UUID) -> int:
|
||||||
return like_comment(self.token, id)
|
"""Лайкнуть комментарий
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id (UUID): UUID комментария
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotFound: Комментарий не найден
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: Количество лайков
|
||||||
|
"""
|
||||||
|
res = like_comment(self.token, id)
|
||||||
|
if res.json().get('error', {}).get('code') == 'NOT_FOUND':
|
||||||
|
raise NotFound('Comment')
|
||||||
|
res.raise_for_status()
|
||||||
|
|
||||||
|
return res.json()['likesCount']
|
||||||
|
|
||||||
@refresh_on_error
|
@refresh_on_error
|
||||||
def unlike_comment(self, id: UUID):
|
def unlike_comment(self, id: UUID) -> int:
|
||||||
return unlike_comment(self.token, id)
|
"""Убрать лайк с комментария
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id (UUID): UUID комментария
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotFound: Комментарий не найден
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: Количество лайков
|
||||||
|
"""
|
||||||
|
res = unlike_comment(self.token, id)
|
||||||
|
if res.json().get('error', {}).get('code') == 'NOT_FOUND':
|
||||||
|
raise NotFound('Comment')
|
||||||
|
res.raise_for_status()
|
||||||
|
|
||||||
|
return res.json()['likesCount']
|
||||||
|
|
||||||
@refresh_on_error
|
@refresh_on_error
|
||||||
def delete_comment(self, id: UUID):
|
def delete_comment(self, id: UUID) -> None:
|
||||||
return delete_comment(self.token, id)
|
"""Удалить комментарий
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id (UUID): UUID комментария
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotFound: Комментарий не найден
|
||||||
|
Forbidden: Нет прав на удаление
|
||||||
|
"""
|
||||||
|
res = delete_comment(self.token, id)
|
||||||
|
if res.status_code == 204:
|
||||||
|
return
|
||||||
|
if res.json().get('error', {}).get('code') == 'NOT_FOUND':
|
||||||
|
raise NotFound('Comment')
|
||||||
|
if res.json().get('error', {}).get('code') == 'FORBIDDEN':
|
||||||
|
raise Forbidden('delete comment')
|
||||||
|
res.raise_for_status()
|
||||||
|
|
||||||
|
|
||||||
@refresh_on_error
|
@refresh_on_error
|
||||||
def get_hastags(self, limit: int = 10):
|
def get_hastags(self, limit: int = 10) -> list[Hashtag]:
|
||||||
return get_hastags(self.token, limit)
|
"""Получить список популярных хэштэгов
|
||||||
|
|
||||||
|
Args:
|
||||||
|
limit (int, optional): Лимит. Defaults to 10.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[Hashtag]: Список хэштэгов
|
||||||
|
"""
|
||||||
|
res = get_hastags(self.token, limit)
|
||||||
|
res.raise_for_status()
|
||||||
|
|
||||||
|
return [Hashtag.model_validate(hashtag) for hashtag in res.json()['data']['hashtags']]
|
||||||
|
|
||||||
@refresh_on_error
|
@refresh_on_error
|
||||||
def get_posts_by_hashtag(self, hashtag: str, limit: int = 20, cursor: int = 0):
|
def get_posts_by_hashtag(self, hashtag: str, limit: int = 20, cursor: UUID | None = None) -> tuple[Hashtag | None, list[Post], Pagination]:
|
||||||
return get_posts_by_hastag(self.token, hashtag, limit, cursor)
|
"""Получить посты по хэштэгу
|
||||||
|
|
||||||
|
Args:
|
||||||
|
hashtag (str): Хэштэг (без #)
|
||||||
|
limit (int, optional): Лимит. Defaults to 20.
|
||||||
|
cursor (UUID | None, optional): Курсор (UUID последнего поста, после которого брать данные). Defaults to None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Hashtag | None: Хэштэг
|
||||||
|
list[Post]: Посты
|
||||||
|
Pagination: Пагинация
|
||||||
|
"""
|
||||||
|
res = get_posts_by_hastag(self.token, hashtag, limit, cursor)
|
||||||
|
res.raise_for_status()
|
||||||
|
data = res.json()['data']
|
||||||
|
|
||||||
|
return Hashtag.model_validate(data['hashtag']), [Post.model_validate(post) for post in data['posts']], Pagination.model_validate(data['pagination'])
|
||||||
|
|
||||||
|
|
||||||
@refresh_on_error
|
@refresh_on_error
|
||||||
def get_notifications(self, limit: int = 20, cursor: int = 0, type: str | None = None):
|
def get_notifications(self, limit: int = 20, offset: int = 0) -> tuple[list[Notification], Pagination]:
|
||||||
return get_notifications(self.token, limit, cursor, type)
|
"""Получить уведомления
|
||||||
|
|
||||||
|
Args:
|
||||||
|
limit (int, optional): Лимит. Defaults to 20.
|
||||||
|
offset (int, optional): Сдвиг. Defaults to 0.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[Notification]: Уведомления
|
||||||
|
Pagination: Пагинация
|
||||||
|
"""
|
||||||
|
res = get_notifications(self.token, limit, offset)
|
||||||
|
res.raise_for_status()
|
||||||
|
|
||||||
|
return (
|
||||||
|
[Notification.model_validate(notification) for notification in res.json()['notifications']],
|
||||||
|
Pagination(page=(offset // limit) + 1, limit=limit, hasMore=res.json()['hasMore'], nextCursor=None)
|
||||||
|
)
|
||||||
|
|
||||||
@refresh_on_error
|
@refresh_on_error
|
||||||
def mark_as_read(self, id: str):
|
def mark_as_read(self, id: UUID) -> bool:
|
||||||
return mark_as_read(self.token, id)
|
"""Прочитать уведомление
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id (UUID): UUID уведомления
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: Успешно (False - уже прочитано)
|
||||||
|
"""
|
||||||
|
res = mark_as_read(self.token, id)
|
||||||
|
res.raise_for_status()
|
||||||
|
|
||||||
|
return res.json()['success']
|
||||||
|
|
||||||
@refresh_on_error
|
@refresh_on_error
|
||||||
def mark_all_as_read(self):
|
def mark_all_as_read(self) -> None:
|
||||||
return mark_all_as_read(self.token)
|
"""Прочитать все уведомления"""
|
||||||
|
res = mark_all_as_read(self.token)
|
||||||
@refresh_on_error
|
res.raise_for_status()
|
||||||
def get_unread_notifications_count(self):
|
|
||||||
return get_unread_notifications_count(self.token)
|
|
||||||
|
|
||||||
|
|
||||||
@refresh_on_error
|
@refresh_on_error
|
||||||
def create_post(self, content: str, wall_recipient_id: int | None = None, attach_ids: list[str] = []):
|
def get_unread_notifications_count(self) -> int:
|
||||||
return create_post(self.token, content, wall_recipient_id, attach_ids)
|
"""Получить количество непрочитанных уведомлений
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: Количество
|
||||||
|
"""
|
||||||
|
res = get_unread_notifications_count(self.token)
|
||||||
|
res.raise_for_status()
|
||||||
|
|
||||||
|
return res.json()['count']
|
||||||
|
|
||||||
|
|
||||||
|
@refresh_on_error
|
||||||
|
def create_post(self, content: str, wall_recipient_id: UUID | None = None, attach_ids: list[UUID] = []) -> NewPost:
|
||||||
|
res = create_post(self.token, content, wall_recipient_id, attach_ids)
|
||||||
|
if res.json().get('error', {}).get('code') == 'NOT_FOUND':
|
||||||
|
raise NotFound('Wall recipient')
|
||||||
|
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 get_posts(self, username: str | None = None, limit: int = 20, cursor: int = 0, sort: str = '', tab: str = ''):
|
def get_posts(self, username: str | None = None, limit: int = 20, cursor: int = 0, sort: str = '', tab: str = ''):
|
||||||
@@ -513,7 +658,7 @@ class Client:
|
|||||||
|
|
||||||
@refresh_on_error
|
@refresh_on_error
|
||||||
def upload_file(self, name: str, data: BufferedReader):
|
def upload_file(self, name: str, data: BufferedReader):
|
||||||
return upload_file(self.token, name, data)
|
return upload_file(self.token, name, data).json()
|
||||||
|
|
||||||
def update_banner(self, name: str) -> UserProfileUpdate:
|
def update_banner(self, name: str) -> UserProfileUpdate:
|
||||||
id = self.upload_file(name, cast(BufferedReader, open(name, 'rb')))['id']
|
id = self.upload_file(name, cast(BufferedReader, open(name, 'rb')))['id']
|
||||||
|
|||||||
@@ -48,4 +48,14 @@ class RateLimitExceeded(Exception):
|
|||||||
def __init__(self, retry_after: int):
|
def __init__(self, retry_after: int):
|
||||||
self.retry_after = retry_after
|
self.retry_after = retry_after
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'Rate limit exceeded - too much requests. Retry after {self.retry_after} seconds'
|
return f'Rate limit exceeded - too much requests. Retry after {self.retry_after} seconds'
|
||||||
|
|
||||||
|
class Forbidden(Exception):
|
||||||
|
def __init__(self, action: str):
|
||||||
|
self.action = action
|
||||||
|
def __str__(self):
|
||||||
|
return f'Forbidden to {self.action}'
|
||||||
|
|
||||||
|
class UsernameTaken(Exception):
|
||||||
|
def __str__(self):
|
||||||
|
return 'Username is already taken'
|
||||||
@@ -3,15 +3,10 @@ from datetime import datetime
|
|||||||
|
|
||||||
from pydantic import BaseModel, Field, field_validator
|
from pydantic import BaseModel, Field, field_validator
|
||||||
|
|
||||||
from itd.models.user import UserPost
|
|
||||||
from itd.models.file import Attach
|
|
||||||
|
|
||||||
|
|
||||||
class TextObject(BaseModel):
|
class TextObject(BaseModel):
|
||||||
id: UUID
|
id: UUID
|
||||||
content: str
|
content: str
|
||||||
author: UserPost
|
|
||||||
attachments: list[Attach] = []
|
|
||||||
|
|
||||||
created_at: datetime = Field(alias='createdAt')
|
created_at: datetime = Field(alias='createdAt')
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,16 @@ from pydantic import Field
|
|||||||
|
|
||||||
from itd.models._text import TextObject
|
from itd.models._text import TextObject
|
||||||
from itd.models.user import UserPost
|
from itd.models.user import UserPost
|
||||||
|
from itd.models.file import Attach
|
||||||
|
|
||||||
|
|
||||||
class Comment(TextObject):
|
class Comment(TextObject):
|
||||||
|
author: UserPost
|
||||||
|
|
||||||
likes_count: int = Field(0, alias='likesCount')
|
likes_count: int = Field(0, alias='likesCount')
|
||||||
replies_count: int = Field(0, alias='repliesCount')
|
replies_count: int = Field(0, alias='repliesCount')
|
||||||
is_liked: bool = Field(False, alias='isLiked')
|
is_liked: bool = Field(False, alias='isLiked')
|
||||||
|
|
||||||
|
attachments: list[Attach] = []
|
||||||
replies: list['Comment'] = []
|
replies: list['Comment'] = []
|
||||||
reply_to: UserPost | None = None # author of replied comment, if this comment is reply
|
reply_to: UserPost | None = None # author of replied comment, if this comment is reply
|
||||||
@@ -12,15 +12,18 @@ class File(BaseModel):
|
|||||||
size: int
|
size: int
|
||||||
|
|
||||||
|
|
||||||
class Attach(BaseModel):
|
class PostAttach(BaseModel):
|
||||||
id: UUID
|
id: UUID
|
||||||
type: AttachType = AttachType.IMAGE
|
type: AttachType = AttachType.IMAGE
|
||||||
url: str
|
url: str
|
||||||
thumbnail_url: str | None = Field(None, alias='thumbnailUrl')
|
thumbnail_url: str | None = Field(None, alias='thumbnailUrl')
|
||||||
|
width: int | None = None
|
||||||
|
height: int | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class Attach(PostAttach):
|
||||||
filename: str
|
filename: str
|
||||||
mime_type: str = Field(alias='mimeType')
|
mime_type: str = Field(alias='mimeType')
|
||||||
size: int
|
size: int
|
||||||
width: int | None = None
|
|
||||||
height: int | None = None
|
|
||||||
duration: int | None = None
|
duration: int | None = None
|
||||||
order: int = 0
|
order: int = 0
|
||||||
@@ -11,7 +11,7 @@ class Notification(BaseModel):
|
|||||||
type: NotificationType
|
type: NotificationType
|
||||||
|
|
||||||
target_type: NotificationTargetType | None = Field(None, alias='targetType') # none - follows, other - NotificationTragetType.POST
|
target_type: NotificationTargetType | None = Field(None, alias='targetType') # none - follows, other - NotificationTragetType.POST
|
||||||
target_id: int | None = Field(None, alias='targetId') # none - follows
|
target_id: UUID | None = Field(None, alias='targetId') # none - follows
|
||||||
|
|
||||||
preview: str | None = None # follow - none, comment/reply - content, repost - original post content, like - post content, wall_post - wall post content
|
preview: str | None = None # follow - none, comment/reply - content, repost - original post content, like - post content, wall_post - wall post content
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
|
from uuid import UUID
|
||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
class Pagination(BaseModel):
|
class Pagination(BaseModel):
|
||||||
page: int = 1
|
page: int | None = 1
|
||||||
limit: int = 20
|
limit: int = 20
|
||||||
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')
|
||||||
@@ -1,29 +1,47 @@
|
|||||||
|
from uuid import UUID
|
||||||
|
|
||||||
from pydantic import Field
|
from pydantic import Field
|
||||||
|
|
||||||
from itd.models.user import UserPost
|
from itd.models.user import UserPost, UserNewPost
|
||||||
from itd.models._text import TextObject
|
from itd.models._text import TextObject
|
||||||
|
from itd.models.file import PostAttach
|
||||||
|
from itd.models.comment import Comment
|
||||||
|
|
||||||
|
|
||||||
class PostShort(TextObject):
|
class _PostShort(TextObject):
|
||||||
likes_count: int = Field(0, alias='likesCount')
|
likes_count: int = Field(0, alias='likesCount')
|
||||||
comments_count: int = Field(0, alias='commentsCount')
|
comments_count: int = Field(0, alias='commentsCount')
|
||||||
reposts_count: int = Field(0, alias='repostsCount')
|
reposts_count: int = Field(0, alias='repostsCount')
|
||||||
views_count: int = Field(0, alias='viewsCount')
|
views_count: int = Field(0, alias='viewsCount')
|
||||||
|
|
||||||
|
|
||||||
|
class PostShort(_PostShort):
|
||||||
|
author: UserPost
|
||||||
|
|
||||||
|
|
||||||
class OriginalPost(PostShort):
|
class OriginalPost(PostShort):
|
||||||
is_deleted: bool = Field(False, alias='isDeleted')
|
is_deleted: bool = Field(False, alias='isDeleted')
|
||||||
|
|
||||||
|
|
||||||
class Post(PostShort):
|
class _Post(_PostShort):
|
||||||
is_liked: bool = Field(False, alias='isLiked')
|
is_liked: bool = Field(False, alias='isLiked')
|
||||||
is_reposted: bool = Field(False, alias='isReposted')
|
is_reposted: bool = Field(False, alias='isReposted')
|
||||||
is_viewed: bool = Field(False, alias='isViewed')
|
is_viewed: bool = Field(False, alias='isViewed')
|
||||||
is_owner: bool = Field(False, alias='isOwner')
|
is_owner: bool = Field(False, alias='isOwner')
|
||||||
|
|
||||||
comments: list = []
|
attachments: list[PostAttach] = []
|
||||||
|
comments: list[Comment] = []
|
||||||
|
|
||||||
original_post: OriginalPost | None = None
|
original_post: OriginalPost | None = None
|
||||||
|
|
||||||
wall_recipient_id: int | None = None
|
wall_recipient_id: UUID | None = Field(None, alias='wallRecipientId')
|
||||||
wall_recipient: UserPost | None = None
|
wall_recipient: UserPost | None = Field(None, alias='wallRecipient')
|
||||||
|
|
||||||
|
|
||||||
|
class Post(_Post, PostShort):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NewPost(_Post):
|
||||||
|
author: UserNewPost
|
||||||
|
|
||||||
|
|||||||
@@ -19,22 +19,23 @@ class UserProfileUpdate(BaseModel):
|
|||||||
|
|
||||||
updated_at: datetime | None = Field(None, alias='updatedAt')
|
updated_at: datetime | None = Field(None, alias='updatedAt')
|
||||||
|
|
||||||
model_config = {'populate_by_name': True}
|
|
||||||
|
|
||||||
|
class UserNewPost(BaseModel):
|
||||||
class UserNotification(BaseModel):
|
|
||||||
id: UUID
|
|
||||||
username: str | None = None
|
username: str | None = None
|
||||||
display_name: str = Field(alias='displayName')
|
display_name: str = Field(alias='displayName')
|
||||||
avatar: str
|
avatar: str
|
||||||
|
|
||||||
model_config = {'populate_by_name': True}
|
|
||||||
|
|
||||||
|
|
||||||
class UserPost(UserNotification):
|
|
||||||
verified: bool = False
|
verified: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
class UserNotification(UserNewPost):
|
||||||
|
id: UUID
|
||||||
|
|
||||||
|
|
||||||
|
class UserPost(UserNotification, UserNewPost):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class UserWhoToFollow(UserPost):
|
class UserWhoToFollow(UserPost):
|
||||||
followers_count: int = Field(0, alias='followersCount')
|
followers_count: int = Field(0, alias='followersCount')
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from _io import BufferedReader
|
from _io import BufferedReader
|
||||||
|
|
||||||
from requests import Session
|
from requests import Session
|
||||||
|
from requests.exceptions import JSONDecodeError
|
||||||
|
|
||||||
from itd.exceptions import InvalidToken, InvalidCookie, RateLimitExceeded
|
from itd.exceptions import InvalidToken, InvalidCookie, RateLimitExceeded
|
||||||
|
|
||||||
@@ -29,10 +30,13 @@ def fetch(token: str, method: str, url: str, params: dict = {}, files: dict[str,
|
|||||||
if method == "get":
|
if method == "get":
|
||||||
res = s.get(base, timeout=120 if files else 20, params=params, headers=headers)
|
res = s.get(base, timeout=120 if files else 20, params=params, headers=headers)
|
||||||
else:
|
else:
|
||||||
res = s.request(method.upper(), base, timeout=20, json=params, headers=headers, files=files)
|
res = s.request(method.upper(), base, timeout=120 if files else 20, json=params, headers=headers, files=files)
|
||||||
|
|
||||||
if res.json().get('error', {}).get('code') == 'RATE_LIMIT_EXCEEDED':
|
try:
|
||||||
raise RateLimitExceeded(res.json()['error'].get('retryAfter', 0))
|
if res.json().get('error', {}).get('code') == 'RATE_LIMIT_EXCEEDED':
|
||||||
|
raise RateLimitExceeded(res.json()['error'].get('retryAfter', 0))
|
||||||
|
except JSONDecodeError:
|
||||||
|
pass
|
||||||
|
|
||||||
print(res.text)
|
print(res.text)
|
||||||
return res
|
return res
|
||||||
@@ -75,9 +79,12 @@ def auth_fetch(cookies: str, method: str, url: str, params: dict = {}, token: st
|
|||||||
# print(res.text)
|
# print(res.text)
|
||||||
if res.text == 'UNAUTHORIZED':
|
if res.text == 'UNAUTHORIZED':
|
||||||
raise InvalidToken()
|
raise InvalidToken()
|
||||||
if res.json().get('error', {}).get('code') == 'RATE_LIMIT_EXCEEDED':
|
try:
|
||||||
raise RateLimitExceeded(res.json()['error'].get('retryAfter', 0))
|
if res.json().get('error', {}).get('code') == 'RATE_LIMIT_EXCEEDED':
|
||||||
if res.json().get('error', {}).get('code') in ('SESSION_NOT_FOUND', 'REFRESH_TOKEN_MISSING'):
|
raise RateLimitExceeded(res.json()['error'].get('retryAfter', 0))
|
||||||
raise InvalidCookie()
|
if res.json().get('error', {}).get('code') in ('SESSION_NOT_FOUND', 'REFRESH_TOKEN_MISSING'):
|
||||||
|
raise InvalidCookie()
|
||||||
|
except JSONDecodeError:
|
||||||
|
pass
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
|
from uuid import UUID
|
||||||
from itd.request import fetch
|
from itd.request import fetch
|
||||||
|
|
||||||
def get_hastags(token: str, limit: int = 10):
|
def get_hastags(token: str, limit: int = 10):
|
||||||
return fetch(token, 'get', 'hashtags/trending', {'limit': limit})
|
return fetch(token, 'get', 'hashtags/trending', {'limit': limit})
|
||||||
|
|
||||||
def get_posts_by_hastag(token: str, hashtag: str, limit: int = 20, cursor: int = 0):
|
def get_posts_by_hastag(token: str, hashtag: str, limit: int = 20, cursor: UUID | None = None):
|
||||||
return fetch(token, 'get', f'hashtags/{hashtag}/posts', {'limit': limit, 'cursor': cursor})
|
return fetch(token, 'get', f'hashtags/{hashtag}/posts', {'limit': limit, 'cursor': cursor})
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
|
from uuid import UUID
|
||||||
|
|
||||||
from itd.request import fetch
|
from itd.request import fetch
|
||||||
|
|
||||||
def get_notifications(token: str, limit: int = 20, cursor: int = 0, type: str | None = None):
|
def get_notifications(token: str, limit: int = 20, offset: int = 0):
|
||||||
data = {'limit': str(limit), 'cursor': str(cursor)}
|
return fetch(token, 'get', 'notifications', {'limit': limit, 'offset': offset})
|
||||||
if type:
|
|
||||||
data['type'] = type
|
|
||||||
return fetch(token, 'get', 'notifications', data)
|
|
||||||
|
|
||||||
def mark_as_read(token: str, id: str):
|
def mark_as_read(token: str, id: UUID):
|
||||||
return fetch(token, 'post', f'notification/{id}/read')
|
return fetch(token, 'post', f'notifications/{id}/read')
|
||||||
|
|
||||||
def mark_all_as_read(token: str):
|
def mark_all_as_read(token: str):
|
||||||
return fetch(token, 'post', f'notification/read-all')
|
return fetch(token, 'post', f'notifications/read-all')
|
||||||
|
|
||||||
def get_unread_notifications_count(token: str):
|
def get_unread_notifications_count(token: str):
|
||||||
return fetch(token, 'get', 'notifications/count')
|
return fetch(token, 'get', 'notifications/count')
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
|
from uuid import UUID
|
||||||
|
|
||||||
from itd.request import fetch
|
from itd.request import fetch
|
||||||
|
|
||||||
def create_post(token: str, content: str, wall_recipient_id: int | None = None, attach_ids: list[str] = []):
|
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}
|
||||||
if wall_recipient_id:
|
if wall_recipient_id:
|
||||||
data['wallRecipientId'] = wall_recipient_id
|
data['wallRecipientId'] = str(wall_recipient_id)
|
||||||
if attach_ids:
|
if attach_ids:
|
||||||
data['attachmentIds'] = attach_ids
|
data['attachmentIds'] = list(map(str, attach_ids))
|
||||||
|
|
||||||
return fetch(token, 'post', 'posts', data)
|
return fetch(token, 'post', 'posts', data)
|
||||||
|
|
||||||
@@ -20,28 +22,29 @@ def get_posts(token: str, username: str | None = None, limit: int = 20, cursor:
|
|||||||
|
|
||||||
return fetch(token, 'get', 'posts', data)
|
return fetch(token, 'get', 'posts', data)
|
||||||
|
|
||||||
def get_post(token: str, id: str):
|
def get_post(token: str, id: UUID):
|
||||||
return fetch(token, 'get', f'posts/{id}')
|
return fetch(token, 'get', f'posts/{id}')
|
||||||
|
|
||||||
def edit_post(token: str, id: str, content: str):
|
def edit_post(token: str, id: UUID, content: str):
|
||||||
return fetch(token, 'put', f'posts/{id}', {'content': content})
|
return fetch(token, 'put', f'posts/{id}', {'content': content})
|
||||||
|
|
||||||
def delete_post(token: str, id: str):
|
def delete_post(token: str, id: UUID):
|
||||||
return fetch(token, 'delete', f'posts/{id}')
|
return fetch(token, 'delete', f'posts/{id}')
|
||||||
|
|
||||||
def pin_post(token: str, id: str):
|
def pin_post(token: str, id: UUID):
|
||||||
return fetch(token, 'post', f'posts/{id}/pin')
|
return fetch(token, 'post', f'posts/{id}/pin')
|
||||||
|
|
||||||
def repost(token: str, id: str, content: str | None = None):
|
def repost(token: str, id: UUID, content: str | None = None):
|
||||||
data = {}
|
data = {}
|
||||||
if content:
|
if content:
|
||||||
data['content'] = content
|
data['content'] = content
|
||||||
return fetch(token, 'post', f'posts/{id}/repost', data)
|
return fetch(token, 'post', f'posts/{id}/repost', data)
|
||||||
|
|
||||||
def view_post(token: str, id: str):
|
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: str, 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}/liked', {'limit': limit, 'cursor': cursor})
|
||||||
|
|
||||||
# todo post restore
|
# todo post restore
|
||||||
|
# todo post like
|
||||||
Reference in New Issue
Block a user