Примеры¶
Здесь собраны практические примеры использования MaxAPI для различных задач.
Эхо-бот¶
Простейший пример бота, который повторяет все текстовые сообщения:
import asyncio
import logging
from maxapi import Bot, Dispatcher, F
from maxapi.types import MessageCreated
logging.basicConfig(level=logging.INFO)
bot = Bot()
dp = Dispatcher()
@dp.message_created(F.message.body.text)
async def echo(event: MessageCreated):
await event.message.answer(f"Повторяю за вами: {event.message.body.text}")
async def main():
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
Обработка событий¶
Пример обработки различных событий, доступных в MaxAPI:
import asyncio
import logging
from maxapi import Bot, Dispatcher
from maxapi.types import (
BotStarted,
Command,
MessageCreated,
CallbackButton,
MessageCallback,
BotAdded,
ChatTitleChanged,
MessageEdited,
MessageRemoved,
UserAdded,
UserRemoved,
BotStopped,
DialogCleared,
DialogMuted,
DialogUnmuted,
ChatButton, # deprecated: 0.9.14
MessageChatCreated, # deprecated: 0.9.14
)
from maxapi.utils.inline_keyboard import InlineKeyboardBuilder
logging.basicConfig(level=logging.INFO)
bot = Bot()
dp = Dispatcher()
@dp.message_created(Command("start"))
async def hello(event: MessageCreated):
builder = InlineKeyboardBuilder()
builder.row(
CallbackButton(
text="Кнопка 1",
payload="btn_1",
),
CallbackButton(
text="Кнопка 2",
payload="btn_2",
),
)
builder.add(
ChatButton( # deprecated: 0.9.14
text="Создать чат",
chat_title="Тест чат",
)
)
await event.message.answer(
text="Привет!",
attachments=[
builder.as_markup(),
],
)
@dp.bot_added()
async def bot_added(event: BotAdded):
chat = await event.fetch_chat()
if chat is None:
logging.info(
"Не удалось получить chat, возможно отключен auto_requests!"
)
return
await bot.send_message(
chat_id=event.chat_id, text=f"Привет чат {chat.title}!"
)
@dp.message_removed()
async def message_removed(event: MessageRemoved):
await bot.send_message(
chat_id=event.chat_id,
text="Я всё видел!",
)
@dp.bot_started()
async def bot_started(event: BotStarted):
await bot.send_message(
chat_id=event.chat_id,
text="Привет! Отправь мне /start",
)
@dp.chat_title_changed()
async def chat_title_changed(event: ChatTitleChanged):
await bot.send_message(
chat_id=event.chat_id,
text=f'Крутое новое название "{event.title}"!',
)
@dp.message_callback()
async def message_callback(event: MessageCallback):
await event.answer(
new_text=f"Вы нажали на кнопку {event.callback.payload}!"
)
@dp.message_edited()
async def message_edited(event: MessageEdited):
await event.message.answer(text="Вы отредактировали сообщение!")
@dp.user_removed()
async def user_removed(event: UserRemoved):
from_user = await event.fetch_from_user()
if from_user is None:
return await bot.send_message(
chat_id=event.chat_id,
text=f"Неизвестный кикнул {event.user.first_name} 😢",
)
await bot.send_message(
chat_id=event.chat_id,
text=f"{from_user.first_name} кикнул {event.user.first_name} 😢",
)
@dp.user_added()
async def user_added(event: UserAdded):
chat = await event.fetch_chat()
if chat is None:
return await bot.send_message(
chat_id=event.chat_id,
text=f"Чат приветствует вас, {event.user.first_name}!",
)
await bot.send_message(
chat_id=event.chat_id,
text=f'Чат "{chat.title}" приветствует вас, {event.user.first_name}!',
)
async def main():
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
Если бот создан с auto_requests=False, то event.chat и
event.from_user могут быть не загружены автоматически. Чтобы безопасно
получить их независимо от режима, используйте явный fetch:
После этого работайте уже с локальными переменными:
MagicFilter¶
Использование MagicFilter для гибкой фильтрации сообщений:
import asyncio
import logging
from maxapi import Bot, Dispatcher, F
from maxapi.types import MessageCreated
logging.basicConfig(level=logging.INFO)
bot = Bot()
dp = Dispatcher()
@dp.message_created(F.message.body.text == "привет")
async def on_hello(event: MessageCreated):
await event.message.answer("Привет!")
@dp.message_created(F.message.body.text.lower().contains("помощь"))
async def on_help(event: MessageCreated):
await event.message.answer("Чем могу помочь?")
@dp.message_created(F.message.body.text.regexp(r"^\d{4}$"))
async def on_code(event: MessageCreated):
await event.message.answer("Принят 4-значный код")
@dp.message_created(F.message.body.attachments)
async def on_attachment(event: MessageCreated):
await event.message.answer("Получено вложение")
@dp.message_created(F.message.body.text.len() > 20)
async def on_long_text(event: MessageCreated):
await event.message.answer("Слишком длинное сообщение")
@dp.message_created(F.message.body.text.len() > 0)
async def on_non_empty(event: MessageCreated):
await event.message.answer("Вы что-то написали.")
async def main():
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
Клавиатуры¶
Примеры работы с различными типами кнопок и клавиатур:
import asyncio
import logging
from maxapi import Bot, Dispatcher
from maxapi.types import (
ChatButton, # deprecated: 0.9.14
LinkButton,
CallbackButton,
RequestGeoLocationButton,
MessageButton,
ButtonsPayload,
RequestContactButton,
OpenAppButton,
MessageCreated,
MessageCallback,
MessageChatCreated, # deprecated: 0.9.14
CommandStart,
Command,
)
from maxapi.utils.inline_keyboard import InlineKeyboardBuilder
logging.basicConfig(level=logging.INFO)
bot = Bot()
dp = Dispatcher()
@dp.message_created(CommandStart())
async def echo(event: MessageCreated):
await event.message.answer(
(
"Привет! Мои команды:\n\n"
"/builder - Клавиатура из InlineKeyboardBuilder\n"
"/payload - Клавиатура из pydantic моделей\n"
)
)
@dp.message_created(Command("builder"))
async def builder(event: MessageCreated):
builder = InlineKeyboardBuilder()
builder.row(
ChatButton( # deprecated: 0.9.14
text="Создать чат",
chat_title="Test",
chat_description="Test desc",
),
LinkButton(
text="Документация MAX",
url="https://dev.max.ru/docs",
),
)
builder.row(
RequestGeoLocationButton(text="Геолокация"),
MessageButton(text="Сообщение"),
)
builder.row(
RequestContactButton(text="Контакт"),
OpenAppButton(
text="Приложение",
web_app=event.bot.me.username,
contact_id=event.bot.me.user_id,
),
)
builder.row(
CallbackButton(
text="Callback",
payload="test",
)
)
await event.message.answer(
text="Клавиатура из InlineKeyboardBuilder",
attachments=[builder.as_markup()],
)
@dp.message_created(Command("payload"))
async def payload(event: MessageCreated):
buttons = [
[
ChatButton( # deprecated: 0.9.14
text="Создать чат",
chat_title="Test",
chat_description="Test desc",
),
LinkButton(
text="Документация MAX",
url="https://dev.max.ru/docs",
),
],
[
RequestGeoLocationButton(text="Геолокация"),
MessageButton(text="Сообщение"),
],
[
RequestContactButton(text="Контакт"),
OpenAppButton(
text="Приложение",
web_app=event.bot.me.username,
contact_id=event.bot.me.user_id,
),
],
[
CallbackButton(
text="Callback",
payload="test",
)
],
]
buttons_payload = ButtonsPayload(buttons=buttons).pack()
await event.message.answer(
text="Клавиатура из pydantic моделей",
attachments=[buttons_payload],
)
@dp.message_chat_created() # deprecated: 0.9.14
async def message_chat_created(obj: MessageChatCreated):
await obj.bot.send_message(
chat_id=obj.chat.chat_id,
text=f"Чат создан! Ссылка: {obj.chat.link}",
)
@dp.message_callback()
async def message_callback(callback: MessageCallback):
await callback.message.answer("Вы нажали на Callback!")
async def main():
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
Форматирование текста¶
Для сборки текста в HTML и Markdown можно использовать helper-ы из
maxapi.utils.formatting.
| Элемент | HTML | Markdown |
|---|---|---|
| Заголовок | <h1>Заголовок</h1> |
# Заголовок |
| Жирный | <b>текст</b> |
**текст** |
| Курсив | <i>текст</i> |
*текст* |
| Подчёркнутый | <ins>текст</ins> |
++текст++ |
| Зачёркнутый | <s>текст</s> |
~~текст~~ |
| Моноширинный | <code>текст</code> |
`текст` |
| Цитата | <blockquote>текст</blockquote> |
> текст |
| Ссылка | <a href="https://example.com">текст</a> |
[текст](https://example.com) |
Пример HTML:
from maxapi.enums.format import Format
from maxapi.utils.formatting import (
Blockquote,
Bold,
Heading,
Italic,
Link,
as_html,
)
text = as_html(
Heading("Проверка форматирования"),
"\n",
Bold("Жирный текст"),
"\n",
Italic("Курсивная строка"),
"\n",
Blockquote("Цитата"),
"\n",
Link("Документация", url="https://love-apples.github.io/maxapi/"),
)
await event.message.answer(text, format=Format.HTML)
Пример Markdown:
from maxapi.enums.format import Format
from maxapi.utils.formatting import Blockquote, Bold, Heading, as_markdown
text = as_markdown(
Heading("Проверка форматирования"),
"\n",
Bold("Жирный текст"),
"\n",
Blockquote("Цитата"),
)
await event.message.answer(text, format=Format.MARKDOWN)
Если вы читаете входящее сообщение из API, можно получить каноническое
представление текста через message.body.html_text и message.body.md_text.
Это нормализованный вывод из markup, поэтому он может не совпадать с исходной
строкой символ в символ.
Получение ID¶
Пример получения различных ID из событий:
import asyncio
import logging
from maxapi import Bot, Dispatcher, F
from maxapi.enums.format import Format
from maxapi.types import MessageCreated
logging.basicConfig(level=logging.INFO)
bot = Bot()
dp = Dispatcher()
@dp.message_created(F.message.link.type == "forward")
async def get_ids_from_forward(event: MessageCreated):
text = (
"Информация о пересланном сообщении:\n\n"
f"Из чата: <b>{event.message.link.chat_id}</b>\n"
f"От пользователя: <b>{event.message.link.sender.user_id}</b>"
)
await event.message.reply(text)
@dp.message_created()
async def get_ids(event: MessageCreated):
from_user = await event.fetch_from_user()
chat = await event.fetch_chat()
if from_user is None or chat is None:
return
text = (
f"Ваш ID: <b>{from_user.user_id}</b>\n"
f"ID этого чата: <b>{chat.chat_id}</b>"
)
await event.message.answer(text, format=Format.HTML)
async def main():
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
Создание собственного фильтра¶
Пример создания кастомного фильтра на основе BaseFilter:
import asyncio
import logging
from maxapi import Bot, Dispatcher
from maxapi.types import MessageCreated, CommandStart, UpdateUnion
from maxapi.filters import BaseFilter
logging.basicConfig(level=logging.INFO)
bot = Bot()
dp = Dispatcher()
class FilterChat(BaseFilter):
"""
Фильтр, который срабатывает только в чате с названием `Test`
"""
async def __call__(self, event: UpdateUnion):
chat = await event.fetch_chat()
if chat is None:
return False
return chat.title == "Test"
@dp.message_created(CommandStart(), FilterChat())
async def custom_data(event: MessageCreated):
await event.message.answer("Привет!")
async def main():
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
Фильтр callback payload¶
Пример использования типизированных payload для callback-кнопок:
import asyncio
import logging
from maxapi import Bot, Dispatcher, F
from maxapi.filters.callback_payload import CallbackPayload
from maxapi.filters.command import CommandStart
from maxapi.types import (
CallbackButton,
MessageCreated,
MessageCallback,
)
from maxapi.utils.inline_keyboard import InlineKeyboardBuilder
logging.basicConfig(level=logging.INFO)
bot = Bot()
dp = Dispatcher()
class MyPayload(CallbackPayload, prefix="mypayload"):
foo: str
action: str
class AnotherPayload(CallbackPayload, prefix="another"):
bar: str
value: int
@dp.message_created(CommandStart())
async def show_keyboard(event: MessageCreated):
kb = InlineKeyboardBuilder()
kb.row(
CallbackButton(
text="Первая кнопка",
payload=MyPayload(foo="123", action="edit").pack(),
),
CallbackButton(
text="Вторая кнопка",
payload=AnotherPayload(bar="abc", value=42).pack(),
),
)
await event.message.answer("Нажми кнопку!", attachments=[kb.as_markup()])
@dp.message_callback(MyPayload.filter(F.foo == "123"))
async def on_first_callback(event: MessageCallback, payload: MyPayload):
await event.answer(
new_text=f"Первая кнопка: foo={payload.foo}, action={payload.action}"
)
@dp.message_callback(AnotherPayload.filter())
async def on_second_callback(event: MessageCallback, payload: AnotherPayload):
await event.answer(
new_text=f"Вторая кнопка: bar={payload.bar}, value={payload.value}"
)
async def main():
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
Middleware в обработчиках¶
Пример использования middleware на уровне обработчиков:
import asyncio
import logging
from typing import Any, Awaitable, Callable, Dict
from maxapi import Bot, Dispatcher
from maxapi.filters.middleware import BaseMiddleware
from maxapi.types import MessageCreated, Command, UpdateUnion
logging.basicConfig(level=logging.INFO)
bot = Bot()
dp = Dispatcher()
class CheckChatTitleMiddleware(BaseMiddleware):
async def __call__(
self,
handler: Callable[[Any, Dict[str, Any]], Awaitable[Any]],
event_object: UpdateUnion,
data: Dict[str, Any],
) -> Any:
if event_object.chat.title == "MAXApi":
return await handler(event_object, data)
class CustomDataMiddleware(BaseMiddleware):
async def __call__(
self,
handler: Callable[[Any, Dict[str, Any]], Awaitable[Any]],
event_object: UpdateUnion,
data: Dict[str, Any],
) -> Any:
data["custom_data"] = (
f"Это ID того кто вызвал команду: {event_object.from_user.user_id}"
)
await handler(event_object, data)
@dp.message_created(Command("start"), CheckChatTitleMiddleware())
async def start(event: MessageCreated):
await event.message.answer(
'Это сообщение было отправлено, так как ваш чат называется "MAXApi"!'
)
@dp.message_created(Command("custom_data"), CustomDataMiddleware())
async def custom_data(event: MessageCreated, custom_data: str):
await event.message.answer(custom_data)
@dp.message_created(
Command("many_middlewares"),
CheckChatTitleMiddleware(),
CustomDataMiddleware(),
)
async def many_middlewares(event: MessageCreated, custom_data: str):
await event.message.answer(
'Это сообщение было отправлено, так как ваш чат называется "MAXApi"!'
)
await event.message.answer(custom_data)
async def main():
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
Middleware в роутерах¶
Пример использования middleware на уровне роутера:
import asyncio
import logging
from typing import Any, Awaitable, Callable, Dict
from maxapi import Bot, Dispatcher
from maxapi.types import MessageCreated, Command, UpdateUnion
from maxapi.filters.middleware import BaseMiddleware
logging.basicConfig(level=logging.INFO)
bot = Bot()
dp = Dispatcher()
class CustomDataForRouterMiddleware(BaseMiddleware):
async def __call__(
self,
handler: Callable[[Any, Dict[str, Any]], Awaitable[Any]],
event_object: UpdateUnion,
data: Dict[str, Any],
) -> Any:
data["custom_data"] = (
f"Это ID того кто вызвал команду: {event_object.from_user.user_id}"
)
result = await handler(event_object, data)
return result
@dp.message_created(Command("custom_data"))
async def custom_data(event: MessageCreated, custom_data: str):
await event.message.answer(custom_data)
async def main():
dp.register_outer_middleware(CustomDataForRouterMiddleware())
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
Роутеры, InputMedia и контекст¶
Пример использования роутеров, InputMedia и работы с контекстом и состояниями:
import asyncio
import logging
from maxapi import Bot, Dispatcher, F
from maxapi.context import MemoryContext, State, StatesGroup
from maxapi.types import (
BotStarted,
Command,
MessageCreated,
CallbackButton,
MessageCallback,
BotCommand,
)
from maxapi.utils.inline_keyboard import InlineKeyboardBuilder
from router import router
logging.basicConfig(level=logging.INFO)
bot = Bot()
dp = Dispatcher()
dp.include_routers(router)
start_text = """Пример чат-бота для MAX 💙
Мои команды:
/clear очищает ваш контекст
/state или /context показывают ваше контекстное состояние
/data показывает вашу контекстную память
"""
class Form(StatesGroup):
name = State()
age = State()
@dp.on_started()
async def _():
logging.info("Бот стартовал!")
@dp.bot_started()
async def bot_started(event: BotStarted):
await event.bot.send_message(
chat_id=event.chat_id,
text="Привет! Отправь мне /start",
)
@dp.message_created(Command("clear"))
async def hello(event: MessageCreated, context: MemoryContext):
await context.clear()
await event.message.answer(f"Ваш контекст был очищен!")
@dp.message_created(Command("data"))
async def hello(event: MessageCreated, context: MemoryContext):
data = await context.get_data()
await event.message.answer(f"Ваша контекстная память: {str(data)}")
@dp.message_created(Command("context"))
@dp.message_created(Command("state"))
async def hello(event: MessageCreated, context: MemoryContext):
data = await context.get_state()
await event.message.answer(f"Ваше контекстное состояние: {str(data)}")
@dp.message_created(Command("start"))
async def hello(event: MessageCreated):
builder = InlineKeyboardBuilder()
builder.row(
CallbackButton(text="Ввести свое имя", payload="btn_1"),
CallbackButton(text="Ввести свой возраст", payload="btn_2"),
)
builder.row(CallbackButton(text="Не хочу", payload="btn_3"))
await event.message.answer(
text=start_text,
attachments=[
builder.as_markup(),
],
)
@dp.message_callback(F.callback.payload == "btn_1")
async def hello(event: MessageCallback, context: MemoryContext):
await context.set_state(Form.name)
await event.message.delete()
await event.message.answer(f"Отправьте свое имя:")
@dp.message_callback(F.callback.payload == "btn_2")
async def hello(event: MessageCallback, context: MemoryContext):
await context.set_state(Form.age)
await event.message.delete()
await event.message.answer(f"Отправьте ваш возраст:")
@dp.message_callback(F.callback.payload == "btn_3")
async def hello(event: MessageCallback, context: MemoryContext):
await event.message.delete()
await event.message.answer(f"Ну ладно 🥲")
@dp.message_created(F.message.body.text, Form.name)
async def hello(event: MessageCreated, context: MemoryContext):
await context.update_data(name=event.message.body.text)
data = await context.get_data()
await event.message.answer(
f"Приятно познакомиться, {data['name'].title()}!"
)
@dp.message_created(F.message.body.text, Form.age)
async def hello(event: MessageCreated, context: MemoryContext):
await context.update_data(age=event.message.body.text)
await event.message.answer(f"Ого! А мне всего пару недель 😁")
async def main():
await bot.set_commands(
BotCommand(
name="/state", description="Показывают ваше контекстное состояние"
),
BotCommand(
name="/data", description="Показывает вашу контекстную память"
),
BotCommand(
name="/context",
description="Показывают ваше контекстное состояние",
),
)
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
И соответствующий роутер (router.py):
from maxapi import F, Router
from maxapi.types import Command, MessageCreated
from maxapi.types import InputMedia
router = Router()
file = __file__.split("\\")[-1]
@router.message_created(Command("router"))
async def hello(obj: MessageCreated):
await obj.message.answer(f"Пишу тебе из роута {file}")
# новая команда для примера, /media,
# пример использования: /media image.png (медиафайл берется указанному пути)
@router.message_created(Command("media"))
async def hello(event: MessageCreated):
await event.message.answer(
attachments=[
InputMedia(path=event.message.body.text.replace("/media ", ""))
]
)
Отправка файлов¶
Два основных способа отправить файл:
- Передать
InputMediaнапрямую вattachments. - Предварительно загрузить файл через
bot.upload_media(...), а затем отправить полученный объект вложения.
import asyncio
from maxapi import Bot
from maxapi.types import InputMedia
bot = Bot()
async def main():
# Вариант 1: отправка напрямую через InputMedia
await bot.send_message(
chat_id=...,
attachments=[
InputMedia(path="logo.png"),
],
)
# Вариант 2: ручная загрузка + отправка attachment. (Подходит для рассылок)
media = InputMedia("logo.png")
attachment = await bot.upload_media(media)
await bot.send_message(
chat_id=...,
attachments=[attachment],
)
if __name__ == "__main__":
asyncio.run(main())
Ограничения на загружаемые файлы
Сервер MAX ограничивает загружаемые файлы по типу (см. POST /uploads):
| Тип | Форматы | Лимиты |
|---|---|---|
| image | JPG, JPEG, PNG, GIF, TIFF, BMP, HEIC | до 50 МБ и не более 7680×7680 px |
| video | MP4, MOV, MKV, WEBM | до 250 МБ |
| audio | MP3, WAV, M4A и др. | до 256 МБ и не более 60 мин |
| file | TXT, DOC, PDF и распространённые форматы | до 4 ГБ |
Для image и audio оба условия (размер и вторая величина)
проверяются одновременно — должны выполняться оба. Актуальные
значения — в Upload limits.
Библиотека проверяет размер файла при любой загрузке
(bot.upload_media, attachments, ручной bot.upload_file)
и лишь предупреждает в логе (bot) при его превышении, не
выбрасывая исключение — окончательную проверку выполняет
сервер MAX. Файлы читаются в память целиком, поэтому для
больших файлов file практический потолок ниже 4 ГБ и
зависит от доступной ОЗУ.
Webhook¶
Высокоуровневый подход¶
Простой способ работы с webhook через встроенный метод (использует aiohttp, входит в базовый пакет):
import asyncio
import logging
from maxapi import Bot, Dispatcher
from maxapi.types import MessageCreated
logging.basicConfig(level=logging.INFO)
bot = Bot()
dp = Dispatcher()
@dp.message_created()
async def handle_message(event: MessageCreated):
await event.message.answer("Бот работает через вебхук!")
async def main():
webhook_url = "https://ваш-домен.рф/webhook" # <-- укажите свой
webhook_secret = "my-secret-token" # <-- укажите свой (5–256 символов)
# Регистрируем вебхук на стороне MAX — платформа будет отправлять
# заголовок X-Max-Bot-Api-Secret с каждым запросом.
await bot.subscribe_webhook(url=webhook_url, secret=webhook_secret)
# Фреймворк автоматически проверяет X-Max-Bot-Api-Secret в каждом
# входящем запросе и возвращает 403, если заголовок отсутствует
# или не совпадает (защита от посторонних запросов).
await dp.handle_webhook(
bot=bot,
host="0.0.0.0",
port=8080,
secret=webhook_secret,
)
if __name__ == "__main__":
asyncio.run(main())
Низкоуровневый подход¶
Более гибкий способ: получаем FastAPI-приложение, регистрируем собственные маршруты (например, healthcheck) и отдельно подключаем MAX webhook-модуль.
Требует опциональных зависимостей:
import asyncio
import logging
import uvicorn
from fastapi import FastAPI
from maxapi import Bot, Dispatcher
from maxapi.types import MessageCreated
from maxapi.webhook.fastapi import FastAPIMaxWebhook
logging.basicConfig(level=logging.INFO)
bot = Bot()
dp = Dispatcher()
@dp.message_created()
async def handle_message(event: MessageCreated):
await event.message.answer("Бот работает через вебхук!")
async def main():
webhook_url = "https://ваш-домен.рф/webhook" # <-- укажите свой
webhook_secret = "my-secret-token" # <-- укажите свой (5–256 символов)
# Передаём secret в конструктор — он сохраняется в webhook.secret.
# Фреймворк будет автоматически проверять заголовок X-Max-Bot-Api-Secret
# в каждом входящем POST-запросе и возвращать 403 при несоответствии.
webhook = FastAPIMaxWebhook(dp=dp, bot=bot, secret=webhook_secret)
# Создаём FastAPI-приложение с lifespan-инициализацией диспетчера
app = FastAPI(lifespan=webhook.lifespan)
# Собственные маршруты — например, healthcheck
@app.get("/health")
async def health():
return {"status": "ok"}
# Подключаем MAX webhook-обработчик к нашему приложению
webhook.setup(app, path="/webhook")
# Подписываемся на webhook — передаём тот же secret,
# чтобы платформа MAX добавляла X-Max-Bot-Api-Secret в каждый запрос.
await bot.subscribe_webhook(url=webhook_url, secret=webhook_secret)
# Запускаем сервер uvicorn
config = uvicorn.Config(app=app, host="0.0.0.0", port=8080)
server = uvicorn.Server(config)
await server.serve()
if __name__ == "__main__":
asyncio.run(main())
Настройка прокси¶
Подключение через прокси¶
Для использования прокси-сервера передайте параметр proxy в DefaultConnectionProperties:
import asyncio
from maxapi import Bot, Dispatcher
from maxapi.client import DefaultConnectionProperties
from maxapi.types import MessageCreated, Command
# URL прокси в формате: http://login:password@ip:port
proxy_url = "http://login:password@ip:port"
# Создание настроек соединения с прокси
connection_props = DefaultConnectionProperties(proxy=proxy_url)
# Инициализация бота с настройками соединения
bot = Bot(default_connection=connection_props)
dp = Dispatcher()
@dp.message_created(Command("start"))
async def start_handler(event: MessageCreated):
await event.message.answer("Привет!")
async def main():
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
Использование прокси из переменных окружения¶
Для использования прокси из переменных окружения используйте параметр trust_env=True:
import asyncio
from maxapi import Bot, Dispatcher
from maxapi.client import DefaultConnectionProperties
from maxapi.types import MessageCreated, Command
bot = Bot(
"YOUR-TOKEN",
default_connection=DefaultConnectionProperties(trust_env=True),
)
dp = Dispatcher()
@dp.message_created(Command("start"))
async def start_handler(event: MessageCreated):
await event.message.answer("Привет!")
async def main():
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
Что такое trust_env?¶
Параметр trust_env=True в aiohttp позволяет автоматически читать настройки прокси из переменных окружения системы. Когда этот параметр включен, aiohttp будет искать следующие переменные окружения:
HTTP_PROXY— прокси для HTTP-запросов (например,http://proxy.example.com:8080)HTTPS_PROXY— прокси для HTTPS-запросов (например,https://proxy.example.com:8080)NO_PROXY— список доменов, для которых прокси не используется (например,localhost,127.0.0.1,*.local)
Важно: Если переменные окружения не установлены, trust_env=True не вызовет ошибку — просто прокси использоваться не будет.