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

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

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

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

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

  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 комментариев
Написать комментарий...
Alexander Ivanov

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

Ответить
Развернуть ветку
Sergey Komarov

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

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

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

Ответить
Развернуть ветку
M J

 Прикольно. Я пользуюсь стандартным IOS-овским.  Диктуешь ему и всё.  Никаких проблем. Единственное, надо выстраивать правильно предложение.

Ответить
Развернуть ветку
Настя Каргаполова

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

Ответить
Развернуть ветку
5 комментариев
Юрий Туов

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

Ответить
Развернуть ветку
1 комментарий
Александра Крамаренко

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

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

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

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

Никогда не видела людей без сири?

Ответить
Развернуть ветку
Максим Попов

Парень хотел влезть в тему, которая уже раскрыта - чего вы хотели? Если будет уровень выше, то его сделают Google

Ответить
Развернуть ветку
Прочел это-потратил время зря

Блин, чувак, только сегодня днём искал бота-напоминалку, но с поиске нашёл только который от клавиатуры. Потом сижу и думаю: Ну неужели нельзя сделать голосовой ввод задач? И тут - бац! И ты через пару часов публикуешь этот пост.
Ты реально посланник Вселенной!!!
Спасибо!

Ответить
Развернуть ветку
Валентин Хирш

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

Ответить
Развернуть ветку
Sergey Komarov

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

Ответить
Развернуть ветку
Maksim Isaev

Не совсем так на самом деле, если дата и время разделены в тексте - Todoist берет последнее для преобразования

Ответить
Развернуть ветку
2 комментария
Аккаунт удален

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

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

Да, я в курсе про них. Я уже сравнивал их функциональность с возможностями моего бота, результат приведен ниже:

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

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

Ответить
Развернуть ветку
Виталий

Голосом не пробовал, а первый же тест текстом провалился. Написал "аптека в 9.00", а бот не понял чего хочу.

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

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

Ответить
Развернуть ветку
1 комментарий
Sergei Zotov
 Прежде чем приступать к распознаванию времени, нам сначала нужно заменить все слова в строке, записанные в виде слов, реальными числами. Для этого я написал npm библиотеку

А зачем было писать что-то свое? Неужели тысячи NLP либ на гитхабе не принесли результатов?)

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

Для самообразования, до этого проекта я даже ни разу не писал на JS.

Ответить
Развернуть ветку
1 комментарий
Sergey Komarov

они вроде под python в большинстве своем или под js их так же активно штампуют?

Ответить
Развернуть ветку
1 комментарий
S.Z

На айфоне проще сири пользоваться

Ответить
Развернуть ветку
Sergey Komarov

на Android - Google Assistant, но лично мне удобнее набить в телеге, например

Ответить
Развернуть ветку
Vitaliy Sitnikov

Указал время 19 00
Но бот почему-то зафиксировал в 18:59
Фича? Или баг?

Ответить
Развернуть ветку
Alexey Rusanov

Склонен считать что не 18:59, а 06:59, ваше текущее время на скрине. Бот не распарсил 19 00 и указал текущее

Ответить
Развернуть ветку
2 комментария
Igor Tarasov

Приходите к нам работать.

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

Спасибо за предложение. Я подумаю.

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

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

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

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

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

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

Бот и это умеет.

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

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

Ответить
Развернуть ветку
4 комментария
Михаил Кутузов

Попробуйте иранский мод телеграмма Telegraph, в нем есть локальное избранное, очень удобно одним тапом заносить в него, причем сохраняется разбивка по чатам и каналам.

Ответить
Развернуть ветку
Александр Шустик

А чем отличается от @remindmemegabot ?
Им пользуюсь уже 2 года

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

Отличия Умного планировщика от бота RemindMe, а также Skeddy приведены тут:

Ответить
Развернуть ветку
2 комментария
Александр Привалов

Так почему в обед, а не после работы?

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

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

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

Спасибо за труды!Буду следить за твоей работой😎

Ответить
Развернуть ветку
Sergei Zotov

да, кстати, принцип похож на команду /remind в слаке, если я правильно понял. Только там чуть более структурированный текст этой команды

Ответить
Развернуть ветку
Nick Chernykh

Почему часть вопросов на русском, часть на англ?

Ответить
Развернуть ветку
Vlad Musii

Крутой бот, именно то, что я искал. Хотел бы подписаться на телеграм канал автора, чтобы следить за разработкой и информацией про нововведения.

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

Моего личного телеграмм-канала нет, но есть телеграмм-канал, посвященный боту: https://t.me/SmartScheduler_Info

Ответить
Развернуть ветку
1 комментарий
Aleksandr Talalaev

Автор молодец в любом случае.
Ну и если позволяет время и ресурсы, я советую проект не бросать и нащупать свою нишу. Тем более как показывают комментарии в целом такие функции востребованы.
Единственное что отмечу, таки написание своих библиотек крайне нежелательно (случаи, когда цель обучение я не беру в расчёт), вы таким образом сами себе делаете медвежью услугу. Нужно отвлекаться на баги, костыли, фичи вместо разработки основного продукта. Лучше начать принимать участие в развитии уже какого-нибудь готового и хорошего опенсурс решения, если уж хочется. а свои велосипеды, почти всегда не имеют смысла.

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

Спасибо за совет, учту, бота бросать не планирую.

Ответить
Развернуть ветку
Дмитрий Толстокоров

Отправил задачу утром, а получается из будущего в прошлое:)

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

Машина времени исправлена, спасибо за баг-репорт.

Ответить
Развернуть ветку
Ильдар Мирзаянов

/2. 8 Апреля 17:05 (четверг) "0 9 0 0 анализ найти"

Говорю 8 апреля 0900 анализ найти. Почему 17:05

Ответить
Развернуть ветку
universe collaboration

бот просто супееер
ращраб, ты крутой :3

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