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.
Files changed (74) hide show
  1. maxapi_python-1.1.21/.coderabbit.yaml +6 -0
  2. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/PKG-INFO +37 -54
  3. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/README.md +36 -53
  4. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/examples/example.py +88 -19
  5. maxapi_python-1.1.21/examples/flt_test.py +51 -0
  6. maxapi_python-1.1.21/examples/large_file_upload.py +51 -0
  7. maxapi_python-1.1.21/examples/reg.py +34 -0
  8. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/examples/telegram_bridge.py +30 -46
  9. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/pyproject.toml +4 -1
  10. maxapi_python-1.1.21/pytest.ini +6 -0
  11. maxapi_python-1.1.21/redocs/Makefile +20 -0
  12. maxapi_python-1.1.21/redocs/build.sh +1 -0
  13. maxapi_python-1.1.21/redocs/make.bat +35 -0
  14. maxapi_python-1.1.21/redocs/source/_static/logo.svg +13 -0
  15. maxapi_python-1.1.21/redocs/source/clients.rst +126 -0
  16. maxapi_python-1.1.21/redocs/source/conf.py +49 -0
  17. maxapi_python-1.1.21/redocs/source/decorators.rst +6 -0
  18. maxapi_python-1.1.21/redocs/source/examples.rst +374 -0
  19. maxapi_python-1.1.21/redocs/source/guides.rst +483 -0
  20. maxapi_python-1.1.21/redocs/source/index.rst +112 -0
  21. maxapi_python-1.1.21/redocs/source/installation.rst +151 -0
  22. maxapi_python-1.1.21/redocs/source/quickstart.rst +246 -0
  23. maxapi_python-1.1.21/redocs/source/types.rst +8 -0
  24. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/core.py +109 -48
  25. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/crud.py +4 -8
  26. maxapi_python-1.1.21/src/pymax/filters.py +164 -0
  27. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/formatter.py +1 -0
  28. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/interfaces.py +12 -6
  29. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/__init__.py +3 -0
  30. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/auth.py +74 -17
  31. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/channel.py +36 -37
  32. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/group.py +127 -8
  33. maxapi_python-1.1.21/src/pymax/mixins/handler.py +291 -0
  34. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/message.py +251 -97
  35. maxapi_python-1.1.21/src/pymax/mixins/scheduler.py +28 -0
  36. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/self.py +79 -40
  37. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/socket.py +243 -257
  38. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/user.py +63 -42
  39. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/websocket.py +145 -145
  40. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/payloads.py +12 -0
  41. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/static/constant.py +4 -2
  42. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/static/enum.py +1 -0
  43. maxapi_python-1.1.20/docs/api.md +0 -716
  44. maxapi_python-1.1.20/docs/assets/icon.svg +0 -13
  45. maxapi_python-1.1.20/docs/client.md +0 -284
  46. maxapi_python-1.1.20/docs/examples.md +0 -149
  47. maxapi_python-1.1.20/docs/index.md +0 -45
  48. maxapi_python-1.1.20/docs/methods.md +0 -876
  49. maxapi_python-1.1.20/docs/types.md +0 -512
  50. maxapi_python-1.1.20/examples/reg.py +0 -13
  51. maxapi_python-1.1.20/src/pymax/filters.py +0 -47
  52. maxapi_python-1.1.20/src/pymax/mixins/handler.py +0 -167
  53. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/.github/FUNDING.yml +0 -0
  54. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  55. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  56. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/.github/ISSUE_TEMPLATE/refactor.md +0 -0
  57. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/.github/pull_request_template.md +0 -0
  58. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/.github/workflows/publish.yml +0 -0
  59. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/.gitignore +0 -0
  60. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/.pre-commit-config.yaml +0 -0
  61. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/LICENSE +0 -0
  62. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/assets/icon.svg +0 -0
  63. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/assets/logo.svg +0 -0
  64. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/mkdocs.yml +0 -0
  65. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/ruff.toml +0 -0
  66. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/__init__.py +0 -0
  67. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/exceptions.py +0 -0
  68. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/files.py +0 -0
  69. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/formatting.py +0 -0
  70. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/telemetry.py +0 -0
  71. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/mixins/utils.py +0 -0
  72. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/models.py +0 -0
  73. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/navigation.py +0 -0
  74. {maxapi_python-1.1.20 → maxapi_python-1.1.21}/src/pymax/types.py +0 -0
@@ -0,0 +1,6 @@
1
+ reviews:
2
+ auto_review:
3
+ enabled: true
4
+ branches:
5
+ include:
6
+ - "*"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maxapi-python
3
- Version: 1.1.20
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
- phone = "+1234567890"
86
- client = MaxClient(phone=phone, work_dir="cache")
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 handle_start() -> None:
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
- if history:
101
- for message in history:
102
- user = await client.get_user(message.sender)
103
- if user:
104
- print(f"{user.names[0].name}: {message.text}")
105
-
106
- async def main() -> None:
107
- await client.start()
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
- [WIP](https://ink-developer.github.io/PyMax)
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
- phone = "+1234567890"
64
- client = MaxClient(phone=phone, work_dir="cache")
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 handle_start() -> None:
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
- if history:
79
- for message in history:
80
- user = await client.get_user(message.sender)
81
- if user:
82
- print(f"{user.names[0].name}: {message.text}")
83
-
84
- async def main() -> None:
85
- await client.start()
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
- [WIP](https://ink-developer.github.io/PyMax)
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.filters import Filter
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 = "+7903223423"
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
- client = MaxClient(phone=phone, work_dir="cache", reconnect=False, logger=None)
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
- folder_list = await client.get_folders()
20
- for folder in folder_list.folders:
21
- if folder.title == "My Folder Renamed":
22
- folder_update = await client.delete_folder(folder_id=folder.id)
23
- print(f"Folder deleted: {folder_update.folder_sync}")
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
- message_id: str, chat_id: int, reaction_info: ReactionInfo
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"Reaction changed on message {message_id} in chat {chat_id}: "
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.on_chat_update
79
- async def handle_chat_update(chat: Chat) -> None:
80
- print(f"Chat updated: {chat.id}, new title: {chat.title}")
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
- import os
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
- from pymax import Chat, MaxClient, Message, User
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
- # Загружаем .env
38
- load_dotenv(override=True)
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
- tg_id = chats[message.chat_id]
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 handle_message(message: types.Message, bot: Bot) -> None:
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(chat_id=max_id, text=message.text, notify=True)
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
- while True: # (Не) спрашивайте 😃
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.20"
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]