maxapi-python 1.1.8__tar.gz → 1.1.10__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.
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/PKG-INFO +1 -1
- maxapi_python-1.1.10/examples/example.py +68 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/pyproject.toml +1 -1
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/mixins/__init__.py +2 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/mixins/group.py +44 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/mixins/user.py +17 -1
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/payloads.py +4 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/types.py +65 -0
- maxapi_python-1.1.8/examples/example.py +0 -62
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/.github/FUNDING.yml +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/.github/ISSUE_TEMPLATE/refactor.md +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/.github/pull_request_template.md +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/.github/workflows/publish.yml +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/.gitignore +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/LICENSE +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/README.md +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/assets/icon.svg +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/assets/logo.svg +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/docs/api.md +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/docs/assets/icon.svg +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/docs/examples.md +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/docs/index.md +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/mkdocs.yml +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/ruff.toml +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/scripts/build.py +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/__init__.py +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/core.py +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/crud.py +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/exceptions.py +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/files.py +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/filters.py +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/interfaces.py +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/mixins/auth.py +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/mixins/channel.py +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/mixins/handler.py +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/mixins/message.py +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/mixins/self.py +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/mixins/socket.py +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/mixins/telemetry.py +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/mixins/websocket.py +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/models.py +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/navigation.py +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/static.py +0 -0
- {maxapi_python-1.1.8 → maxapi_python-1.1.10}/src/pymax/utils.py +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from pymax import MaxClient, Message, SocketMaxClient
|
|
5
|
+
from pymax.files import Photo
|
|
6
|
+
from pymax.filters import Filter
|
|
7
|
+
from pymax.static import AttachType
|
|
8
|
+
|
|
9
|
+
phone = "+1234567890"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
client = MaxClient(phone=phone, work_dir="cache")
|
|
13
|
+
# client = SocketMaxClient(phone=phone, work_dir="cache")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@client.on_message(filter=Filter(chat_id=0))
|
|
17
|
+
async def handle_message(message: Message) -> None:
|
|
18
|
+
print(str(message.sender) + ": " + message.text)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@client.on_start
|
|
22
|
+
async def handle_start() -> None:
|
|
23
|
+
print("Client started successfully!")
|
|
24
|
+
sessions = await client.get_sessions()
|
|
25
|
+
for session in sessions:
|
|
26
|
+
print(session.client)
|
|
27
|
+
# print(client.dialogs)
|
|
28
|
+
chat = await client.join_group("join/sdfdfsfdf")
|
|
29
|
+
print(chat.title)
|
|
30
|
+
# if history:
|
|
31
|
+
# for message in history:
|
|
32
|
+
# if message.link:
|
|
33
|
+
# print(message.link.chat_id)
|
|
34
|
+
# print(message.link.message.text)
|
|
35
|
+
# for attach in message.attaches:
|
|
36
|
+
# if attach.type == AttachType.CONTROL:
|
|
37
|
+
# print(attach.event)
|
|
38
|
+
# print(attach.extra)
|
|
39
|
+
# if attach.type == AttachType.VIDEO:
|
|
40
|
+
# print(message)
|
|
41
|
+
# vid = await client.get_video_by_id(
|
|
42
|
+
# chat_id=0,
|
|
43
|
+
# video_id=attach.video_id,
|
|
44
|
+
# message_id=message.id,
|
|
45
|
+
# )
|
|
46
|
+
# print(vid.url)
|
|
47
|
+
# elif attach.type == AttachType.FILE:
|
|
48
|
+
# file = await client.get_file_by_id(
|
|
49
|
+
# chat_id=0,
|
|
50
|
+
# file_id=attach.file_id,
|
|
51
|
+
# message_id=message.id,
|
|
52
|
+
# )
|
|
53
|
+
# print(file.url)
|
|
54
|
+
# print(client.me.names[0].first_name)
|
|
55
|
+
# user = await client.get_user(client.me.id)
|
|
56
|
+
|
|
57
|
+
# print(user.names[0].first_name)
|
|
58
|
+
|
|
59
|
+
# photo1 = Photo(path="tests/test.jpeg")
|
|
60
|
+
# photo2 = Photo(path="tests/test.jpg")
|
|
61
|
+
|
|
62
|
+
# await client.send_message(
|
|
63
|
+
# "Hello with photo!", chat_id=0, photos=[photo1, photo2], notify=True
|
|
64
|
+
# )
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if __name__ == "__main__":
|
|
68
|
+
asyncio.run(client.start())
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from .auth import AuthMixin
|
|
2
2
|
from .channel import ChannelMixin
|
|
3
|
+
from .group import GroupMixin
|
|
3
4
|
from .handler import HandlerMixin
|
|
4
5
|
from .message import MessageMixin
|
|
5
6
|
from .self import SelfMixin
|
|
@@ -17,6 +18,7 @@ class ApiMixin(
|
|
|
17
18
|
SelfMixin,
|
|
18
19
|
MessageMixin,
|
|
19
20
|
TelemetryMixin,
|
|
21
|
+
GroupMixin,
|
|
20
22
|
):
|
|
21
23
|
pass
|
|
22
24
|
|
|
@@ -9,6 +9,7 @@ from pymax.payloads import (
|
|
|
9
9
|
CreateGroupMessage,
|
|
10
10
|
CreateGroupPayload,
|
|
11
11
|
InviteUsersPayload,
|
|
12
|
+
JoinGroupPayload,
|
|
12
13
|
RemoveUsersPayload,
|
|
13
14
|
)
|
|
14
15
|
from pymax.static import Opcode
|
|
@@ -218,3 +219,46 @@ class GroupMixin(ClientProtocol):
|
|
|
218
219
|
|
|
219
220
|
except Exception:
|
|
220
221
|
self.logger.exception("Change group profile failed")
|
|
222
|
+
|
|
223
|
+
def _process_chat_join_link(self, link: str) -> str | None:
|
|
224
|
+
idx = link.find("join/")
|
|
225
|
+
return link[idx:] if idx != -1 else None
|
|
226
|
+
|
|
227
|
+
async def join_group(self, link: str) -> Chat | None:
|
|
228
|
+
"""
|
|
229
|
+
Вступает в группу по ссылке
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
link (str): Ссылка на группу.
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
bool: True, если успешно вступил в группу
|
|
236
|
+
"""
|
|
237
|
+
try:
|
|
238
|
+
proceed_link = self._process_chat_join_link(link)
|
|
239
|
+
if proceed_link is None:
|
|
240
|
+
self.logger.error("Invalid group link: %s", link)
|
|
241
|
+
return None
|
|
242
|
+
|
|
243
|
+
payload = JoinGroupPayload(link=proceed_link).model_dump(by_alias=True)
|
|
244
|
+
|
|
245
|
+
data = await self._send_and_wait(opcode=Opcode.CHAT_JOIN, payload=payload)
|
|
246
|
+
|
|
247
|
+
if error := data.get("payload", {}).get("error"):
|
|
248
|
+
self.logger.error("Join group error: %s", error)
|
|
249
|
+
return None
|
|
250
|
+
|
|
251
|
+
chat = Chat.from_dict(data["payload"]["chat"])
|
|
252
|
+
if chat:
|
|
253
|
+
cached_chat = await self._get_chat(chat.id)
|
|
254
|
+
if cached_chat is None:
|
|
255
|
+
self.chats.append(chat)
|
|
256
|
+
else:
|
|
257
|
+
idx = self.chats.index(cached_chat)
|
|
258
|
+
self.chats[idx] = chat
|
|
259
|
+
|
|
260
|
+
return chat
|
|
261
|
+
|
|
262
|
+
except Exception:
|
|
263
|
+
self.logger.exception("Join group failed")
|
|
264
|
+
return None
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from pymax.interfaces import ClientProtocol
|
|
2
2
|
from pymax.payloads import FetchContactsPayload, SearchByPhonePayload
|
|
3
3
|
from pymax.static import Opcode
|
|
4
|
-
from pymax.types import User
|
|
4
|
+
from pymax.types import Session, User
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class UserMixin(ClientProtocol):
|
|
@@ -115,3 +115,19 @@ class UserMixin(ClientProtocol):
|
|
|
115
115
|
except Exception:
|
|
116
116
|
self.logger.exception("Search by phone failed")
|
|
117
117
|
return None
|
|
118
|
+
|
|
119
|
+
async def get_sessions(self) -> list[Session] | None:
|
|
120
|
+
try:
|
|
121
|
+
self.logger.info("Fetching sessions")
|
|
122
|
+
|
|
123
|
+
data = await self._send_and_wait(opcode=Opcode.SESSIONS_INFO, payload={})
|
|
124
|
+
|
|
125
|
+
if error := data.get("payload", {}).get("error"):
|
|
126
|
+
self.logger.error("Fetching sessions error: %s", error)
|
|
127
|
+
return None
|
|
128
|
+
|
|
129
|
+
return [Session.from_dict(s) for s in data["payload"].get("sessions", [])]
|
|
130
|
+
|
|
131
|
+
except Exception:
|
|
132
|
+
self.logger.exception("Fetching sessions failed")
|
|
133
|
+
return None
|
|
@@ -294,6 +294,29 @@ class Element:
|
|
|
294
294
|
return f"{self.type}({self.length})"
|
|
295
295
|
|
|
296
296
|
|
|
297
|
+
class MessageLink:
|
|
298
|
+
def __init__(self, chat_id: int, message: "Message", type: str) -> None:
|
|
299
|
+
self.chat_id = chat_id
|
|
300
|
+
self.message = message
|
|
301
|
+
self.type = type
|
|
302
|
+
|
|
303
|
+
@classmethod
|
|
304
|
+
def from_dict(cls, data: dict[str, Any]) -> "MessageLink":
|
|
305
|
+
return cls(
|
|
306
|
+
chat_id=data["chatId"],
|
|
307
|
+
message=Message.from_dict(data["message"]),
|
|
308
|
+
type=data["type"],
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
@override
|
|
312
|
+
def __repr__(self) -> str:
|
|
313
|
+
return f"MessageLink(chat_id={self.chat_id!r}, message={self.message!r}, type={self.type!r})"
|
|
314
|
+
|
|
315
|
+
@override
|
|
316
|
+
def __str__(self) -> str:
|
|
317
|
+
return f"MessageLink: {self.chat_id}/{self.message.id}"
|
|
318
|
+
|
|
319
|
+
|
|
297
320
|
class Message:
|
|
298
321
|
def __init__(
|
|
299
322
|
self,
|
|
@@ -304,6 +327,7 @@ class Message:
|
|
|
304
327
|
options: int | None,
|
|
305
328
|
id: int,
|
|
306
329
|
time: int,
|
|
330
|
+
link: MessageLink | None,
|
|
307
331
|
text: str,
|
|
308
332
|
status: MessageStatus | str | None,
|
|
309
333
|
type: MessageType | str,
|
|
@@ -319,6 +343,7 @@ class Message:
|
|
|
319
343
|
self.type = type
|
|
320
344
|
self.attaches = attaches
|
|
321
345
|
self.status = status
|
|
346
|
+
self.link = link
|
|
322
347
|
self.reactionInfo = reaction_info
|
|
323
348
|
|
|
324
349
|
@classmethod
|
|
@@ -345,6 +370,9 @@ class Message:
|
|
|
345
370
|
type=message["type"],
|
|
346
371
|
attaches=attaches,
|
|
347
372
|
status=message.get("status"),
|
|
373
|
+
link=MessageLink.from_dict(message.get("link"))
|
|
374
|
+
if message.get("link")
|
|
375
|
+
else None,
|
|
348
376
|
reaction_info=message.get("reactionInfo"),
|
|
349
377
|
)
|
|
350
378
|
|
|
@@ -642,3 +670,40 @@ class Attach: # УБРАТЬ ГАДА!!! или нет...
|
|
|
642
670
|
@override
|
|
643
671
|
def __str__(self) -> str:
|
|
644
672
|
return f"Attach: {self.type}"
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
class Session:
|
|
676
|
+
def __init__(
|
|
677
|
+
self,
|
|
678
|
+
client: str,
|
|
679
|
+
info: str,
|
|
680
|
+
location: str,
|
|
681
|
+
time: int,
|
|
682
|
+
current: bool | None = None,
|
|
683
|
+
) -> None:
|
|
684
|
+
self.client = client
|
|
685
|
+
self.info = info
|
|
686
|
+
self.location = location
|
|
687
|
+
self.time = time
|
|
688
|
+
self.current = current if current is not None else False
|
|
689
|
+
|
|
690
|
+
@classmethod
|
|
691
|
+
def from_dict(cls, data: dict[str, Any]) -> "Session":
|
|
692
|
+
return cls(
|
|
693
|
+
client=data["client"],
|
|
694
|
+
info=data["info"],
|
|
695
|
+
location=data["location"],
|
|
696
|
+
time=data["time"],
|
|
697
|
+
current=data.get("current"),
|
|
698
|
+
)
|
|
699
|
+
|
|
700
|
+
@override
|
|
701
|
+
def __repr__(self) -> str:
|
|
702
|
+
return (
|
|
703
|
+
f"Session(client={self.client!r}, info={self.info!r}, "
|
|
704
|
+
f"location={self.location!r}, time={self.time!r}, current={self.current!r})"
|
|
705
|
+
)
|
|
706
|
+
|
|
707
|
+
@override
|
|
708
|
+
def __str__(self) -> str:
|
|
709
|
+
return f"Session: {self.client} from {self.location} at {self.time} (current={self.current})"
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import logging
|
|
3
|
-
|
|
4
|
-
from pymax import MaxClient, Message, SocketMaxClient
|
|
5
|
-
from pymax.files import Photo
|
|
6
|
-
from pymax.filters import Filter
|
|
7
|
-
from pymax.static import AttachType
|
|
8
|
-
|
|
9
|
-
phone = "+1234567890"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
client = MaxClient(phone=phone, work_dir="cache")
|
|
13
|
-
# client = SocketMaxClient(phone=phone, work_dir="cache")
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
@client.on_message(filter=Filter(chat_id=0))
|
|
17
|
-
async def handle_message(message: Message) -> None:
|
|
18
|
-
print(str(message.sender) + ": " + message.text)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@client.on_start
|
|
22
|
-
async def handle_start() -> None:
|
|
23
|
-
print("Client started successfully!")
|
|
24
|
-
# print(client.dialogs)
|
|
25
|
-
history = await client.fetch_history(chat_id=0)
|
|
26
|
-
if history:
|
|
27
|
-
for message in history:
|
|
28
|
-
if message.attaches:
|
|
29
|
-
for attach in message.attaches:
|
|
30
|
-
if attach.type == AttachType.CONTROL:
|
|
31
|
-
print(attach.event)
|
|
32
|
-
print(attach.extra)
|
|
33
|
-
# if attach.type == AttachType.VIDEO:
|
|
34
|
-
# print(message)
|
|
35
|
-
# vid = await client.get_video_by_id(
|
|
36
|
-
# chat_id=0,
|
|
37
|
-
# video_id=attach.video_id,
|
|
38
|
-
# message_id=message.id,
|
|
39
|
-
# )
|
|
40
|
-
# print(vid.url)
|
|
41
|
-
# elif attach.type == AttachType.FILE:
|
|
42
|
-
# file = await client.get_file_by_id(
|
|
43
|
-
# chat_id=0,
|
|
44
|
-
# file_id=attach.file_id,
|
|
45
|
-
# message_id=message.id,
|
|
46
|
-
# )
|
|
47
|
-
# print(file.url)
|
|
48
|
-
# print(client.me.names[0].first_name)
|
|
49
|
-
# user = await client.get_user(client.me.id)
|
|
50
|
-
|
|
51
|
-
# print(user.names[0].first_name)
|
|
52
|
-
|
|
53
|
-
# photo1 = Photo(path="tests/test.jpeg")
|
|
54
|
-
# photo2 = Photo(path="tests/test.jpg")
|
|
55
|
-
|
|
56
|
-
# await client.send_message(
|
|
57
|
-
# "Hello with photo!", chat_id=0, photos=[photo1, photo2], notify=True
|
|
58
|
-
# )
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if __name__ == "__main__":
|
|
62
|
-
asyncio.run(client.start())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|