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.
- {pyplayerokapi-1.0.1 → pyplayerokapi-1.0.2}/PKG-INFO +12 -12
- pyplayerokapi-1.0.2/PyPlayerokAPI/__init__.py +15 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/account/__init__.py +30 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/account/bank.py +119 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/account/base.py +47 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/account/chat.py +269 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/account/deals.py +135 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/account/games.py +333 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/account/items.py +429 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/account/profile.py +127 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/account/transactions.py +227 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/graphql.py +44 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/models/__init__.py +17 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/models/account.py +85 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/models/chat.py +205 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/models/etc.py +31 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/models/game.py +418 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/models/item.py +295 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/models/review.py +53 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/models/transaction.py +257 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/models/user.py +81 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/stream/__init__.py +15 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/stream/async_listener.py +224 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/stream/events/__init__.py +24 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/stream/events/account_event.py +18 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/stream/events/async_dispatcher.py +64 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/stream/events/async_router.py +54 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/stream/events/event_factory.py +86 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/stream/events/event_wrapper.py +23 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/stream/events/markers.py +92 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/stream/listener/__init__.py +16 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/stream/listener/chat_storage.py +34 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/stream/listener/deals_watcher.py +101 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/stream/listener/message_resolver.py +40 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/stream/listener/review_watcher.py +86 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/stream/listener/websocket_client.py +281 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/transport.py +177 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/types/__init__.py +2 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/types/enums.py +190 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/types/exceptions.py +78 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI/types/queries.py +55 -0
- {pyplayerokapi-1.0.1 → pyplayerokapi-1.0.2}/PyPlayerokAPI.egg-info/PKG-INFO +12 -12
- pyplayerokapi-1.0.2/PyPlayerokAPI.egg-info/SOURCES.txt +48 -0
- pyplayerokapi-1.0.2/PyPlayerokAPI.egg-info/top_level.txt +1 -0
- {pyplayerokapi-1.0.1 → pyplayerokapi-1.0.2}/README.md +11 -11
- {pyplayerokapi-1.0.1 → pyplayerokapi-1.0.2}/setup.py +1 -1
- pyplayerokapi-1.0.1/PyPlayerokAPI.egg-info/SOURCES.txt +0 -8
- pyplayerokapi-1.0.1/PyPlayerokAPI.egg-info/top_level.txt +0 -1
- {pyplayerokapi-1.0.1 → pyplayerokapi-1.0.2}/PyPlayerokAPI.egg-info/dependency_links.txt +0 -0
- {pyplayerokapi-1.0.1 → pyplayerokapi-1.0.2}/PyPlayerokAPI.egg-info/requires.txt +0 -0
- {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.
|
|
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
|
|
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
|
+
|