maxion 0.1.0__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 (60) hide show
  1. maxion-0.1.0/LICENSE +21 -0
  2. maxion-0.1.0/PKG-INFO +355 -0
  3. maxion-0.1.0/README.md +316 -0
  4. maxion-0.1.0/maxion/__init__.py +82 -0
  5. maxion-0.1.0/maxion/client.py +508 -0
  6. maxion-0.1.0/maxion/enums.py +125 -0
  7. maxion-0.1.0/maxion/errors.py +73 -0
  8. maxion-0.1.0/maxion/filters.py +228 -0
  9. maxion-0.1.0/maxion/handlers.py +97 -0
  10. maxion-0.1.0/maxion/parser.py +103 -0
  11. maxion-0.1.0/maxion/raw/__init__.py +125 -0
  12. maxion-0.1.0/maxion/raw/client.py +552 -0
  13. maxion-0.1.0/maxion/raw/const.py +70 -0
  14. maxion-0.1.0/maxion/raw/device.py +247 -0
  15. maxion-0.1.0/maxion/raw/enums.py +222 -0
  16. maxion-0.1.0/maxion/raw/errors.py +100 -0
  17. maxion-0.1.0/maxion/raw/events.py +349 -0
  18. maxion-0.1.0/maxion/raw/filters.py +213 -0
  19. maxion-0.1.0/maxion/raw/methods/__init__.py +47 -0
  20. maxion-0.1.0/maxion/raw/methods/assets.py +139 -0
  21. maxion-0.1.0/maxion/raw/methods/auth.py +438 -0
  22. maxion-0.1.0/maxion/raw/methods/base.py +39 -0
  23. maxion-0.1.0/maxion/raw/methods/calls.py +218 -0
  24. maxion-0.1.0/maxion/raw/methods/chats.py +689 -0
  25. maxion-0.1.0/maxion/raw/methods/contacts.py +172 -0
  26. maxion-0.1.0/maxion/raw/methods/media.py +272 -0
  27. maxion-0.1.0/maxion/raw/methods/messages.py +471 -0
  28. maxion-0.1.0/maxion/raw/methods/misc.py +54 -0
  29. maxion-0.1.0/maxion/raw/methods/profile.py +131 -0
  30. maxion-0.1.0/maxion/raw/methods/reactions.py +94 -0
  31. maxion-0.1.0/maxion/raw/methods/stories.py +214 -0
  32. maxion-0.1.0/maxion/raw/opcodes.py +269 -0
  33. maxion-0.1.0/maxion/raw/protocol.py +190 -0
  34. maxion-0.1.0/maxion/raw/router.py +135 -0
  35. maxion-0.1.0/maxion/raw/session.py +84 -0
  36. maxion-0.1.0/maxion/raw/transport/__init__.py +17 -0
  37. maxion-0.1.0/maxion/raw/transport/base.py +47 -0
  38. maxion-0.1.0/maxion/raw/transport/tcp.py +105 -0
  39. maxion-0.1.0/maxion/raw/transport/ws.py +100 -0
  40. maxion-0.1.0/maxion/raw/types/__init__.py +66 -0
  41. maxion-0.1.0/maxion/raw/types/attach.py +240 -0
  42. maxion-0.1.0/maxion/raw/types/base.py +98 -0
  43. maxion-0.1.0/maxion/raw/types/chat.py +239 -0
  44. maxion-0.1.0/maxion/raw/types/message.py +210 -0
  45. maxion-0.1.0/maxion/raw/types/misc.py +192 -0
  46. maxion-0.1.0/maxion/raw/types/user.py +198 -0
  47. maxion-0.1.0/maxion/raw/utils.py +236 -0
  48. maxion-0.1.0/maxion/types.py +447 -0
  49. maxion-0.1.0/maxion.egg-info/PKG-INFO +355 -0
  50. maxion-0.1.0/maxion.egg-info/SOURCES.txt +58 -0
  51. maxion-0.1.0/maxion.egg-info/dependency_links.txt +1 -0
  52. maxion-0.1.0/maxion.egg-info/requires.txt +14 -0
  53. maxion-0.1.0/maxion.egg-info/top_level.txt +1 -0
  54. maxion-0.1.0/pyproject.toml +52 -0
  55. maxion-0.1.0/setup.cfg +4 -0
  56. maxion-0.1.0/tests/test_client.py +180 -0
  57. maxion-0.1.0/tests/test_coverage.py +184 -0
  58. maxion-0.1.0/tests/test_device.py +232 -0
  59. maxion-0.1.0/tests/test_maxion.py +295 -0
  60. maxion-0.1.0/tests/test_protocol.py +124 -0
maxion-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 PureAholy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
maxion-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,355 @@
1
+ Metadata-Version: 2.4
2
+ Name: maxion
3
+ Version: 0.1.0
4
+ Summary: Клиент мессенджера MAX: юзербот-библиотека на Python
5
+ Author-email: PureAholy <prec1zedev@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/pureaholy/maxion
8
+ Project-URL: Repository, https://github.com/pureaholy/maxion
9
+ Project-URL: Issues, https://github.com/pureaholy/maxion/issues
10
+ Keywords: max,messenger,userbot,client,oneme,async
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Framework :: AsyncIO
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Communications :: Chat
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: websockets>=12.0
27
+ Requires-Dist: aiohttp>=3.9
28
+ Requires-Dist: msgpack>=1.0
29
+ Requires-Dist: lz4>=4.3
30
+ Provides-Extra: re
31
+ Requires-Dist: mitmproxy>=10.0; extra == "re"
32
+ Requires-Dist: androguard>=4.1; extra == "re"
33
+ Provides-Extra: dev
34
+ Requires-Dist: pytest>=8.0; extra == "dev"
35
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
36
+ Requires-Dist: build>=1.2; extra == "dev"
37
+ Requires-Dist: twine>=5.0; extra == "dev"
38
+ Dynamic: license-file
39
+
40
+ # maxion
41
+
42
+ Библиотека-клиент мессенджера **MAX** на Python: работа от лица обычного
43
+ аккаунта, а не через Bot API.
44
+
45
+ Два слоя:
46
+
47
+ 1. **Высокий** — `Client`, `filters`, `types`, `handlers`: обработчики с
48
+ фильтрами, группы и управление распространением события, модели со
49
+ связанными методами.
50
+ 2. **Низкий** — `maxion.raw`: все 153 опкода внутреннего протокола, транспорт
51
+ и кадры, сверенные с APK `ru.oneme.app` 26.29.1. Доступен как `app.raw`.
52
+
53
+ ## Установка
54
+
55
+ ```bash
56
+ pip install -e .
57
+ ```
58
+
59
+ Зависимости: `websockets`, `aiohttp`, `msgpack`, `lz4`.
60
+
61
+ ## Быстрый старт
62
+
63
+ ```python
64
+ from maxion import Client, filters
65
+
66
+ app = Client("my_account", phone_number="+79991234567")
67
+
68
+ @app.on_message(filters.command("start") & filters.private)
69
+ async def start(client, message):
70
+ await message.reply(f"Привет, **{message.from_user.full_name}**!")
71
+
72
+ app.run()
73
+ ```
74
+
75
+ Токен и `deviceId` кладутся в `my_account.session` — второй запуск без SMS.
76
+
77
+ ## Возможности
78
+
79
+ | | |
80
+ | --- | --- |
81
+ | клиент | `Client(name, phone_number=...)`, `start`/`stop`/`run`/`idle`, `async with`, `compose` |
82
+ | обработчики | `@app.on_message`, `on_edited_message`, `on_deleted_messages`, `on_raw_update`, `add_handler`, группы, `StopPropagation`/`ContinuePropagation` |
83
+ | фильтры | `text`, `command`, `regex`, `user`, `chat`, `private`, `group`, `channel`, `me`, `incoming`, `outgoing`, `photo`, `video`, `document`, `sticker`, `reply`, `forwarded`, `create` + операторы `& | ~` |
84
+ | типы | `Message`, `Chat`, `User`, `ChatMember`, `Dialog`, `MessageEntity` с связанными методами (`message.reply`, `.edit_text`, `.delete`, `.forward`, `.download`) |
85
+ | методы | `send_message`, `send_photo`, `send_video`, `send_document`, `edit_message_text`, `delete_messages`, `forward_messages`, `get_chat`, `get_chat_history`, `get_dialogs`, `get_users`, `get_chat_members`, `promote_chat_member`, `ban_chat_member`, `pin_chat_message`, `download_media`, `send_chat_action`, … |
86
+ | разметка | `ParseMode.MARKDOWN` (по умолчанию), `ParseMode.HTML`, `ParseMode.DISABLED` |
87
+ | ошибки | `RPCError`, `FloodWait` (с `.value`), `Unauthorized`, `SessionPasswordNeeded` |
88
+
89
+ Чего нет в самом MAX, того нет и здесь.
90
+
91
+ ## Ниже уровнем
92
+
93
+ Любой опкод доступен напрямую, минуя высокий слой:
94
+
95
+ ```python
96
+ await app.raw.get_stories_feed(count=30)
97
+ await app.raw.set_reactions_settings(chat_id, reaction_ids=["❤️"])
98
+ await app.invoke("CHAT_SUGGEST", {"folderId": 1})
99
+ ```
100
+
101
+ ## Профиль устройства
102
+
103
+ Сервер узнаёт клиента по объекту `userAgent` в SESSION_INIT (6), и от этого
104
+ зависит набор доступных методов: **в web-версии авторизация по номеру телефона
105
+ вырезана**, поэтому вход по SMS делается с профилем телефона.
106
+
107
+ ```python
108
+ MaxClient.mobile("my.session") # как мобильное приложение
109
+ MaxClient.web("my.session") # как браузер
110
+ MaxClient("my.session", device="android") # телефон на Android
111
+ MaxClient("my.session", device=Device.ios()) # телефон на iOS
112
+ MaxClient("my.session", device=Device.desktop()) # десктопное приложение
113
+ MaxClient("my.session") # браузер (по умолчанию)
114
+ MaxClient("my.session", transport="tcp") # профиль телефона сам
115
+ ```
116
+
117
+ Профиль настраивается по частям, а `user_agent=` кладётся поверх:
118
+
119
+ ```python
120
+ MaxClient(
121
+ "my.session",
122
+ device=Device.android(device_name="Pixel 8", android_version="14"),
123
+ user_agent={"timezone": "Asia/Tashkent", "locale": "uz"},
124
+ )
125
+ ```
126
+
127
+ Мобильные и десктопные профили дополнительно шлют `clientSessionId` и
128
+ `mt_instanceid`. Если запросить код с браузерным профилем и сервер откажет,
129
+ `request_code` заменит ошибку на подсказку с готовым решением.
130
+
131
+ Профиль виден и в списке сессий (`client.get_sessions()`) — там будут
132
+ `device_name` и `app_version` отсюда.
133
+
134
+ Android-профиль **снят с APK** `ru.oneme.app` 26.29.1 (класс `Lhti;`, сборщик
135
+ `Liti;->a()`), поэтому набор полей у него другой, чем у браузера:
136
+
137
+ | поле | значение | откуда в клиенте |
138
+ | --- | --- | --- |
139
+ | `deviceType` | `ANDROID` | константа |
140
+ | `appVersion` | `26.29.1` | константа |
141
+ | `buildNumber` | `6808` | константа, int |
142
+ | `osVersion` | `Android 13` | `String.format("Android %s", Build.VERSION.RELEASE)` |
143
+ | `arch` | `arm64-v8a` | `Build.SUPPORTED_ABIS[0]`, иначе `UNKNOWN` |
144
+ | `deviceName` | `Xiaomi Redmi Note 12` | `MANUFACTURER + " " + MODEL` |
145
+ | `screen` | `xxhdpi 480dpi 1080x2400` | бакет плотности + dpi + ширина×высота |
146
+ | `pushDeviceType` | `GCM` | enum: `GCM`, `HUAWEI`, `RUSTORE` |
147
+ | `timezone` | `Europe/Moscow` | `TimeZone.getDefault().getID()` |
148
+
149
+ `headerUserAgent` мобильный клиент не шлёт вовсе — это поле только web-версии.
150
+ `screen` собирается из размеров: `Device.android(width=1440, height=3200, dpi=640)`.
151
+ Профили iOS и desktop дампом не подтверждены.
152
+
153
+ ## Протокол
154
+
155
+ Один RPC в двух представлениях, оба реализованы:
156
+
157
+ | | web | приложение |
158
+ | --- | --- | --- |
159
+ | адрес | `wss://ws-api.oneme.ru/websocket` | `api.oneme.ru:443` (TLS) |
160
+ | кадр | JSON | `[ver:1][cmd:1][seq:2][opcode:2][cof:1][len:3][payload]` |
161
+ | payload | JSON | MsgPack, LZ4 при длине > 32 байт |
162
+ | `ver` | 11 | 10 |
163
+ | транспорт | `WebSocketTransport` | `TcpTransport` |
164
+
165
+ ```python
166
+ MaxClient("my.session", transport="tcp") # бинарный протокол приложения
167
+ ```
168
+
169
+ `cmd`: `0` — запрос, `1` — ответ, `3` — ошибка. `seq` связывает запрос с ответом;
170
+ кадры с опкодами `NOTIF_*` приходят сами по себе и превращаются в события.
171
+
172
+ Кадры разбираются и собираются напрямую:
173
+
174
+ ```python
175
+ from maxion.raw import Packet
176
+
177
+ p = Packet.from_bytes(raw) # бинарный кадр приложения
178
+ p = Packet.from_json(text) # JSON-кадр web-версии
179
+ raw = Packet(opcode=64, payload={...}, seq=1, ver=10).to_bytes()
180
+ ```
181
+
182
+ ## Что умеет клиент
183
+
184
+ **Все 153 вызываемых опкода обёрнуты именованными методами**, из них 144 — с
185
+ разобранными параметрами, а не `**payload`. Остальные 29 опкодов — события
186
+ `NOTIF_*`, для них не методы, а классы событий. Полнота держится тестами:
187
+ новый опкод без обёртки или обёртка без сигнатуры роняют сборку. Основное:
188
+
189
+ ```python
190
+ # чаты
191
+ chats, marker = await client.get_chats(count=40)
192
+ async for chat in client.iter_chats(): ...
193
+ chat = await client.get_chat(chat_id)
194
+ await client.join_chat("https://max.ru/durov")
195
+ await client.create_chat([user_id], title="Тестовая")
196
+ await client.set_chat_title(chat_id, "Новое имя")
197
+ await client.mute_chat(chat_id) # навсегда
198
+ await client.add_members(chat_id, [user_id])
199
+ await client.promote_members(chat_id, user_id)
200
+
201
+ # сообщения
202
+ msg = await client.send_message(chat_id, "**жирно**", markdown=True)
203
+ await msg.reply("ответ")
204
+ await msg.react("🔥")
205
+ await client.edit_message(chat_id, msg.id, "новый текст")
206
+ await client.delete_messages(chat_id, [msg.id])
207
+ await client.pin_message(chat_id, msg.id)
208
+ history = await client.get_history(chat_id, limit=200)
209
+ async for m in client.iter_history(chat_id, limit=1000): ...
210
+
211
+ # медиа
212
+ await client.send_photo(chat_id, "cat.jpg", "котик")
213
+ await client.send_file(chat_id, "report.pdf")
214
+ url = await client.get_file_url(chat_id, msg.id, file_id)
215
+ await client.download(url, "report.pdf")
216
+
217
+ # контакты
218
+ user = await client.get_contact_by_phone("+79991234567")
219
+ await client.block_user(user.id)
220
+ presence = await client.get_presence([user.id])
221
+
222
+ # профиль и безопасность
223
+ await client.update_profile(first_name="Артур", description="…")
224
+ await client.set_hidden(True)
225
+ for s in await client.get_sessions():
226
+ print(s.device_name, s.ip, s.last_active)
227
+ ```
228
+
229
+ Метода ещё нет в библиотеке — вызывается напрямую по имени или номеру:
230
+
231
+ ```python
232
+ await client.call("CHAT_SUGGEST", {"count": 10})
233
+ await client.call(300, {"count": 10})
234
+ ```
235
+
236
+ ### Форматирование
237
+
238
+ ```python
239
+ from maxion.raw import Text
240
+
241
+ await client.send_message(chat_id, Text("Привет, ")
242
+ .bold("мир").text("! ")
243
+ .link("документация", "https://max.ru")
244
+ .mention("Артур", user_id))
245
+
246
+ await client.send_message(chat_id, "это **важно** и `код`", markdown=True)
247
+ ```
248
+
249
+ ### События
250
+
251
+ ```python
252
+ from maxion.raw import Router, filters
253
+
254
+ router = Router()
255
+
256
+ @router.on_message(filters.group & filters.regex(r"(?i)привет"))
257
+ async def hello(update):
258
+ await update.reply(f"и тебе привет, {update.message.sender_id}")
259
+
260
+ @router.on("chat")
261
+ async def chat_changed(update):
262
+ print("чат изменился:", update.chat)
263
+
264
+ @router.on_raw(filters.opcode(155)) # любой опкод, даже неизвестный
265
+ async def raw(update):
266
+ print(update.name, update.payload)
267
+
268
+ client.include_router(router)
269
+ ```
270
+
271
+ События: `message`, `message_deleted`, `typing`, `mark`, `chat`, `contact`,
272
+ `presence`, `reactions`, `callback`, `location`, `folders`, `stories`, `profile`,
273
+ `config`, `attach`, `call`, `raw`.
274
+
275
+ Фильтры: `command`, `text`, `contains`, `regex`, `from_user`, `in_chat`,
276
+ `has_attach`, `opcode`, `incoming`, `outgoing`, `private`, `group`, `custom`
277
+ — комбинируются через `&`, `|`, `~`.
278
+
279
+ ### Устойчивость
280
+
281
+ Пинг каждые 30 с, автопереподключение с нарастающей задержкой и повторным
282
+ логином по токену, обработка `RECONNECT` (3) от сервера. Отключается через
283
+ `MaxClient(..., auto_reconnect=False)`.
284
+
285
+ ## Ошибки
286
+
287
+ ```python
288
+ from maxion.raw import RpcError, FloodWaitError, TwoFactorRequired
289
+
290
+ try:
291
+ await client.send_message(chat_id, "…")
292
+ except FloodWaitError as exc:
293
+ await asyncio.sleep(exc.seconds)
294
+ except RpcError as exc:
295
+ print(exc.code, exc.message)
296
+ ```
297
+
298
+ `TwoFactorRequired` при входе обрабатывается автоматически, если передать
299
+ `password=` в `client.start()`.
300
+
301
+ ## Откуда взяты опкоды и поля
302
+
303
+ Протокол не документирован публично, поэтому таблица опкодов и имена полей
304
+ payload сняты с самого клиента `ru.oneme.app` 26.29.1 (versionCode 6808), а
305
+ кадрирование проверено побайтово на реальном пакете. Полнота покрытия и
306
+ совпадение имён держатся тестами: обёртка без сигнатуры или опкод без метода
307
+ роняют сборку.
308
+
309
+ Где данных не нашлось, об этом честно написано в докстроке метода, а не
310
+ придуманы правдоподобные имена параметров.
311
+
312
+ ## Примеры
313
+
314
+ ```bash
315
+ python examples/echo_bot.py +79991234567 # юзербот: /ping, /id, /echo, реакции
316
+ python examples/dump_account.py --history <chat> # выгрузка чатов, контактов, сессий, истории
317
+ python examples/send_media.py <chat> cat.jpg # отправка медиа и скачивание вложений
318
+ python examples/stories_feed.py --details --view # лента историй: посмотреть и отметить
319
+ python examples/raw_explorer.py # консоль опкодов, копит дамп для infer_schema
320
+ ```
321
+
322
+ `stories_feed.py` и `raw_explorer.py` умеют складывать сырые ответы в JSONL
323
+ (`--dump`) — их сразу можно скормить `tools/infer_schema.py` и уточнить схемы
324
+ тех опкодов, у которых поля пока выведены по соглашениям.
325
+
326
+ ## Структура
327
+
328
+ ```
329
+ maxion/raw/
330
+ protocol.py кадрирование обоих транспортов
331
+ opcodes.py все 182 опкода
332
+ enums.py строковые перечисления протокола
333
+ client.py соединение, RPC, диспетчеризация, реконнект
334
+ session.py токен + deviceId на диске
335
+ transport/ ws.py (JSON) и tcp.py (MsgPack+LZ4)
336
+ methods/ обёртки опкодов по доменам
337
+ types/ модели: Chat, Message, User, Attach, …
338
+ events.py NOTIF_* → типизированные события
339
+ filters.py фильтры обработчиков
340
+ router.py регистрация и доставка
341
+ ```
342
+
343
+ ## Тесты
344
+
345
+ ```bash
346
+ pytest -q
347
+ ```
348
+
349
+ ## Оговорки
350
+
351
+ API внутренний и недокументированный: имена полей и номера опкодов меняются
352
+ вместе с версией приложения. Когда что-то отвалилось — снимите свежий дамп и
353
+ прогоните `tools/`, это ровно тот сценарий, под который тулкит и написан.
354
+ Автоматизация чужих аккаунтов и рассылки нарушают правила сервиса; используйте
355
+ на своём аккаунте и на свой риск.