{"id":14285,"url":"\/distributions\/14285\/click?bit=1&hash=346f3dd5dee2d88930b559bfe049bf63f032c3f6597a81b363a99361cc92d37d","title":"\u0421\u0442\u0438\u043f\u0435\u043d\u0434\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0442\u0440\u0430\u0442\u0438\u0442\u044c \u043d\u0430 \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u0435 \u0438\u043b\u0438 \u043f\u0443\u0442\u0435\u0448\u0435\u0441\u0442\u0432\u0438\u044f","buttonText":"","imageUuid":""}

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

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

Демонстрация работы "Умного планировщика"

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

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

  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. Она берёт строку и возвращает строку, в которой все возможные слова были заменены числами.
Для соотнесения фильтруемых слов из исходной строки с подходящими словами из словаря я использовал расстояние Дамерау-Левенштайна. Это позволяет библиотеке понимать слова, написанные с ошибками.

Особенности parse-word-to-number

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

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

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

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

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

Использование date-parser

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

0
67 комментариев
Написать комментарий...
Роман Рабочий

Чисто моя боль, делюсь.

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

И началось, надо скопировать, это сообщение, в планировщик, поставить время и прочее, а это происходит за рулем или в метро, короче неудобно мне.

Как было бы удобно....
Пишет тебе кто то важный в телеге, ты читаешь сообщение, форвардишь это сообщение боту в телеге, бот быстро спрашивает когда напомнить. Интеграция с trello и всяким остальным уже вторична.

Ответить
Развернуть ветку
Maxim Syabro

Форвардишь в saved messages + просишь Сири напомнить.

Ответить
Развернуть ветку
Николай Полянский

Почему некоторые люди на vc даже маленьшей возможности не оставляют, что у собеседника может не быть сири? Загадка

Ответить
Развернуть ветку
Maxim Syabro

На андроиде нет возможности голосом назначить оповещение?

Ответить
Развернуть ветку
Роман Рабочий

не пользуюсь сири вообще, но идея хорошая, попробую.

Ответить
Развернуть ветку
Maxim Syabro

Я Сири как раз пользуюсь для напоминаний таких и установки таймера. Очень удобно.

Ответить
Развернуть ветку
64 комментария
Раскрывать всегда