Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

aiomax2

CI Documentation Python License

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/

Начало работы

Обработка событий

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 автоматически.

Правила участия в разработке:

CONTRIBUTING.md


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/


Безопасность

Инструкции по сообщению об уязвимостях находятся в:

SECURITY.md

Не публикуйте реальные bot tokens в issues, logs, examples или публичных репозиториях.


Changelog

История изменений:

CHANGELOG.md


Лицензия

Проект распространяется по лицензии MIT.

LICENSE

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)

Source distribution for aiomax2 0.1.0a2
File Size Uploaded
aiomax2-0.1.0a2.tar.gz 81.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for aiomax2 0.1.0a2
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

0.1.0

2 release files

This release

0.1.0a2 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page