maxapi-python 1.1.20__tar.gz → 1.1.21__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.21/.coderabbit.yaml +6 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/PKG-INFO +37 -54
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/README.md +36 -53
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/examples/example.py +88 -19
- maxapi_python-1.1.21/examples/flt_test.py +51 -0
- maxapi_python-1.1.21/examples/large_file_upload.py +51 -0
- maxapi_python-1.1.21/examples/reg.py +34 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/examples/telegram_bridge.py +30 -46
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/pyproject.toml +4 -1
- maxapi_python-1.1.21/pytest.ini +6 -0
- maxapi_python-1.1.21/redocs/Makefile +20 -0
- maxapi_python-1.1.21/redocs/build.sh +1 -0
- maxapi_python-1.1.21/redocs/make.bat +35 -0
- maxapi_python-1.1.21/redocs/source/_static/logo.svg +13 -0
- maxapi_python-1.1.21/redocs/source/clients.rst +126 -0
- maxapi_python-1.1.21/redocs/source/conf.py +49 -0
- maxapi_python-1.1.21/redocs/source/decorators.rst +6 -0
- maxapi_python-1.1.21/redocs/source/examples.rst +374 -0
- maxapi_python-1.1.21/redocs/source/guides.rst +483 -0
- maxapi_python-1.1.21/redocs/source/index.rst +112 -0
- maxapi_python-1.1.21/redocs/source/installation.rst +151 -0
- maxapi_python-1.1.21/redocs/source/quickstart.rst +246 -0
- maxapi_python-1.1.21/redocs/source/types.rst +8 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/core.py +109 -48
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/crud.py +4 -8
- maxapi_python-1.1.21/src/pymax/filters.py +164 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/formatter.py +1 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/interfaces.py +12 -6
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/__init__.py +3 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/auth.py +74 -17
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/channel.py +36 -37
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/group.py +127 -8
- maxapi_python-1.1.21/src/pymax/mixins/handler.py +291 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/message.py +251 -97
- maxapi_python-1.1.21/src/pymax/mixins/scheduler.py +28 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/self.py +79 -40
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/socket.py +243 -257
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/user.py +63 -42
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/websocket.py +145 -145
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/payloads.py +12 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/static/constant.py +4 -2
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/static/enum.py +1 -0
- maxapi_python-1.1.20/docs/api.md +0 -716
- maxapi_python-1.1.20/docs/assets/icon.svg +0 -13
- maxapi_python-1.1.20/docs/client.md +0 -284
- maxapi_python-1.1.20/docs/examples.md +0 -149
- maxapi_python-1.1.20/docs/index.md +0 -45
- maxapi_python-1.1.20/docs/methods.md +0 -876
- maxapi_python-1.1.20/docs/types.md +0 -512
- maxapi_python-1.1.20/examples/reg.py +0 -13
- maxapi_python-1.1.20/src/pymax/filters.py +0 -47
- maxapi_python-1.1.20/src/pymax/mixins/handler.py +0 -167
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/.github/FUNDING.yml +0 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/.github/ISSUE_TEMPLATE/refactor.md +0 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/.github/pull_request_template.md +0 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/.github/workflows/publish.yml +0 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/.gitignore +0 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/.pre-commit-config.yaml +0 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/LICENSE +0 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/assets/icon.svg +0 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/assets/logo.svg +0 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/mkdocs.yml +0 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/ruff.toml +0 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/__init__.py +0 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/exceptions.py +0 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/files.py +0 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/formatting.py +0 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/telemetry.py +0 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/utils.py +0 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/models.py +0 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/navigation.py +0 -0
- {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/types.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: maxapi-python
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.21
|
|
4
4
|
Summary: Python wrapper для API мессенджера Max
|
|
5
5
|
Project-URL: Homepage, https://github.com/ink-developer/PyMax
|
|
6
6
|
Project-URL: Repository, https://github.com/ink-developer/PyMax
|
|
@@ -79,65 +79,47 @@ uv add -U maxapi-python
|
|
|
79
79
|
|
|
80
80
|
```python
|
|
81
81
|
import asyncio
|
|
82
|
+
|
|
82
83
|
from pymax import MaxClient, Message
|
|
84
|
+
from pymax.filters import Filters
|
|
85
|
+
|
|
86
|
+
client = MaxClient(
|
|
87
|
+
phone="+1234567890",
|
|
88
|
+
work_dir="cache", # директория для сессий
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# Обработка входящих сообщений
|
|
93
|
+
@client.on_message(Filters.chat(0)) # фильтр по ID чата
|
|
94
|
+
async def on_message(msg: Message) -> None:
|
|
95
|
+
print(f"[{msg.sender}] {msg.text}")
|
|
83
96
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
97
|
+
await client.send_message(
|
|
98
|
+
chat_id=msg.chat_id,
|
|
99
|
+
text="Привет, я бот на PyMax!",
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
await client.add_reaction(
|
|
103
|
+
chat_id=msg.chat_id,
|
|
104
|
+
message_id=str(msg.id),
|
|
105
|
+
reaction="👍",
|
|
106
|
+
)
|
|
87
107
|
|
|
88
|
-
# Обработчик входящих сообщений
|
|
89
|
-
@client.on_message()
|
|
90
|
-
async def handle_message(message: Message) -> None:
|
|
91
|
-
print(f"{message.sender}: {message.text}")
|
|
92
108
|
|
|
93
|
-
# Обработчик запуска клиента
|
|
94
109
|
@client.on_start
|
|
95
|
-
async def
|
|
96
|
-
print("Клиент
|
|
110
|
+
async def on_start() -> None:
|
|
111
|
+
print(f"Клиент запущен. Ваш ID: {client.me.id}")
|
|
97
112
|
|
|
98
|
-
# Получение истории
|
|
113
|
+
# Получение истории
|
|
99
114
|
history = await client.fetch_history(chat_id=0)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
# Работа с чатами
|
|
110
|
-
for chat in client.chats:
|
|
111
|
-
print(f"Чат: {chat.title}")
|
|
112
|
-
|
|
113
|
-
# Отправка сообщения
|
|
114
|
-
message = await client.send_message(
|
|
115
|
-
"Привет от PyMax!",
|
|
116
|
-
chat.id,
|
|
117
|
-
notify=True
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
# Редактирование сообщения
|
|
121
|
-
await asyncio.sleep(2)
|
|
122
|
-
await client.edit_message(
|
|
123
|
-
chat.id,
|
|
124
|
-
message.id,
|
|
125
|
-
"Привет от PyMax! (отредактировано)"
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
# Удаление сообщения
|
|
129
|
-
await asyncio.sleep(2)
|
|
130
|
-
await client.delete_message(chat.id, [message.id], for_me=False)
|
|
131
|
-
|
|
132
|
-
# Работа с диалогами
|
|
133
|
-
for dialog in client.dialogs:
|
|
134
|
-
print(f"Диалог: {dialog.last_message.text}")
|
|
135
|
-
|
|
136
|
-
# Работа с каналами
|
|
137
|
-
for channel in client.channels:
|
|
138
|
-
print(f"Канал: {channel.title}")
|
|
139
|
-
|
|
140
|
-
await client.close()
|
|
115
|
+
print("Последние сообщения из чата 0:")
|
|
116
|
+
for m in history:
|
|
117
|
+
print(f"- {m.text}")
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
async def main():
|
|
121
|
+
await client.start() # подключение и авторизация
|
|
122
|
+
|
|
141
123
|
|
|
142
124
|
if __name__ == "__main__":
|
|
143
125
|
asyncio.run(main())
|
|
@@ -145,7 +127,8 @@ if __name__ == "__main__":
|
|
|
145
127
|
|
|
146
128
|
## Документация
|
|
147
129
|
|
|
148
|
-
[
|
|
130
|
+
[GitHub Pages](https://maxapiteam.github.io/PyMax/)
|
|
131
|
+
[DeepWiki](https://deepwiki.com/MaxApiTeam/PyMax)
|
|
149
132
|
|
|
150
133
|
## Лицензия
|
|
151
134
|
|
|
@@ -57,65 +57,47 @@ uv add -U maxapi-python
|
|
|
57
57
|
|
|
58
58
|
```python
|
|
59
59
|
import asyncio
|
|
60
|
+
|
|
60
61
|
from pymax import MaxClient, Message
|
|
62
|
+
from pymax.filters import Filters
|
|
63
|
+
|
|
64
|
+
client = MaxClient(
|
|
65
|
+
phone="+1234567890",
|
|
66
|
+
work_dir="cache", # директория для сессий
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# Обработка входящих сообщений
|
|
71
|
+
@client.on_message(Filters.chat(0)) # фильтр по ID чата
|
|
72
|
+
async def on_message(msg: Message) -> None:
|
|
73
|
+
print(f"[{msg.sender}] {msg.text}")
|
|
61
74
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
75
|
+
await client.send_message(
|
|
76
|
+
chat_id=msg.chat_id,
|
|
77
|
+
text="Привет, я бот на PyMax!",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
await client.add_reaction(
|
|
81
|
+
chat_id=msg.chat_id,
|
|
82
|
+
message_id=str(msg.id),
|
|
83
|
+
reaction="👍",
|
|
84
|
+
)
|
|
65
85
|
|
|
66
|
-
# Обработчик входящих сообщений
|
|
67
|
-
@client.on_message()
|
|
68
|
-
async def handle_message(message: Message) -> None:
|
|
69
|
-
print(f"{message.sender}: {message.text}")
|
|
70
86
|
|
|
71
|
-
# Обработчик запуска клиента
|
|
72
87
|
@client.on_start
|
|
73
|
-
async def
|
|
74
|
-
print("Клиент
|
|
88
|
+
async def on_start() -> None:
|
|
89
|
+
print(f"Клиент запущен. Ваш ID: {client.me.id}")
|
|
75
90
|
|
|
76
|
-
# Получение истории
|
|
91
|
+
# Получение истории
|
|
77
92
|
history = await client.fetch_history(chat_id=0)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
# Работа с чатами
|
|
88
|
-
for chat in client.chats:
|
|
89
|
-
print(f"Чат: {chat.title}")
|
|
90
|
-
|
|
91
|
-
# Отправка сообщения
|
|
92
|
-
message = await client.send_message(
|
|
93
|
-
"Привет от PyMax!",
|
|
94
|
-
chat.id,
|
|
95
|
-
notify=True
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
# Редактирование сообщения
|
|
99
|
-
await asyncio.sleep(2)
|
|
100
|
-
await client.edit_message(
|
|
101
|
-
chat.id,
|
|
102
|
-
message.id,
|
|
103
|
-
"Привет от PyMax! (отредактировано)"
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
# Удаление сообщения
|
|
107
|
-
await asyncio.sleep(2)
|
|
108
|
-
await client.delete_message(chat.id, [message.id], for_me=False)
|
|
109
|
-
|
|
110
|
-
# Работа с диалогами
|
|
111
|
-
for dialog in client.dialogs:
|
|
112
|
-
print(f"Диалог: {dialog.last_message.text}")
|
|
113
|
-
|
|
114
|
-
# Работа с каналами
|
|
115
|
-
for channel in client.channels:
|
|
116
|
-
print(f"Канал: {channel.title}")
|
|
117
|
-
|
|
118
|
-
await client.close()
|
|
93
|
+
print("Последние сообщения из чата 0:")
|
|
94
|
+
for m in history:
|
|
95
|
+
print(f"- {m.text}")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
async def main():
|
|
99
|
+
await client.start() # подключение и авторизация
|
|
100
|
+
|
|
119
101
|
|
|
120
102
|
if __name__ == "__main__":
|
|
121
103
|
asyncio.run(main())
|
|
@@ -123,7 +105,8 @@ if __name__ == "__main__":
|
|
|
123
105
|
|
|
124
106
|
## Документация
|
|
125
107
|
|
|
126
|
-
[
|
|
108
|
+
[GitHub Pages](https://maxapiteam.github.io/PyMax/)
|
|
109
|
+
[DeepWiki](https://deepwiki.com/MaxApiTeam/PyMax)
|
|
127
110
|
|
|
128
111
|
## Лицензия
|
|
129
112
|
|
|
@@ -1,26 +1,75 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import datetime
|
|
3
|
+
import logging
|
|
4
|
+
from time import time
|
|
5
|
+
from typing import Any
|
|
3
6
|
|
|
7
|
+
import pymax
|
|
4
8
|
from pymax import MaxClient, Message, ReactionInfo, SocketMaxClient
|
|
5
9
|
from pymax.files import File, Video
|
|
6
|
-
from pymax.
|
|
10
|
+
from pymax.payloads import UserAgentPayload
|
|
7
11
|
from pymax.static.enum import AttachType, Opcode
|
|
8
12
|
from pymax.types import Chat
|
|
9
13
|
|
|
10
|
-
phone = "+
|
|
14
|
+
phone = "+7903223111"
|
|
15
|
+
headers = UserAgentPayload(device_type="WEB")
|
|
11
16
|
|
|
17
|
+
client = MaxClient(
|
|
18
|
+
phone=phone,
|
|
19
|
+
work_dir="cache",
|
|
20
|
+
reconnect=False,
|
|
21
|
+
logger=None,
|
|
22
|
+
headers=headers,
|
|
23
|
+
)
|
|
24
|
+
client.logger.setLevel(logging.INFO)
|
|
12
25
|
|
|
13
|
-
|
|
26
|
+
|
|
27
|
+
@client.on_raw_receive
|
|
28
|
+
async def handle_raw_receive(data: dict[str, Any]) -> None:
|
|
29
|
+
print(f"Raw data received: {data}")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@client.task(seconds=10)
|
|
33
|
+
async def periodic_task() -> None:
|
|
34
|
+
# print(f"Periodic task executed at {datetime.datetime.now()}")
|
|
35
|
+
...
|
|
14
36
|
|
|
15
37
|
|
|
16
38
|
@client.on_start
|
|
17
39
|
async def handle_start() -> None:
|
|
18
40
|
print(f"Client started as {client.me.names[0].first_name}!")
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
41
|
+
|
|
42
|
+
chat_id = -1
|
|
43
|
+
max_messages = 1000
|
|
44
|
+
messages = []
|
|
45
|
+
from_time = int(time() * 1000)
|
|
46
|
+
while len(messages) < max_messages:
|
|
47
|
+
r = await client.fetch_history(
|
|
48
|
+
chat_id=chat_id, from_time=from_time, backward=30
|
|
49
|
+
)
|
|
50
|
+
if not r:
|
|
51
|
+
break
|
|
52
|
+
from_time = r[0].time
|
|
53
|
+
messages.extend(r)
|
|
54
|
+
print(f"First message time: {from_time}, id: {r[0].id}, text: {r[0].text}")
|
|
55
|
+
print(f"Last message time: {from_time}, id: {r[-1].id}, text: {r[-1].text}")
|
|
56
|
+
print(f"Loaded {len(messages)}/{max_messages} messages...")
|
|
57
|
+
# channel = await client.resolve_channel_by_name("fm92")
|
|
58
|
+
# if channel:
|
|
59
|
+
# print(f"Resolved channel by name: {channel.title}, ID: {channel.id}")
|
|
60
|
+
# else:
|
|
61
|
+
# print("Channel not found by name.")
|
|
62
|
+
|
|
63
|
+
# channel = await client.join_channel(link)
|
|
64
|
+
# if channel:
|
|
65
|
+
# print(f"Joined channel: {channel.title}, ID: {channel.id}")
|
|
66
|
+
# else:
|
|
67
|
+
# print("Failed to join channel.")
|
|
68
|
+
# await client.send_message(
|
|
69
|
+
# "Hello! The client has started successfully.",
|
|
70
|
+
# chat_id=2265456546456,
|
|
71
|
+
# notify=True,
|
|
72
|
+
# )
|
|
24
73
|
# folder_update = await client.create_folder(
|
|
25
74
|
# title="My Folder",
|
|
26
75
|
# chat_include=[0],
|
|
@@ -63,21 +112,41 @@ async def handle_start() -> None:
|
|
|
63
112
|
# print(f"Member {member.contact.names[0].first_name}, ID: {member.contact.id}")
|
|
64
113
|
|
|
65
114
|
|
|
66
|
-
@client.on_reaction_change
|
|
67
|
-
async def handle_reaction_change(
|
|
68
|
-
|
|
69
|
-
) -> None:
|
|
115
|
+
# @client.on_reaction_change
|
|
116
|
+
# async def handle_reaction_change(
|
|
117
|
+
# message_id: str, chat_id: int, reaction_info: ReactionInfo
|
|
118
|
+
# ) -> None:
|
|
119
|
+
# print(
|
|
120
|
+
# f"Reaction changed on message {message_id} in chat {chat_id}: "
|
|
121
|
+
# f"Total count: {reaction_info.total_count}, "
|
|
122
|
+
# f"Your reaction: {reaction_info.your_reaction}, "
|
|
123
|
+
# f"Counters: {reaction_info.counters[0].reaction}={reaction_info.counters[0].count}"
|
|
124
|
+
# )
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# @client.on_chat_update
|
|
128
|
+
# async def handle_chat_update(chat: Chat) -> None:
|
|
129
|
+
# print(f"Chat updated: {chat.id}, new title: {chat.title}")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@client.on_message()
|
|
133
|
+
async def handle_message(message: Message) -> None:
|
|
70
134
|
print(
|
|
71
|
-
f"
|
|
72
|
-
f"Total count: {reaction_info.total_count}, "
|
|
73
|
-
f"Your reaction: {reaction_info.your_reaction}, "
|
|
74
|
-
f"Counters: {reaction_info.counters[0].reaction}={reaction_info.counters[0].count}"
|
|
135
|
+
f"New message in chat {message.chat_id} from {message.sender}: {message.text}"
|
|
75
136
|
)
|
|
137
|
+
# if message.link and message.link.message.attaches:
|
|
138
|
+
# for attach in message.link.message.attaches:
|
|
139
|
+
# print(f"Link attach type: {attach.type}")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@client.on_message_edit()
|
|
143
|
+
async def handle_edited_message(message: Message) -> None:
|
|
144
|
+
print(f"Edited message in chat {message.chat_id}: {message.text}")
|
|
76
145
|
|
|
77
146
|
|
|
78
|
-
@client.
|
|
79
|
-
async def
|
|
80
|
-
print(f"
|
|
147
|
+
@client.on_message_delete()
|
|
148
|
+
async def handle_deleted_message(message: Message) -> None:
|
|
149
|
+
print(f"Deleted message in chat {message.chat_id}: {message.id}")
|
|
81
150
|
|
|
82
151
|
|
|
83
152
|
# async def login_flow_test():
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
import pymax
|
|
5
|
+
import pymax.static
|
|
6
|
+
from pymax import MaxClient
|
|
7
|
+
from pymax.filters import Filters
|
|
8
|
+
from pymax.payloads import UserAgentPayload
|
|
9
|
+
from pymax.static.enum import Opcode
|
|
10
|
+
|
|
11
|
+
phone = "+7903223423"
|
|
12
|
+
headers = UserAgentPayload(device_type="WEB")
|
|
13
|
+
|
|
14
|
+
client = MaxClient(
|
|
15
|
+
phone=phone,
|
|
16
|
+
work_dir="cache",
|
|
17
|
+
reconnect=False,
|
|
18
|
+
logger=None,
|
|
19
|
+
headers=headers,
|
|
20
|
+
)
|
|
21
|
+
client.logger.setLevel(logging.DEBUG)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@client.task(seconds=10)
|
|
25
|
+
async def periodic_task() -> None:
|
|
26
|
+
client.logger.info("Periodic task executed")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@client.on_message(Filters.text("test") & ~Filters.chat(0))
|
|
30
|
+
async def handle_message(message: pymax.Message) -> None:
|
|
31
|
+
print(f"New message from {message.sender}: {message.text}")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@client.on_start
|
|
35
|
+
async def on_start():
|
|
36
|
+
print("Client started")
|
|
37
|
+
data = await client._send_and_wait(
|
|
38
|
+
opcode=Opcode.FILE_UPLOAD,
|
|
39
|
+
payload={"count": 1},
|
|
40
|
+
)
|
|
41
|
+
print("File upload response:", data)
|
|
42
|
+
# opcode=pymax.static.enum.Opcode.CHATS_LIST,
|
|
43
|
+
# payload={
|
|
44
|
+
# "marker": 1765721869777,
|
|
45
|
+
# },
|
|
46
|
+
# )
|
|
47
|
+
|
|
48
|
+
# print("Chats list:", data)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
asyncio.run(client.start())
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from pymax import MaxClient
|
|
6
|
+
from pymax.files import File, Video
|
|
7
|
+
|
|
8
|
+
client = MaxClient(phone="+1234567890", work_dir="cache", reconnect=False)
|
|
9
|
+
client.logger.setLevel(logging.INFO)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_big_file(file_path: Path, size_in_mb: int) -> None:
|
|
13
|
+
with open(file_path, "wb") as f:
|
|
14
|
+
f.seek(size_in_mb * 1024 * 1024 - 1)
|
|
15
|
+
f.write(b"\0")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@client.on_start
|
|
19
|
+
async def upload_large_file_example():
|
|
20
|
+
await asyncio.sleep(2)
|
|
21
|
+
|
|
22
|
+
file_path = Path("tests2/large_file.dat")
|
|
23
|
+
|
|
24
|
+
if not file_path.exists():
|
|
25
|
+
create_big_file(file_path, size_in_mb=300)
|
|
26
|
+
file_size = file_path.stat().st_size
|
|
27
|
+
client.logger.info(f"File size: {file_size / (1024 * 1024):.2f} MB")
|
|
28
|
+
|
|
29
|
+
file = File(path=str(file_path))
|
|
30
|
+
chat_id = 0
|
|
31
|
+
|
|
32
|
+
client.logger.info("Starting file upload...")
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
await client.send_message(
|
|
36
|
+
chat_id=chat_id,
|
|
37
|
+
text="📎 Вот большой файл",
|
|
38
|
+
attachment=file,
|
|
39
|
+
)
|
|
40
|
+
client.logger.info("File uploaded successfully!")
|
|
41
|
+
|
|
42
|
+
except OSError as e:
|
|
43
|
+
if "malloc failure" in str(e):
|
|
44
|
+
client.logger.error("Memory error - file too large for current memory")
|
|
45
|
+
client.logger.info("Recommendation: Upload smaller files or free up memory")
|
|
46
|
+
else:
|
|
47
|
+
raise
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
if __name__ == "__main__":
|
|
51
|
+
asyncio.run(client.start())
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
from pymax import MaxClient, Message
|
|
4
|
+
from pymax.filters import Filters
|
|
5
|
+
|
|
6
|
+
client = MaxClient(
|
|
7
|
+
phone="+1234567890",
|
|
8
|
+
work_dir="cache",
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@client.on_message(Filters.chat(0))
|
|
13
|
+
async def on_message(msg: Message):
|
|
14
|
+
print(f"[{msg.sender}] {msg.text}")
|
|
15
|
+
await client.send_message(chat_id=msg.chat_id, text="Привет!")
|
|
16
|
+
await client.add_reaction(
|
|
17
|
+
chat_id=msg.chat_id, message_id=str(msg.id), reaction="👍"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@client.on_start
|
|
22
|
+
async def on_start():
|
|
23
|
+
print(f"Клиент запущен. Ваш ID: {client.me.id}")
|
|
24
|
+
history = await client.fetch_history(chat_id=0)
|
|
25
|
+
for m in history:
|
|
26
|
+
print(f"- {m.text}")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def main():
|
|
30
|
+
await client.start()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
if __name__ == "__main__":
|
|
34
|
+
asyncio.run(main())
|
|
@@ -1,31 +1,26 @@
|
|
|
1
|
+
# Всякая всячина
|
|
1
2
|
import asyncio
|
|
2
|
-
|
|
3
|
+
|
|
4
|
+
# Библиотека для работы с файлами
|
|
3
5
|
from io import BytesIO
|
|
4
6
|
|
|
5
7
|
import aiohttp
|
|
8
|
+
|
|
9
|
+
# Импорты библиотеки aiogram для TG-бота
|
|
6
10
|
from aiogram import Bot, Dispatcher, types
|
|
7
|
-
from dotenv import load_dotenv
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
# Импорты библиотеки PyMax
|
|
13
|
+
from pymax import MaxClient, Message, Photo
|
|
10
14
|
from pymax.types import FileAttach, PhotoAttach, VideoAttach
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
Зависимости:
|
|
14
|
-
|
|
15
|
-
pip install maxapi-python==1.1.13 aiogram==3.22.0 python-dotenv
|
|
16
|
-
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"""
|
|
21
|
-
В .env нужно указать:
|
|
16
|
+
# УСТАНОВИТЬ ЗАВИСИМОСТИ - pip install maxapi-python aiogram==3.22.0
|
|
22
17
|
|
|
23
|
-
PHONE = "+7123456789" # Твой номер для Max
|
|
24
|
-
BOT_TOKEN = "23456789:AAH0cJ3SNzZ2zzD0uF8HOqmxtKpwsKwggM" # Твой токен Telegram-бота
|
|
25
18
|
|
|
26
|
-
|
|
19
|
+
# Настройки ботов
|
|
20
|
+
PHONE = "+79998887766" # Номер телефона Max
|
|
21
|
+
telegram_bot_TOKEN = "token" # Токен TG-бота
|
|
27
22
|
|
|
28
|
-
chats = { # В формате айди чата в Max: айди чата в Telegram
|
|
23
|
+
chats = { # В формате айди чата в Max: айди чата в Telegram (айди чата Max можно узнать из ссылки на чат в веб версии web.max.ru)
|
|
29
24
|
-68690734055662: -1003177746657,
|
|
30
25
|
}
|
|
31
26
|
|
|
@@ -34,16 +29,8 @@ chats = { # В формате айди чата в Max: айди чата в Te
|
|
|
34
29
|
chats_telegram = {value: key for key, value in chats.items()}
|
|
35
30
|
|
|
36
31
|
|
|
37
|
-
#
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
# Настройки из .env
|
|
42
|
-
PHONE = os.getenv("PHONE") # Номер телефона Max
|
|
43
|
-
telegram_bot_TOKEN = os.getenv("BOT_TOKEN") # Токен TG-бота
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
client = MaxClient(phone=PHONE, work_dir="cache")
|
|
32
|
+
# Инициализация клиента MAX
|
|
33
|
+
client = MaxClient(phone=PHONE, work_dir="cache", reconnect=True)
|
|
47
34
|
|
|
48
35
|
|
|
49
36
|
# Инициализация TG-бота
|
|
@@ -54,7 +41,10 @@ dp = Dispatcher()
|
|
|
54
41
|
# Обработчик входящих сообщений MAX
|
|
55
42
|
@client.on_message()
|
|
56
43
|
async def handle_message(message: Message) -> None:
|
|
57
|
-
|
|
44
|
+
try:
|
|
45
|
+
tg_id = chats[message.chat_id]
|
|
46
|
+
except KeyError:
|
|
47
|
+
return
|
|
58
48
|
|
|
59
49
|
sender = await client.get_user(user_id=message.sender)
|
|
60
50
|
|
|
@@ -75,17 +65,14 @@ async def handle_message(message: Message) -> None:
|
|
|
75
65
|
async with session.get(video.url) as response:
|
|
76
66
|
response.raise_for_status() # Проверка на ошибки HTTP
|
|
77
67
|
video_bytes = BytesIO(await response.read())
|
|
78
|
-
video_bytes.name = response.headers.get(
|
|
79
|
-
"X-File-Name"
|
|
80
|
-
)
|
|
68
|
+
video_bytes.name = response.headers.get("X-File-Name")
|
|
81
69
|
|
|
82
70
|
# Отправляем видео через телеграм бота
|
|
83
71
|
await telegram_bot.send_video(
|
|
84
72
|
chat_id=tg_id,
|
|
85
73
|
caption=f"{sender.names[0].name}: {message.text}",
|
|
86
74
|
video=types.BufferedInputFile(
|
|
87
|
-
video_bytes.getvalue(),
|
|
88
|
-
filename=video_bytes.name,
|
|
75
|
+
video_bytes.getvalue(), filename=video_bytes.name
|
|
89
76
|
),
|
|
90
77
|
)
|
|
91
78
|
|
|
@@ -105,17 +92,14 @@ async def handle_message(message: Message) -> None:
|
|
|
105
92
|
async with session.get(attach.base_url) as response:
|
|
106
93
|
response.raise_for_status() # Проверка на ошибки HTTP
|
|
107
94
|
photo_bytes = BytesIO(await response.read())
|
|
108
|
-
photo_bytes.name = response.headers.get(
|
|
109
|
-
"X-File-Name"
|
|
110
|
-
)
|
|
95
|
+
photo_bytes.name = response.headers.get("X-File-Name")
|
|
111
96
|
|
|
112
97
|
# Отправляем фото через телеграм бота
|
|
113
98
|
await telegram_bot.send_photo(
|
|
114
99
|
chat_id=tg_id,
|
|
115
100
|
caption=f"{sender.names[0].name}: {message.text}",
|
|
116
101
|
photo=types.BufferedInputFile(
|
|
117
|
-
photo_bytes.getvalue(),
|
|
118
|
-
filename=photo_bytes.name,
|
|
102
|
+
photo_bytes.getvalue(), filename=photo_bytes.name
|
|
119
103
|
),
|
|
120
104
|
)
|
|
121
105
|
|
|
@@ -142,9 +126,7 @@ async def handle_message(message: Message) -> None:
|
|
|
142
126
|
async with session.get(file.url) as response:
|
|
143
127
|
response.raise_for_status() # Проверка на ошибки HTTP
|
|
144
128
|
file_bytes = BytesIO(await response.read())
|
|
145
|
-
file_bytes.name = response.headers.get(
|
|
146
|
-
"X-File-Name"
|
|
147
|
-
)
|
|
129
|
+
file_bytes.name = response.headers.get("X-File-Name")
|
|
148
130
|
|
|
149
131
|
# Отправляем файл через телеграм бота
|
|
150
132
|
await telegram_bot.send_document(
|
|
@@ -184,9 +166,13 @@ async def handle_start() -> None:
|
|
|
184
166
|
|
|
185
167
|
# Обработчик сообщений Telegram
|
|
186
168
|
@dp.message()
|
|
187
|
-
async def
|
|
169
|
+
async def handle_tg_message(message: types.Message, bot: Bot) -> None:
|
|
188
170
|
max_id = chats_telegram[message.chat.id]
|
|
189
|
-
await client.send_message(
|
|
171
|
+
await client.send_message(
|
|
172
|
+
chat_id=max_id,
|
|
173
|
+
text=f"{message.from_user.first_name}: {message.text}",
|
|
174
|
+
notify=True,
|
|
175
|
+
)
|
|
190
176
|
|
|
191
177
|
|
|
192
178
|
# Раннер ботов
|
|
@@ -195,9 +181,7 @@ async def main() -> None:
|
|
|
195
181
|
telegram_bot_task = asyncio.create_task(dp.start_polling(telegram_bot))
|
|
196
182
|
|
|
197
183
|
try:
|
|
198
|
-
|
|
199
|
-
await client.start()
|
|
200
|
-
|
|
184
|
+
await client.start()
|
|
201
185
|
finally:
|
|
202
186
|
await client.close()
|
|
203
187
|
telegram_bot_task.cancel()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "maxapi-python"
|
|
3
|
-
version = "1.1.
|
|
3
|
+
version = "1.1.21"
|
|
4
4
|
description = "Python wrapper для API мессенджера Max"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -37,11 +37,14 @@ where = ["src"]
|
|
|
37
37
|
|
|
38
38
|
[dependency-groups]
|
|
39
39
|
dev = [
|
|
40
|
+
"furo>=2025.9.25",
|
|
41
|
+
"ghp-import>=2.1.0",
|
|
40
42
|
"mkdocs>=1.6.1",
|
|
41
43
|
"mkdocs-material>=9.6.18",
|
|
42
44
|
"mkdocstrings[python]>=0.30.0",
|
|
43
45
|
"pre-commit>=4.3.0",
|
|
44
46
|
"pydocstring>=0.2.1",
|
|
47
|
+
"sphinx>=8.1.3",
|
|
45
48
|
]
|
|
46
49
|
|
|
47
50
|
[tool.hatch.build.targets.wheel]
|