maxapi-python 2.3.1__py3-none-any.whl → 2.4.1__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-2.3.1.dist-info → maxapi_python-2.4.1.dist-info}/METADATA +8 -4
- {maxapi_python-2.3.1.dist-info → maxapi_python-2.4.1.dist-info}/RECORD +79 -66
- {maxapi_python-2.3.1.dist-info → maxapi_python-2.4.1.dist-info}/WHEEL +1 -1
- pymax/__init__.py +9 -2
- pymax/_data/apk_fingerprints.json +767 -0
- pymax/_data/rootca_ssl_rsa2022.crt +33 -0
- pymax/api/auth/enums.py +1 -0
- pymax/api/auth/payloads.py +28 -2
- pymax/api/auth/service.py +100 -3
- pymax/api/chats/__init__.py +1 -0
- pymax/api/chats/enums.py +15 -0
- pymax/api/chats/payloads.py +18 -2
- pymax/api/chats/service.py +43 -1
- pymax/api/messages/__init__.py +1 -1
- pymax/api/messages/payloads.py +21 -7
- pymax/api/messages/service.py +161 -24
- pymax/api/self/__init__.py +2 -1
- pymax/api/self/enums.py +9 -0
- pymax/api/self/payloads.py +40 -1
- pymax/api/self/service.py +37 -0
- pymax/api/session/service.py +21 -8
- pymax/api/uploads/payloads.py +39 -1
- pymax/api/uploads/service.py +247 -55
- pymax/app.py +91 -18
- pymax/auth/__init__.py +2 -0
- pymax/auth/exceptions.py +9 -0
- pymax/auth/providers.py +6 -2
- pymax/auth/sms.py +11 -1
- pymax/base.py +90 -8
- pymax/client.py +39 -10
- pymax/client_web.py +17 -14
- pymax/config.py +98 -19
- pymax/connection/connection.py +29 -18
- pymax/dispatch/enums.py +1 -0
- pymax/dispatch/mapping.py +3 -1
- pymax/dispatch/resolvers.py +7 -1
- pymax/files/__init__.py +4 -1
- pymax/files/base.py +58 -1
- pymax/files/video.py +51 -1
- pymax/files/voice.py +53 -0
- pymax/fingerprint/__init__.py +1 -0
- pymax/fingerprint/fingerprint.py +30 -0
- pymax/fingerprint/models.py +12 -0
- pymax/infra/auth.py +4 -0
- pymax/infra/bots.py +3 -3
- pymax/infra/chat.py +47 -5
- pymax/infra/message.py +49 -14
- pymax/infra/protocol.py +5 -1
- pymax/infra/self.py +25 -1
- pymax/protocol/enums.py +13 -0
- pymax/protocol/tcp/payload.py +21 -1
- pymax/session/__init__.py +1 -1
- pymax/session/models.py +30 -0
- pymax/session/protocol.py +33 -7
- pymax/session/store.py +73 -5
- pymax/transport/tcp.py +17 -4
- pymax/transport/websocket.py +6 -3
- pymax/types/domain/__init__.py +2 -1
- pymax/types/domain/attachments/__init__.py +2 -1
- pymax/types/domain/attachments/call.py +7 -1
- pymax/types/domain/attachments/contact.py +3 -3
- pymax/types/domain/attachments/control.py +3 -0
- pymax/types/domain/attachments/enums.py +33 -1
- pymax/types/domain/attachments/poll.py +111 -0
- pymax/types/domain/attachments/sticker.py +1 -1
- pymax/types/domain/attachments/video.py +32 -11
- pymax/types/domain/chat.py +9 -9
- pymax/types/domain/enums.py +7 -0
- pymax/types/domain/handshake.py +16 -0
- pymax/types/domain/login.py +30 -1
- pymax/types/domain/message.py +108 -27
- pymax/types/domain/user.py +1 -3
- pymax/types/events/__init__.py +1 -0
- pymax/types/events/reaction.py +2 -2
- pymax/types/events/voice.py +5 -0
- pymax/versions/__init__.py +2 -0
- pymax/versions/catalog.py +104 -0
- pymax/versions/exceptions.py +10 -0
- {maxapi_python-2.3.1.dist-info → maxapi_python-2.4.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
MIIFwjCCA6qgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwcDELMAkGA1UEBhMCUlUx
|
|
3
|
+
PzA9BgNVBAoMNlRoZSBNaW5pc3RyeSBvZiBEaWdpdGFsIERldmVsb3BtZW50IGFu
|
|
4
|
+
ZCBDb21tdW5pY2F0aW9uczEgMB4GA1UEAwwXUnVzc2lhbiBUcnVzdGVkIFJvb3Qg
|
|
5
|
+
Q0EwHhcNMjIwMzAxMjEwNDE1WhcNMzIwMjI3MjEwNDE1WjBwMQswCQYDVQQGEwJS
|
|
6
|
+
VTE/MD0GA1UECgw2VGhlIE1pbmlzdHJ5IG9mIERpZ2l0YWwgRGV2ZWxvcG1lbnQg
|
|
7
|
+
YW5kIENvbW11bmljYXRpb25zMSAwHgYDVQQDDBdSdXNzaWFuIFRydXN0ZWQgUm9v
|
|
8
|
+
dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMfFOZ8pUAL3+r2n
|
|
9
|
+
qqE0Zp52selXsKGFYoG0GM5bwz1bSFtCt+AZQMhkWQheI3poZAToYJu69pHLKS6Q
|
|
10
|
+
XBiwBC1cvzYmUYKMYZC7jE5YhEU2bSL0mX7NaMxMDmH2/NwuOVRj8OImVa5s1F4U
|
|
11
|
+
zn4Kv3PFlDBjjSjXKVY9kmjUBsXQrIHeaqmUIsPIlNWUnimXS0I0abExqkbdrXbX
|
|
12
|
+
YwCOXhOO2pDUx3ckmJlCMUGacUTnylyQW2VsJIyIGA8V0xzdaeUXg0VZ6ZmNUr5Y
|
|
13
|
+
Ber/EAOLPb8NYpsAhJe2mXjMB/J9HNsoFMBFJ0lLOT/+dQvjbdRZoOT8eqJpWnVD
|
|
14
|
+
U+QL/qEZnz57N88OWM3rabJkRNdU/Z7x5SFIM9FrqtN8xewsiBWBI0K6XFuOBOTD
|
|
15
|
+
4V08o4TzJ8+Ccq5XlCUW2L48pZNCYuBDfBh7FxkB7qDgGDiaftEkZZfApRg2E+M9
|
|
16
|
+
G8wkNKTPLDc4wH0FDTijhgxR3Y4PiS1HL2Zhw7bD3CbslmEGgfnnZojNkJtcLeBH
|
|
17
|
+
BLa52/dSwNU4WWLubaYSiAmA9IUMX1/RpfpxOxd4Ykmhz97oFbUaDJFipIggx5sX
|
|
18
|
+
ePAlkTdWnv+RWBxlJwMQ25oEHmRguNYf4Zr/Rxr9cS93Y+mdXIZaBEE0KS2iLRqa
|
|
19
|
+
OiWBki9IMQU4phqPOBAaG7A+eP8PAgMBAAGjZjBkMB0GA1UdDgQWBBTh0YHlzlpf
|
|
20
|
+
BKrS6badZrHF+qwshzAfBgNVHSMEGDAWgBTh0YHlzlpfBKrS6badZrHF+qwshzAS
|
|
21
|
+
BgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF
|
|
22
|
+
AAOCAgEAALIY1wkilt/urfEVM5vKzr6utOeDWCUczmWX/RX4ljpRdgF+5fAIS4vH
|
|
23
|
+
tmXkqpSCOVeWUrJV9QvZn6L227ZwuE15cWi8DCDal3Ue90WgAJJZMfTshN4OI8cq
|
|
24
|
+
W9E4EG9wglbEtMnObHlms8F3CHmrw3k6KmUkWGoa+/ENmcVl68u/cMRl1JbW2bM+
|
|
25
|
+
/3A+SAg2c6iPDlehczKx2oa95QW0SkPPWGuNA/CE8CpyANIhu9XFrj3RQ3EqeRcS
|
|
26
|
+
AQQod1RNuHpfETLU/A2gMmvn/w/sx7TB3W5BPs6rprOA37tutPq9u6FTZOcG1Oqj
|
|
27
|
+
C/B7yTqgI7rbyvox7DEXoX7rIiEqyNNUguTk/u3SZ4VXE2kmxdmSh3TQvybfbnXV
|
|
28
|
+
4JbCZVaqiZraqc7oZMnRoWrXRG3ztbnbes/9qhRGI7PqXqeKJBztxRTEVj8ONs1d
|
|
29
|
+
WN5szTwaPIvhkhO3CO5ErU2rVdUr89wKpNXbBODFKRtgxUT70YpmJ46VVaqdAhOZ
|
|
30
|
+
D9EUUn4YaeLaS8AjSF/h7UkjOibNc4qVDiPP+rkehFWM66PVnP1Msh93tc+taIfC
|
|
31
|
+
EYVMxjh8zNbFuoc7fzvvrFILLe7ifvEIUqSVIC/AzplM/Jxw7buXFeGP1qVCBEHq
|
|
32
|
+
391d/9RAfaZ12zkwFsl+IKwE/OZxW8AHa9i1p4GO0YSNuczzEm4=
|
|
33
|
+
-----END CERTIFICATE-----
|
pymax/api/auth/enums.py
CHANGED
pymax/api/auth/payloads.py
CHANGED
|
@@ -10,12 +10,13 @@ from .enums import AuthType, TwoFactorAction
|
|
|
10
10
|
class RequestCodePayload(CamelModel):
|
|
11
11
|
phone: str
|
|
12
12
|
type: AuthType = AuthType.START_AUTH
|
|
13
|
-
|
|
13
|
+
mode: bytes | None
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class SendCodePayload(CamelModel):
|
|
17
17
|
token: str
|
|
18
18
|
verify_code: str
|
|
19
|
+
|
|
19
20
|
auth_token_type: AuthType = AuthType.CHECK_CODE
|
|
20
21
|
|
|
21
22
|
|
|
@@ -42,19 +43,21 @@ class WebSyncPayload(CamelModel):
|
|
|
42
43
|
cls,
|
|
43
44
|
token: str,
|
|
44
45
|
sync: SyncState,
|
|
46
|
+
interactive: bool = True,
|
|
45
47
|
) -> "WebSyncPayload":
|
|
46
48
|
return cls(
|
|
47
49
|
token=token,
|
|
48
50
|
chats_sync=sync.chats_sync,
|
|
49
51
|
contacts_sync=sync.contacts_sync,
|
|
50
52
|
drafts_sync=sync.drafts_sync,
|
|
53
|
+
interactive=interactive,
|
|
51
54
|
)
|
|
52
55
|
|
|
53
56
|
|
|
54
57
|
class SyncPayload(CamelModel):
|
|
55
58
|
user_agent: MobileUserAgentPayload
|
|
56
59
|
token: str
|
|
57
|
-
|
|
60
|
+
chat_cache_fingerprint: bytes | None = None
|
|
58
61
|
chats_count: int | None = None
|
|
59
62
|
chats_sync: int = -1
|
|
60
63
|
contacts_sync: int = -1
|
|
@@ -70,15 +73,19 @@ class SyncPayload(CamelModel):
|
|
|
70
73
|
user_agent: MobileUserAgentPayload,
|
|
71
74
|
token: str,
|
|
72
75
|
sync: SyncState,
|
|
76
|
+
chat_cache_fingerprint: bytes | None = None,
|
|
77
|
+
interactive: bool = True,
|
|
73
78
|
) -> "SyncPayload":
|
|
74
79
|
return cls(
|
|
75
80
|
user_agent=user_agent,
|
|
76
81
|
token=token,
|
|
82
|
+
chat_cache_fingerprint=chat_cache_fingerprint,
|
|
77
83
|
chats_sync=sync.chats_sync,
|
|
78
84
|
contacts_sync=sync.contacts_sync,
|
|
79
85
|
drafts_sync=sync.drafts_sync,
|
|
80
86
|
presence_sync=sync.presence_sync,
|
|
81
87
|
config_hash=sync.config_hash,
|
|
88
|
+
interactive=interactive,
|
|
82
89
|
)
|
|
83
90
|
|
|
84
91
|
|
|
@@ -138,3 +145,22 @@ class ConfirmRegistrationPayload(CamelModel):
|
|
|
138
145
|
last_name: str | None = None
|
|
139
146
|
token: str
|
|
140
147
|
token_type: AuthType = AuthType.REGISTER
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class Login2Payload(CamelModel):
|
|
151
|
+
need_profile: bool
|
|
152
|
+
contacts_sync: int
|
|
153
|
+
config_hash: ConfigHash
|
|
154
|
+
|
|
155
|
+
@classmethod
|
|
156
|
+
def from_sync_state(
|
|
157
|
+
cls,
|
|
158
|
+
sync: SyncState,
|
|
159
|
+
profile_enabled: bool,
|
|
160
|
+
contact_enabled: bool,
|
|
161
|
+
) -> "Login2Payload":
|
|
162
|
+
return cls(
|
|
163
|
+
need_profile=profile_enabled,
|
|
164
|
+
contacts_sync=sync.contacts_sync if contact_enabled else -1,
|
|
165
|
+
config_hash=sync.config_hash,
|
|
166
|
+
)
|
pymax/api/auth/service.py
CHANGED
|
@@ -13,6 +13,7 @@ from pymax.auth import EmailCodeProvider
|
|
|
13
13
|
from pymax.auth.providers import ConsoleEmailCodeProvider
|
|
14
14
|
from pymax.logging import get_logger
|
|
15
15
|
from pymax.protocol import Opcode
|
|
16
|
+
from pymax.types.domain import Login2Flags, Login2Response
|
|
16
17
|
from pymax.types.domain.auth import (
|
|
17
18
|
CheckCodeResponse,
|
|
18
19
|
CheckPasswordResponse,
|
|
@@ -31,6 +32,7 @@ from .payloads import (
|
|
|
31
32
|
ConfirmQrPayload,
|
|
32
33
|
ConfirmRegistrationPayload,
|
|
33
34
|
CreateAuthTrackPayload,
|
|
35
|
+
Login2Payload,
|
|
34
36
|
MobileUserAgentPayload,
|
|
35
37
|
RemoveTwoFactorPayload,
|
|
36
38
|
RequestCodePayload,
|
|
@@ -58,7 +60,38 @@ class AuthService:
|
|
|
58
60
|
|
|
59
61
|
async def request_code(self, phone: str) -> StartAuthResponse:
|
|
60
62
|
logger.info("requesting sms code phone_set=%s", bool(phone))
|
|
61
|
-
|
|
63
|
+
|
|
64
|
+
if not self.app.handshake_response:
|
|
65
|
+
logger.error("request_code requested without handshake response")
|
|
66
|
+
raise RuntimeError("No handshake response available for request_code")
|
|
67
|
+
|
|
68
|
+
device_id = (
|
|
69
|
+
self.app.session.device_id if self.app.session else self.app.config.device.device_id
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
if self.app.config.device.user_agent.device_type != DeviceType.WEB:
|
|
73
|
+
if self.app.handshake_response.calls_seed is None:
|
|
74
|
+
raise ValueError(
|
|
75
|
+
"Unexpected internal state: handshake_response.calls_seed is missing "
|
|
76
|
+
+ "in AuthService.request_code. Please report this issue to the developer."
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
if self.app.fingerprint_generator is None:
|
|
80
|
+
raise ValueError(
|
|
81
|
+
"Unexpected internal state: self.app.fingerprint_generator is missing and "
|
|
82
|
+
+ "DeviceType != WEB"
|
|
83
|
+
+ "in AuthService.request_code. Please report this issue to the developer."
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
mode = self.app.fingerprint_generator.generate_fingerprint(
|
|
87
|
+
device_id=device_id,
|
|
88
|
+
calls_seed=self.app.handshake_response.calls_seed,
|
|
89
|
+
arch=self.app.config.device.user_agent.arch or "arm64-v8a",
|
|
90
|
+
)
|
|
91
|
+
else:
|
|
92
|
+
mode = None
|
|
93
|
+
|
|
94
|
+
frame = RequestCodePayload(phone=phone, mode=mode)
|
|
62
95
|
response = await self.app.invoke(Opcode.AUTH_REQUEST, frame.to_payload())
|
|
63
96
|
logger.debug(
|
|
64
97
|
"sms code request accepted payload_keys=%s",
|
|
@@ -72,7 +105,11 @@ class AuthService:
|
|
|
72
105
|
bool(token),
|
|
73
106
|
bool(verify_code),
|
|
74
107
|
)
|
|
75
|
-
|
|
108
|
+
|
|
109
|
+
frame = SendCodePayload(
|
|
110
|
+
token=token,
|
|
111
|
+
verify_code=verify_code,
|
|
112
|
+
)
|
|
76
113
|
response = await self.app.invoke(Opcode.AUTH, frame.to_payload())
|
|
77
114
|
logger.debug(
|
|
78
115
|
"sms code response payload_keys=%s",
|
|
@@ -117,11 +154,41 @@ class AuthService:
|
|
|
117
154
|
raise RuntimeError("No session available for login")
|
|
118
155
|
|
|
119
156
|
logger.info("logging in")
|
|
157
|
+
|
|
158
|
+
if not self.app.handshake_response:
|
|
159
|
+
logger.error("login requested without handshake response")
|
|
160
|
+
raise RuntimeError("No handshake response available for login")
|
|
161
|
+
|
|
162
|
+
device_id = (
|
|
163
|
+
self.app.session.device_id if self.app.session else self.app.config.device.device_id
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
if self.app.handshake_response.calls_seed is None:
|
|
167
|
+
raise ValueError(
|
|
168
|
+
"Unexpected internal state: handshake_response.calls_seed is missing "
|
|
169
|
+
+ "in AuthService.mobile_login. Please report this issue to the developer."
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
if self.app.fingerprint_generator is None:
|
|
173
|
+
raise ValueError(
|
|
174
|
+
"Unexpected internal state: self.app.fingerprint_generator is missing and "
|
|
175
|
+
+ "DeviceType != WEB"
|
|
176
|
+
+ "in AuthService.request_code. Please report this issue to the developer."
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
ccf = self.app.fingerprint_generator.generate_fingerprint(
|
|
180
|
+
device_id=device_id,
|
|
181
|
+
calls_seed=self.app.handshake_response.calls_seed,
|
|
182
|
+
arch=self.app.config.device.user_agent.arch or "arm64-v8a",
|
|
183
|
+
)
|
|
184
|
+
|
|
120
185
|
sync = self.app.config.sync.resolve(session.sync)
|
|
121
186
|
frame = SyncPayload.from_sync_state(
|
|
122
187
|
user_agent=self.app.config.device.user_agent,
|
|
123
188
|
token=session.token,
|
|
124
189
|
sync=sync,
|
|
190
|
+
chat_cache_fingerprint=ccf,
|
|
191
|
+
interactive=self.app.config.interactive,
|
|
125
192
|
)
|
|
126
193
|
response = await self.app.invoke(Opcode.LOGIN, frame.to_payload())
|
|
127
194
|
|
|
@@ -134,6 +201,30 @@ class AuthService:
|
|
|
134
201
|
await self._update_session(login_response)
|
|
135
202
|
return login_response
|
|
136
203
|
|
|
204
|
+
async def mobile_login2(self, flags: Login2Flags) -> Login2Response:
|
|
205
|
+
session = self.app.session
|
|
206
|
+
if session is None:
|
|
207
|
+
logger.error("login2 requested without session")
|
|
208
|
+
raise RuntimeError("No session available for login2")
|
|
209
|
+
|
|
210
|
+
sync = self.app.config.sync.resolve(session.sync)
|
|
211
|
+
|
|
212
|
+
frame = Login2Payload.from_sync_state(
|
|
213
|
+
sync,
|
|
214
|
+
profile_enabled=flags.profile_enabled,
|
|
215
|
+
contact_enabled=flags.contact_enabled,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
response = await self.app.invoke(Opcode.LOGIN2, frame.to_payload())
|
|
219
|
+
logger.debug("login2 response payload_keys=%s", payload_keys(response))
|
|
220
|
+
|
|
221
|
+
login2_response = bind_api_model(
|
|
222
|
+
self.app,
|
|
223
|
+
require_payload_model(response, Login2Response),
|
|
224
|
+
)
|
|
225
|
+
await self._update_session(login2_response)
|
|
226
|
+
return login2_response
|
|
227
|
+
|
|
137
228
|
async def web_login(self) -> LoginResponse:
|
|
138
229
|
session = self.app.session
|
|
139
230
|
if session is None:
|
|
@@ -146,6 +237,7 @@ class AuthService:
|
|
|
146
237
|
frame = WebSyncPayload.from_sync_state(
|
|
147
238
|
token=session.token,
|
|
148
239
|
sync=sync,
|
|
240
|
+
interactive=self.app.config.interactive,
|
|
149
241
|
)
|
|
150
242
|
response = await self.app.invoke(Opcode.LOGIN, frame.to_payload())
|
|
151
243
|
|
|
@@ -177,7 +269,7 @@ class AuthService:
|
|
|
177
269
|
|
|
178
270
|
return require_payload_model(response, CheckCodeResponse)
|
|
179
271
|
|
|
180
|
-
async def _update_session(self, response: LoginResponse) -> None:
|
|
272
|
+
async def _update_session(self, response: LoginResponse | Login2Response) -> None:
|
|
181
273
|
session = self.app.session
|
|
182
274
|
if session is None:
|
|
183
275
|
return
|
|
@@ -377,3 +469,8 @@ class AuthService:
|
|
|
377
469
|
response = await self.app.invoke(Opcode.AUTH_CONFIRM, frame.to_payload())
|
|
378
470
|
|
|
379
471
|
return require_payload_model(response, ConfirmRegistrationResponse)
|
|
472
|
+
|
|
473
|
+
def is_update_available(self) -> bool:
|
|
474
|
+
return bool(
|
|
475
|
+
self.app.handshake_response.app_update_type if self.app.handshake_response else False
|
|
476
|
+
)
|
pymax/api/chats/__init__.py
CHANGED
pymax/api/chats/enums.py
CHANGED
|
@@ -22,7 +22,22 @@ class ChatPayloadKey(str, Enum):
|
|
|
22
22
|
CHAT = "chat"
|
|
23
23
|
CHATS = "chats"
|
|
24
24
|
MEMBERS = "members"
|
|
25
|
+
MARKER = "marker"
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
class ChatLinkPrefix(str, Enum):
|
|
28
29
|
JOIN = "join/"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ChannelPermissions(int, Enum):
|
|
33
|
+
ADD_REMOVE_MEMBER = 2
|
|
34
|
+
ADD_ADMIN = 4
|
|
35
|
+
CHANGE_CHAT_INFO = 8
|
|
36
|
+
PIN_MESSAGE = 16
|
|
37
|
+
POST_MESSAGE = 256
|
|
38
|
+
EDIT_MESSAGE = 512
|
|
39
|
+
DELETE_MESSAGE = 1024
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class PermType(str, Enum):
|
|
43
|
+
ADMIN = "ADMIN"
|
pymax/api/chats/payloads.py
CHANGED
|
@@ -6,7 +6,7 @@ from pymax.api.models import CamelModel
|
|
|
6
6
|
from pymax.types.domain.attachments.enums import AttachmentType
|
|
7
7
|
from pymax.types.domain.enums import ChatType
|
|
8
8
|
|
|
9
|
-
from .enums import ChatMemberOperation, ChatOption, ControlEvent
|
|
9
|
+
from .enums import ChatMemberOperation, ChatOption, ControlEvent, PermType
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class CreateGroupAttach(CamelModel):
|
|
@@ -74,8 +74,9 @@ class ChangeGroupSettingsPayload(CamelModel):
|
|
|
74
74
|
|
|
75
75
|
class ChangeGroupProfilePayload(CamelModel):
|
|
76
76
|
chat_id: int
|
|
77
|
-
theme: str | None
|
|
77
|
+
theme: str | None = None
|
|
78
78
|
description: str | None = None
|
|
79
|
+
photo_token: str | None = None
|
|
79
80
|
|
|
80
81
|
|
|
81
82
|
class JoinChatPayload(CamelModel):
|
|
@@ -95,6 +96,13 @@ class GetChatInfoPayload(CamelModel):
|
|
|
95
96
|
chat_ids: list[int]
|
|
96
97
|
|
|
97
98
|
|
|
99
|
+
class GetChatMembersPayload(CamelModel):
|
|
100
|
+
type: str = "MEMBER" # TODO: ENUMM!!!
|
|
101
|
+
chat_id: int
|
|
102
|
+
marker: int
|
|
103
|
+
count: int = 50
|
|
104
|
+
|
|
105
|
+
|
|
98
106
|
class LeaveChatPayload(CamelModel):
|
|
99
107
|
chat_id: int
|
|
100
108
|
|
|
@@ -121,3 +129,11 @@ class DeleteChatPayload(CamelModel):
|
|
|
121
129
|
chat_id: int
|
|
122
130
|
last_event_time: int
|
|
123
131
|
for_all: bool = True
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class AddAdminPayload(CamelModel):
|
|
135
|
+
chat_id: int
|
|
136
|
+
user_ids: list[int]
|
|
137
|
+
type: PermType = PermType.ADMIN
|
|
138
|
+
operation: str = "add"
|
|
139
|
+
permissions: int
|
pymax/api/chats/service.py
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import time
|
|
4
|
+
from functools import reduce
|
|
5
|
+
from operator import or_
|
|
4
6
|
from typing import TYPE_CHECKING
|
|
5
7
|
|
|
6
8
|
from pymax.api.binding import bind_api_model
|
|
7
9
|
from pymax.api.response import (
|
|
8
10
|
parse_payload_item_model,
|
|
9
11
|
parse_payload_list,
|
|
12
|
+
payload_item,
|
|
10
13
|
require_payload_item_model,
|
|
11
14
|
require_payload_model,
|
|
12
15
|
)
|
|
13
16
|
from pymax.exceptions import PyMaxError
|
|
17
|
+
from pymax.files import Photo
|
|
14
18
|
from pymax.logging import get_logger
|
|
15
19
|
from pymax.protocol import Opcode
|
|
16
20
|
from pymax.types.domain import Chat, Member, Message
|
|
17
21
|
|
|
18
|
-
from .enums import ChatLinkPrefix, ChatMemberOperation, ChatPayloadKey
|
|
22
|
+
from .enums import ChannelPermissions, ChatLinkPrefix, ChatMemberOperation, ChatPayloadKey
|
|
19
23
|
from .payloads import (
|
|
24
|
+
AddAdminPayload,
|
|
20
25
|
ChangeGroupProfilePayload,
|
|
21
26
|
ChangeGroupSettingsOptions,
|
|
22
27
|
ChangeGroupSettingsPayload,
|
|
@@ -27,6 +32,7 @@ from .payloads import (
|
|
|
27
32
|
FetchChatsPayload,
|
|
28
33
|
FetchJoinRequests,
|
|
29
34
|
GetChatInfoPayload,
|
|
35
|
+
GetChatMembersPayload,
|
|
30
36
|
InviteUsersPayload,
|
|
31
37
|
JoinChatPayload,
|
|
32
38
|
JoinRequestActionPayload,
|
|
@@ -206,13 +212,19 @@ class ChatService:
|
|
|
206
212
|
chat_id: int,
|
|
207
213
|
name: str | None,
|
|
208
214
|
description: str | None = None,
|
|
215
|
+
photo: Photo | None = None,
|
|
209
216
|
) -> None:
|
|
217
|
+
|
|
210
218
|
frame = ChangeGroupProfilePayload(
|
|
211
219
|
chat_id=chat_id,
|
|
212
220
|
theme=name,
|
|
213
221
|
description=description,
|
|
214
222
|
)
|
|
215
223
|
|
|
224
|
+
if photo:
|
|
225
|
+
photo_payload = await self.app.api.uploads.upload_photo(photo)
|
|
226
|
+
frame.photo_token = photo_payload.photo_token
|
|
227
|
+
|
|
216
228
|
response = await self.app.invoke(Opcode.CHAT_UPDATE, frame.to_payload())
|
|
217
229
|
chat = parse_payload_item_model(response, ChatPayloadKey.CHAT, Chat)
|
|
218
230
|
if chat:
|
|
@@ -266,6 +278,22 @@ class ChatService:
|
|
|
266
278
|
|
|
267
279
|
return [cached[chat_id] for chat_id in chat_ids if chat_id in cached]
|
|
268
280
|
|
|
281
|
+
async def get_chat_members(
|
|
282
|
+
self,
|
|
283
|
+
chat_id: int,
|
|
284
|
+
marker: int | None = None,
|
|
285
|
+
count: int = 50,
|
|
286
|
+
) -> tuple[list[Member], int]:
|
|
287
|
+
frame = GetChatMembersPayload(chat_id=chat_id, marker=marker or 0, count=count)
|
|
288
|
+
response = await self.app.invoke(Opcode.CHAT_MEMBERS, frame.to_payload())
|
|
289
|
+
|
|
290
|
+
members = bind_api_model(
|
|
291
|
+
self.app,
|
|
292
|
+
parse_payload_list(response, ChatPayloadKey.MEMBERS, Member),
|
|
293
|
+
)
|
|
294
|
+
next_marker = payload_item(response, ChatPayloadKey.MARKER, int) or 0
|
|
295
|
+
return members, next_marker
|
|
296
|
+
|
|
269
297
|
async def get_chat(self, chat_id: int) -> Chat:
|
|
270
298
|
chats = await self.get_chats([chat_id])
|
|
271
299
|
if not chats:
|
|
@@ -380,3 +408,17 @@ class ChatService:
|
|
|
380
408
|
|
|
381
409
|
await self.app.invoke(Opcode.CHAT_DELETE, frame.to_payload())
|
|
382
410
|
self._remove_cached_chat(chat_id)
|
|
411
|
+
|
|
412
|
+
async def add_admin(
|
|
413
|
+
self,
|
|
414
|
+
chat_id: int,
|
|
415
|
+
user_id: int,
|
|
416
|
+
permissions: list[ChannelPermissions],
|
|
417
|
+
) -> None:
|
|
418
|
+
frame = AddAdminPayload(
|
|
419
|
+
chat_id=chat_id,
|
|
420
|
+
user_ids=[user_id],
|
|
421
|
+
permissions=reduce(or_, permissions),
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
await self.app.invoke(Opcode.CHAT_MEMBERS_UPDATE, frame.to_payload())
|
pymax/api/messages/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
from .service import MessageService
|
|
1
|
+
from .service import DateTimeUnion, MessageService
|
pymax/api/messages/payloads.py
CHANGED
|
@@ -8,6 +8,7 @@ from pymax.api.uploads.payloads import (
|
|
|
8
8
|
AttachPhotoPayload,
|
|
9
9
|
VideoAttachPayload,
|
|
10
10
|
)
|
|
11
|
+
from pymax.types.domain import Poll
|
|
11
12
|
|
|
12
13
|
from .enums import ItemType, ReadAction
|
|
13
14
|
|
|
@@ -20,9 +21,9 @@ class GetMessagesPayload(CamelModel):
|
|
|
20
21
|
class EditMessagePayload(CamelModel):
|
|
21
22
|
chat_id: int
|
|
22
23
|
message_id: int
|
|
23
|
-
text: str
|
|
24
|
+
text: str | None = None
|
|
24
25
|
elements: list[Any]
|
|
25
|
-
attachments: list[AttachPhotoPayload | VideoAttachPayload | AttachFilePayload] = Field(
|
|
26
|
+
attachments: list[AttachPhotoPayload | VideoAttachPayload | AttachFilePayload | Poll] = Field(
|
|
26
27
|
default_factory=list
|
|
27
28
|
)
|
|
28
29
|
|
|
@@ -32,12 +33,18 @@ class ReplyLink(CamelModel):
|
|
|
32
33
|
message_id: int
|
|
33
34
|
|
|
34
35
|
|
|
36
|
+
class DelayedAttributes(CamelModel):
|
|
37
|
+
time_to_fire: int
|
|
38
|
+
notify_sender: bool
|
|
39
|
+
|
|
40
|
+
|
|
35
41
|
class SendMessagePayloadMessage(CamelModel):
|
|
36
|
-
text: str
|
|
42
|
+
text: str | None = None
|
|
37
43
|
cid: int
|
|
38
44
|
elements: list[Any]
|
|
39
|
-
attaches: list[AttachPhotoPayload | VideoAttachPayload | AttachFilePayload]
|
|
45
|
+
attaches: list[AttachPhotoPayload | VideoAttachPayload | AttachFilePayload | Poll]
|
|
40
46
|
link: ReplyLink | None = None
|
|
47
|
+
delayed_attributes: DelayedAttributes | None = None
|
|
41
48
|
|
|
42
49
|
|
|
43
50
|
class SendMessagePayload(CamelModel):
|
|
@@ -110,18 +117,18 @@ class ReactionInfoPayload(CamelModel):
|
|
|
110
117
|
|
|
111
118
|
class AddReactionPayload(CamelModel):
|
|
112
119
|
chat_id: int
|
|
113
|
-
message_id:
|
|
120
|
+
message_id: int
|
|
114
121
|
reaction: ReactionInfoPayload
|
|
115
122
|
|
|
116
123
|
|
|
117
124
|
class GetReactionsPayload(CamelModel):
|
|
118
125
|
chat_id: int
|
|
119
|
-
message_ids: list[
|
|
126
|
+
message_ids: list[int]
|
|
120
127
|
|
|
121
128
|
|
|
122
129
|
class RemoveReactionPayload(CamelModel):
|
|
123
130
|
chat_id: int
|
|
124
|
-
message_id:
|
|
131
|
+
message_id: int
|
|
125
132
|
|
|
126
133
|
|
|
127
134
|
class ReadMessagesPayload(CamelModel):
|
|
@@ -129,3 +136,10 @@ class ReadMessagesPayload(CamelModel):
|
|
|
129
136
|
chat_id: int
|
|
130
137
|
message_id: str | int # Сокет просит int а вс str
|
|
131
138
|
mark: int
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class VotePollPayload(CamelModel):
|
|
142
|
+
chat_id: int
|
|
143
|
+
message_id: int
|
|
144
|
+
poll_id: int
|
|
145
|
+
answers_ids: list[int]
|