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.
- maxion-0.1.0/LICENSE +21 -0
- maxion-0.1.0/PKG-INFO +355 -0
- maxion-0.1.0/README.md +316 -0
- maxion-0.1.0/maxion/__init__.py +82 -0
- maxion-0.1.0/maxion/client.py +508 -0
- maxion-0.1.0/maxion/enums.py +125 -0
- maxion-0.1.0/maxion/errors.py +73 -0
- maxion-0.1.0/maxion/filters.py +228 -0
- maxion-0.1.0/maxion/handlers.py +97 -0
- maxion-0.1.0/maxion/parser.py +103 -0
- maxion-0.1.0/maxion/raw/__init__.py +125 -0
- maxion-0.1.0/maxion/raw/client.py +552 -0
- maxion-0.1.0/maxion/raw/const.py +70 -0
- maxion-0.1.0/maxion/raw/device.py +247 -0
- maxion-0.1.0/maxion/raw/enums.py +222 -0
- maxion-0.1.0/maxion/raw/errors.py +100 -0
- maxion-0.1.0/maxion/raw/events.py +349 -0
- maxion-0.1.0/maxion/raw/filters.py +213 -0
- maxion-0.1.0/maxion/raw/methods/__init__.py +47 -0
- maxion-0.1.0/maxion/raw/methods/assets.py +139 -0
- maxion-0.1.0/maxion/raw/methods/auth.py +438 -0
- maxion-0.1.0/maxion/raw/methods/base.py +39 -0
- maxion-0.1.0/maxion/raw/methods/calls.py +218 -0
- maxion-0.1.0/maxion/raw/methods/chats.py +689 -0
- maxion-0.1.0/maxion/raw/methods/contacts.py +172 -0
- maxion-0.1.0/maxion/raw/methods/media.py +272 -0
- maxion-0.1.0/maxion/raw/methods/messages.py +471 -0
- maxion-0.1.0/maxion/raw/methods/misc.py +54 -0
- maxion-0.1.0/maxion/raw/methods/profile.py +131 -0
- maxion-0.1.0/maxion/raw/methods/reactions.py +94 -0
- maxion-0.1.0/maxion/raw/methods/stories.py +214 -0
- maxion-0.1.0/maxion/raw/opcodes.py +269 -0
- maxion-0.1.0/maxion/raw/protocol.py +190 -0
- maxion-0.1.0/maxion/raw/router.py +135 -0
- maxion-0.1.0/maxion/raw/session.py +84 -0
- maxion-0.1.0/maxion/raw/transport/__init__.py +17 -0
- maxion-0.1.0/maxion/raw/transport/base.py +47 -0
- maxion-0.1.0/maxion/raw/transport/tcp.py +105 -0
- maxion-0.1.0/maxion/raw/transport/ws.py +100 -0
- maxion-0.1.0/maxion/raw/types/__init__.py +66 -0
- maxion-0.1.0/maxion/raw/types/attach.py +240 -0
- maxion-0.1.0/maxion/raw/types/base.py +98 -0
- maxion-0.1.0/maxion/raw/types/chat.py +239 -0
- maxion-0.1.0/maxion/raw/types/message.py +210 -0
- maxion-0.1.0/maxion/raw/types/misc.py +192 -0
- maxion-0.1.0/maxion/raw/types/user.py +198 -0
- maxion-0.1.0/maxion/raw/utils.py +236 -0
- maxion-0.1.0/maxion/types.py +447 -0
- maxion-0.1.0/maxion.egg-info/PKG-INFO +355 -0
- maxion-0.1.0/maxion.egg-info/SOURCES.txt +58 -0
- maxion-0.1.0/maxion.egg-info/dependency_links.txt +1 -0
- maxion-0.1.0/maxion.egg-info/requires.txt +14 -0
- maxion-0.1.0/maxion.egg-info/top_level.txt +1 -0
- maxion-0.1.0/pyproject.toml +52 -0
- maxion-0.1.0/setup.cfg +4 -0
- maxion-0.1.0/tests/test_client.py +180 -0
- maxion-0.1.0/tests/test_coverage.py +184 -0
- maxion-0.1.0/tests/test_device.py +232 -0
- maxion-0.1.0/tests/test_maxion.py +295 -0
- 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
|
+
на своём аккаунте и на свой риск.
|