Бот «Умный планировщик» — понимает с полуслова
Нужно быстро куда-то записывать напоминания, которые будут потом вам сами присылать уведомления? Да так, чтобы можно было писать обычным человеческим языком, а вас при этом прекрасно понимали? Мне тоже, поэтому я написал бота под это дело.
Когда я в очередной раз вручную переносил напоминания нашей мамы, которые она писала в семейном телеграмм-чате, у меня возникла идея создать телеграмм-бота, который будет записывать, что мне надо сделать, и напоминать об этом, но при этом он должен был понимать естественную человеческую речь.
Забегая вперёд, скажу, что этого удалось добиться, как и также парочки других приятных особенностей:
- Бот понимает слова с ошибками.
- Бот понимает голосовые сообщения.
- Бот умеет прикреплять файлы к напоминаниям.
- Бот может быть интегрирован в вашу Trello доску.
Если вам не терпится опробовать бота прямо сейчас, то вот ссылка на него.
Для всех остальных, кому интересно как 16-ти летний школьник смог написать его, я расскажу небольшую историю разработки и о том, как устроена система парсинга напоминаний внутри «Умного планировщика».
Краткая история создания
Бот был написан на Node.js. Почему? Просто потому что javascript в данный момент является одним из самых популярных языков программирования. Я также думал попробовать написать бота на Python или php, но я остановился на js, так как он обладает схожим синтаксисом с знакомым мне C++.
Первая версия «Умного планировщика» работала иначе, но служила для одной и той же цели — создания напоминаний из естественной речи. Я хотел проверить имеет ли право на жизнь эта идея, и для этого я опубликовал статью про бота на Хабрахабре. Получив положительные отзывы, я решил продолжить разрабатывать бота.
Я не буду рассказывать вам как работала эта «кривая» и топорная версия, которая была создана почти год назад. Лучше перейдём сразу к текущей стабильной версии.
Архитектура бота
Алгоритм работы бота следующий:
Проверка напоминаний сделана при помощи минутного таймера. На диаграмме эта часть обозначена «Планировщик задач». Он загружает все напоминания из базы данных, проверяет их время и если оно вышло, то уведомляет о них. Истёкшие напоминания удаляются
Изначально планировалось, что бот будет понимать русский и английский языки, поэтому я написал каждый ответ на двух языках. Для определения языка пользователя бот считает количество кириллических и латинских букв в тексте сообщения. Если кириллических больше чем латинских, тогда язык русский, иначе английский.
Для повышения безопасности я добавил шифровку текста напоминаний при помощи встроенной в Node.js библиотеки crypto.
База данных PostgreSQL
Выбор СУБД PostgreSQL обусловлен тем, что она бесплатна и изначально интегрирована в платформу Heroku, на которой хостится бот. В базе данных всего 3 таблицы: Пользователи, Чаты и Напоминания.
Также мною была с нуля написан ORM интерфейс для базы данных. Я знаю, что наверняка кто-то это уже сделал, но я хотел попробовать сделать это сам и, таким образом, уменьшить количество зависимостей.
Извлечение даты
Процесс обработки сообщений пользователя разделён на два этапа: работу с числами и распознавание введённого напоминания.
Парсинг чисел
Прежде чем приступать к распознаванию времени, нам сначала нужно заменить все слова в строке, записанные в виде слов, реальными числами. Для этого я написал npm библиотеку parse-word-to-number. Она берёт строку и возвращает строку, в которой все возможные слова были заменены числами.
Для соотнесения фильтруемых слов из исходной строки с подходящими словами из словаря я использовал расстояние Дамерау-Левенштайна. Это позволяет библиотеке понимать слова, написанные с ошибками.
Библиотека работает по такому алгоритму:
- Выбрать следующее слово в строке и найти его в словаре.
- Если оно найдено, то:
⠀⠀Если предыдущее слово это число:
⠀⠀⠀⠀Если разряд предыдущего числа больше текущего, то сложить текущее с предыдущим и соединить их.
⠀⠀⠀⠀Иначе если текущее число может быть умножено на предыдущее, то умножить текущее на предыдущее и соединить их. - Во всех остальных случаях записать это слово в конец новой строки.
Она также поддерживает английский язык.
Распознавание текста и времени
Теперь, когда все числа в сообщении были заменены реальными числами, мы можем наконец извлечь описание времени из него. Для этого я написал ещё одну npm библиотеку date-parser. Она принимает строку и возвращает массив объектов класса ParsedDate (подробная информация в readme библиотеки). Каждый из этих объектов представляет собой одно событие и содержит в себе его дату и описание. Таким образом библиотека способна извлекать сразу несколько напоминаний из строки за раз.
Библиотека также поддерживает повторяющиеся напоминания и ограничения времени для них, как например: «Разминка каждые 30 минут до 8 вечера».
Процесс парсинга разделён на три стадии: упрощение, поиск и формирование.
Чтобы ускорить поиск описаний времени по шаблонам из человеческой речи, нам сначала нужно упростить исходную строку. Это делается при помощи замены всех слов в исходной строке на соответствующие им символы из словаря. Рассмотрим этот процесс на примере сообщения «Сходить завтра в магазин в шесть вечера».
Теперь мы можем просто применять к этой строке разные регулярные выражения для поиска описаний времени. Это позволяет нам обрабатывать абсолютно любые шаблоны определения времени! Все, что нам необходимо сделать, это написать достаточно регулярных выражений:
В конце, после извлечения времени, мы формируем описание из незадействованных слов:
Эта самая важная и основная часть парсинга. Настоящая библиотека date-parser устроена в разы сложней. Я не стал углубляться в технические нюансы, потому что этого алгоритма будет уже достаточно для мощного, но в то же время простого парсера даты.
Настройка часового пояса
Последняя, но не менее важная часть создания напоминаний это использование часового пояса. Для настройки часового пояса я сделал две опции.
Отправить геолокацию
Если вы пишите боту с смартфона, тогда вы можете настроить ваш часовой пояс в 1 клик: просто отправьте свою геолокацию и бот автоматически определит ваш часовой пояс.
Для определения часового пояса по долготе и широте бот использует сайт GeoNames.
Написать вручную
Или же, если вы используете Telegram с ПК, вы можете сами написать часовое смещение в формате ±ЧЧ:ММ.
Эта опция также доступна на мобильных устройствах.
Изменения в будущем
Даже имея столько полезных свойств, «Умному планировщику» ещё есть куда расти. В планах есть ещё множество улучшений, вот основные из них:
- Интеграция бота с Google календарём пользователя.
- Настройка индивидуального времени для неявных ключевых слов, как «утро», «день», «вечер» и «ночь».
- Улучшение интерфейса бот-человек при помощи замены большинства текстовых команд удобными встроенными клавиатурами.
Прочие мелкие доработки описаны в списке на Github’е проекта.
"Умный планировщик" — это открытый проект с открытым исходным кодом, так что не стесняйтесь сообщать о любых ошибках или предлагать улучшения, я буду премного благодарен.
Спасибо за внимание, надеюсь у вас появится больше свободного времени с моим ботом!
З.Ы. Это перевод и адаптация моей статьи с английского языка. Оригинал вот.
Алексей, жму руку, было очень интересно прочитать о Вашем проекте. И не слушайте критиков, которые говорят, что все уже придумано до нас. Даже если проект сам по себе и не выстрелит, у Вас останется опыт и код в открытой репе, а это открытая дорога к успешной it-карьере, за Вас будут драться рекрутеры. А критики продолжат ныть в комментах :)
а еще он умеет писать читабельный юзер гайд к своему сервису. сколько же нечитаемых ридми на гитхабе, которые не задумывались такими
Большое спасибо!
Прикольно. Я пользуюсь стандартным IOS-овским. Диктуешь ему и всё. Никаких проблем. Единственное, надо выстраивать правильно предложение.
Расскажите пожалуйста, вы имеете ввиду напоминания на иос? Или сири? Спасибо.
Просто убогая, не продуманная программа. К сожалению.
Зачем, если можно попросить напомнить Сири
Проект был создан в целях самообразования (и, если удастся, облегчения жизни пользователей), но зато бота можно добавить в рабочий чат, чтобы, не выходя из телеги, планировать задачи себе и коллегам.
Никогда не видела людей без сири?
Парень хотел влезть в тему, которая уже раскрыта - чего вы хотели? Если будет уровень выше, то его сделают Google
Блин, чувак, только сегодня днём искал бота-напоминалку, но с поиске нашёл только который от клавиатуры. Потом сижу и думаю: Ну неужели нельзя сделать голосовой ввод задач? И тут - бац! И ты через пару часов публикуешь этот пост.
Ты реально посланник Вселенной!!!
Спасибо!
Мне жаль но именно такой функционал полностью реализован в тудуисте, только с куда большим количеством плюшек и вариации. Поддерживает любую форму текстовой подачи, даже в сленговых словах
ну типа это минимальная прога. я давно юзаю для напоминалок отложенные мсджи тг и этот бот делает чуть проще.
да, я знаю про ассистента и т.д., но почему то так проще для меня. набивай напоминалки, не отходя от кассы, так сказать
Не совсем так на самом деле, если дата и время разделены в тексте - Todoist берет последнее для преобразования
Комментарий недоступен
Да, я в курсе про них. Я уже сравнивал их функциональность с возможностями моего бота, результат приведен ниже:
Все эти умные планировщики и прочее - нафиг никому не нужны, потому что основная проблема - это выполнить, то что запланировал. Когда уже кто-то напишет бота, который будет не планировать, а выполнять запланированные задачи?! ))
Голосом не пробовал, а первый же тест текстом провалился. Написал "аптека в 9.00", а бот не понял чего хочу.
Не знаю в чем может быть проблема, у меня этот текст смог правильно обработаться.
А зачем было писать что-то свое? Неужели тысячи NLP либ на гитхабе не принесли результатов?)
Для самообразования, до этого проекта я даже ни разу не писал на JS.
они вроде под python в большинстве своем или под js их так же активно штампуют?
На айфоне проще сири пользоваться
на Android - Google Assistant, но лично мне удобнее набить в телеге, например
Указал время 19 00
Но бот почему-то зафиксировал в 18:59
Фича? Или баг?
Склонен считать что не 18:59, а 06:59, ваше текущее время на скрине. Бот не распарсил 19 00 и указал текущее
Приходите к нам работать.
Спасибо за предложение. Я подумаю.
Чисто моя боль, делюсь.
Пишет тебе кто то важный в телеге, ты читаешь сообщение, и такой думаешь, надо не потерять это сообщение в потоке инфо мусора.
И началось, надо скопировать, это сообщение, в планировщик, поставить время и прочее, а это происходит за рулем или в метро, короче неудобно мне.
Как было бы удобно....
Пишет тебе кто то важный в телеге, ты читаешь сообщение, форвардишь это сообщение боту в телеге, бот быстро спрашивает когда напомнить. Интеграция с trello и всяким остальным уже вторична.
Бот и это умеет.
Форвардишь в saved messages + просишь Сири напомнить.
Попробуйте иранский мод телеграмма Telegraph, в нем есть локальное избранное, очень удобно одним тапом заносить в него, причем сохраняется разбивка по чатам и каналам.
А чем отличается от @remindmemegabot ?
Им пользуюсь уже 2 года
Отличия Умного планировщика от бота RemindMe, а также Skeddy приведены тут:
Так почему в обед, а не после работы?
Кажется я забыл поменять время из оригинальной статьи, когда переводил текст на диаграммах. Спасибо, будет исправлено.
Спасибо за труды!Буду следить за твоей работой😎
да, кстати, принцип похож на команду /remind в слаке, если я правильно понял. Только там чуть более структурированный текст этой команды
Почему часть вопросов на русском, часть на англ?
Крутой бот, именно то, что я искал. Хотел бы подписаться на телеграм канал автора, чтобы следить за разработкой и информацией про нововведения.
Моего личного телеграмм-канала нет, но есть телеграмм-канал, посвященный боту: https://t.me/SmartScheduler_Info
Автор молодец в любом случае.
Ну и если позволяет время и ресурсы, я советую проект не бросать и нащупать свою нишу. Тем более как показывают комментарии в целом такие функции востребованы.
Единственное что отмечу, таки написание своих библиотек крайне нежелательно (случаи, когда цель обучение я не беру в расчёт), вы таким образом сами себе делаете медвежью услугу. Нужно отвлекаться на баги, костыли, фичи вместо разработки основного продукта. Лучше начать принимать участие в развитии уже какого-нибудь готового и хорошего опенсурс решения, если уж хочется. а свои велосипеды, почти всегда не имеют смысла.
Спасибо за совет, учту, бота бросать не планирую.
Отправил задачу утром, а получается из будущего в прошлое:)
Машина времени исправлена, спасибо за баг-репорт.
/2. 8 Апреля 17:05 (четверг) "0 9 0 0 анализ найти"
Говорю 8 апреля 0900 анализ найти. Почему 17:05
бот просто супееер
ращраб, ты крутой :3