itd-sdk 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
itd/__init__.py ADDED
@@ -0,0 +1 @@
1
+ from itd.client import Client as ITDClient
itd/client.py ADDED
@@ -0,0 +1,193 @@
1
+ from requests.exceptions import HTTPError
2
+
3
+ from itd.users import get_user, update_profile, follow, unfollow, get_followers, get_following
4
+ from itd.etc import get_top_clans, get_who_to_follow, get_platform_status
5
+ from itd.comments import get_comments, add_comment, delete_comment, like_comment, unlike_comment
6
+ from itd.hashtags import get_hastags, get_posts_by_hastag
7
+ from itd.notifications import get_notifications, mark_as_read, mark_all_as_read, get_unread_notifications_count
8
+ from itd.posts import create_post, get_posts, get_post, edit_post, delete_post, pin_post, repost, view_post
9
+ from itd.reports import report
10
+ from itd.search import search
11
+ from itd.request import refresh_auth
12
+
13
+
14
+ def refresh_on_error(func):
15
+ def wrapper(self, *args, **kwargs):
16
+ try:
17
+ return func(self, *args, **kwargs)
18
+ except HTTPError as e:
19
+ if '401' in str(e):
20
+ self.refresh_auth()
21
+ return func(self, *args, **kwargs)
22
+ raise e
23
+ return wrapper
24
+
25
+
26
+ class Client:
27
+ def __init__(self, token: str | None, cookies: str | None = None):
28
+ self.cookies = cookies
29
+
30
+ if token:
31
+ self.token = token.replace('Bearer ', '')
32
+ elif self.cookies:
33
+ self.refresh_auth()
34
+ else:
35
+ raise ValueError('Provide token or cookie')
36
+
37
+ @refresh_on_error
38
+ def refresh_auth(self):
39
+ if self.cookies:
40
+ self.token = refresh_auth(self.cookies)
41
+ else:
42
+ print('no cookies!')
43
+
44
+ @refresh_on_error
45
+ def get_user(self, username: str) -> dict:
46
+ return get_user(self.token, username)
47
+
48
+ @refresh_on_error
49
+ def get_me(self) -> dict:
50
+ return self.get_user('me')
51
+
52
+ @refresh_on_error
53
+ def update_profile(self, username: str | None = None, display_name: str | None = None, bio: str | None = None) -> dict:
54
+ return update_profile(self.token, bio, display_name, username)
55
+
56
+ @refresh_on_error
57
+ def follow(self, username: str) -> dict:
58
+ return follow(self.token, username)
59
+
60
+ @refresh_on_error
61
+ def unfollow(self, username: str) -> dict:
62
+ return unfollow(self.token, username)
63
+
64
+ @refresh_on_error
65
+ def get_followers(self, username: str) -> dict:
66
+ return get_followers(self.token, username)
67
+
68
+ @refresh_on_error
69
+ def get_following(self, username: str) -> dict:
70
+ return get_following(self.token, username)
71
+
72
+
73
+ @refresh_on_error
74
+ def get_who_to_follow(self) -> dict:
75
+ return get_who_to_follow(self.token)
76
+
77
+ @refresh_on_error
78
+ def get_top_clans(self) -> dict:
79
+ return get_top_clans(self.token)
80
+
81
+ @refresh_on_error
82
+ def get_platform_status(self) -> dict:
83
+ return get_platform_status(self.token)
84
+
85
+
86
+ @refresh_on_error
87
+ def add_comment(self, post_id: str, content: str, reply_comment_id: str | None = None):
88
+ return add_comment(self.token, post_id, content, reply_comment_id)
89
+
90
+ @refresh_on_error
91
+ def get_comments(self, post_id: str, limit: int = 20, cursor: int = 0, sort: str = 'popular'):
92
+ return get_comments(self.token, post_id, limit, cursor, sort)
93
+
94
+ @refresh_on_error
95
+ def like_comment(self, id: str):
96
+ return like_comment(self.token, id)
97
+
98
+ @refresh_on_error
99
+ def unlike_comment(self, id: str):
100
+ return unlike_comment(self.token, id)
101
+
102
+ @refresh_on_error
103
+ def delete_comment(self, id: str):
104
+ return delete_comment(self.token, id)
105
+
106
+
107
+ @refresh_on_error
108
+ def get_hastags(self, limit: int = 10):
109
+ return get_hastags(self.token, limit)
110
+
111
+ @refresh_on_error
112
+ def get_posts_by_hashtag(self, hashtag: str, limit: int = 20, cursor: int = 0):
113
+ return get_posts_by_hastag(self.token, hashtag, limit, cursor)
114
+
115
+
116
+ @refresh_on_error
117
+ def get_notifications(self, limit: int = 20, cursor: int = 0, type: str | None = None):
118
+ return get_notifications(self.token, limit, cursor, type)
119
+
120
+ @refresh_on_error
121
+ def mark_as_read(self, id: str):
122
+ return mark_as_read(self.token, id)
123
+
124
+ @refresh_on_error
125
+ def mark_all_as_read(self):
126
+ return mark_all_as_read(self.token)
127
+
128
+ @refresh_on_error
129
+ def get_unread_notifications_count(self):
130
+ return get_unread_notifications_count(self.token)
131
+
132
+
133
+ @refresh_on_error
134
+ def create_post(self, content: str, wall_recipient_id: int | None = None, attach_ids: list[str] = []):
135
+ return create_post(self.token, content, wall_recipient_id, attach_ids)
136
+
137
+ @refresh_on_error
138
+ def get_posts(self, username: str | None = None, limit: int = 20, cursor: int = 0, sort: str = '', tab: str = ''):
139
+ return get_posts(self.token, username, limit, cursor, sort, tab)
140
+
141
+ @refresh_on_error
142
+ def get_post(self, id: str):
143
+ return get_post(self.token, id)
144
+
145
+ @refresh_on_error
146
+ def edit_post(self, id: str, content: str):
147
+ return edit_post(self.token, id, content)
148
+
149
+ @refresh_on_error
150
+ def delete_post(self, id: str):
151
+ return delete_post(self.token, id)
152
+
153
+ @refresh_on_error
154
+ def pin_post(self, id: str):
155
+ return pin_post(self.token, id)
156
+
157
+ @refresh_on_error
158
+ def repost(self, id: str, content: str | None = None):
159
+ return repost(self.token, id, content)
160
+
161
+ @refresh_on_error
162
+ def view_post(self, id: str):
163
+ return view_post(self.token, id)
164
+
165
+
166
+ @refresh_on_error
167
+ def report(self, id: str, type: str = 'post', reason: str = 'other', description: str = ''):
168
+ return report(self.token, id, type, reason, description)
169
+
170
+ @refresh_on_error
171
+ def report_user(self, id: str, reason: str = 'other', description: str = ''):
172
+ return report(self.token, id, 'user', reason, description)
173
+
174
+ @refresh_on_error
175
+ def report_post(self, id: str, reason: str = 'other', description: str = ''):
176
+ return report(self.token, id, 'post', reason, description)
177
+
178
+ @refresh_on_error
179
+ def report_comment(self, id: str, reason: str = 'other', description: str = ''):
180
+ return report(self.token, id, 'comment', reason, description)
181
+
182
+
183
+ @refresh_on_error
184
+ def search(self, query: str, user_limit: int = 5, hashtag_limit: int = 5):
185
+ return search(self.token, query, user_limit, hashtag_limit)
186
+
187
+ @refresh_on_error
188
+ def search_user(self, query: str, limit: int = 5):
189
+ return search(self.token, query, limit, 0)
190
+
191
+ @refresh_on_error
192
+ def search_hashtag(self, query: str, limit: int = 5):
193
+ return search(self.token, query, 0, limit)
itd/comments.py ADDED
@@ -0,0 +1,19 @@
1
+ from itd.request import fetch
2
+
3
+ def add_comment(token: str, post_id: str, content: str, reply_comment_id: str | None = None):
4
+ data = {'content': content}
5
+ if reply_comment_id:
6
+ data['replyTo'] = str(reply_comment_id)
7
+ return fetch(token, 'post', f'posts/{post_id}/comments', data)
8
+
9
+ def get_comments(token: str, post_id: str, limit: int = 20, cursor: int = 0, sort: str = 'popular'):
10
+ return fetch(token, 'get', f'posts/{post_id}/comments', {'limit': limit, 'sort': sort, 'cursor': cursor})
11
+
12
+ def like_comment(token: str, comment_id: str):
13
+ return fetch(token, 'post', f'comments/{comment_id}/like')
14
+
15
+ def unlike_comment(token: str, comment_id: str):
16
+ return fetch(token, 'delete', f'comments/{comment_id}/like')
17
+
18
+ def delete_comment(token: str, comment_id: str):
19
+ return fetch(token, 'delete', f'comments/{comment_id}')
itd/etc.py ADDED
@@ -0,0 +1,10 @@
1
+ from itd.request import fetch
2
+
3
+ def get_top_clans(token: str):
4
+ return fetch(token, 'get', 'users/stats/top-clans')
5
+
6
+ def get_who_to_follow(token: str):
7
+ return fetch(token, 'get', 'users/suggestions/who-to-follow')
8
+
9
+ def get_platform_status(token: str):
10
+ return fetch(token, 'get', 'platform/status')
itd/hashtags.py ADDED
@@ -0,0 +1,7 @@
1
+ from itd.request import fetch
2
+
3
+ def get_hastags(token: str, limit: int = 10):
4
+ return fetch(token, 'get', 'hashtags/trending', {'limit': limit})
5
+
6
+ def get_posts_by_hastag(token: str, hashtag: str, limit: int = 20, cursor: int = 0):
7
+ return fetch(token, 'get', f'hashtags/{hashtag}/posts', {'limit': limit, 'cursor': cursor})
itd/notifications.py ADDED
@@ -0,0 +1,16 @@
1
+ from itd.request import fetch
2
+
3
+ def get_notifications(token: str, limit: int = 20, cursor: int = 0, type: str | None = None):
4
+ data = {'limit': str(limit), 'cursor': str(cursor)}
5
+ if type:
6
+ data['type'] = type
7
+ return fetch(token, 'get', 'notifications', data)
8
+
9
+ def mark_as_read(token: str, id: str):
10
+ return fetch(token, 'post', f'notification/{id}/read')
11
+
12
+ def mark_all_as_read(token: str):
13
+ return fetch(token, 'post', f'notification/read-all')
14
+
15
+ def get_unread_notifications_count(token: str):
16
+ return fetch(token, 'get', 'notifications/count')
itd/posts.py ADDED
@@ -0,0 +1,42 @@
1
+ from itd.request import fetch
2
+
3
+ def create_post(token: str, content: str, wall_recipient_id: int | None = None, attach_ids: list[str] = []):
4
+ data: dict = {'content': content}
5
+ if wall_recipient_id:
6
+ data['wallRecipientId'] = wall_recipient_id
7
+ if attach_ids:
8
+ data['attachmentIds'] = attach_ids
9
+
10
+ return fetch(token, 'post', 'posts', data)
11
+
12
+ def get_posts(token: str, username: str | None = None, limit: int = 20, cursor: int = 0, sort: str = '', tab: str = ''):
13
+ data: dict = {'limit': limit, 'cursor': cursor}
14
+ if username:
15
+ data['username'] = username
16
+ if sort:
17
+ data['sort'] = sort
18
+ if tab:
19
+ data['tab'] = tab
20
+
21
+ return fetch(token, 'get', 'posts', data)
22
+
23
+ def get_post(token: str, id: str):
24
+ return fetch(token, 'get', f'posts/{id}')
25
+
26
+ def edit_post(token: str, id: str, content: str):
27
+ return fetch(token, 'put', f'posts/{id}', {'content': content})
28
+
29
+ def delete_post(token: str, id: str):
30
+ return fetch(token, 'delete', f'posts/{id}')
31
+
32
+ def pin_post(token: str, id: str):
33
+ return fetch(token, 'post', f'posts/{id}/pin')
34
+
35
+ def repost(token: str, id: str, content: str | None = None):
36
+ data = {}
37
+ if content:
38
+ data['content'] = content
39
+ return fetch(token, 'post', f'posts/{id}/repost', data)
40
+
41
+ def view_post(token: str, id: str):
42
+ return fetch(token, 'post', f'posts/{id}/view')
itd/reports.py ADDED
@@ -0,0 +1,4 @@
1
+ from itd.request import fetch
2
+
3
+ def report(token: str, id: str, type: str = 'post', reason: str = 'other', description: str = ''):
4
+ return fetch(token, 'post', 'reports', {'targetId': id, 'targetType': type, 'reason': reason, 'description': description})
itd/request.py ADDED
@@ -0,0 +1,54 @@
1
+ from requests import Session
2
+
3
+ s = Session()
4
+
5
+
6
+ def fetch(token: str, method: str, url: str, params: dict = {}):
7
+ res = eval(f's.{method}')(f'https://xn--d1ah4a.com/api/{url}', timeout=20, params=params, headers={
8
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
9
+ "Accept-Language": "ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3",
10
+ "Accept-Encoding": "gzip, deflate, br, zstd",
11
+ "Authorization": 'Bearer ' + token,
12
+ "Sec-GPC": "1",
13
+ "Upgrade-Insecure-Requests": "1",
14
+ "Sec-Fetch-Dest": "document",
15
+ "Sec-Fetch-Mode": "navigate",
16
+ "Sec-Fetch-Site": "none",
17
+ "Sec-Fetch-User": "?1",
18
+ "Priority": "u=0, i",
19
+ "Pragma": "no-cache",
20
+ "Cache-Control": "no-cache",
21
+ "TE": "trailers"
22
+ })
23
+ res.raise_for_status()
24
+ return res.json()
25
+
26
+ def set_cookies(cookies: str):
27
+ for cookie in cookies.split('; '):
28
+ s.cookies.set(cookie.split('=')[0], cookie.split('=')[-1], path='/', domain='xn--d1ah4a.com.com')
29
+
30
+ def refresh_auth(cookies: str):
31
+ print('refresh')
32
+ res = s.post(f'https://xn--d1ah4a.com/api/v1/auth/refresh', timeout=10, headers={
33
+ "Host": "xn--d1ah4a.com",
34
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0",
35
+ "Accept": "*/*",
36
+ "Accept-Language": "ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3",
37
+ "Accept-Encoding": "gzip, deflate, br, zstd",
38
+ "Referer": "https://xn--d1ah4a.com/",
39
+ "Content-Type": "application/json",
40
+ "Origin": "https://xn--d1ah4a.com",
41
+ "Sec-GPC": "1",
42
+ "Connection": "keep-alive",
43
+ "Cookie": cookies,
44
+ "Sec-Fetch-Dest": "empty",
45
+ "Sec-Fetch-Mode": "cors",
46
+ "Sec-Fetch-Site": "same-origin",
47
+ "Priority": "u=4",
48
+ "Pragma": "no-cache",
49
+ "Cache-Control": "no-cache",
50
+ "Content-Length": "0",
51
+ "TE": "trailers",
52
+ })
53
+ res.raise_for_status()
54
+ return res.json()['accessToken']
itd/search.py ADDED
@@ -0,0 +1,4 @@
1
+ from itd.request import fetch
2
+
3
+ def search(token: str, query: str, user_limit: int = 5, hashtag_limit: int = 5):
4
+ return fetch(token, 'get', 'search', {'userLimit': user_limit, 'hashtagLimit': hashtag_limit, 'q': query})
itd/users.py ADDED
@@ -0,0 +1,27 @@
1
+ from itd.request import fetch
2
+
3
+
4
+ def get_user(token: str, username: str):
5
+ return fetch(token, 'get', f'users/{username}')
6
+
7
+ def update_profile(token: str, bio: str | None = None, display_name: str | None = None, username: str | None = None):
8
+ data = {}
9
+ if bio:
10
+ data['bio'] = bio
11
+ if display_name:
12
+ data['displayName'] = display_name
13
+ if username:
14
+ data['username'] = username
15
+ return fetch(token, 'put', 'users/me', data)
16
+
17
+ def follow(token: str, username: str):
18
+ return fetch(token, 'post', f'users/{username}/follow')
19
+
20
+ def unfollow(token: str, username: str):
21
+ return fetch(token, 'delete', f'users/{username}/follow')
22
+
23
+ def get_followers(token: str, username: str, limit: int = 30, page: int = 1):
24
+ return fetch(token, 'get', f'users/{username}/followers', {'limit': limit, 'page': page})
25
+
26
+ def get_following(token: str, username: str, limit: int = 30, page: int = 1):
27
+ return fetch(token, 'get', f'users/{username}/following', {'limit': limit, 'page': page})
@@ -0,0 +1,63 @@
1
+ Metadata-Version: 2.4
2
+ Name: itd-sdk
3
+ Version: 0.1.0
4
+ Summary: ITD client for python
5
+ Author-email: firedotguy <nta16022013@gmail.com>
6
+ License-Expression: MIT
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: requests
11
+ Dynamic: license-file
12
+ Dynamic: requires-python
13
+
14
+ # pyITDclient
15
+ Клиент ITD для python
16
+
17
+
18
+ ## Установка
19
+
20
+ ```bash
21
+ pip install pyITDclient
22
+ ```
23
+
24
+ ## Пример
25
+
26
+ ```python
27
+ from itd import ITDClient
28
+
29
+ c = ITDClient('TOKEN', 'refresh_token=...; __ddg1_=...; __ddgid_=...; is_auth=1; __ddg2_=...; ddg_last_challenge=...; __ddg8_=...; __ddg10_=...; __ddg9_=...')
30
+ # можно указать только токен, тогда после просрочки перестанет работать, либо только куки чтобы токен сразу подтянулся, либо оба сразу
31
+
32
+ print(c.get_me())
33
+ ```
34
+
35
+ ### Встроенные запросы
36
+ Существуют встроенные эндпоинты для комментариев, хэштэгов, уведомлений, постов, репортов, поиска, пользователей, итд.
37
+ ```python
38
+ c.get_user('ITD_API') # получение данных пользователя
39
+ c.get_me() # получение своих данных (me)
40
+ c.update_profile(display_name='22:26') # изменение данных профиля, например имя, био итд
41
+ c.create_post('тест1') # создание постов
42
+ # итд
43
+ ```
44
+
45
+ ### Кастомные запросы
46
+
47
+ ```python
48
+ from itd.request import fetch
49
+
50
+ fetch(c.token, 'метод', 'эндпоинт', {'данные': 'данные'})
51
+ ```
52
+ Из методов поддерживается `get`, `post`, `put` итд, которые есть в `requests`
53
+ К названию эндпоинта добавляется домен итд и `api`, то есть в этом примере отпарвится `https://xn--d1ah4a.com/api/эндпоинт`.
54
+
55
+ > ![INFO]
56
+ > `xn--d1ah4a.com` - punycode от "итд.com"
57
+
58
+ ## прочее
59
+ Лицезия: [MIT](./LICENSE)
60
+ Идея (и часть эндпоинтов): https://github.com/FriceKa/ITD-SDK-js
61
+ - По сути этот проект является реворком, просто на другом языке
62
+
63
+ Автор: [SizedBox](https://xn--d1ah4a.com/SizedBox) (в итд) [@desicars](https://t.me/desicars) (в тг)
@@ -0,0 +1,16 @@
1
+ itd/__init__.py,sha256=aw5IL4oMiYMByyb3dies4cFtalvbwIhK5l8UZIY33CU,42
2
+ itd/client.py,sha256=e0anfjZkaUZjm9M1sRKuQb7eIVxrWNQQjIRtCyaxc3U,6438
3
+ itd/comments.py,sha256=K1D-oB7sP3cu6Nlvwv01AoHW2MQ_hTHzdY-6wJCVS5Q,845
4
+ itd/etc.py,sha256=qpvmfZnxmjn_qEGF6RfdZB7JqXb6X24xAIdIpsR9BLg,307
5
+ itd/hashtags.py,sha256=8J_FZoYrVWUOO7YVZHNYgKtm-FhfsltlNGpuk2jjjV4,329
6
+ itd/notifications.py,sha256=VOByurp1L_zTjOppNMb4w9ooClaLpFxQUgoRLlb3emY,570
7
+ itd/posts.py,sha256=RvSGWs5H0PmU7dyh0QH1D_w9p-D3dRQ6zeIDQgUsMRA,1369
8
+ itd/reports.py,sha256=PQN7G_ZlqHiP_QAfJbv1Tr_RR2-4dYq4Em5wxHj6ozY,256
9
+ itd/request.py,sha256=M8p5G694FP79yht98KWu9UQk1-axHW6280IJo9MHH6s,2004
10
+ itd/search.py,sha256=_VDm2mzV33SqknscB5shL1mYnqPb5U64OCETDwz_maQ,222
11
+ itd/users.py,sha256=U_nMol2j2D0M-E3Xgp2_COBmLg-UlFLpKzTcjGgv3JY,1012
12
+ itd_sdk-0.1.0.dist-info/licenses/LICENSE,sha256=_lEsNh_HklWcRHDJ7hk7P08s5__iwsGCHXyFUyT4GEw,1088
13
+ itd_sdk-0.1.0.dist-info/METADATA,sha256=vTajYGI88ew7d1t2E0LrkXcxI8pQlYf8-2kR40WEC_g,2482
14
+ itd_sdk-0.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
15
+ itd_sdk-0.1.0.dist-info/top_level.txt,sha256=pN5yGtQiWeh0Kox8lh1b_Cp5azwV71qEEGnTIXMH7fE,4
16
+ itd_sdk-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 firedotguy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ itd