Бот «Умный планировщик» — понимает с полуслова

Нужно быстро куда-то записывать напоминания, которые будут потом вам сами присылать уведомления? Да так, чтобы можно было писать обычным человеческим языком, а вас при этом прекрасно понимали? Мне тоже, поэтому я написал бота под это дело.

Демонстрация работы "<a href="https://api.vc.ru/v2.8/redirect?to=https%3A%2F%2Ft.me%2FSmartScheduler_bot&postId=228652" rel="nofollow noreferrer noopener" target="_blank">Умного планировщика</a>"<br />
Демонстрация работы "Умного планировщика"

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

Забегая вперёд, скажу, что этого удалось добиться, как и также парочки других приятных особенностей:

  1. Бот понимает слова с ошибками.
  2. Бот понимает голосовые сообщения.
  3. Бот умеет прикреплять файлы к напоминаниям.
  4. Бот может быть интегрирован в вашу Trello доску.

Если вам не терпится опробовать бота прямо сейчас, то вот ссылка на него.

Для всех остальных, кому интересно как 16-ти летний школьник смог написать его, я расскажу небольшую историю разработки и о том, как устроена система парсинга напоминаний внутри «Умного планировщика».

Краткая история создания

Бот был написан на Node.js. Почему? Просто потому что javascript в данный момент является одним из самых популярных языков программирования. Я также думал попробовать написать бота на Python или php, но я остановился на js, так как он обладает схожим синтаксисом с знакомым мне C++.

Первая версия «Умного планировщика» работала иначе, но служила для одной и той же цели — создания напоминаний из естественной речи. Я хотел проверить имеет ли право на жизнь эта идея, и для этого я опубликовал статью про бота на Хабрахабре. Получив положительные отзывы, я решил продолжить разрабатывать бота.

Я не буду рассказывать вам как работала эта «кривая» и топорная версия, которая была создана почти год назад. Лучше перейдём сразу к текущей стабильной версии.

Архитектура бота

Алгоритм работы бота следующий:

Бот «Умный планировщик» — понимает с полуслова

Проверка напоминаний сделана при помощи минутного таймера. На диаграмме эта часть обозначена «Планировщик задач». Он загружает все напоминания из базы данных, проверяет их время и если оно вышло, то уведомляет о них. Истёкшие напоминания удаляются

Изначально планировалось, что бот будет понимать русский и английский языки, поэтому я написал каждый ответ на двух языках. Для определения языка пользователя бот считает количество кириллических и латинских букв в тексте сообщения. Если кириллических больше чем латинских, тогда язык русский, иначе английский.

Для повышения безопасности я добавил шифровку текста напоминаний при помощи встроенной в Node.js библиотеки crypto.

База данных PostgreSQL

Выбор СУБД PostgreSQL обусловлен тем, что она бесплатна и изначально интегрирована в платформу Heroku, на которой хостится бот. В базе данных всего 3 таблицы: Пользователи, Чаты и Напоминания.

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

Извлечение даты

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

Парсинг чисел

Прежде чем приступать к распознаванию времени, нам сначала нужно заменить все слова в строке, записанные в виде слов, реальными числами. Для этого я написал npm библиотеку parse-word-to-number. Она берёт строку и возвращает строку, в которой все возможные слова были заменены числами.
Для соотнесения фильтруемых слов из исходной строки с подходящими словами из словаря я использовал расстояние Дамерау-Левенштайна. Это позволяет библиотеке понимать слова, написанные с ошибками.

Особенности <a href="https://api.vc.ru/v2.8/redirect?to=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2F%40alordash%2Fparse-word-to-number&postId=228652" rel="nofollow noreferrer noopener" target="_blank">parse-word-to-number</a>
Особенности parse-word-to-number

Библиотека работает по такому алгоритму:

  1. Выбрать следующее слово в строке и найти его в словаре.
  2. Если оно найдено, то:
    ⠀⠀Если предыдущее слово это число:
    ⠀⠀⠀⠀Если разряд предыдущего числа больше текущего, то сложить текущее с предыдущим и соединить их.
    ⠀⠀⠀⠀Иначе если текущее число может быть умножено на предыдущее, то умножить текущее на предыдущее и соединить их.
  3. Во всех остальных случаях записать это слово в конец новой строки.

Она также поддерживает английский язык.

Распознавание текста и времени

Теперь, когда все числа в сообщении были заменены реальными числами, мы можем наконец извлечь описание времени из него. Для этого я написал ещё одну npm библиотеку date-parser. Она принимает строку и возвращает массив объектов класса ParsedDate (подробная информация в readme библиотеки). Каждый из этих объектов представляет собой одно событие и содержит в себе его дату и описание. Таким образом библиотека способна извлекать сразу несколько напоминаний из строки за раз.

Использование <a href="https://api.vc.ru/v2.8/redirect?to=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2F%40alordash%2Fdate-parser&postId=228652" rel="nofollow noreferrer noopener" target="_blank">date-parser</a>
Использование date-parser

Библиотека также поддерживает повторяющиеся напоминания и ограничения времени для них, как например: «Разминка каждые 30 минут до 8 вечера».

Процесс парсинга разделён на три стадии: упрощение, поиск и формирование.

Чтобы ускорить поиск описаний времени по шаблонам из человеческой речи, нам сначала нужно упростить исходную строку. Это делается при помощи замены всех слов в исходной строке на соответствующие им символы из словаря. Рассмотрим этот процесс на примере сообщения «Сходить завтра в магазин в шесть вечера».

<i>(Слову "завтра" соответствует символ A, предлогу "в" – p, "6" – n, "вечера" – O. "Сходить" и "магазин" не были найдены в словаре – они помечены точками)</i>
(Слову "завтра" соответствует символ A, предлогу "в" – p, "6" – n, "вечера" – O. "Сходить" и "магазин" не были найдены в словаре – они помечены точками)

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

Поиск ключевых слов с помощью регулярных выражений
Поиск ключевых слов с помощью регулярных выражений

В конце, после извлечения времени, мы формируем описание из незадействованных слов:

Описание формируется из оставшихся слов<br />
Описание формируется из оставшихся слов

Эта самая важная и основная часть парсинга. Настоящая библиотека date-parser устроена в разы сложней. Я не стал углубляться в технические нюансы, потому что этого алгоритма будет уже достаточно для мощного, но в то же время простого парсера даты.

Настройка часового пояса

Последняя, но не менее важная часть создания напоминаний это использование часового пояса. Для настройки часового пояса я сделал две опции.

Отправить геолокацию

Бот «Умный планировщик» — понимает с полуслова

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

Для определения часового пояса по долготе и широте бот использует сайт GeoNames.

Написать вручную

Бот «Умный планировщик» — понимает с полуслова

Или же, если вы используете Telegram с ПК, вы можете сами написать часовое смещение в формате ±ЧЧ:ММ.
Эта опция также доступна на мобильных устройствах.

Изменения в будущем

Даже имея столько полезных свойств, «Умному планировщику» ещё есть куда расти. В планах есть ещё множество улучшений, вот основные из них:

  1. Интеграция бота с Google календарём пользователя.
  2. Настройка индивидуального времени для неявных ключевых слов, как «утро», «день», «вечер» и «ночь».
  3. Улучшение интерфейса бот-человек при помощи замены большинства текстовых команд удобными встроенными клавиатурами.

Прочие мелкие доработки описаны в списке на Github’е проекта.

"Умный планировщик" — это открытый проект с открытым исходным кодом, так что не стесняйтесь сообщать о любых ошибках или предлагать улучшения, я буду премного благодарен.

Спасибо за внимание, надеюсь у вас появится больше свободного времени с моим ботом!

З.Ы. Это перевод и адаптация моей статьи с английского языка. Оригинал вот.

8989
67 комментариев

Алексей, жму руку, было очень интересно прочитать о Вашем проекте. И не слушайте критиков, которые говорят, что все уже придумано до нас. Даже если проект сам по себе и не выстрелит,  у Вас останется опыт и код в открытой репе, а это открытая дорога к успешной it-карьере, за Вас будут драться рекрутеры. А критики продолжат ныть в комментах :)

17
Ответить

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

7
Ответить

Большое спасибо!

1
Ответить

Комментарий недоступен

13
Ответить

Расскажите пожалуйста, вы имеете ввиду напоминания на иос? Или сири? Спасибо.

Ответить

Просто убогая, не продуманная программа. К сожалению.

Ответить

Зачем, если можно попросить напомнить Сири

5
Ответить