maxapi-python 0.1.0__py3-none-any.whl → 0.1.2__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.
- maxapi_python-0.1.2.dist-info/METADATA +163 -0
- maxapi_python-0.1.2.dist-info/RECORD +25 -0
- {maxapi_python-0.1.0.dist-info → maxapi_python-0.1.2.dist-info}/WHEEL +1 -2
- pymax/core.py +37 -481
- pymax/crud.py +1 -1
- pymax/files.py +85 -0
- pymax/filters.py +38 -0
- pymax/interfaces.py +67 -0
- pymax/mixins/__init__.py +18 -0
- pymax/mixins/auth.py +81 -0
- pymax/mixins/channel.py +25 -0
- pymax/mixins/group.py +220 -0
- pymax/mixins/handler.py +60 -0
- pymax/mixins/message.py +293 -0
- pymax/mixins/self.py +38 -0
- pymax/mixins/user.py +82 -0
- pymax/mixins/websocket.py +242 -0
- pymax/payloads.py +175 -0
- pymax/static.py +143 -19
- pymax/types.py +134 -29
- pymax/utils.py +38 -0
- maxapi_python-0.1.0.dist-info/METADATA +0 -110
- maxapi_python-0.1.0.dist-info/RECORD +0 -12
- maxapi_python-0.1.0.dist-info/top_level.txt +0 -1
- {maxapi_python-0.1.0.dist-info → maxapi_python-0.1.2.dist-info}/licenses/LICENSE +0 -0
pymax/payloads.py
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
from typing import Any, Literal
|
2
|
+
|
3
|
+
from pydantic import BaseModel, Field
|
4
|
+
|
5
|
+
from pymax.static import AttachType, AuthType
|
6
|
+
|
7
|
+
|
8
|
+
def to_camel(string: str) -> str:
|
9
|
+
parts = string.split("_")
|
10
|
+
return parts[0] + "".join(word.capitalize() for word in parts[1:])
|
11
|
+
|
12
|
+
|
13
|
+
class CamelModel(BaseModel):
|
14
|
+
model_config = {
|
15
|
+
"alias_generator": to_camel,
|
16
|
+
"populate_by_name": True,
|
17
|
+
}
|
18
|
+
|
19
|
+
|
20
|
+
class BaseWebSocketMessage(BaseModel):
|
21
|
+
ver: int = 11
|
22
|
+
cmd: int
|
23
|
+
seq: int
|
24
|
+
opcode: int
|
25
|
+
payload: dict[str, Any]
|
26
|
+
|
27
|
+
|
28
|
+
class RequestCodePayload(CamelModel):
|
29
|
+
phone: str
|
30
|
+
type: AuthType = AuthType.START_AUTH
|
31
|
+
language: str = "ru"
|
32
|
+
|
33
|
+
|
34
|
+
class SendCodePayload(CamelModel):
|
35
|
+
token: str
|
36
|
+
verify_code: str
|
37
|
+
auth_token_type: AuthType = AuthType.CHECK_CODE
|
38
|
+
|
39
|
+
|
40
|
+
class SyncPayload(CamelModel):
|
41
|
+
interactive: bool = True
|
42
|
+
token: str
|
43
|
+
chats_sync: int = 0
|
44
|
+
contacts_sync: int = 0
|
45
|
+
presence_sync: int = 0
|
46
|
+
drafts_sync: int = 0
|
47
|
+
chats_count: int = 40
|
48
|
+
|
49
|
+
|
50
|
+
class ReplyLink(CamelModel):
|
51
|
+
type: str = "REPLY"
|
52
|
+
message_id: str
|
53
|
+
|
54
|
+
|
55
|
+
class UploadPhotoPayload(CamelModel):
|
56
|
+
count: int = 1
|
57
|
+
|
58
|
+
|
59
|
+
class AttachPhotoPayload(CamelModel):
|
60
|
+
type: AttachType = Field(AttachType.PHOTO, alias="_type")
|
61
|
+
photo_token: str
|
62
|
+
|
63
|
+
|
64
|
+
class SendMessagePayloadMessage(CamelModel):
|
65
|
+
text: str
|
66
|
+
cid: int
|
67
|
+
elements: list[Any]
|
68
|
+
attaches: list[dict[str, Any]]
|
69
|
+
link: ReplyLink | None = None
|
70
|
+
|
71
|
+
|
72
|
+
class SendMessagePayload(CamelModel):
|
73
|
+
chat_id: int
|
74
|
+
message: SendMessagePayloadMessage
|
75
|
+
notify: bool = False
|
76
|
+
|
77
|
+
|
78
|
+
class EditMessagePayload(CamelModel):
|
79
|
+
chat_id: int
|
80
|
+
message_id: int
|
81
|
+
text: str
|
82
|
+
elements: list[Any]
|
83
|
+
attaches: list[Any]
|
84
|
+
|
85
|
+
|
86
|
+
class DeleteMessagePayload(CamelModel):
|
87
|
+
chat_id: int
|
88
|
+
message_ids: list[int]
|
89
|
+
for_me: bool = False
|
90
|
+
|
91
|
+
|
92
|
+
class FetchContactsPayload(CamelModel):
|
93
|
+
contact_ids: list[int]
|
94
|
+
|
95
|
+
|
96
|
+
class FetchHistoryPayload(CamelModel):
|
97
|
+
chat_id: int
|
98
|
+
from_time: int = Field(alias="from")
|
99
|
+
forward: int
|
100
|
+
backward: int = 200
|
101
|
+
get_messages: bool = True
|
102
|
+
|
103
|
+
|
104
|
+
class ChangeProfilePayload(CamelModel):
|
105
|
+
first_name: str
|
106
|
+
last_name: str | None = None
|
107
|
+
description: str | None = None
|
108
|
+
|
109
|
+
|
110
|
+
class ResolveLinkPayload(CamelModel):
|
111
|
+
link: str
|
112
|
+
|
113
|
+
|
114
|
+
class PinMessagePayload(CamelModel):
|
115
|
+
chat_id: int
|
116
|
+
notify_pin: bool
|
117
|
+
pin_message_id: int
|
118
|
+
|
119
|
+
|
120
|
+
class CreateGroupAttach(CamelModel):
|
121
|
+
type: Literal["CONTROL"] = Field("CONTROL", alias="_type")
|
122
|
+
event: str = "new"
|
123
|
+
chat_type: str = "CHAT"
|
124
|
+
title: str
|
125
|
+
user_ids: list[int]
|
126
|
+
|
127
|
+
|
128
|
+
class CreateGroupMessage(CamelModel):
|
129
|
+
cid: int
|
130
|
+
attaches: list[CreateGroupAttach]
|
131
|
+
|
132
|
+
|
133
|
+
class CreateGroupPayload(CamelModel):
|
134
|
+
message: CreateGroupMessage
|
135
|
+
notify: bool = True
|
136
|
+
|
137
|
+
|
138
|
+
class InviteUsersPayload(CamelModel):
|
139
|
+
chat_id: int
|
140
|
+
user_ids: list[int]
|
141
|
+
show_history: bool
|
142
|
+
operation: str = "add"
|
143
|
+
|
144
|
+
|
145
|
+
class RemoveUsersPayload(CamelModel):
|
146
|
+
chat_id: int
|
147
|
+
user_ids: list[int]
|
148
|
+
operation: str = "remove"
|
149
|
+
clean_msg_period: int
|
150
|
+
|
151
|
+
|
152
|
+
class ChangeGroupSettingsOptions(BaseModel):
|
153
|
+
ONLY_OWNER_CAN_CHANGE_ICON_TITLE: bool | None
|
154
|
+
ALL_CAN_PIN_MESSAGE: bool | None
|
155
|
+
ONLY_ADMIN_CAN_ADD_MEMBER: bool | None
|
156
|
+
ONLY_ADMIN_CAN_CALL: bool | None
|
157
|
+
MEMBERS_CAN_SEE_PRIVATE_LINK: bool | None
|
158
|
+
|
159
|
+
|
160
|
+
class ChangeGroupSettingsPayload(CamelModel):
|
161
|
+
chat_id: int
|
162
|
+
options: ChangeGroupSettingsOptions
|
163
|
+
|
164
|
+
|
165
|
+
class ChangeGroupProfilePayload(CamelModel):
|
166
|
+
chat_id: int
|
167
|
+
theme: str | None
|
168
|
+
description: str | None
|
169
|
+
|
170
|
+
|
171
|
+
class GetGroupMembersPayload(CamelModel):
|
172
|
+
type: str = "MEMBER"
|
173
|
+
marker: int
|
174
|
+
chat_id: int
|
175
|
+
count: int
|
pymax/static.py
CHANGED
@@ -3,27 +3,144 @@ from enum import Enum, IntEnum
|
|
3
3
|
|
4
4
|
class Opcode(IntEnum):
|
5
5
|
PING = 1
|
6
|
-
|
7
|
-
|
6
|
+
DEBUG = 2
|
7
|
+
RECONNECT = 3
|
8
|
+
LOG = 5
|
9
|
+
SESSION_INIT = 6
|
8
10
|
PROFILE = 16
|
9
|
-
|
10
|
-
|
11
|
-
|
11
|
+
AUTH_REQUEST = 17
|
12
|
+
AUTH = 18
|
13
|
+
LOGIN = 19
|
14
|
+
LOGOUT = 20
|
15
|
+
SYNC = 21
|
16
|
+
CONFIG = 22
|
17
|
+
AUTH_CONFIRM = 23
|
18
|
+
PRESET_AVATARS = 25
|
19
|
+
ASSETS_GET = 26
|
12
20
|
UNKNOWN_26 = 26
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
21
|
+
ASSETS_UPDATE = 27
|
22
|
+
ASSETS_GET_BY_IDS = 28
|
23
|
+
ASSETS_ADD = 29
|
24
|
+
SEARCH_FEEDBACK = 31
|
25
|
+
CONTACT_INFO = 32
|
26
|
+
CONTACT_ADD = 33
|
27
|
+
CONTACT_UPDATE = 34
|
28
|
+
CONTACT_PRESENCE = 35
|
29
|
+
CONTACT_LIST = 36
|
30
|
+
CONTACT_SEARCH = 37
|
31
|
+
CONTACT_MUTUAL = 38
|
32
|
+
CONTACT_PHOTOS = 39
|
33
|
+
CONTACT_SORT = 40
|
34
|
+
CONTACT_VERIFY = 42
|
35
|
+
REMOVE_CONTACT_PHOTO = 43
|
36
|
+
CONTACT_INFO_BY_PHONE = 46
|
37
|
+
CHAT_INFO = 48
|
38
|
+
CHAT_HISTORY = 49
|
39
|
+
CHAT_MARK = 50
|
40
|
+
CHAT_MEDIA = 51
|
41
|
+
CHAT_DELETE = 52
|
42
|
+
CHATS_LIST = 53
|
43
|
+
CHAT_CLEAR = 54
|
44
|
+
CHAT_UPDATE = 55
|
45
|
+
CHAT_CHECK_LINK = 56
|
46
|
+
CHAT_JOIN = 57
|
47
|
+
CHAT_LEAVE = 58
|
48
|
+
CHAT_MEMBERS = 59
|
49
|
+
PUBLIC_SEARCH = 60
|
50
|
+
CHAT_CLOSE = 61
|
51
|
+
CHAT_CREATE = 63
|
52
|
+
MSG_SEND = 64
|
53
|
+
MSG_TYPING = 65
|
54
|
+
MSG_DELETE = 66
|
55
|
+
MSG_EDIT = 67
|
56
|
+
CHAT_SEARCH = 68
|
57
|
+
MSG_SHARE_PREVIEW = 70
|
58
|
+
MSG_GET = 71
|
59
|
+
MSG_SEARCH_TOUCH = 72
|
60
|
+
MSG_SEARCH = 73
|
61
|
+
MSG_GET_STAT = 74
|
62
|
+
CHAT_SUBSCRIBE = 75
|
63
|
+
VIDEO_CHAT_START = 76
|
64
|
+
CHAT_MEMBERS_UPDATE = 77
|
65
|
+
VIDEO_CHAT_HISTORY = 79
|
66
|
+
PHOTO_UPLOAD = 80
|
67
|
+
STICKER_UPLOAD = 81
|
68
|
+
VIDEO_UPLOAD = 82
|
69
|
+
VIDEO_PLAY = 83
|
70
|
+
CHAT_PIN_SET_VISIBILITY = 86
|
71
|
+
FILE_UPLOAD = 87
|
72
|
+
FILE_DOWNLOAD = 88
|
73
|
+
LINK_INFO = 89
|
74
|
+
MSG_DELETE_RANGE = 92
|
75
|
+
SESSIONS_INFO = 96
|
76
|
+
SESSIONS_CLOSE = 97
|
77
|
+
PHONE_BIND_REQUEST = 98
|
78
|
+
PHONE_BIND_CONFIRM = 99
|
79
|
+
CONFIRM_PRESENT = 101
|
80
|
+
GET_INBOUND_CALLS = 103
|
81
|
+
EXTERNAL_CALLBACK = 105
|
82
|
+
AUTH_VALIDATE_PASSWORD = 107
|
83
|
+
AUTH_VALIDATE_HINT = 108
|
84
|
+
AUTH_VERIFY_EMAIL = 109
|
85
|
+
AUTH_CHECK_EMAIL = 110
|
86
|
+
AUTH_SET_2FA = 111
|
87
|
+
AUTH_CREATE_TRACK = 112
|
88
|
+
AUTH_LOGIN_CHECK_PASSWORD = 115
|
89
|
+
CHAT_COMPLAIN = 117
|
90
|
+
MSG_SEND_CALLBACK = 118
|
91
|
+
SUSPEND_BOT = 119
|
92
|
+
LOCATION_STOP = 124
|
93
|
+
LOCATION_SEND = 125
|
94
|
+
LOCATION_REQUEST = 126
|
95
|
+
GET_LAST_MENTIONS = 127
|
96
|
+
NOTIF_MESSAGE = 128
|
97
|
+
NOTIF_TYPING = 129
|
98
|
+
NOTIF_MARK = 130
|
99
|
+
NOTIF_CONTACT = 131
|
100
|
+
NOTIF_PRESENCE = 132
|
101
|
+
NOTIF_CONFIG = 134
|
102
|
+
NOTIF_CHAT = 135
|
103
|
+
NOTIF_ATTACH = 136
|
104
|
+
NOTIF_CALL_START = 137
|
105
|
+
NOTIF_CONTACT_SORT = 139
|
106
|
+
NOTIF_MSG_DELETE_RANGE = 140
|
107
|
+
NOTIF_MSG_DELETE = 142
|
108
|
+
NOTIF_CALLBACK_ANSWER = 143
|
109
|
+
CHAT_BOT_COMMANDS = 144
|
110
|
+
BOT_INFO = 145
|
111
|
+
NOTIF_LOCATION = 147
|
112
|
+
NOTIF_LOCATION_REQUEST = 148
|
113
|
+
NOTIF_ASSETS_UPDATE = 150
|
114
|
+
NOTIF_DRAFT = 152
|
115
|
+
NOTIF_DRAFT_DISCARD = 153
|
116
|
+
NOTIF_MSG_DELAYED = 154
|
117
|
+
NOTIF_MSG_REACTIONS_CHANGED = 155
|
118
|
+
NOTIF_MSG_YOU_REACTED = 156
|
119
|
+
CALLS_TOKEN = 158
|
120
|
+
NOTIF_PROFILE = 159
|
121
|
+
WEB_APP_INIT_DATA = 160
|
122
|
+
DRAFT_SAVE = 176
|
123
|
+
DRAFT_DISCARD = 177
|
124
|
+
MSG_REACTION = 178
|
125
|
+
MSG_CANCEL_REACTION = 179
|
126
|
+
MSG_GET_REACTIONS = 180
|
127
|
+
MSG_GET_DETAILED_REACTIONS = 181
|
128
|
+
STICKER_CREATE = 193
|
129
|
+
STICKER_SUGGEST = 194
|
130
|
+
VIDEO_CHAT_MEMBERS = 195
|
131
|
+
CHAT_HIDE = 196
|
132
|
+
CHAT_SEARCH_COMMON_PARTICIPANTS = 198
|
133
|
+
PROFILE_DELETE = 199
|
134
|
+
PROFILE_DELETE_TIME = 200
|
135
|
+
ASSETS_REMOVE = 259
|
136
|
+
ASSETS_MOVE = 260
|
137
|
+
ASSETS_LIST_MODIFY = 261
|
138
|
+
FOLDERS_GET = 272
|
139
|
+
FOLDERS_GET_BY_ID = 273
|
140
|
+
FOLDERS_UPDATE = 274
|
141
|
+
FOLDERS_REORDER = 275
|
142
|
+
FOLDERS_DELETE = 276
|
143
|
+
NOTIF_FOLDERS = 277
|
27
144
|
|
28
145
|
|
29
146
|
class ChatType(str, Enum):
|
@@ -69,6 +186,13 @@ class DeviceType(str, Enum):
|
|
69
186
|
IOS = "IOS"
|
70
187
|
|
71
188
|
|
189
|
+
class AttachType(str, Enum):
|
190
|
+
PHOTO = "PHOTO"
|
191
|
+
VIDEO = "VIDEO"
|
192
|
+
FILE = "FILE"
|
193
|
+
STICKER = "STICKER"
|
194
|
+
|
195
|
+
|
72
196
|
class Constants(Enum):
|
73
197
|
PHONE_REGEX = r"^\+?\d{10,15}$"
|
74
198
|
WEBSOCKET_URI = "wss://ws-api.oneme.ru/websocket"
|
pymax/types.py
CHANGED
@@ -1,10 +1,83 @@
|
|
1
|
-
from typing import Any
|
1
|
+
from typing import Any, override
|
2
2
|
|
3
|
-
from .static import
|
3
|
+
from .static import (
|
4
|
+
AccessType,
|
5
|
+
AttachType,
|
6
|
+
ChatType,
|
7
|
+
ElementType,
|
8
|
+
MessageStatus,
|
9
|
+
MessageType,
|
10
|
+
)
|
11
|
+
|
12
|
+
|
13
|
+
class Names:
|
14
|
+
def __init__(
|
15
|
+
self, name: str, first_name: str, last_name: str | None, type: str
|
16
|
+
) -> None:
|
17
|
+
self.name = name
|
18
|
+
self.first_name = first_name
|
19
|
+
self.last_name = last_name
|
20
|
+
self.type = type
|
21
|
+
|
22
|
+
@classmethod
|
23
|
+
def from_dict(cls, data: dict[str, Any]) -> "Names":
|
24
|
+
return cls(
|
25
|
+
name=data["name"],
|
26
|
+
first_name=data["firstName"],
|
27
|
+
last_name=data.get("lastName"),
|
28
|
+
type=data["type"],
|
29
|
+
)
|
30
|
+
|
31
|
+
@override
|
32
|
+
def __repr__(self) -> str:
|
33
|
+
return f"Names(name={self.name!r}, first_name={self.first_name!r}, last_name={self.last_name!r}, type={self.type!r})"
|
34
|
+
|
35
|
+
@override
|
36
|
+
def __str__(self) -> str:
|
37
|
+
return self.name
|
38
|
+
|
39
|
+
|
40
|
+
class Me:
|
41
|
+
def __init__(
|
42
|
+
self,
|
43
|
+
id: int,
|
44
|
+
account_status: int,
|
45
|
+
phone: str,
|
46
|
+
names: list[Names],
|
47
|
+
update_time: int,
|
48
|
+
options: list[str] | None = None,
|
49
|
+
) -> None:
|
50
|
+
self.id = id
|
51
|
+
self.account_status = account_status
|
52
|
+
self.phone = phone
|
53
|
+
self.update_time = update_time
|
54
|
+
self.options = options
|
55
|
+
self.names = names
|
56
|
+
|
57
|
+
@classmethod
|
58
|
+
def from_dict(cls, data: dict[str, Any]) -> "Me":
|
59
|
+
return cls(
|
60
|
+
id=data["id"],
|
61
|
+
account_status=data["accountStatus"],
|
62
|
+
phone=data["phone"],
|
63
|
+
names=[Names.from_dict(n) for n in data["names"]],
|
64
|
+
update_time=data["updateTime"],
|
65
|
+
options=data.get("options"),
|
66
|
+
)
|
67
|
+
|
68
|
+
@override
|
69
|
+
def __repr__(self) -> str:
|
70
|
+
return f"Me(id={self.id!r}, account_status={self.account_status!r}, phone={self.phone!r}, names={self.names!r}, update_time={self.update_time!r}, options={self.options!r})"
|
71
|
+
|
72
|
+
@override
|
73
|
+
def __str__(self) -> str:
|
74
|
+
return f"Me {self.id}: {', '.join(str(n) for n in self.names)}"
|
4
75
|
|
5
76
|
|
6
77
|
class Element:
|
7
|
-
def __init__(
|
78
|
+
def __init__(
|
79
|
+
self, type: ElementType | str, length: int, from_: int | None = None
|
80
|
+
) -> None:
|
8
81
|
self.type = type
|
9
82
|
self.length = length
|
10
83
|
self.from_ = from_
|
@@ -13,9 +86,13 @@ class Element:
|
|
13
86
|
def from_dict(cls, data: dict[Any, Any]) -> "Element":
|
14
87
|
return cls(type=data["type"], length=data["length"], from_=data.get("from"))
|
15
88
|
|
89
|
+
@override
|
16
90
|
def __repr__(self) -> str:
|
17
|
-
return
|
91
|
+
return (
|
92
|
+
f"Element(type={self.type!r}, length={self.length!r}, from_={self.from_!r})"
|
93
|
+
)
|
18
94
|
|
95
|
+
@override
|
19
96
|
def __str__(self) -> str:
|
20
97
|
return f"{self.type}({self.length})"
|
21
98
|
|
@@ -60,12 +137,14 @@ class Message:
|
|
60
137
|
reaction_info=data.get("reactionInfo"),
|
61
138
|
)
|
62
139
|
|
140
|
+
@override
|
63
141
|
def __repr__(self) -> str:
|
64
142
|
return (
|
65
143
|
f"Message(id={self.id!r}, sender={self.sender!r}, text={self.text!r}, "
|
66
144
|
f"type={self.type!r}, status={self.status!r}, elements={self.elements!r})"
|
67
145
|
)
|
68
146
|
|
147
|
+
@override
|
69
148
|
def __str__(self) -> str:
|
70
149
|
return f"Message {self.id} from {self.sender}: {self.text}"
|
71
150
|
|
@@ -130,9 +209,11 @@ class Dialog:
|
|
130
209
|
participants=data["participants"],
|
131
210
|
)
|
132
211
|
|
212
|
+
@override
|
133
213
|
def __repr__(self) -> str:
|
134
214
|
return f"Dialog(id={self.id!r}, owner={self.owner!r}, type={self.type!r}, last_message={self.last_message!r})"
|
135
215
|
|
216
|
+
@override
|
136
217
|
def __str__(self) -> str:
|
137
218
|
return f"Dialog {self.id} ({self.type})"
|
138
219
|
|
@@ -199,10 +280,14 @@ class Chat:
|
|
199
280
|
@classmethod
|
200
281
|
def from_dict(cls, data: dict[Any, Any]) -> "Chat":
|
201
282
|
raw_admins = data.get("adminParticipants", {}) or {}
|
202
|
-
admin_participants: dict[int, dict[Any, Any]] = {
|
283
|
+
admin_participants: dict[int, dict[Any, Any]] = {
|
284
|
+
int(k): v for k, v in raw_admins.items()
|
285
|
+
}
|
203
286
|
raw_participants = data.get("participants", {}) or {}
|
204
287
|
participants: dict[int, int] = {int(k): v for k, v in raw_participants.items()}
|
205
|
-
last_msg =
|
288
|
+
last_msg = (
|
289
|
+
Message.from_dict(data["lastMessage"]) if data.get("lastMessage") else None
|
290
|
+
)
|
206
291
|
return cls(
|
207
292
|
participants_count=data.get("participantsCount", 0),
|
208
293
|
access=AccessType(data.get("access", AccessType.PUBLIC.value)),
|
@@ -233,44 +318,25 @@ class Chat:
|
|
233
318
|
cid=data.get("cid", 0),
|
234
319
|
)
|
235
320
|
|
321
|
+
@override
|
236
322
|
def __repr__(self) -> str:
|
237
323
|
return f"Chat(id={self.id!r}, title={self.title!r}, type={self.type!r})"
|
238
324
|
|
325
|
+
@override
|
239
326
|
def __str__(self) -> str:
|
240
327
|
return f"{self.title} ({self.type})"
|
241
328
|
|
242
329
|
|
243
330
|
class Channel(Chat):
|
331
|
+
@override
|
244
332
|
def __repr__(self) -> str:
|
245
333
|
return f"Channel(id={self.id!r}, title={self.title!r})"
|
246
334
|
|
335
|
+
@override
|
247
336
|
def __str__(self) -> str:
|
248
337
|
return f"Channel: {self.title}"
|
249
338
|
|
250
339
|
|
251
|
-
class Names:
|
252
|
-
def __init__(self, name: str, first_name: str, last_name: str | None, type: str) -> None:
|
253
|
-
self.name = name
|
254
|
-
self.first_name = first_name
|
255
|
-
self.last_name = last_name
|
256
|
-
self.type = type
|
257
|
-
|
258
|
-
@classmethod
|
259
|
-
def from_dict(cls, data: dict[str, Any]) -> "Names":
|
260
|
-
return cls(
|
261
|
-
name=data["name"],
|
262
|
-
first_name=data["firstName"],
|
263
|
-
last_name=data.get("lastName"),
|
264
|
-
type=data["type"],
|
265
|
-
)
|
266
|
-
|
267
|
-
def __repr__(self) -> str:
|
268
|
-
return f"Names(name={self.name!r}, first_name={self.first_name!r}, last_name={self.last_name!r}, type={self.type!r})"
|
269
|
-
|
270
|
-
def __str__(self) -> str:
|
271
|
-
return self.name
|
272
|
-
|
273
|
-
|
274
340
|
class User:
|
275
341
|
def __init__(
|
276
342
|
self,
|
@@ -320,8 +386,47 @@ class User:
|
|
320
386
|
menu_button=data.get("menuButton"),
|
321
387
|
)
|
322
388
|
|
389
|
+
@override
|
323
390
|
def __repr__(self) -> str:
|
324
391
|
return f"User(id={self.id!r}, names={self.names!r}, status={self.account_status!r})"
|
325
392
|
|
393
|
+
@override
|
326
394
|
def __str__(self) -> str:
|
327
395
|
return f"User {self.id}: {', '.join(str(n) for n in self.names)}"
|
396
|
+
|
397
|
+
|
398
|
+
class Attach:
|
399
|
+
def __init__(
|
400
|
+
self,
|
401
|
+
_type: AttachType,
|
402
|
+
video_id: int | None = None,
|
403
|
+
photo_token: str | None = None,
|
404
|
+
file_id: int | None = None,
|
405
|
+
token: str | None = None,
|
406
|
+
) -> None:
|
407
|
+
self.type = _type
|
408
|
+
self.video_id = video_id
|
409
|
+
self.photo_token = photo_token
|
410
|
+
self.file_id = file_id
|
411
|
+
self.token = token
|
412
|
+
|
413
|
+
@classmethod
|
414
|
+
def from_dict(cls, data: dict[str, Any]) -> "Attach":
|
415
|
+
return cls(
|
416
|
+
_type=AttachType(data["type"]),
|
417
|
+
video_id=data.get("videoId"),
|
418
|
+
photo_token=data.get("photoToken"),
|
419
|
+
file_id=data.get("fileId"),
|
420
|
+
token=data.get("token"),
|
421
|
+
)
|
422
|
+
|
423
|
+
@override
|
424
|
+
def __repr__(self) -> str:
|
425
|
+
return (
|
426
|
+
f"Attach(type={self.type!r}, video_id={self.video_id!r}, "
|
427
|
+
f"photo_token={self.photo_token!r}, file_id={self.file_id!r}, token={self.token!r})"
|
428
|
+
)
|
429
|
+
|
430
|
+
@override
|
431
|
+
def __str__(self) -> str:
|
432
|
+
return f"Attach: {self.type}"
|
pymax/utils.py
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
3
|
+
import lz4.block
|
4
|
+
import msgpack
|
5
|
+
|
6
|
+
|
7
|
+
def unpack_packet(data: bytes) -> None | dict[str, Any]:
|
8
|
+
ver = int.from_bytes(data[0:1], "big")
|
9
|
+
cmd = int.from_bytes(data[1:3], "big")
|
10
|
+
seq = int.from_bytes(data[3:4], "big")
|
11
|
+
opcode = int.from_bytes(data[4:6], "big")
|
12
|
+
packed_len = int.from_bytes(data[6:10], "big", signed=False)
|
13
|
+
comp_flag = packed_len >> 24
|
14
|
+
payload_length = packed_len & 0xFFFFFF
|
15
|
+
payload_bytes = data[10 : 10 + payload_length]
|
16
|
+
if comp_flag != 0:
|
17
|
+
compressed_data = payload_bytes
|
18
|
+
try:
|
19
|
+
payload_bytes = lz4.block.decompress(compressed_data, uncompressed_size=255)
|
20
|
+
except lz4.block.LZ4BlockError:
|
21
|
+
return None
|
22
|
+
payload = msgpack.unpackb(payload_bytes, raw=False)
|
23
|
+
return {"ver": ver, "cmd": cmd, "seq": seq, "opcode": opcode, "payload": payload}
|
24
|
+
|
25
|
+
|
26
|
+
# ToDo: Add lz4 compression
|
27
|
+
def pack_packet(
|
28
|
+
ver: int, cmd: int, seq: int, opcode: int, payload: dict[str, Any]
|
29
|
+
) -> bytes:
|
30
|
+
ver_b = ver.to_bytes(1, "big")
|
31
|
+
cmd_b = cmd.to_bytes(2, "big")
|
32
|
+
seq_b = seq.to_bytes(1, "big")
|
33
|
+
opcode_b = opcode.to_bytes(2, "big")
|
34
|
+
payload_bytes = msgpack.packb(payload)
|
35
|
+
if payload_bytes is None:
|
36
|
+
payload_bytes = b""
|
37
|
+
payload_len_b = len(payload_bytes).to_bytes(4, "big")
|
38
|
+
return ver_b + cmd_b + seq_b + opcode_b + payload_len_b + payload_bytes
|