Недавно VK сделал новый мессенджер - Max - который рекламируют как отечественный аналог WeChat. Пока что он немного сыроват, но в нём видно потенциал. Сегодня мы будем писать бота на Python для этого мессенджера.
Создаем аккаунт бота
Для создания аккаунтов ботов есть специальный бот - @MasterBot. Переходим в него и нажимаем "Начать"

Дальше пишем /create, чтобы создать нового бота.
MasterBot предложит нам придумать уникальное имя пользователя для бота, которое должно быть больше 11 символов (да, не меньше) и должно заканчиваться на _bot или bot. Я назову своего бота @aiomax_test_bot.
Дальше пишем имя бота, которое будет отображаться в чатах и вверху диалога с ботом. Оно не должно быть уникальным. Я назову своего "Кликер бот"

Бюрократическая часть окончена! Копируем токен бота, который вам прислал MasterBot и переходим в редактор кода.
Скелет бота
Переходим в командную строку и пишем следующее:
pip install aiomax
В новом Python-файле пишем это:
import aiomax
import logging
Библиотека logging нам понадобится для проверки, точно ли бот работает и получать от него вывод. Использовать её необязательно.
Далее нам пригодится ранее скопированный токен бота. Добавляем к коду следующее:
bot = aiomax.Bot("TOKEN", default_format="markdown")
Вместо TOKEN вставляем ваш токен.
default_format="markdown" устанавливает систему разметки Markdown сообщений по умолчанию. Если её не указать тут, то разметку (жирный, курсивный и все другие шрифты) использовать не получится.
В конец вашего файла пишем это:
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
bot.run()
logging.basicConfig(level=logging.INFO) настраивает логгер для вывода нужной информации, а bot.run() запустит бота при запуске Python-файла.
Скелет готов! Приступаем к созданию самого бота.
Эхо-бот
Очень часто первые написанные боты делают эхо-ботами. И мы не будем исключением. Пишем вот этот код до блока if __name__ == "__main__":
# Отправка информации о боте при нажатии кнопки "Начать" в мессенджере
@bot.on_bot_start()
async def info(pd: aiomax.BotStartPayload):
await pd.send("Я повторяю за тобой")
# Функция будет выполняться при отправке любого сообщения
@bot.on_message()
async def echo(message: aiomax.Message):
await message.send(message.content)
Давайте разберём код.
Декоратор @bot.on_bot_start() запускает функцию ниже него, когда кто-то запускает бота в мессенджере и передаёт функции параметр pd (Payload). У этого pd есть функция send, которая отправляет сообщение в чат тому, кто запустил бота.
Декоратор @bot.on_message() запускает функцию ниже него, когда кто-то отправляет сообщение в любой чат и передает функции параметр message (отправленное сообщение). У message есть функция send, которая отправляет сообщение в тот же чат, в который поступило сообщение, и параметр content, в котором содержится текст сообщения.
Вместо send в await message.send(message.content) можно написать reply - тогда вы не просто отправите сообщение в тот же чат, а ответите на поступившее сообщение.
Полный код бота, которого мы только что написали:
import aiomax
import logging
bot = aiomax.Bot("TOKEN", default_format="markdown")
# Отправка информации о боте при нажатии кнопки "Начать" в мессенджере
@bot.on_bot_start()
async def info(pd: aiomax.BotStartPayload):
await pd.send("Я повторяю за тобой")
# Функция будет выполняться при отправке любого сообщения
@bot.on_message()
async def echo(message: aiomax.Message):
await message.send(message.content)
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
bot.run()
Запускаем Python-файл, пишем боту и видим, что всё работает!

Кликер-бот
А теперь напишем бота с кнопками у сообщений.
Есть глобальный счётчик. При нажатии пользователем на кнопку, этот счётчик будет увеличиваться для всех.
Для начала возьмём старый скелет и добавим туда функции:
import aiomax
import logging
bot = aiomax.Bot("TOKEN", default_format="markdown")
taps = 0
# Создаём клавиатуру
kb = aiomax.buttons.KeyboardBuilder()
button = aiomax.buttons.CallbackButton('Нажми на меня!', 'click')
kb.add(button)
# Отправляем сообщение с информацией о боте при запуске
@bot.on_bot_start()
async def info(pd: aiomax.BotStartPayload):
await pd.send(f"**Жми**!\nТапы: {taps}", keyboard=kb)
# Отправляем сообщение с кнопкой при вводе команды /tap
@bot.on_command('tap')
async def tap_command(ctx: aiomax.CommandContext):
await ctx.reply(f"**Жми!**\nТапы: {taps}", keyboard=kb)
# Обрабатываем нажатие на кнопку в сообщении
@bot.on_button_callback('click')
async def on_tap(callback: aiomax.Callback):
global taps # Делаем taps глобальной переменной, чтобы изменять её по всему боту
taps += 1
await callback.answer(text=f"Жми!\nТапы: **{taps}**")
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
bot.run()
Кроме привычного создания бота, его запуска, и декоратора on_bot_start, появилось ещё много нового. Давайте рассмотрим поближе.
kb = aiomax.buttons.KeyboardBuilder() создаёт новую клавиатуру, которую мы будем прикреплять к сообщениям.button = aiomax.buttons.CallbackButton('Нажми на меня!', 'click') создаёт новую кнопку с текстом Нажми на меня! и специальным пейлоадом click, по которому можно проверять, какая именно кнопка нажата.
И наконец, kb.add(button) добавляет эту кнопку на нашу клавиатуру.
@bot.on_command('tap') создаёт новую команду с именем tap.
Наш декоратор on_command вызовется тогда, когда пользователь отправит боту /tap.
Вместо message декоратор on_command передаёт нам объект CommandsContext, который отличается от Message, но точно также имеет функции send и reply.
Функция после @bot.on_button_callback('click') запускается при нажатии кнопки с указанным ожидаемым пейлоадом. Так как пейлоад у нашей кнопки - click, тут напишем также.
Этот декоратор возвращает объект Callback, который уже отличается от прошлых on_command и on_message. В нём нету ни send, ни reply, зато есть функция answer - она отвечает на нажатие кнопки определенным действием.
В нашем случае мы просто поменяем текст сообщения на новый, который напишем после text=.
Будьте внимательны - если не указать text=, а просто написать строку, то текст сообщения не изменится. Вместо этого пользователю отправится уведомление, которые сейчас отображаются только на iPhone.
Запускаем файл, и видим, что бот работает как нужно!

FSM
Если захотелось, чтобы у каждого пользователя был свой отдельный счётчик, то можно использовать FSM.
import aiomax
import logging
from aiomax import fsm
bot = aiomax.Bot("TOKEN", default_format="markdown")
# Создаём клавиатуру
kb = aiomax.buttons.KeyboardBuilder()
button = aiomax.buttons.CallbackButton('Нажми на меня!', 'click')
kb.add(button)
# Отправляем сообщение с информацией о боте при запуске
@bot.on_bot_start()
async def info(pd: aiomax.BotStartPayload, cursor: fsm.FSMCursor):
taps = cursor.get_data()
if not taps:
taps = 0
await pd.send(f"**Жми**!\nТапы: {taps}", keyboard=kb)
# Отправляем сообщение с кнопкой при вводе команды /tap
@bot.on_command('tap')
async def tap_command(ctx: aiomax.CommandContext, cursor: fsm.FSMCursor):
taps = cursor.get_data()
if not taps:
taps = 0
await ctx.reply(f"**Жми!**\nТапы: {taps}", keyboard=kb)
# Обрабатываем нажатие на кнопку в сообщении
@bot.on_button_callback('click')
async def on_tap(callback: aiomax.Callback, cursor: fsm.FSMCursor):
taps = cursor.get_data()
if not taps:
taps = 0
taps += 1
cursor.change_data(taps)
await callback.answer(text=f"Жми!\nТапы: **{taps}**")
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
bot.run()
Можно сразу заметить, что taps сверху пропало, добавилось from aiomax import fsm и в каждой функции появились конструкции с cursor. Давайте рассмотрим.
Чтобы получить курсор пользователя в почти любом декораторе, в списках аргументов вашей функции можно добавить аргумент cursor - aiomax его увидит и передаст туда курсор.
С помощью курсора можно менять состояние и данные определенного пользователя.
taps = cursor.get_data() получает данные, которые хранятся у пользователя в текущий момент.if not taps: taps = 0 проверяет, хранятся ли какие-либо данные у пользователя - если нет, то сохраняет в переменной 0, дабы избежать ошибок.
cursor.change_data(taps) изменяет текущие хранящиеся данные пользователя на новые - в нашем случае новое количество нажатий.
Заключение
Если вы хотите подробнее ознакомиться с библиотекой aiomax, можете почитать документацию в репозитории проекта.
Обсудить aiomax или задать вопросы можно в чатах aiomax Community в Telegram или самом Max.