This release is a pre-release and may not be stable for production use.
aiomax2
aiomax2 — асинхронный Python-фреймворк для MAX Bot API.
Он предоставляет знакомую по aiogram 3.x архитектуру с Bot, Dispatcher,
Router, фильтрами, middleware, FSM и shortcuts, но работает с реальными
сущностями, методами и ограничениями MAX.
Статус: Alpha (
0.1.0a2)Библиотека находится в активной разработке. Публичный API до стабильного релиза может изменяться.
Документация: https://candycrimsie.github.io/aiomax2/
Возможности
- полностью асинхронная работа через
asyncioиaiohttp; - типизированные модели MAX API на Pydantic 2;
Bot,Dispatcherи вложенныеRouter;- фильтры и magic filter
F; Command,CommandStartиCommandObject;- outer и inner middleware;
- dependency/context injection по сигнатуре handler;
- FSM с
FSMContext,State,StatesGroupиMemoryStorage; - shortcuts для сообщений и callback;
InlineKeyboardBuilderиreply_markupдля inline-клавиатур MAX;- HTML и MAX Markdown через
TextFormat; - Long Polling;
- Webhook и интеграция с FastAPI;
- загрузка изображений, видео, аудио и файлов;
- управление webhook-подписками;
- типизированные исключения MAX API;
- автоматическая обработка
429иRetry-After; - встроенные rate limits;
- безопасный по умолчанию HTTP transport с TLS verification;
- поддержка custom
aiohttp.ClientSession; - поддержка дополнительного CA bundle;
- низкоуровневый доступ к MAX API через
Bot.request().
Требования
- Python 3.12+
aiohttp- Pydantic 2
- FastAPI
- Uvicorn
Установка
Пока библиотека не опубликована на PyPI, её можно установить напрямую из GitHub:
pip install "aiomax2 @ git+https://github.com/CandyCrimsie/aiomax2.git"
Эта единственная установка включает Long Polling, Webhook, FastAPI и Uvicorn. Режим доставки updates выбирается в приложении или deployment configuration, а не через package extra. После публикации на PyPI будет достаточно:
pip install aiomax2
Для локальной разработки:
git clone https://github.com/CandyCrimsie/aiomax2.git
cd aiomax2
python -m pip install -e ".[dev]"
Быстрый старт
Создайте бота в MAX и передайте его token через переменную окружения
MAX_BOT_TOKEN.
import asyncio
import os
from aiomax2 import Bot, Dispatcher, Router
from aiomax2.filters import Command
from aiomax2.types import Message
router = Router()
@router.message(Command("start"))
async def start(message: Message) -> None:
await message.answer("Привет из aiomax2!")
async def main() -> None:
bot = Bot(os.environ["MAX_BOT_TOKEN"])
dp = Dispatcher()
dp.include_router(router)
try:
await dp.start_polling(bot)
finally:
await bot.close()
await dp.close()
if __name__ == "__main__":
asyncio.run(main())
Запуск:
python bot.py
Dispatcher и Router
Обработчики группируются внутри Router, после чего router подключается к
Dispatcher.
from aiomax2 import Dispatcher, Router
from aiomax2.filters import Command
from aiomax2.types import Message
router = Router()
@router.message(Command("help"))
async def help_handler(message: Message) -> None:
await message.answer("Помощь")
dp = Dispatcher()
dp.include_router(router)
Можно создавать несколько router и вкладывать их друг в друга для разделения логики приложения.
Фильтры
Для простых условий доступен magic filter F:
from aiomax2 import F
from aiomax2.types import Message
@router.message(F.text == "ping")
async def ping(message: Message) -> None:
await message.answer("pong")
@router.message(F.text.startswith("hello"))
async def hello(message: Message) -> None:
await message.reply("Привет!")
Команды обрабатываются отдельными фильтрами:
from aiomax2.filters import Command, CommandObject
@router.message(Command("echo"))
async def echo(message: Message, command: CommandObject) -> None:
await message.answer(command.args or "Нет аргументов")
Shortcuts
Типы MAX привязываются к экземпляру Bot, поэтому для большинства обычных
операций не требуется вручную передавать идентификаторы.
@router.message(Command("test"))
async def test(message: Message) -> None:
sent = await message.answer("Первый текст")
await sent.edit_text("Текст изменён")
await sent.delete()
Доступны, в частности:
await message.answer("Ответ")
await message.reply("Ответ с привязкой к сообщению")
await message.edit_text("Новый текст")
await message.delete()
Callback и клавиатуры
Клавиатура в MAX является attachment сообщения.
from aiomax2 import F
from aiomax2.types import CallbackQuery
from aiomax2.utils.keyboard import InlineKeyboardBuilder
builder = InlineKeyboardBuilder()
builder.button(text="Подтвердить", callback_data="confirm")
builder.button(text="Отменить", callback_data="cancel")
builder.adjust(2)
await message.answer(
"Выберите действие",
reply_markup=builder.as_markup(),
)
@router.callback_query(F.payload == "confirm")
async def confirm(callback_query: CallbackQuery) -> None:
await callback_query.answer(notification="Готово")
Builder поддерживает семь актуальных типов кнопок MAX. Low-level
CallbackButton, Keyboard и InlineKeyboardAttachmentRequest остаются
доступны. Подробнее: клавиатуры.
Форматирование
from aiomax2 import TextFormat
await message.answer(
"<b>Жирный</b> <i>курсив</i>",
format=TextFormat.HTML,
)
await message.answer(
"**Жирный** _курсив_",
format=TextFormat.MARKDOWN,
)
MAX поддерживает значения html и markdown; отдельного
markdown_v2 в актуальном API нет. Подробнее:
форматирование текста.
FSM
В библиотеке есть встроенная FSM:
from aiomax2.fsm.context import FSMContext
from aiomax2.fsm.state import State, StatesGroup
from aiomax2.filters import Command
from aiomax2.types import Message
class Form(StatesGroup):
name = State()
age = State()
@router.message(Command("form"))
async def start_form(message: Message, state: FSMContext) -> None:
await state.set_state(Form.name)
await message.answer("Как вас зовут?")
@router.message(Form.name)
async def process_name(message: Message, state: FSMContext) -> None:
await state.update_data(name=message.text)
await state.set_state(Form.age)
await message.answer("Сколько вам лет?")
По умолчанию используется process-local MemoryStorage.
Для multi-worker production deployment рекомендуется внешнее общее хранилище.
Webhook
Для production рекомендуется использовать Webhook.
Пример с FastAPI:
import os
from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager
from fastapi import FastAPI
from aiomax2 import Bot, Dispatcher, Router
WEBHOOK_PATH = "/webhook"
MAX_WEBHOOK_BASE_URL = os.environ["MAX_WEBHOOK_BASE_URL"].rstrip("/")
MAX_WEBHOOK_SECRET = os.environ["MAX_WEBHOOK_SECRET"]
WEBHOOK_URL = f"{MAX_WEBHOOK_BASE_URL}{WEBHOOK_PATH}"
bot = Bot(os.environ["MAX_BOT_TOKEN"])
dp = Dispatcher()
router = Router()
dp.include_router(router)
@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
try:
await bot.subscribe(
WEBHOOK_URL,
secret=MAX_WEBHOOK_SECRET,
update_types=dp.resolve_used_update_types(),
)
yield
finally:
await bot.close()
await dp.close()
app = FastAPI(lifespan=lifespan)
app.include_router(
dp.webhook_router(
WEBHOOK_PATH,
bot=bot,
secret=MAX_WEBHOOK_SECRET,
)
)
export MAX_BOT_TOKEN="..."
export MAX_WEBHOOK_BASE_URL="https://bot.example.ru"
export MAX_WEBHOOK_SECRET="replace-with-random-secret"
webhook_router() получает только локальный route path /webhook, а
subscribe() — полный публичный HTTPS URL. Например:
MAX -> https://bot.example.ru/webhook
-> reverse proxy
-> 127.0.0.1:8000/webhook
-> FastAPI
-> aiomax2 Dispatcher
Для одной кодовой базы режим можно выбрать через MAX_MODE=polling или
MAX_MODE=webhook; готовый pattern находится в
examples/modes.py. Оба режима одновременно не
запускаются.
MAX ожидает 200 OK от Webhook не позднее чем через 30 секунд, поэтому долгие
CPU-bound и blocking операции не следует выполнять непосредственно внутри
handler.
Подробнее:
https://candycrimsie.github.io/aiomax2/webhook/
Загрузка файлов
aiomax2 поддерживает загрузку изображений, видео, аудио и обычных файлов:
image = await bot.upload_image("photo.png")
video = await bot.upload_video("video.mp4")
audio = await bot.upload_audio("audio.mp3")
document = await bot.upload_file("document.pdf")
await bot.send_message(
"Файлы",
chat_id=123,
attachments=[image, document],
)
Transport отдельно обрабатывает внешние upload URL и не переносит на них headers основной API session.
Если MAX вернул документированный attachment.not.ready, отправка или
редактирование сообщения с уже загруженным attachment ограниченно повторяется
с растущей паузой. Сам binary upload после неоднозначной ошибки не повторяется.
Подробнее:
https://candycrimsie.github.io/aiomax2/uploads/
Работа с MAX API
Основные методы доступны непосредственно через Bot:
me = await bot.get_my_info()
message = await bot.send_message(
"Привет!",
chat_id=123,
)
same_message = await bot.get_message(message.message_id)
Для endpoint, который ещё не получил отдельный high-level wrapper, можно использовать:
payload = await bot.request(
"GET",
"/me",
)
При этом сохраняются transport safeguards библиотеки: авторизация, TLS, rate limiting и обработка API errors.
Rate limits
aiomax2 применяет встроенный глобальный limiter к исходящим запросам MAX API.
Для операций отправки, редактирования и удаления сообщений, а также callback answers используется дополнительный консервативный per-target limiter:
MAX API
└── до 30 requests/sec
target A
└── до 2 operations/sec
target B
└── отдельное окно до 2 operations/sec
Встроенные limiter хранят состояние в памяти процесса.
Например:
uvicorn main:app --workers 4
создаст четыре независимых limiter. Для multi-worker или multi-container deployment необходим внешний shared/distributed limiter.
Подробнее:
https://candycrimsie.github.io/aiomax2/rate-limits/
TLS и сертификаты
По умолчанию aiomax2 проверяет TLS certificate chain и hostname.
Даже если переданная пользователем aiohttp.ClientSession создана с:
aiohttp.TCPConnector(ssl=False)
для HTTPS-запросов transport явно применяет собственный проверяющий
SSLContext.
Дополнительный CA bundle можно передать через ca_file:
bot = Bot(
token,
ca_file="/path/to/ca.pem",
)
Или через собственный безопасный SSLContext:
import ssl
context = ssl.create_default_context()
context.load_verify_locations(cafile="/path/to/ca.pem")
bot = Bot(
token,
ssl_context=context,
)
Пользовательский SSLContext должен сохранять проверку сертификата и hostname.
Если системное trust store не доверяет цепочке MAX, установите CA в системе
или передайте ca_file. Сертификаты Минцифры и инструкции опубликованы на
Госуслугах.
verify_ssl=False существует только как явный небезопасный opt-in для
диагностики. Он отключает peer authentication для всех исходящих API и upload
HTTPS-соединений и выдаёт InsecureTLSWarning; default остаётся безопасным.
Этот параметр не ослабляет требования к TLS публичного Webhook endpoint.
Подробнее:
https://candycrimsie.github.io/aiomax2/certificates/
Custom aiohttp.ClientSession
Можно использовать собственную aiohttp.ClientSession:
import aiohttp
from aiomax2 import Bot
session = aiohttp.ClientSession(
headers={
"User-Agent": "my-max-bot/1.0",
}
)
bot = Bot(
token,
session=session,
)
Authorization для MAX API добавляется самой библиотекой.
Переданная пользователем session принадлежит пользователю, поэтому
Bot.close() её не закрывает автоматически.
Обработка ошибок
API и transport ошибки представлены отдельными исключениями:
from aiomax2.exceptions import (
BadRequestError,
ForbiddenError,
NetworkError,
RateLimitError,
UnauthorizedError,
)
try:
await bot.get_my_info()
except UnauthorizedError:
print("Неверный token")
except RateLimitError as error:
print("Rate limit:", error.retry_after)
except NetworkError as error:
print("Network error:", error)
Подробнее:
https://candycrimsie.github.io/aiomax2/errors/
Миграция с aiogram
aiomax2 использует знакомые концепции:
Bot
Dispatcher
Router
Message
Command
F
FSMContext
Middleware
Но библиотека не эмулирует Telegram API.
MAX остаётся источником истины для:
- моделей;
- названий полей;
- callback;
- клавиатур;
- attachments;
- Webhook;
- rate limits;
- upload flow;
- API semantics.
Например, клавиатура MAX на wire является attachment сообщения. Параметр
reply_markup в aiomax2 — только convenience над этим attachment, а не
Telegram keyboard model.
Подробное руководство:
https://candycrimsie.github.io/aiomax2/migration-from-aiogram/
Документация
Полная документация публикуется через MkDocs и GitHub Pages:
https://candycrimsie.github.io/aiomax2/
Начало работы
Обработка событий
- Dispatcher и Router
- Handlers
- Фильтры
- Команды
- Magic filter F
- Callback и клавиатуры
- Inline-клавиатуры
- Форматирование текста
- Middleware
- FSM
MAX API
Проект
Примеры
Готовые примеры находятся в каталоге examples.
В репозитории есть примеры для:
- Long Polling;
- Webhook с FastAPI;
- команд;
- фильтров;
- callback-кнопок;
- всех MAX inline button types и builder API;
- HTML и MAX Markdown;
- FSM;
- middleware;
- вложенных router;
- message shortcuts;
- uploads;
- subscriptions;
- custom
ClientSession; - custom CA;
- обработки ошибок.
Разработка
Установите development dependencies:
python -m pip install -e ".[dev]"
Перед commit рекомендуется запускать:
python -m ruff format --check .
python -m ruff check .
python -m mypy src/aiomax2
python -m pytest
python -m mkdocs build --strict
CI выполняет проверки на поддерживаемых версиях Python автоматически.
Правила участия в разработке:
Roadmap
В следующих версиях планируются, среди прочего:
- Redis-backed distributed rate limiter;
- Redis FSM storage;
- настраиваемая event isolation;
- background/queue-backed Webhook processing;
- resumable/chunked uploads;
- дополнительные production deployment recipes.
Полный roadmap:
https://candycrimsie.github.io/aiomax2/roadmap/
Безопасность
Инструкции по сообщению об уязвимостях находятся в:
Не публикуйте реальные bot tokens в issues, logs, examples или публичных репозиториях.
Changelog
История изменений:
Лицензия
Проект распространяется по лицензии MIT.
Metadata
Release files for aiomax2 0.1.0a2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| aiomax2-0.1.0a2.tar.gz | 81.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aiomax2-0.1.0a2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 130.6 kB
Release files / aiomax2-0.1.0a2.tar.gz
| Download URL | aiomax2-0.1.0a2.tar.gz |
|---|---|
| Size | 81.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9b246475f15f2ac3940c55231da45fefeaa57ba6767ec1af859698d4e4bc8e26
|
|
BLAKE2b-256 checksum How to use checksums |
ee8625a369497a3e8135f1205244a8b63eed9b642798135791ffbd3472084c56
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.
Transparency logRelease files / aiomax2-0.1.0a2-py3-none-any.whl
| Download URL | aiomax2-0.1.0a2-py3-none-any.whl |
|---|---|
| Size | 48.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
15249b2fcb21ed255bbe99054e5fa988a430a1fea4c74299d8d80b3bb9efa388
|
|
BLAKE2b-256 checksum How to use checksums |
73151dd74d13ee4d13bac7b2f36060ade38fa5b095eda1376a1bc052a4cf2118
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.
Transparency log