Search
Write a publication
Pull to refresh

Пишем бота для «Отечественного WeChat» — Max на Python

Level of difficultyEasy

Недавно 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.

Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.