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 или Максе
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file maxo-0.8.1.tar.gz.
File metadata
- Download URL: maxo-0.8.1.tar.gz
- Upload date:
- Size: 226.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.9.13 {"installer":{"name":"uv","version":"0.9.13"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7dc415d6d03c43b93af29da9a1826af6584fb1870728cb3fe6da44aff4e826aa
|
|
| MD5 |
78bd61c8aaf0b5f24d65673a9ff59e9a
|
|
| BLAKE2b-256 |
880a2a761adde243b0f4d041293862f4d362a8f3803f26bbf69c3f673edf3bfa
|
File details
Details for the file maxo-0.8.1-py3-none-any.whl.
File metadata
- Download URL: maxo-0.8.1-py3-none-any.whl
- Upload date:
- Size: 422.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.9.13 {"installer":{"name":"uv","version":"0.9.13"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
595a01ca2912cf4563281cdd8f05ca0894de91771f9b64dead82d54e5d32be4c
|
|
| MD5 |
1344b4ee780995206956ff2f98b14eac
|
|
| BLAKE2b-256 |
87ccc4d09807723d08d9ba3a213532662be3429b9f1dd1c491aaf0e733eb9e31
|