maxo - библиотека и асинхронный Python-фреймворк для разработки ботов MAX (max.ru) и работы с MAX Bot API
Асинхронный Python-фреймворк для разработки ботов в MAX
Документация
Интерфейс основан на aiogram
maxo/dialogs сделано из aiogram_dialog
maxo/transport/webhook сделано из aiogram-webhook
Почему maxo?
- Интерфейс намеренно близок к
aiogram: роутеры, фильтры, мидлвари, FSM и диалоги работают так, как вы привыкли - Диалоги (
maxo.dialogs): окна, виджеты, пагинация, календарь. Интерфейс можно посмотреть в браузере до запуска бота, а сценарии - тестировать без сети - Long-polling и вебхуки (
aiohttp/fastapi), FSM с Redis, DI черезdishkaи фильтры наmagic_filter - Методы, типы и апдейты генерируются по официальной документации MAX Bot API - меньше расхождений с платформой
- 100% аннотаций и
mypy --strict- ошибки видно в IDE, а не в проде - Ошибки API типизированы: можно писать
except MaxBotTooManyRequestsError, а не разбирать голый HTTP-ответ - Российский доверенный сертификат уже вшит в HTTP-клиент, настраивать SSL для API MAX вручную не нужно
- Асинхронность на
aiohttpиunihttp, валидация данных черезadaptix, документация на русском
Установка
Через pip:
pip install maxo
В pyproject.toml:
[project]
dependencies = [
"maxo",
]
Для чего подходит maxo
- Разработка ботов MAX на Python
- Работа с MAX Bot API
- long-polling и webhook для MAX
- FSM, диалоги и inline-клавиатуры для ботов
- Миграция с aiogram-подхода на MAX
Быстрый старт
Больше примеров в примерах
Эхо-бот
from maxo import Bot, Dispatcher
from maxo.types import MessageCreated
bot = Bot("TOKEN")
dp = Dispatcher()
@dp.message_created()
async def echo_handler(message: MessageCreated) -> None:
text = message.text or "Текста нет"
await message.answer(text)
dp.run_polling(bot)
Команды
from maxo import Bot, Dispatcher
from maxo.routing.filters import Command, DeeplinkFilter
from maxo.types import BotStarted, MessageCreated
bot = Bot("TOKEN")
dp = Dispatcher()
@dp.bot_started(DeeplinkFilter())
async def deeplink_handler(bot_started: BotStarted, deeplink: str) -> None:
await bot_started.send_message(f"Привет! Я бот. Диплинк: {deeplink}")
@dp.bot_started()
async def start_handler(bot_started: BotStarted) -> None:
await bot_started.send_message(f"Привет! Я бот. А ты {bot_started.user.fullname}")
@dp.message_created(Command("help"))
async def help_handler(message: MessageCreated) -> None:
await message.send_message("За помощью обращайтесь в t.me/maxo_py")
dp.run_polling(bot)
Клавиатуры
from magic_filter import F
from maxo import Bot, Dispatcher
from maxo.integrations.magic_filter import MagicFilter
from maxo.routing.filters import CommandStart
from maxo.types import MessageCallback, MessageCreated
from maxo.utils.builders import KeyboardBuilder
bot = Bot("TOKEN")
dp = Dispatcher()
@dp.message_created(CommandStart())
async def start_handler(message: MessageCreated) -> None:
maxo_url = "https://github.com/K1rL3s/maxo"
keyboard = (
KeyboardBuilder()
.add_callback(text="Колбэк", payload="click_me")
.add_message(text="Сообщение")
.add_link(text="Перейти в maxo", url=maxo_url)
.add_clipboard(text="Скопировать maxo", payload=maxo_url)
.add_request_contact(text="Поделиться контактами")
.add_request_geo_location(text="Поделиться геопозицией")
.adjust(2, 2, 1, 1)
)
await message.answer(text="Кнопочки :3", keyboard=keyboard.build())
@dp.message_callback(MagicFilter(F.payload == "click_me"))
async def button_handler(callback: MessageCallback) -> None:
await callback.callback_answer("Вы нажали на кнопку!")
dp.run_polling(bot)
Диалоги
Многошаговый сценарий - это окна и виджеты, переходами управляет менеджер
диалога. Окна можно отрисовать в HTML-превью без запуска бота, а сценарии -
тестировать без сети через maxo.dialogs.test_tools:
from maxo import Bot, Dispatcher
from maxo.dialogs import Dialog, DialogManager, StartMode, Window, setup_dialogs
from maxo.dialogs.widgets.kbd import Button
from maxo.dialogs.widgets.text import Const
from maxo.fsm import State, StatesGroup
from maxo.fsm.key_builder import DefaultKeyBuilder
from maxo.routing.filters import CommandStart
from maxo.types import MessageCallback, MessageCreated
bot = Bot("TOKEN")
# Для диалогов нужен key builder с destiny
dp = Dispatcher(key_builder=DefaultKeyBuilder(with_destiny=True))
class MainState(StatesGroup):
main = State()
async def close_dialog(
callback: MessageCallback,
button: Button,
manager: DialogManager,
) -> None:
await manager.done()
dialog = Dialog(
Window(
Const("Главное меню"),
Button(Const("Закрыть"), id="close", on_click=close_dialog),
state=MainState.main,
),
)
@dp.message_created(CommandStart())
async def start_handler(
message: MessageCreated,
dialog_manager: DialogManager,
) -> None:
await dialog_manager.start(MainState.main, mode=StartMode.RESET_STACK)
dp.include(dialog)
setup_dialogs(dp)
dp.run_polling(bot)
Вебхук
import logging
from aiohttp import web
from maxo import Bot, Dispatcher, Router
from maxo.enums import TextFormat
from maxo.routing.utils import collect_used_updates
from maxo.transport.webhook.adapters.aiohttp import AiohttpWebAdapter
from maxo.transport.webhook.engines import SimpleEngine, WebhookEngine
from maxo.transport.webhook.routing import StaticRouting
from maxo.transport.webhook.security import Security, StaticSecretToken
from maxo.types import BotStarted, MessageCreated
bot = Bot("TOKEN")
router = Router()
@router.bot_started()
async def start_handler(bot_started: BotStarted) -> None:
await bot_started.send_message(
text=f"Привет из вебхука, {bot_started.user.first_name}!",
)
@router.message_created()
async def echo_handler(message: MessageCreated) -> None:
await message.answer(
text=message.message.body.html_text,
format=TextFormat.HTML,
)
@router.after_startup()
async def on_startup(dispatcher: Dispatcher, webhook_engine: WebhookEngine) -> None:
await webhook_engine.set_webhook(update_types=collect_used_updates(dispatcher))
def main() -> None:
dispatcher = Dispatcher()
dispatcher.include(router)
engine = SimpleEngine(
dispatcher,
bot,
web_adapter=AiohttpWebAdapter(),
routing=StaticRouting(url="https://example.com/webhook"),
security=Security(secret_token=StaticSecretToken("pepa_pig")),
)
app = web.Application()
engine.register(app)
web.run_app(app, host="127.0.0.1", port=8080)
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
main()
FAQ
Что такое MAX?
MAX - российский мессенджер. У него есть открытое Bot API, для работы с которым и создан maxo.
Чем maxo отличается от aiogram?
maxo - отдельный фреймворк именно для ботов MAX, но интерфейс намеренно близок к aiogram, чтобы переход был максимально безболезненным. Диалоги (maxo.dialogs) портированы из aiogram_dialog, вебхуки (maxo.transport.webhook) - из aiogram-webhook.
Можно ли перенести бота с aiogram на maxo?
Код один в один не переносится: MAX и Telegram - разные платформы со своими типами и методами. Но подход остаётся тем же: роутеры, фильтры, хэндлеры, FSM и диалоги называются и ведут себя привычно, поэтому переучиваться почти не придётся.
Поддерживает ли maxo вебхуки?
Да. Поддерживается и long-polling, и webhook через aiohttp или fastapi - см. примеры выше.
Какой Python нужен?
Python 3.12, 3.13 или 3.14.
Где взять токен бота MAX?
Как добавить FSM?
FSM встроена в maxo - есть MemoryStorage из коробки и опциональное хранилище в Redis (maxo[redis]). Подробности - в документации.
Можно ли отправлять фото, видео и файлы?
Да. maxo умеет отправлять и принимать вложения - фото, видео, аудио и документы - через InputFile (загрузка файла) или по токену уже загруженного медиа. Крупные файлы грузятся частями (resumable). Подробности - в документации.
Есть ли dependency injection?
Да, через интеграцию с dishka (maxo[dishka]). Зависимости можно прокидывать в хэндлеры, фильтры и мидлвари.
Можно ли обслуживать несколько ботов в одном приложении?
Пока частично. Готовый SimpleEngine обслуживает одного бота. Для мульти-бот сценария есть заготовки: PathRouting и QueryRouting извлекают токен бота из URL или query-параметра, а выбор бота по токену реализуется наследником WebhookEngine (метод _get_bot_from_request).
Как масштабировать бота под нагрузку?
Для продакшена используйте вебхуки: сервер MAX доставляет каждый апдейт один раз, и нагрузку можно распределить между воркерами (например, за Nginx или в Kubernetes). Long-polling для этого не подходит - при нескольких процессах с одним токеном апдейты дублируются.
Как тестировать бота без реального MAX?
Для диалогов есть maxo.dialogs.test_tools: BotClient эмулирует пользователя, MockMessageManager записывает отправленные сообщения, локаторы находят кнопки по тексту. Сценарий "клик по кнопке - смена окна - новый текст" проверяется без единого сетевого вызова. Пример - в examples/dialogs_testing.py.
maxo бесплатный? Какая лицензия?
Да, maxo - open-source под лицензией Apache 2.0. Можно использовать в том числе в коммерческих проектах.
Связь
Если у вас есть вопросы, вы можете задать их в Телеграме @maxo_py или Максе
Metadata
Release files for maxo 0.9.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| maxo-0.9.0.tar.gz | 229.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| maxo-0.9.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 661.4 kB
Release files / maxo-0.9.0.tar.gz
| Download URL | maxo-0.9.0.tar.gz |
|---|---|
| Size | 229.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a7b44738ca715c0e4fda2bc2b5aa857e1e9d9091afc2347a4f600ba77547afc2
|
|
BLAKE2b-256 checksum How to use checksums |
33b572597b3d316d9a01a8041a62c8b04f7feca411cf5f17c9b8fcb7871d5383
|
| 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 Sep 15, 2026.
Transparency logRelease files / maxo-0.9.0-py3-none-any.whl
| Download URL | maxo-0.9.0-py3-none-any.whl |
|---|---|
| Size | 432.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
738c398a1d7e4c1c463eae9206e00cf5924b2793bd7d3d172fa04694edfe5ded
|
|
BLAKE2b-256 checksum How to use checksums |
89f9b605e907f3cc76f192a0cba1c6ba83d16caec13c0743cb44d0a7d15c383b
|
| 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 Sep 15, 2026.
Transparency log