From c7e3812ee83a9bd41a1c5260886a9074e419d14b Mon Sep 17 00:00:00 2001 From: firedotguy Date: Fri, 30 Jan 2026 20:49:49 +0300 Subject: [PATCH] feat: add models and enum --- itd/client.py | 2 ++ itd/enums.py | 25 +++++++++++++++++++++++++ itd/models/_text.py | 17 +++++++++++++++++ itd/models/comment.py | 14 ++++++++++++++ itd/models/file.py | 10 ++++++++++ itd/models/hashtag.py | 8 ++++++++ itd/models/notification.py | 22 ++++++++++++++++++++++ itd/models/post.py | 29 +++++++++++++++++++++++++++++ itd/models/report.py | 16 ++++++++++++++++ itd/models/user.py | 37 +++++++++++++++++++++++++++++++++++++ itd/models/verification.py | 23 +++++++++++++++++++++++ itd/routes/comments.py | 2 +- itd/routes/hashtags.py | 2 +- itd/routes/posts.py | 2 +- pyproject.toml | 2 +- requirements.txt | 2 ++ setup.py | 2 +- 17 files changed, 210 insertions(+), 5 deletions(-) create mode 100644 itd/enums.py create mode 100644 itd/models/_text.py create mode 100644 itd/models/comment.py create mode 100644 itd/models/file.py create mode 100644 itd/models/hashtag.py create mode 100644 itd/models/notification.py create mode 100644 itd/models/post.py create mode 100644 itd/models/report.py create mode 100644 itd/models/user.py create mode 100644 itd/models/verification.py diff --git a/itd/client.py b/itd/client.py index ba7a36b..326241b 100644 --- a/itd/client.py +++ b/itd/client.py @@ -14,6 +14,7 @@ from itd.routes.search import search from itd.routes.files import upload_file from itd.routes.auth import refresh_token, change_password, logout from itd.routes.verification import verificate, get_verification_status +from itd.request import set_cookies def refresh_on_error(func): @@ -35,6 +36,7 @@ class Client: if token: self.token = token.replace('Bearer ', '') elif self.cookies: + set_cookies(self.cookies) self.refresh_auth() else: raise ValueError('Provide token or cookie') diff --git a/itd/enums.py b/itd/enums.py new file mode 100644 index 0000000..3d88e59 --- /dev/null +++ b/itd/enums.py @@ -0,0 +1,25 @@ +from enum import Enum + +class NotificationType(Enum): + WALL_POST = 'wall_post' + REPLY = 'reply' + REPOST = 'repost' + COMMENT = 'comment' + FOLLOW = 'follow' + LIKE = 'like' + +class NotificationTargetType(Enum): + POST = 'post' + +class ReportTargetType(Enum): + POST = 'post' + USER = 'user' + COMMENT = 'comment' + +class ReportTargetReason(Enum): + SPAM = 'spam' # спам + VIOLENCE = 'violence' # насилие + HATE = 'hate' # ненависть + ADULT = 'adult' # 18+ + FRAUD = 'fraud' # обман\мошенничество + OTHER = 'other' # другое \ No newline at end of file diff --git a/itd/models/_text.py b/itd/models/_text.py new file mode 100644 index 0000000..aa6a372 --- /dev/null +++ b/itd/models/_text.py @@ -0,0 +1,17 @@ +from uuid import UUID +from datetime import datetime + +from pydantic import BaseModel, Field + +from itd.models.user import UserPost + + +class _TextObject(BaseModel): + id: UUID + content: str + author: UserPost + attachments: list[UUID] + + created_at: datetime = Field(alias='createdAt') + + model_config = {'populate_by_name': True} \ No newline at end of file diff --git a/itd/models/comment.py b/itd/models/comment.py new file mode 100644 index 0000000..ecf4324 --- /dev/null +++ b/itd/models/comment.py @@ -0,0 +1,14 @@ +from uuid import UUID +from datetime import datetime + +from pydantic import Field + +from itd.models._text import _TextObject + + +class CommentShort(_TextObject): + likes_count: int = Field(0, alias='likesCount') + replies_count: int = Field(0, alias='repliesCount') + is_liked: bool = Field(False, alias='isLiked') + + replies: list['CommentShort'] = [] \ No newline at end of file diff --git a/itd/models/file.py b/itd/models/file.py new file mode 100644 index 0000000..63fe4b4 --- /dev/null +++ b/itd/models/file.py @@ -0,0 +1,10 @@ +from uuid import UUID + +from pydantic import BaseModel, Field + +class File(BaseModel): + id: UUID + url: str + filename: str + mime_type: str = Field('image/png', alias='mimeType') + size: int \ No newline at end of file diff --git a/itd/models/hashtag.py b/itd/models/hashtag.py new file mode 100644 index 0000000..d1d970f --- /dev/null +++ b/itd/models/hashtag.py @@ -0,0 +1,8 @@ +from uuid import UUID + +from pydantic import BaseModel, Field + +class Hashtag(BaseModel): + id: UUID + name: str + posts_count: int = Field(0, alias='postsCount') \ No newline at end of file diff --git a/itd/models/notification.py b/itd/models/notification.py new file mode 100644 index 0000000..7ea3ea9 --- /dev/null +++ b/itd/models/notification.py @@ -0,0 +1,22 @@ +from uuid import UUID +from datetime import datetime + +from pydantic import BaseModel, Field + +from itd.enums import NotificationType, NotificationTargetType +from itd.models.user import UserNotification + +class Notification(BaseModel): + id: UUID + type: NotificationType + + target_type: NotificationTargetType | None = Field(None, alias='targetType') # none - follows, other - NotificationTragetType.POST + target_id: int | 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 + + read: bool = False + read_at: datetime | None = Field(None, alias='readAt') + created_at: datetime = Field(alias='createdAt') + + actor: UserNotification \ No newline at end of file diff --git a/itd/models/post.py b/itd/models/post.py new file mode 100644 index 0000000..8969cad --- /dev/null +++ b/itd/models/post.py @@ -0,0 +1,29 @@ +from pydantic import Field + +from itd.models.user import UserPost +from itd.models._text import _TextObject + + +class PostShort(_TextObject): + likes_count: int = Field(0, alias='likesCount') + comments_count: int = Field(0, alias='commentsCount') + reposts_count: int = Field(0, alias='repostsCount') + views_count: int = Field(0, alias='viewsCount') + + +class OriginalPost(PostShort): + is_deleted: bool = Field(False, alias='isDeleted') + + +class Post(PostShort): + is_liked: bool = Field(False, alias='isLiked') + is_reposted: bool = Field(False, alias='isReposted') + is_viewed: bool = Field(False, alias='isViewed') + is_owner: bool = Field(False, alias='isOwner') + + comments: list = [] + + original_post: OriginalPost | None = None + + wall_recipient_id: int | None = None + wall_recipient: UserPost | None = None diff --git a/itd/models/report.py b/itd/models/report.py new file mode 100644 index 0000000..2a6108d --- /dev/null +++ b/itd/models/report.py @@ -0,0 +1,16 @@ +from uuid import UUID +from datetime import datetime + +from pydantic import BaseModel, Field + +from itd.enums import ReportTargetType, ReportTargetReason + +class Report(BaseModel): + id: UUID + reason: ReportTargetReason + description: str | None = None + + target_type: ReportTargetType = Field(alias='targetType') + target_id: UUID + + created_at: datetime = Field(alias='createdAt') \ No newline at end of file diff --git a/itd/models/user.py b/itd/models/user.py new file mode 100644 index 0000000..760128b --- /dev/null +++ b/itd/models/user.py @@ -0,0 +1,37 @@ +from uuid import UUID +from datetime import datetime + +from pydantic import BaseModel, Field + +class UserNotification(BaseModel): + id: UUID + username: str + display_name: str = Field(alias='displayName') + avatar: str + + model_config = {'populate_by_name': True} + + +class UserPost(UserNotification): + verified: bool = False + + +class UserSearch(UserPost): + followers_count: int = Field(0, alias='followersCount') + + +class User(UserSearch): + banner: str | None = None + bio: str | None = None + pinned_post_id: UUID | None + + private: bool | None = Field(None, alias='isPrivate') # none for not me + wall_closed: bool = Field(False, alias='wallClosed') + + following_count: int = Field(0, alias='followingCount') + posts_count: int = Field(0, alias='postsCount') + + is_following: bool | None = Field(None, alias='isFollowing') # none for me + is_followed: bool | None = Field(None, alias='isFollowedBy') # none for me + + created_at: datetime = Field(alias='createdAt') diff --git a/itd/models/verification.py b/itd/models/verification.py new file mode 100644 index 0000000..6b55b0d --- /dev/null +++ b/itd/models/verification.py @@ -0,0 +1,23 @@ +from uuid import UUID +from datetime import datetime + +from pydantic import BaseModel, Field + +class Verification(BaseModel): + id: UUID + user_id: UUID = Field(alias='userId') + video_url: str = Field(alias='videoUrl') + status: str # should be enum, but we dont know all statuses (what status for accepted?) + + reject_reason: str | None = Field(None, alias='rejectionReason') + reviewer: str | None = Field(None, alias='reviewedBy') + reviewed_at: datetime | None = Field(None, alias='reviewedAt') + + created_at: datetime = Field(alias='createdAt') + updated_at: datetime = Field(alias='updatedAt') + + +class VerificationStatus(BaseModel): + status: str # should be enum, but we dont know all statuses (what status for accepted?) + request_id: UUID = Field(alias='requestId') + submitted_at: datetime = Field(alias='submittedAt') \ No newline at end of file diff --git a/itd/routes/comments.py b/itd/routes/comments.py index e0e7395..0cdc702 100644 --- a/itd/routes/comments.py +++ b/itd/routes/comments.py @@ -7,7 +7,7 @@ def add_comment(token: str, post_id: str, content: str, reply_comment_id: str | return fetch(token, 'post', f'posts/{post_id}/comments', data) def get_comments(token: str, post_id: str, limit: int = 20, cursor: int = 0, sort: str = 'popular'): - return fetch(token, 'get', f'posts/{post_id}/comments', {'limit': limit, 'sort': sort, 'cursor': cursor}) + return fetch(token, 'get', f'posts/{post_id}/comments', {'limit': limit, 'sort': sort, 'cursor': cursor})['data'] def like_comment(token: str, comment_id: str): return fetch(token, 'post', f'comments/{comment_id}/like') diff --git a/itd/routes/hashtags.py b/itd/routes/hashtags.py index e6e9a63..b255096 100644 --- a/itd/routes/hashtags.py +++ b/itd/routes/hashtags.py @@ -1,7 +1,7 @@ from itd.request import fetch def get_hastags(token: str, limit: int = 10): - return fetch(token, 'get', 'hashtags/trending', {'limit': limit}) + return fetch(token, 'get', 'hashtags/trending', {'limit': limit})['data'] def get_posts_by_hastag(token: str, hashtag: str, limit: int = 20, cursor: int = 0): return fetch(token, 'get', f'hashtags/{hashtag}/posts', {'limit': limit, 'cursor': cursor}) diff --git a/itd/routes/posts.py b/itd/routes/posts.py index 55a129b..87223cc 100644 --- a/itd/routes/posts.py +++ b/itd/routes/posts.py @@ -18,7 +18,7 @@ def get_posts(token: str, username: str | None = None, limit: int = 20, cursor: if tab: data['tab'] = tab - return fetch(token, 'get', 'posts', data) + return fetch(token, 'get', 'posts', data)['data'] def get_post(token: str, id: str): return fetch(token, 'get', f'posts/{id}') diff --git a/pyproject.toml b/pyproject.toml index 050c285..0c8f326 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,6 @@ authors = [ ] license = "MIT" dependencies = [ - "requests" + "requests", "pydantic" ] requires-python = ">=3.9" diff --git a/requirements.txt b/requirements.txt index e69de29..541740e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -0,0 +1,2 @@ +pydantic==2.11.9 +requests==2.32.3 \ No newline at end of file diff --git a/setup.py b/setup.py index 3d21301..03f39bf 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( version='0.2.0', packages=find_packages(), install_requires=[ - 'requests' + 'requests', 'pydantic' ], python_requires=">=3.9" )