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

Создаем аккаунт бота

Для создания аккаунтов ботов есть специальный бот - @MasterBot. Переходим в него и нажимаем "Начать"

Список команд @MasterBot
Список команд @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.

Комментарии (6)