22 мая 2020

🤖 Бот VK для отрисовки формул в диалогах

Библиотека программиста — ваш источник образовательного контента в IT-сфере. Мы публикуем обзоры книг, видеолекции и видеоуроки, дайджесты и образовательные статьи, которые помогут вам улучшить процесс познания в разработке.
Об опыте расширения возможностей Вконтакте через создание бота на VK API. Задача: автоматическая отрисовка математических формул в LaTeX-разметке при обмене ими в диалогах VK.
🤖  Бот VK для отрисовки формул в диалогах

Приведённая статья подготовлена создателями описываемого бота Замиром Ашурбековым и Алексеем Никитиным. Публикуем после незначительных правок текста.

***

Вот уже более трёх лет мы используем сеть ВКонтакте для быстрого обмена сообщениями и информации по математическому анализу. Это удобно: у каждого студента есть страница ВКонтакте, и они проводят там достаточно много времени. С другой стороны, общение на математические темы в социальных сетях весьма затруднительно – неудобно обмениваться текстом с формулами. Например, формулу Ньютона – Лейбница приходится записывать словами:

Интеграл от a до b от функции f равен разности первообразной F функции f в точках a и b.

То же самое можно записать, используя ТeXовскую нотацию:

        int_a^b f(x) dx = F(b) - F(a).
    

C нашей точки зрения, так удобнее, но даже на математических факультетах многие студенты первых курсов данную нотацию не знают. Это вызывает проблемы, когда формулы становятся сложнее.

Можно воспользоваться online-редакторами LaTeX, и потом вставлять картинки в текст. В этом случае на одно сообщение приходится одна формула, что исключает использование включенных в текст формул, но главное – это существенно усложняет и удлиняет процесс написания «быстрого сообщения» в соцсети.

Решение

Мы решили создать бота, который преобразует в изображение формулы, записанные в формате LaTeX. Механизм работы следующий: пользователь отправляет боту формулу, тот её отрисовывает и отсылает обратно. Пользователь пересылает результат конечному адресату.

В идеальном сценарии бот должен находиться в одной групповой беседе вместе с получателем, тогда всё сводится к простой отправке формулы прямо в беседе. Стоит отметить, что боты ВКонтакте реализуются посредством сообществ: сообществу можно написать личное сообщение или добавить в беседу (если такая функциональность включена).

Здесь самое время перечислить проблемы, связанные с работой ВКонтакте:

  • ВКонтакте не поддерживает ни один из существующих форматов вёрстки математического текста. Грубо говоря, общение ограничено стандартным текстовым боксом и отправкой фотографий.
  • Существуют разные версии клиентов и желательно для многих из них придумать упрощение способа коммуникации. Более того, многие пользуются соцсетью именно через мобильные клиенты, для которых придумать решение сложнее.
  • В целях безопасности есть ограничения на полезную функциональность. К примеру, методы VK API, которые позволяют читать и редактировать сообщения отдельного пользователя, разрешены только для ограниченного круга разработчиков, а именно, для тех, кто делает клиентские приложения.

Серверная часть

В качестве средства разработки серверной части было решено использовать Node.js. На платформе имеется большой набор библиотек и серьёзный стек уже решённых проблем. Преимуществом для создания бота является нативная поддержка асинхронного кода.

Для размещения бота воспользовались серверами Microsoft Azure, которые предоставляют для студентов бесплатные 100$ на счёт. За тариф по цене 6$ в месяц предоставляется 2 ГБ ОЗУ и быстрая внешняя SSD-память, чего хватает с запасом.

Взаимодействие с ВКонтакте

К счастью, разработчиками ВКонтакте создан удобный API с подробной документаций. Так как речь идёт о боте, нас в первую очередь интересует Bots Long Poll API:

Long Polling — это технология, которая позволяет получать данные о новых событиях с помощью «длинных запросов». Сервер получает запрос, но отправляет ответ на него не сразу, а лишь тогда, когда произойдет какое-либо событие (например, придёт новое сообщение), либо истечет заданное время ожидания
Из документации VK API

Альтернативой этому способу является Callback API. Здесь обратный принцип: ВКонтакте сам делает запросы на ваш сервер, как только происходит новое событие. Второй способ лучше подходит для больших ботов и имеет более тонкую настройку. Но в итоге было решено остановиться на первом варианте, так как с ним проще работать.

Раз архитектура бота будет событийной, логика становится достаточно простой. Пользователь отправляет сообщение боту. Сервер ВКонтакте сообщает всю информацию о поступившем боту сообщении нашему удалённому серверу. Удаленный сервер решает, что делать с поступившей информацией. Существующее API делает возможным адекватную реализацию этой логики, однако требуетя некоторая прослойка, чтобы было можно работать с готовыми абстракциями. Для этого была взята библиотека easyvk:

        const easyvk = require('easyvk');
easyvk({ //настройка инициализации
  token: "токен сообщества",
  utils: { //подключение утилит библиотеки
    longpoll: true, //Long Polling
    bots: true, //работа с ботом
    uploader: true //загрузка изображений на сервер
  }
}).then((lp) => {
    //устанавливаем соединение
    lp.bots.longpoll.connect().then((connection) => {
        //включаем прослушку новых сообщений
        connection.on('message_new', (message) => {
            //в message хранится вся информация о входящем сообщении
            doSomethingWithMessage(message);
        });
    });
});
    

Теперь всё сводится к тому, что нужно лишь обработать входящее сообщение и выслать результат назад. Рассматривать то, какие методы используются для отправки сообщений и загрузки фотографий на сервер ВКонтакте, в этой статье мы не будем.

Проблема событий в беседах

Небольшое отступление о проблемах событий во ВКонтакте. Для удобства было решено добавить реакцию на изменения сообщения. После редактирования уже существующего сообщения бот должен скомпилировать новое содержимое. Это необходимо, например, когда компиляция формулы прошла неуспешно и нужно исправить пару символов.

Работает это почти так же, как и событие нового сообщения, просто название события меняется на message\_edit. Однако, как оказалось, эти события работают только в диалогах сообщества, но не работают в обычных беседах с другими пользователями. ВКонтакте не уведомляет бота о том, что в беседе было изменено сообщение.

Мы попытались проверять пришедшие сообщения на изменения раз в секунду. Как оказалось, никакие функции для доступа к истории изменения конкретных сообщений для бота недоступны, так как в событиях вместо глобального id сообщения выдаётся специальный conversation id, а методы, предназначенные для работы с этим conversation id, не работают в беседах, о чём написано в документации. Были подозрения на политику конфиденциальности или отсутствие прав у бота. Для уточнения мы обратились в техническую поддержку ВКонтакте. Как оказалось, ещё не готова инфраструктура поддержки этой функциональности и работа над ней ведётся в настоящее время – пока остаётся только ждать.

Частично проблему можно обойти и сейчас, если использовать User Longpoll API. Если не вдаваться в детали, то это почти то же самое, что и Bots Longpoll API, и в пределах групповых бесед, созданных сообществом, к которому привязан бот, указанная функциональность работает. Правда, из-за того, что User Longpoll API предназначен для управления пользователями, а не ботами, в диалогах, не привязанных к сообществу, не работает вообще ничего (видимо, вступает в силу политика безопасности ВКонтакте).

Компиляция формулы

Теперь имея текст формата LaTeX, нужно сформировать выходное изображение. Были рассмотрены возможности использования библиотек, которые не являются настоящими TeX- компиляторами, но поддерживают самую популярную функциональность. К сожалению, большинство из них – это библиотеки транслирующие LaTeX в MathML. Это хорошее решение проблемы, когда есть возможность отображать MathML на стороне клиента, но в данном случае такой возможности нет, нам нужно именно изображение.

Можно имитировать браузер и делать скриншоты получившейся формулы. Или использовать библиотеки, которые берут на себя роль отображения html-элементов на изображения. Обычно это не более чем полумеры, так как на выходном изображении постоянно возникают непредвиденные артефакты или какие-то формулы отображаются не так, как надо.

Какое-то время бот работал на MathJax. Он предоставляет возможность транслировать формулы в SVG. Но пришлось отказаться и от него, так как не удалось настроить его так, чтобы пользователям было удобно с ним работать.

Вышеперечисленные проблемы возникают из-за того, что большинство библиотек первоначально создавались для фронтенда, а потом уже были перенесены на Node.js. В конце концов было решено честным способом использовать LaTeX-компилятор, используя готовый шаблон и подставляя текст сообщения в тело документа. Как показала практика, компиляция файлов достаточно быстрая и время отклика бота не превышает нескольких секунд.

Сделать задуманное позволила библиотека node-latex, которая упрощает компиляцию до следующего:

        const latex = require('node-latex');
const fs = require('fs')
 
//создаем поток чтения, из сформированного файла input.tex
const input = fs.createReadStream('input.tex')

//создаём поток записи для создания pdf по пути output.pdf
output = fs.createWriteStream('output.pdf'); 

pdf = latex(input);
pdf.pipe(output);
pdf.on('error', (error) => {
    //событие произошла ошибка
    //причины внутри объекта error
});
pdf.on('finish', () => {
    //событие формула успешно скомпилированна
    //нужный нам файл output.pdf
    //обрабатываем его как хотим
});
    

Формируем растровое изображение

Однако отправлять pdf-ку прямо пользователю – не лучшее решение. Нужно преобразовать файл в растровое изображение. На этом этапе используется две библиотеки: pdf-image и sharp. Первая использует ImageMagick переводит pdf в png с удалением ненужных белых полос вокруг текста:

        const PDFImage = require("pdf-image").PDFImage;
let pdfImage = new PDFImage('output.pdf', {
    combinedImage: true,
    convertOptions: {
      "-density": "400", //параметр детализации
      "-trim":"" //обрубание белых полос
    }
});
imagePath = await pdfImage.convertFile();
    

На этом этапе сервер уже может отправлять получившееся изображение, которое хранится по пути imagePath. Но на стороне пользователя такое будет выглядеть не очень красиво, поэтому хорошо бы произвести некоторые предварительные манипуляции. В этом помогает библиотека sharp. К примеру, с помощью неё удалось добиться того, что размер шрифта в выходном изображении при просмотре с компьютера совпадает с размером шрифта текста сообщений ВКонтакте.

Заключение

В нашей статье описаны шаги по написанию специализированного бота ВКонтакте. Мы надеемся, что она поможет не только тем, кто хочет использовать LaTeX ВКонтакте, но и просто желающим написать своего бота под данную систему.

В результате вышеизложенных действий, был написан достаточно быстрый бот, который предоставляет пользователю сети ВКонтакте широкие возможности по использованию LaTeX в этой социальной сети. После этого нами была основана группа ВКонтакте.

По этому адресу можно узнать о боте более подробно, а также получить возможность поработать с ним, посылая сообщения сообществу, или добавив бота в свой диалог (по специально приведённой для этого действия кнопке).

***

Если вы увлекаетесь математикой, советуем вам группу одного из авторов статьи – Алексея Никитина. Группа называется «Ёжик в матане», её участники активно используют VkTeX-бота для обсуждения видеороликов, статей и книг о математике.

МЕРОПРИЯТИЯ

Комментарии

ВАКАНСИИ

Добавить вакансию

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ