PyPlayerokAPI 1.0.1__tar.gz → 1.0.2__tar.gz

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.
Files changed (51) hide show
  1. {pyplayerokapi-1.0.1 → pyplayerokapi-1.0.2}/PKG-INFO +12 -12
  2. pyplayerokapi-1.0.2/PyPlayerokAPI/__init__.py +15 -0
  3. pyplayerokapi-1.0.2/PyPlayerokAPI/account/__init__.py +30 -0
  4. pyplayerokapi-1.0.2/PyPlayerokAPI/account/bank.py +119 -0
  5. pyplayerokapi-1.0.2/PyPlayerokAPI/account/base.py +47 -0
  6. pyplayerokapi-1.0.2/PyPlayerokAPI/account/chat.py +269 -0
  7. pyplayerokapi-1.0.2/PyPlayerokAPI/account/deals.py +135 -0
  8. pyplayerokapi-1.0.2/PyPlayerokAPI/account/games.py +333 -0
  9. pyplayerokapi-1.0.2/PyPlayerokAPI/account/items.py +429 -0
  10. pyplayerokapi-1.0.2/PyPlayerokAPI/account/profile.py +127 -0
  11. pyplayerokapi-1.0.2/PyPlayerokAPI/account/transactions.py +227 -0
  12. pyplayerokapi-1.0.2/PyPlayerokAPI/graphql.py +44 -0
  13. pyplayerokapi-1.0.2/PyPlayerokAPI/models/__init__.py +17 -0
  14. pyplayerokapi-1.0.2/PyPlayerokAPI/models/account.py +85 -0
  15. pyplayerokapi-1.0.2/PyPlayerokAPI/models/chat.py +205 -0
  16. pyplayerokapi-1.0.2/PyPlayerokAPI/models/etc.py +31 -0
  17. pyplayerokapi-1.0.2/PyPlayerokAPI/models/game.py +418 -0
  18. pyplayerokapi-1.0.2/PyPlayerokAPI/models/item.py +295 -0
  19. pyplayerokapi-1.0.2/PyPlayerokAPI/models/review.py +53 -0
  20. pyplayerokapi-1.0.2/PyPlayerokAPI/models/transaction.py +257 -0
  21. pyplayerokapi-1.0.2/PyPlayerokAPI/models/user.py +81 -0
  22. pyplayerokapi-1.0.2/PyPlayerokAPI/stream/__init__.py +15 -0
  23. pyplayerokapi-1.0.2/PyPlayerokAPI/stream/async_listener.py +224 -0
  24. pyplayerokapi-1.0.2/PyPlayerokAPI/stream/events/__init__.py +24 -0
  25. pyplayerokapi-1.0.2/PyPlayerokAPI/stream/events/account_event.py +18 -0
  26. pyplayerokapi-1.0.2/PyPlayerokAPI/stream/events/async_dispatcher.py +64 -0
  27. pyplayerokapi-1.0.2/PyPlayerokAPI/stream/events/async_router.py +54 -0
  28. pyplayerokapi-1.0.2/PyPlayerokAPI/stream/events/event_factory.py +86 -0
  29. pyplayerokapi-1.0.2/PyPlayerokAPI/stream/events/event_wrapper.py +23 -0
  30. pyplayerokapi-1.0.2/PyPlayerokAPI/stream/events/markers.py +92 -0
  31. pyplayerokapi-1.0.2/PyPlayerokAPI/stream/listener/__init__.py +16 -0
  32. pyplayerokapi-1.0.2/PyPlayerokAPI/stream/listener/chat_storage.py +34 -0
  33. pyplayerokapi-1.0.2/PyPlayerokAPI/stream/listener/deals_watcher.py +101 -0
  34. pyplayerokapi-1.0.2/PyPlayerokAPI/stream/listener/message_resolver.py +40 -0
  35. pyplayerokapi-1.0.2/PyPlayerokAPI/stream/listener/review_watcher.py +86 -0
  36. pyplayerokapi-1.0.2/PyPlayerokAPI/stream/listener/websocket_client.py +281 -0
  37. pyplayerokapi-1.0.2/PyPlayerokAPI/transport.py +177 -0
  38. pyplayerokapi-1.0.2/PyPlayerokAPI/types/__init__.py +2 -0
  39. pyplayerokapi-1.0.2/PyPlayerokAPI/types/enums.py +190 -0
  40. pyplayerokapi-1.0.2/PyPlayerokAPI/types/exceptions.py +78 -0
  41. pyplayerokapi-1.0.2/PyPlayerokAPI/types/queries.py +55 -0
  42. {pyplayerokapi-1.0.1 → pyplayerokapi-1.0.2}/PyPlayerokAPI.egg-info/PKG-INFO +12 -12
  43. pyplayerokapi-1.0.2/PyPlayerokAPI.egg-info/SOURCES.txt +48 -0
  44. pyplayerokapi-1.0.2/PyPlayerokAPI.egg-info/top_level.txt +1 -0
  45. {pyplayerokapi-1.0.1 → pyplayerokapi-1.0.2}/README.md +11 -11
  46. {pyplayerokapi-1.0.1 → pyplayerokapi-1.0.2}/setup.py +1 -1
  47. pyplayerokapi-1.0.1/PyPlayerokAPI.egg-info/SOURCES.txt +0 -8
  48. pyplayerokapi-1.0.1/PyPlayerokAPI.egg-info/top_level.txt +0 -1
  49. {pyplayerokapi-1.0.1 → pyplayerokapi-1.0.2}/PyPlayerokAPI.egg-info/dependency_links.txt +0 -0
  50. {pyplayerokapi-1.0.1 → pyplayerokapi-1.0.2}/PyPlayerokAPI.egg-info/requires.txt +0 -0
  51. {pyplayerokapi-1.0.1 → pyplayerokapi-1.0.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyPlayerokAPI
3
- Version: 1.0.1
3
+ Version: 1.0.2
4
4
  Summary: Неофициальная асинхронная Python-библиотека для взаимодействия с торговой площадкой Playerok через GraphQL API и систему потоковых событий.
5
5
  Home-page: https://github.com/kekch127/PyPlayerokAPI
6
6
  Author: kekch127
@@ -72,16 +72,16 @@ GraphQL • Streaming • Proxy • Production-ready transport
72
72
  ---
73
73
  # 📚 Содержание
74
74
 
75
- - [[#Особенности]]
76
- - [[#Требования]]
77
- - [[#Установка]]
78
- - [[#Аутентификация]]
79
- - [[#Структура библиотеки]]
80
- - [[#Примеры использования]]
81
- - [[#Предисловие]]
82
- - [[#Общие варианты использования]]
83
- - [[#Готовые варианты использования]]
84
- - [[#Дополнительная информация]]
75
+ - [Особенности](#Особенности)
76
+ - [Требования](#Требования)
77
+ - [Установка](#Установка)
78
+ - [Аутентификация](#Аутентификация)
79
+ - [Структура библиотеки](#Структура-библиотеки)
80
+ - [Примеры использования](#Примеры-использования)
81
+ - [Предисловие](#Предисловие)
82
+ - [Общие варианты использования](#Общие-варианты-использования)
83
+ - [Готовые варианты использования](#Готовые-варианты-использования)
84
+ - [Дополнительная информация](#Дополнительная-информация)
85
85
 
86
86
  ---
87
87
  # Особенности
@@ -106,7 +106,7 @@ GraphQL • Streaming • Proxy • Production-ready transport
106
106
 
107
107
  ### Через PyPI
108
108
  ```bash
109
- pip install pyplayerokapi
109
+ pip install PyPlayerokAPI
110
110
  ```
111
111
 
112
112
  ### С помощью pip
@@ -0,0 +1,15 @@
1
+ # -*- coding=utf-8 -*-
2
+
3
+ """
4
+ ___init__.py
5
+ PyPLayerokAPI - Playerok API client library for Python
6
+
7
+ Copyright 2026 kekch127
8
+ """
9
+
10
+ from .account import *
11
+ from .models import *
12
+ from .stream import *
13
+ from .types import *
14
+ from .graphql import *
15
+ from .transport import *
@@ -0,0 +1,30 @@
1
+ # -*- coding=utf-8 -*-
2
+
3
+ from .bank import BankMixin
4
+ from .chat import ChatMixin
5
+ from .deals import DealsMixin
6
+ from .games import GameMixin
7
+ from .items import ItemsMixin
8
+ from .profile import ProfileMixin
9
+ from .transactions import TransactionsMixin
10
+
11
+
12
+ class AccountClient(
13
+ BankMixin,
14
+ ChatMixin,
15
+ DealsMixin,
16
+ GameMixin,
17
+ ItemsMixin,
18
+ TransactionsMixin,
19
+ ProfileMixin,
20
+ ):
21
+ pass
22
+
23
+
24
+ # Обратная совместимость
25
+ Client = AccountClient
26
+
27
+ __all__ = [
28
+ "AccountClient",
29
+ "Client",
30
+ ]
@@ -0,0 +1,119 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import List
6
+
7
+ from .profile import ProfileMixin
8
+ from ..graphql import (
9
+ build_query_payload,
10
+ build_persisted_query_payload
11
+ )
12
+ from ..models.etc import SBPBankMember
13
+ from ..models.user import UserBankCardList
14
+ from ..types.enums import SortDirections
15
+
16
+
17
+ class BankMixin(ProfileMixin):
18
+ """
19
+ Миксин действий с картами и банками
20
+ """
21
+
22
+ def get_sbp_bank_members(self) -> List[SBPBankMember]:
23
+ """
24
+ Получает всех членов банка СБП
25
+
26
+ Returns:
27
+ List (SBPBankMember): Список моделей провайдера транзакции
28
+ """
29
+
30
+ payload = build_persisted_query_payload(
31
+ operation_name = "SbpBankMembers",
32
+ hash_key = "SbpBankMembers"
33
+ )
34
+
35
+ response = self.transport.request(
36
+ method = "get",
37
+ payload = payload
38
+ )
39
+
40
+ result = response.json().get("data", {}).get("sbpBankMembers")
41
+
42
+ return [SBPBankMember.model_validate(m) for m in result]
43
+
44
+
45
+ def get_verified_cards(
46
+ self,
47
+ count: int = 24,
48
+ after_cursor: str | None = None,
49
+ direction: SortDirections = SortDirections.ASC
50
+ ) -> UserBankCardList:
51
+ """
52
+ Получает верифицированные карты аккаунта
53
+
54
+ Args:
55
+ count (int, optional): Кол-во банковских карт, которые нужно получить (не более 24 за один запрос). Defaults to 24.
56
+ after_cursor (str | None, optional): Курсор, с которого будет идти парсинг (если нет - ищет с самого начала страницы). Defaults to None.
57
+ direction (SortDirections, optional): Тип сортировки банковских карт. Defaults to SortDirections.ASC.
58
+
59
+ Returns:
60
+ UserBankCardList: Страница банковских карт пользователя
61
+ """
62
+
63
+ payload = build_persisted_query_payload(
64
+ operation_name = "verifiedCards",
65
+ hash_key = "verifiedCards",
66
+ variables = {
67
+ "pagination": {
68
+ "first": count,
69
+ "after": after_cursor
70
+ },
71
+ "sort": {
72
+ "direction": direction.name
73
+ },
74
+ "field": "createdAt"
75
+ }
76
+ )
77
+
78
+ response = self.transport.request(
79
+ method = "get",
80
+ payload = payload
81
+ )
82
+
83
+ result = response.json().get("data", {}).get("verifiedCards")
84
+
85
+ return UserBankCardList.model_validate(result)
86
+
87
+
88
+ def delete_card(
89
+ self,
90
+ card_id: str
91
+ ) -> bool:
92
+ """
93
+ Удаляет карту из сохранённых в аккаунте
94
+
95
+ Args:
96
+ card_id (str): ID банковской карты
97
+
98
+ Returns:
99
+ bool: `True`, если карта удалилась, иначе `False`
100
+ """
101
+
102
+ payload = build_query_payload(
103
+ operation_name = "deleteCard",
104
+ query_key = "deleteCard",
105
+ variables = {
106
+ "input": {
107
+ "cardId": card_id
108
+ }
109
+ }
110
+ )
111
+
112
+ response = self.transport.request(
113
+ method = "post",
114
+ payload = payload
115
+ )
116
+
117
+ result = response.json().get("data", {}).get("deleteCard")
118
+
119
+ return result
@@ -0,0 +1,47 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional
6
+
7
+ from ..transport import Transport
8
+
9
+
10
+ class AccountBase:
11
+ """
12
+ Базовый класс аккаунта.
13
+
14
+ Отвечает только за:
15
+ - хранение токена
16
+ - инициализацию transport
17
+ - базовые настройки клиента
18
+
19
+ Не является Singleton.
20
+ """
21
+
22
+ def __init__(
23
+ self,
24
+ token: str,
25
+ user_agent: str = "",
26
+ proxy: Optional[str] = None,
27
+ requests_timeout: int = 15,
28
+ request_max_retries: int = 5,
29
+ ) -> None:
30
+
31
+ if not token:
32
+ raise ValueError("Token не может быть пустым")
33
+
34
+ self.token = token
35
+ self.user_agent = user_agent or (
36
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
37
+ "AppleWebKit/537.36 (KHTML, like Gecko) "
38
+ "Chrome/144.0.0.0 Safari/537.36"
39
+ )
40
+
41
+ self.transport = Transport(
42
+ token = self.token,
43
+ user_agent = self.user_agent,
44
+ proxy = proxy,
45
+ requests_timeout = requests_timeout,
46
+ request_max_retries = request_max_retries,
47
+ )
@@ -0,0 +1,269 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from typing import Optional
7
+
8
+ from .profile import ProfileMixin
9
+ from ..graphql import (
10
+ build_query_payload,
11
+ build_persisted_query_payload
12
+ )
13
+
14
+ from ..models.chat import ChatList, Chat, ChatMessageList, ChatMessage
15
+ from ..types.enums import ChatTypes, ChatStatuses
16
+ from ..types.exceptions import MissingAttributeError
17
+
18
+
19
+ class ChatMixin(ProfileMixin):
20
+ """
21
+ Миксин чатов
22
+ """
23
+
24
+ def get_chats(
25
+ self,
26
+ count: int = 24,
27
+ type: Optional[ChatTypes] = None,
28
+ status: Optional[ChatStatuses] = None,
29
+ after_cursor: Optional[str] = None
30
+ ) -> ChatList:
31
+ """
32
+ Получает все чаты аккаунта
33
+
34
+ Args:
35
+ count (int, optional): Кол-во чатов, которые нужно получить (не более 24 за один запрос). Defaults to 24.
36
+ type (Optional[ChatTypes], optional): Тип чатов, которые нужно получать (Все, если не указано). Defaults to None.
37
+ status (Optional[ChatStatuses], optional): Статус чатов, которые нужно получать (Любые, если не указано). Defaults to None.
38
+ after_cursor (Optional[str], optional): Курсор, с которого будет идти парсинг (если нет - ищет с самого начала страницы). Defaults to None.
39
+
40
+ Returns:
41
+ ChatList: Страница чатов
42
+ """
43
+
44
+ payload = build_persisted_query_payload(
45
+ operation_name = "userChats",
46
+ hash_key = "userChats",
47
+ variables = {
48
+ "pagination": {
49
+ "first": count,
50
+ "after": after_cursor
51
+ },
52
+ "filter": {
53
+ "userId": self.account_data.id,
54
+ "type": type.name if type else None,
55
+ "status": status.name if status else None
56
+ },
57
+ "hasSupportAccess": False
58
+ }
59
+ )
60
+
61
+ response = self.transport.request(
62
+ method = "get",
63
+ payload = payload
64
+ )
65
+
66
+ result = response.json().get("data", {}).get("chats")
67
+
68
+ return ChatList.model_validate(result)
69
+
70
+
71
+ def get_chat(
72
+ self,
73
+ chat_id: str
74
+ ) -> Chat:
75
+ """
76
+ Получить чат
77
+
78
+ Args:
79
+ chat_id (str): ID чата
80
+
81
+ Returns:
82
+ Chat: Модель чата
83
+ """
84
+
85
+ payload = build_persisted_query_payload(
86
+ operation_name = "chat",
87
+ hash_key = "chat",
88
+ variables = {
89
+ "id": chat_id,
90
+ "hasSupportAccess": False
91
+ }
92
+ )
93
+
94
+ response = self.transport.request(
95
+ method = "get",
96
+ payload = payload
97
+ )
98
+
99
+ result = response.json().get("data", {}).get("chat")
100
+
101
+ return Chat.model_validate(result)
102
+
103
+
104
+ def get_chat_by_username(
105
+ self,
106
+ username: str
107
+ ) -> Optional[Chat]:
108
+ """
109
+ Получить чат по username собеседнка
110
+
111
+ Args:
112
+ username (str): username собеседнка
113
+
114
+ Returns:
115
+ Optional[Chat]: Модель чата
116
+ """
117
+
118
+ next_cursor = None
119
+ while True:
120
+ chats = self.get_chats(after_cursor = next_cursor)
121
+ for chat in chats.chats:
122
+ if any(user for user in chat.users if user.username.lower() == username.lower()): # type: ignore
123
+ return chat
124
+
125
+ if not chats.page_info.has_next_page:
126
+ break
127
+
128
+ next_cursor = chats.page_info.end_cursor
129
+
130
+
131
+ def get_chat_messages(
132
+ self,
133
+ chat_id: str,
134
+ count: int = 24,
135
+ after_cursor: Optional[str] = None
136
+ ) -> ChatMessageList:
137
+ """
138
+ Получает сообщения чата
139
+
140
+ Args:
141
+ chat_id (str): ID чата
142
+ count (int, optional): Кол-во сообщений, которые нужно получить (не более 24 за один запрос). Defaults to 24.
143
+ after_cursor (Optional[str], optional): Курсор, с которого будет идти парсинг (если нет - ищет с самого начала страницы). Defaults to None.
144
+
145
+ Returns:
146
+ ChatMessageList: Страница сообщений
147
+ """
148
+
149
+ payload = build_persisted_query_payload(
150
+ operation_name = "chatMessages",
151
+ hash_key = "chatMessages",
152
+ variables = {
153
+ "pagination": {
154
+ "first": count,
155
+ "after": after_cursor
156
+ },
157
+ "filter": {
158
+ "chatId": chat_id
159
+ },
160
+ "hasSupportAccess": False,
161
+ "showForbiddenImage": True
162
+ }
163
+ )
164
+
165
+ response = self.transport.request(
166
+ method = "get",
167
+ payload = payload
168
+ )
169
+
170
+ result = response.json().get("data", {}).get("chatMessages")
171
+
172
+ return ChatMessageList.model_validate(result)
173
+
174
+
175
+ def mark_chat_as_read(
176
+ self,
177
+ chat_id: str
178
+ ) -> Chat:
179
+ """
180
+ Помечает чат как прочитанный (все сообщения)
181
+
182
+ Args:
183
+ chat_id (str): ID Чата
184
+
185
+ Returns:
186
+ Chat: Модель чата
187
+ """
188
+
189
+ payload = build_query_payload(
190
+ operation_name = "markChatAsRead",
191
+ query_key = "markChatAsRead",
192
+ variables = {
193
+ "input": {
194
+ "chatId": chat_id
195
+ }
196
+ }
197
+ )
198
+
199
+ response = self.transport.request(
200
+ method = "post",
201
+ payload = payload
202
+ )
203
+
204
+ result = response.json().get("data", {}).get("markChatAsRead")
205
+
206
+ return Chat.model_validate(result)
207
+
208
+
209
+ def send_message(
210
+ self,
211
+ chat_id: str,
212
+ text: Optional[str] = None,
213
+ photo_file_path: Optional[str] = None,
214
+ mark_chat_as_read: bool = False
215
+ ) -> ChatMessage:
216
+ """
217
+ Отправляет сообщение в чат.
218
+ Можно отправить текстовое сообщение `text` или фотографию `photo_file_path`.
219
+
220
+ Args:
221
+ chat_id (str): ID чата
222
+ text (Optional[str], optional): Текст сообщения. Defaults to None.
223
+ photo_file_path (Optional[str], optional): Путь к файлу фотографии. Defaults to None.
224
+ mark_chat_as_read (bool, optional): Пометить чат, как прочитанный перед отправкой. Defaults to False.
225
+
226
+ Returns:
227
+ ChatMessage: Модель отправленного сообщения
228
+ """
229
+
230
+ if not any([text, photo_file_path]):
231
+ raise MissingAttributeError("Не был указан обязательный параметр: text/photo_file_path")
232
+
233
+ if mark_chat_as_read:
234
+ self.mark_chat_as_read(chat_id)
235
+
236
+ pre_payload = build_query_payload(
237
+ operation_name = "createChatMessage",
238
+ query_key = "createChatMessage",
239
+ variables = {
240
+ "input": {
241
+ "chatId": chat_id
242
+ }
243
+ }
244
+ )
245
+
246
+ variables = pre_payload["variables"]
247
+
248
+ if photo_file_path:
249
+ variables["file"] = None # type: ignore
250
+
251
+ if text:
252
+ variables["input"]["text"] = text
253
+
254
+ files = {"1": open(photo_file_path, "rb")} if photo_file_path else None
255
+ map = {"1": ["variables.file"]} if photo_file_path else None
256
+
257
+ payload = pre_payload if not files else {
258
+ "operations": json.dumps(pre_payload),
259
+ "map": json.dumps(map)
260
+ }
261
+
262
+ response = self.transport.request(
263
+ method = "post",
264
+ payload = payload
265
+ )
266
+
267
+ result = response.json().get("data", {}).get("createChatMessage")
268
+
269
+ return ChatMessage.model_validate(result)
@@ -0,0 +1,135 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional, List
6
+
7
+ from .profile import ProfileMixin
8
+ from ..graphql import (
9
+ build_query_payload,
10
+ build_persisted_query_payload,
11
+ )
12
+ from ..models.item import ItemDealList, ItemDeal
13
+ from ..types.enums import ItemDealStatuses, ItemDealDirections
14
+
15
+
16
+ class DealsMixin(ProfileMixin):
17
+ """
18
+ Миксин сделок аккаунта.
19
+ """
20
+
21
+ def get_deals(
22
+ self,
23
+ count: int = 24,
24
+ statuses: Optional[List[ItemDealStatuses]] = None,
25
+ direction: Optional[ItemDealDirections] = None,
26
+ after_cursor: Optional[str] = None
27
+ ) -> ItemDealList:
28
+ """
29
+ Получает сделки аккаунта
30
+
31
+ Args:
32
+ count (int, optional): Кол-во сделок, которые нужно получить (не более 24 за один запрос). Defaults to 24.
33
+ statuses (Optional[List[ItemDealStatuses]], optional): Статусы сделок, которые нужно получать. Defaults to None.
34
+ direction (Optional[ItemDealDirections], optional): Направление сделок. Defaults to None.
35
+ after_cursor (str, optional): Курсор, с которого будет идти парсинг (если нет - ищет с самого начала страницы). Defaults to None.
36
+
37
+ Returns:
38
+ ItemDealList: Страница сделок
39
+ """
40
+ payload = build_persisted_query_payload(
41
+ operation_name = "deals",
42
+ hash_key = "deals",
43
+ variables = {
44
+ "pagination": {
45
+ "first": count,
46
+ "after": after_cursor
47
+ },
48
+ "filter": {
49
+ "userId": self.account_data.id,
50
+ "direction": direction.name if direction else None,
51
+ "status": [status.name for status in statuses] if statuses else None
52
+ },
53
+ "showForbiddenImage": True
54
+ }
55
+ )
56
+
57
+ response = self.transport.request(
58
+ method = "get",
59
+ payload = payload
60
+ )
61
+
62
+ result = response.json().get("data", {}).get("deals")
63
+
64
+ return ItemDealList.model_validate(result)
65
+
66
+
67
+ def get_deal(
68
+ self,
69
+ deal_id: str
70
+ ) -> ItemDeal:
71
+ """
72
+ Получает сделку
73
+
74
+ Args:
75
+ deal_id (str): ID сделки
76
+
77
+ Returns:
78
+ ItemDeal: Модель сделки
79
+ """
80
+ payload = build_persisted_query_payload(
81
+ operation_name = "deal",
82
+ hash_key = "deal",
83
+ variables = {
84
+ "id": deal_id,
85
+ "hasSupportAccess": False,
86
+ "showForbiddenImage": True
87
+ }
88
+ )
89
+
90
+ response = self.transport.request(
91
+ method = "get",
92
+ payload = payload
93
+ )
94
+
95
+ result = response.json().get("data", {}).get("deal")
96
+
97
+ return ItemDeal.model_validate(result)
98
+
99
+
100
+ def update_deal(
101
+ self,
102
+ deal_id: str,
103
+ new_status: ItemDealStatuses
104
+ ) -> ItemDeal:
105
+ """
106
+ Обновляет статус сделки
107
+ (используется, чтобы подтвердить, оформить возврат и т.д)
108
+
109
+ Args:
110
+ deal_id (str): ID сделки
111
+ new_status (ItemDealStatuses): Новый статус сделки
112
+
113
+ Returns:
114
+ ItemDeal: Модель сделки
115
+ """
116
+ payload = build_query_payload(
117
+ operation_name = "updateDeal",
118
+ query_key = "updateDeal",
119
+ variables = {
120
+ "input": {
121
+ "id": deal_id,
122
+ "status": new_status.name
123
+ }
124
+ }
125
+ )
126
+
127
+ response = self.transport.request(
128
+ method = "post",
129
+ payload = payload
130
+ )
131
+
132
+ result = response.json().get("data", {}).get("updateDeal")
133
+
134
+ return ItemDeal.model_validate(result)
135
+