aiogram-max 1.1.1__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 (38) hide show
  1. aiogram_max-1.1.1/.github/workflows/ci.yml +43 -0
  2. aiogram_max-1.1.1/.github/workflows/publish.yml +55 -0
  3. aiogram_max-1.1.1/.gitignore +11 -0
  4. aiogram_max-1.1.1/CONTRIBUTING.md +48 -0
  5. aiogram_max-1.1.1/LICENSE +21 -0
  6. aiogram_max-1.1.1/PKG-INFO +256 -0
  7. aiogram_max-1.1.1/README.md +224 -0
  8. aiogram_max-1.1.1/docs/method-coverage.md +165 -0
  9. aiogram_max-1.1.1/examples/demo_bot.py +65 -0
  10. aiogram_max-1.1.1/pyproject.toml +75 -0
  11. aiogram_max-1.1.1/scripts/capture_updates.py +61 -0
  12. aiogram_max-1.1.1/scripts/check_captured.py +56 -0
  13. aiogram_max-1.1.1/scripts/live_e2e.py +86 -0
  14. aiogram_max-1.1.1/scripts/send_test_keyboard.py +54 -0
  15. aiogram_max-1.1.1/src/aiogram_max/__init__.py +39 -0
  16. aiogram_max-1.1.1/src/aiogram_max/bot.py +75 -0
  17. aiogram_max-1.1.1/src/aiogram_max/converters.py +279 -0
  18. aiogram_max-1.1.1/src/aiogram_max/errors.py +44 -0
  19. aiogram_max-1.1.1/src/aiogram_max/markup.py +251 -0
  20. aiogram_max-1.1.1/src/aiogram_max/methods/__init__.py +12 -0
  21. aiogram_max-1.1.1/src/aiogram_max/methods/base.py +73 -0
  22. aiogram_max-1.1.1/src/aiogram_max/methods/chats.py +160 -0
  23. aiogram_max-1.1.1/src/aiogram_max/methods/media.py +313 -0
  24. aiogram_max-1.1.1/src/aiogram_max/methods/settings.py +86 -0
  25. aiogram_max-1.1.1/src/aiogram_max/mids.py +44 -0
  26. aiogram_max-1.1.1/src/aiogram_max/py.typed +0 -0
  27. aiogram_max-1.1.1/src/aiogram_max/session.py +538 -0
  28. aiogram_max-1.1.1/src/aiogram_max/uploads.py +123 -0
  29. aiogram_max-1.1.1/tests/__init__.py +0 -0
  30. aiogram_max-1.1.1/tests/fixtures/live_max_updates.json +95 -0
  31. aiogram_max-1.1.1/tests/test_bot_settings.py +128 -0
  32. aiogram_max-1.1.1/tests/test_chats.py +264 -0
  33. aiogram_max-1.1.1/tests/test_live_fixtures.py +99 -0
  34. aiogram_max-1.1.1/tests/test_markup.py +109 -0
  35. aiogram_max-1.1.1/tests/test_mids.py +52 -0
  36. aiogram_max-1.1.1/tests/test_polling.py +41 -0
  37. aiogram_max-1.1.1/tests/test_session.py +850 -0
  38. aiogram_max-1.1.1/uv.lock +1068 -0
@@ -0,0 +1,43 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master, main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ # 3.12 — минимальная поддерживаемая, 3.14 — на чём разрабатывается.
15
+ python-version: ["3.12", "3.13", "3.14"]
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Install uv
21
+ uses: astral-sh/setup-uv@v5
22
+ with:
23
+ enable-cache: true
24
+
25
+ - name: Create venv on Python ${{ matrix.python-version }}
26
+ # Именно venv, а не --system: системный Python на раннере
27
+ # externally-managed, и uv в него ставить отказывается.
28
+ run: uv venv --python ${{ matrix.python-version }}
29
+
30
+ - name: Install dependencies
31
+ run: uv pip install -e ".[dev]"
32
+
33
+ - name: Ruff (lint)
34
+ run: uv run ruff check .
35
+
36
+ - name: Ruff (format)
37
+ run: uv run ruff format --check .
38
+
39
+ - name: Mypy (strict)
40
+ run: uv run mypy
41
+
42
+ - name: Pytest
43
+ run: uv run pytest -q
@@ -0,0 +1,55 @@
1
+ name: publish
2
+
3
+ # Публикация на PyPI по факту релиза на GitHub: тег уже проставлен, версия
4
+ # в pyproject должна ему соответствовать — это проверяется ниже до сборки.
5
+ on:
6
+ release:
7
+ types: [published]
8
+ # Ручной запуск нужен, чтобы прогнать сборку и проверки без публикации:
9
+ # dry_run по умолчанию включён, публикация требует осознанного выбора.
10
+ workflow_dispatch:
11
+ inputs:
12
+ dry_run:
13
+ description: Только собрать и проверить, без публикации
14
+ type: boolean
15
+ default: true
16
+
17
+ jobs:
18
+ publish:
19
+ runs-on: ubuntu-latest
20
+ permissions:
21
+ # Доверенная публикация PyPI: короткоживущий OIDC-токен вместо
22
+ # постоянного секрета в репозитории. Утёкший API-токен PyPI отзывать
23
+ # некому и некогда — здесь отзывать нечего.
24
+ id-token: write
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+
28
+ - name: Install uv
29
+ uses: astral-sh/setup-uv@v5
30
+ with:
31
+ enable-cache: true
32
+
33
+ - name: Версия в pyproject совпадает с тегом
34
+ # Расхождение тега и версии — самая обидная ошибка релиза: пакет
35
+ # уезжает под чужим номером, а исправить уже нельзя, версии на PyPI
36
+ # не переиспользуются. Проверяем до сборки.
37
+ if: github.event_name == 'release'
38
+ run: |
39
+ set -euo pipefail
40
+ TAG="${GITHUB_REF_NAME#v}"
41
+ VERSION=$(uv run python -c "import tomllib,pathlib; print(tomllib.loads(pathlib.Path('pyproject.toml').read_text())['project']['version'])")
42
+ echo "тег: $TAG, версия в pyproject: $VERSION"
43
+ test "$TAG" = "$VERSION"
44
+
45
+ - name: Сборка
46
+ run: uv build
47
+
48
+ - name: Проверка метаданных
49
+ # twine check ловит то, что ломает страницу пакета: битый README,
50
+ # незакрытую разметку, отсутствующие поля.
51
+ run: uvx twine check dist/*
52
+
53
+ - name: Публикация на PyPI
54
+ if: github.event_name == 'release' || inputs.dry_run == false
55
+ run: uv publish --trusted-publishing always
@@ -0,0 +1,11 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.py[cod]
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ .mypy_cache/
7
+ dist/
8
+ build/
9
+ *.egg-info/
10
+ .env
11
+ captured_updates.jsonl
@@ -0,0 +1,48 @@
1
+ # Как поучаствовать
2
+
3
+ Библиотека покрывает не весь Telegram Bot API, и это осознанно: методы
4
+ добавляются по мере надобности. Если вам не хватило какого-то — скорее всего,
5
+ он уже помечен как «ждём PR».
6
+
7
+ ## Что означает ошибка, которую вы поймали
8
+
9
+ * `NotImplementedYet` — **аналог в MAX есть, мы его не написали**. В тексте
10
+ ошибки лежит подсказка, каким методом MAX это делается. Такой PR приветствуется
11
+ и обычно занимает несколько десятков строк.
12
+ * `UnsupportedByMax` — **у платформы нет такой возможности**. Опросов и
13
+ игральных костей в MAX нет, PR тут не поможет.
14
+
15
+ Список первого вида — в `NOT_IMPLEMENTED_PR_WELCOME` (`src/aiogram_max/session.py`).
16
+
17
+ ## Добавить метод
18
+
19
+ 1. Напишите обработчик в `MaxSession` и зарегистрируйте его в `_HANDLERS`.
20
+ Обработчик обязан вернуть ровно тот тип, который объявлен у метода aiogram
21
+ в `__returning__` — иначе сломается типизация на стороне бота.
22
+ 2. Уберите метод из `NOT_IMPLEMENTED_PR_WELCOME`.
23
+ 3. Тест обязателен. Фейковый MAX (`FakeMax` в `tests/test_session.py`) отвечает
24
+ на новый путь — добавьте туда ветку и проверьте **тело запроса**, а не только
25
+ факт вызова: половина расхождений с MAX — именно в полях.
26
+ 4. Если параметр Telegram при переводе теряется — сообщите об этом через
27
+ `self._degrade(...)`. Молча выбрасывать нельзя.
28
+
29
+ ## Проверки
30
+
31
+ ```bash
32
+ uv venv && uv pip install -e ".[dev]"
33
+ .venv/bin/ruff check . && .venv/bin/ruff format --check .
34
+ .venv/bin/mypy
35
+ .venv/bin/pytest -q
36
+ ```
37
+
38
+ Всё это гоняется в CI на Python 3.12, 3.13 и 3.14.
39
+
40
+ ## О тестах на живых данных
41
+
42
+ `tests/fixtures/live_max_updates.json` — обезличенные события, снятые с
43
+ настоящего MAX Bot API. Тесты на них (`tests/test_live_fixtures.py`) ловят то,
44
+ чего не поймает фейк: расхождение между документацией и реальным поведением.
45
+ Один такой баг уже нашёлся — `recipient.user_id` оказался получателем
46
+ сообщения, а не собеседником, и id бота едва не утёк в `Chat.id`.
47
+
48
+ Если ловите новое событие от живого MAX — обезличьте и добавьте в фикстуры.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sergey Borisov
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.
@@ -0,0 +1,256 @@
1
+ Metadata-Version: 2.4
2
+ Name: aiogram-max
3
+ Version: 1.1.1
4
+ Summary: Запускает aiogram-бота в мессенджере MAX: подменяем сессию, код бота не трогаем
5
+ Project-URL: Homepage, https://github.com/juntatalor/aiogram-max
6
+ Project-URL: Source, https://github.com/juntatalor/aiogram-max
7
+ Project-URL: Issues, https://github.com/juntatalor/aiogram-max/issues
8
+ Project-URL: Changelog, https://github.com/juntatalor/aiogram-max/releases
9
+ Author-email: Sergey Borisov <juntatalor@gmail.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: aiogram,bot,max,messenger,telegram
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Framework :: AsyncIO
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Communications :: Chat
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.12
24
+ Requires-Dist: aiogram>=3.20
25
+ Requires-Dist: httpx>=0.27
26
+ Provides-Extra: dev
27
+ Requires-Dist: mypy>=1.13; extra == 'dev'
28
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
29
+ Requires-Dist: pytest>=8; extra == 'dev'
30
+ Requires-Dist: ruff>=0.8; extra == 'dev'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # aiogram-max
34
+
35
+ [![PyPI](https://img.shields.io/pypi/v/aiogram-max)](https://pypi.org/project/aiogram-max/)
36
+ [![Python](https://img.shields.io/pypi/pyversions/aiogram-max)](https://pypi.org/project/aiogram-max/)
37
+ [![CI](https://github.com/juntatalor/aiogram-max/actions/workflows/ci.yml/badge.svg)](https://github.com/juntatalor/aiogram-max/actions/workflows/ci.yml)
38
+ [![License](https://img.shields.io/pypi/l/aiogram-max)](https://github.com/juntatalor/aiogram-max/blob/master/LICENSE)
39
+ [![Checked with mypy](https://img.shields.io/badge/mypy-strict-2a6db2)](https://mypy-lang.org/)
40
+ [![Ruff](https://img.shields.io/badge/ruff-passing-261230)](https://docs.astral.sh/ruff/)
41
+
42
+ Запускает бота, написанного на **aiogram**, в мессенджере **MAX** — без правок кода бота.
43
+
44
+ ```python
45
+ from aiogram import Dispatcher
46
+ from aiogram_max import make_bot
47
+
48
+ bot = make_bot(max_token="...") # единственная изменённая строка
49
+ await Dispatcher().start_polling(bot)
50
+ ```
51
+
52
+ Роутеры, фильтры (`Command`, магический `F`), FSM, middleware и типы остаются
53
+ родными aiogram-овскими.
54
+
55
+ ## Статус
56
+
57
+ **Рабочая библиотека, проверенная на живом MAX.** Настоящий
58
+ `Dispatcher.start_polling` ходит в MAX Bot API, хендлеры срабатывают, ответы
59
+ и `callback.answer()` доезжают до мессенджера.
60
+
61
+ Покрыто 40 методов — всё, чему нашёлся аналог у платформы: сообщения и
62
+ правки, вложения всех типов, клавиатуры и callback'и, группы с участниками и
63
+ пинами, вебхук, команды бота, разметка. Каждый проверен на реальном боте, а
64
+ не только тестами.
65
+
66
+ ## Как это работает
67
+
68
+ Весь исходящий трафик aiogram проходит через одну функцию:
69
+
70
+ ```
71
+ BaseSession.make_request(bot, method: TelegramMethod, timeout) -> TelegramType
72
+ ```
73
+
74
+ `Bot` не ходит в сеть сам: он собирает типизированный объект метода
75
+ (`SendMessage`, `GetUpdates`, `AnswerCallbackQuery`, …) и отдаёт его сессии.
76
+ Мы подменяем сессию — и перехватываем поток целиком:
77
+
78
+ ```
79
+ Бот на aiogram (код не меняется)
80
+ Dispatcher / Router / фильтры / FSM
81
+ Bot.send_message(...)
82
+ │ TelegramMethod
83
+ ▼
84
+ MaxSession(BaseSession) ← вся библиотека здесь
85
+ │ HTTP
86
+ ▼
87
+ MAX Bot API
88
+ ```
89
+
90
+ Long polling тоже работает штатно: aiogram сам зовёт `GetUpdates`, а сессия
91
+ ходит в `GET /updates?marker=` и собирает из событий MAX валидные
92
+ aiogram-`Update`.
93
+
94
+ Отличие от [obabot](https://github.com/Korean-DOG/obabot), решающего похожую
95
+ задачу: там своя система типов (`obabot.Message`, `obabot.FSMContext`) и
96
+ импорты в боте меняются. Здесь типы остаются aiogram-овскими, подменяется
97
+ только транспорт.
98
+
99
+ ## Что уже проверено тестами
100
+
101
+ | Что | Как проверено |
102
+ | --- | --- |
103
+ | MAX-событие → валидный aiogram `Update` | `test_get_updates_returns_aiogram_updates` |
104
+ | Роутер + фильтр `Command` | `test_dispatcher_routes_message_to_handler` |
105
+ | Inline-клавиатура → MAX `inline_keyboard` | там же, сверка тела запроса |
106
+ | Callback + магический фильтр `F.data` | `test_callback_query_flows_through_aiogram` |
107
+ | FSM (`StatesGroup`, `FSMContext`) | `test_fsm_state_survives_platform_swap` |
108
+ | Родной `Dispatcher.start_polling` | `test_native_polling_loop_delivers_max_events` |
109
+ | Правка сообщения (`seq` ↔ `mid`) | `test_edit_message_uses_max_mid` |
110
+ | Неподдерживаемый метод | `test_unsupported_method_raises_in_strict_mode` |
111
+ | Живые payload'ы MAX | `tests/test_live_fixtures.py` (6 тестов на снятых с API событиях) |
112
+ | Потеря кнопки без аналога | `test_dropped_button_warns_but_keeps_the_rest` |
113
+ | Маппинг parse_mode / notify / reply | `test_supported_params_are_mapped_not_dropped` |
114
+ | Вложения: загрузка и отправка | `test_send_photo_uploads_and_attaches_token` |
115
+ | Ожидание обработки файла на стороне MAX | `test_send_photo_waits_until_attachment_is_ready` |
116
+ | MarkdownV2 → html (включая подчёркивание) | `tests/test_markup.py`, 11 тестов |
117
+ | `entities` → html без потерь | `test_entities_are_no_longer_dropped_silently` |
118
+ | `MarkupPolicy.RAW` не трогает текст | `test_raw_policy_leaves_text_alone` |
119
+
120
+ ## Неподдерживаемое в MAX
121
+
122
+ У MAX нет части возможностей Telegram. Политика задаётся при создании бота:
123
+
124
+ ```python
125
+ make_bot(token) # WARN, по умолчанию
126
+ make_bot(token, unsupported=UnsupportedPolicy.STRICT)
127
+ ```
128
+
129
+ Расхождения бывают трёх видов:
130
+
131
+ 1. **Метода нет вовсе** (`SendPoll`, `SendDice`). `WARN` — предупреждение и
132
+ пропуск, `STRICT` — `UnsupportedByMax` с именем метода.
133
+ 2. **Метод есть, а параметра нет** (кнопка `web_app`, `switch_inline_query`).
134
+ Такая кнопка выбрасывается, остальные остаются: `WARN` пишет в лог что
135
+ именно потерялось, `STRICT` падает. Молча не выбрасываем никогда —
136
+ «кнопка исчезла, а бот не упал» ищется потом часами.
137
+ 3. **Семантика другая** — это работа слоя конвертации, а не политики:
138
+ `message_id` (int) ↔ MAX `mid` (str) сшиваются через `seq`.
139
+
140
+ Где аналог есть — параметр переводится, а не теряется:
141
+
142
+ | aiogram | MAX |
143
+ | --- | --- |
144
+ | `parse_mode="HTML"` | `format: html` |
145
+ | `parse_mode="MarkdownV2"` | конвертация в html, см. «Разметка» |
146
+ | `entities=[...]` | конвертация в html, см. «Разметка» |
147
+ | `disable_notification=True` | `notify: false` |
148
+ | `reply_to_message_id` | `link: {type: reply, mid}` |
149
+
150
+ Отдельный случай — пустой `callback.answer()`. В Telegram он снимает
151
+ индикатор загрузки на кнопке и стоит почти в каждом хендлере; у MAX такой
152
+ семантики нет, `POST /answers` с пустым телом отвечает `400 proto.payload`
153
+ («`message` or `notification` required»). Запрос не уходит вовсе. С текстом —
154
+ `answer("Принято")` — уходит как `notification`.
155
+
156
+ ## Разметка
157
+
158
+ Работает из коробки: бот шлёт привычный телеграмный `parse_mode` или
159
+ `entities`, библиотека переводит это в html, который MAX понимает.
160
+
161
+ ```python
162
+ await bot.send_message(chat_id, "*жирный* и __подчёркнутый__", parse_mode="MarkdownV2")
163
+ # в MAX уедет: {"text": "<b>жирный</b> и <u>подчёркнутый</u>", "format": "html"}
164
+ ```
165
+
166
+ Форматируете под MAX сами — отключите посредника:
167
+
168
+ ```python
169
+ make_bot(token, markup=MarkupPolicy.RAW) # текст уйдёт как есть
170
+ ```
171
+
172
+ **Почему html, а не markdown.** MAX принимает оба формата, но markdown у него
173
+ CommonMark, а в CommonMark `__текст__` — это жирный. В MarkdownV2 та же запись
174
+ означает подчёркивание, и перевод «markdown в markdown» молча превратил бы
175
+ одно в другое. Тип `underline` MAX отдаёт только за `<u>`. Вдобавок html не
176
+ требует правил экранирования, которых в MarkdownV2 полтора десятка, причём
177
+ внутри кодовых спанов они другие.
178
+
179
+ **Что MAX действительно поддерживает.** Официальная страница «Форматирование»
180
+ отдаёт 404, поэтому таблица снята с живого API: бот отправил пробу, MAX
181
+ вернул разобранную разметку.
182
+
183
+ | Разметка | `format: html` | `format: markdown` |
184
+ | --- | --- | --- |
185
+ | жирный | `<b>` → `strong` | `**x**` → `strong` |
186
+ | курсив | `<i>` → `emphasized` | `*x*`, `_x_` → `emphasized` |
187
+ | подчёркнутый | `<u>` → `underline` | **нет**: `__x__` даёт `strong` |
188
+ | зачёркнутый | `<s>` → `strikethrough` | `~~x~~` → `strikethrough` |
189
+ | моноширинный | `<code>`, `<pre>` → `monospaced` | `` `x` `` → `monospaced` |
190
+ | ссылка | `<a href>` → `link` | `[x](url)` → `link` |
191
+ | заголовок | не проверялся | `# x` → `heading` |
192
+ | цитата | `<blockquote>` — **не распознаётся** | `> x` — **не распознаётся** |
193
+ | списки, спойлер | нет | нет |
194
+
195
+ Чего у MAX нет вовсе — спойлер, цитата, кастомные эмодзи — проходит через ту
196
+ же политику `unsupported`: `WARN` пишет в лог что именно потерялось, `STRICT`
197
+ падает. Текст при этом сохраняется, теряется только оформление.
198
+
199
+ ## Грабли MAX, которые стоит знать
200
+
201
+ * **Индикатор набора у MAX есть**, вопреки расхожему мнению: работает и в
202
+ личке, и в группе. Только называется иначе — телеграмное `typing` он не
203
+ понимает, нужно `typing_on`, и библиотека переводит сама.
204
+ * **Вложение не готово сразу после заливки.** Отправка отвечает
205
+ `400 attachment.not.ready`, пока MAX обрабатывает файл. Библиотека ждёт и
206
+ повторяет сама, но знать об этом стоит: в Telegram такого шага нет.
207
+ * **`recipient.user_id` — это получатель сообщения, а не собеседник.** В
208
+ событии от пользователя там лежит id бота, в сообщении бота пользователю —
209
+ id пользователя. Подставите его как `chat_id` — бот начнёт молча отвечать
210
+ сам себе. `chat_id` в диалоге MAX присылает, брать надо только его.
211
+ * **Позиция в ленте — `marker`, и она же `update_id`.** MAX отдаёт в ответе
212
+ «следующую ожидаемую» позицию, что совпадает по смыслу с телеграмным
213
+ `offset`. Поэтому `update_id` события — его позиция в ленте (`marker - 1`
214
+ для последнего события пачки), а не порядковый номер внутри процесса.
215
+ Для бота с дедупом по `update_id` это принципиально: счётчик из памяти
216
+ после рестарта начинается заново, и свежие события выглядят уже
217
+ обработанными.
218
+ * **`seq` — не маленький счётчик.** Живое значение: `116993690454357274`.
219
+ В `int64` влезает и aiogram переваривает, но узкое поле БД (`integer`
220
+ вместо `bigint`) на этом сломается.
221
+ * **`/start` приходит обычным `message_created`**, а не отдельным событием —
222
+ фильтр `Command` работает без спецобработки.
223
+ * **Идентификатор сообщения двойной**: aiogram знает `message_id` (int из
224
+ `seq`), MAX правит и удаляет по строковому `mid`. Соответствие держит
225
+ сессия в памяти (последние 10 000 сообщений), поэтому правка переживает
226
+ рестарт только в пределах жизни процесса. Обратного преобразования
227
+ `mid → seq` MAX не даёт, так что иначе никак.
228
+
229
+ ## Что ещё не сделано
230
+ * Webhook (у MAX это рекомендованный для прода транспорт).
231
+ * Разметка подписей (`caption`, `caption_entities`) — вместе с вложениями.
232
+ * Покрыто 40 методов из 185 в aiogram — всё, чему есть аналог у MAX. Что уже работает, что можно добавить и
233
+ чего в MAX нет вовсе — в [docs/method-coverage.md](docs/method-coverage.md).
234
+
235
+ ## Установка
236
+
237
+ ```bash
238
+ pip install aiogram-max # после публикации; пока — pip install -e .
239
+ ```
240
+
241
+ Python 3.12+, единственные зависимости — `aiogram` и `httpx`. Библиотека
242
+ типизирована (`py.typed`), mypy strict проходит без ignore'ов.
243
+
244
+ ## Разработка
245
+
246
+ ```bash
247
+ uv venv && uv pip install -e ".[dev]"
248
+ .venv/bin/ruff check . && .venv/bin/mypy && .venv/bin/pytest -q
249
+ ```
250
+
251
+ Как добавить метод и чем `NotImplementedYet` отличается от `UnsupportedByMax` —
252
+ в [CONTRIBUTING.md](CONTRIBUTING.md).
253
+
254
+ ## Лицензия
255
+
256
+ MIT