Meekee - это крошечный slackbot, написанный на Node.js, который подключается к Google Calendar API и отправляет уведомления, когда приближается событие.
Как распределенная команда, у нас было много небольших разочарований по поводу ежедневного присоединения к удаленным звонкам. Мы сделали Meekee, чтобы упростить поиск ссылки на видеовстречу / комнату, запомнить, о чем встреча, когда она начинается и т. Д. Meekee отправляет пинг за 3 минуты до ее начала, чтобы вы могли продолжать работать до тех пор, но еще достаточно пора быть в правильном настроении, когда он начнется.
Эта статья посвящена технической стороне дела. Сначала ознакомьтесь с отличной статьей Tomomi Sasaki, чтобы понять, зачем мы ее создали. 😉
Изящное присоединение к конференц-звонку (и наш бот Slack, чтобы помочь с этим)
« Вы все еще там…? medium.com»
Вы можете прочитать эту статью, если:
- вам интересно, как другие люди структурируют свою архитектуру и особенно программное обеспечение без пользовательского интерфейса, например, бот.
- вам интересно, как работать с GoogleCalendar Api в Node.js, обновлять токены с течением времени и обрабатывать обратный вызов аутентификации вне браузера.
В Интернете уже существует множество руководств о том, как создавать ботов, поэтому я не буду вдаваться в подробности. Код Slack’s Easy-Peasy-Bot - хорошее место для начала.
Стек
- Написано на Node.js
- Botkit для помощи с функциями бота (установка пользователем, прослушивание, разговор и т. Д.).
- MongoDB для хранения с использованием драйвера для Botkit botkit-storage-mongo.
- API GoogleCalendar с библиотекой google-api-nodejs-client для отслеживания событий.
- Размещено на Heroku с Codeship для непрерывной интеграции.
Код является открытым исходным кодом, если вы хотите заглянуть: github.com/aqworks/meekee-bot.
Это приблизительный обзор основного процесса:
Основная установка
Сначала Botkit загружает index.js
, инициируется подключение к базе данных и Slack. Botkit обратится onInstallation
при установке новых команд.
var controller = app.configure(process.env.PORT, process.env.SLACK_CLIENT_ID, process.env.SLACK_CLIENT_SECRET, config, onInstallation); controller.on('rtm_open', function (bot) { if (!userLib) userLib = require('./lib/user').init(controller); controller.webserver.get('/google/auth/callback', function(req, res) { res.send("<p>Next, copy and paste this Authentication Code in Slack to @meekee: <br/><strong>"+req.query.code+"</strong></p>"); }); setTimeout(function(){ loop(bot, true); }, 3000); setInterval(function(){ loop(bot, false); }, 60000); });
controller.on("rtm_open")
вызывается для каждой отдельной команды, с которой удалось установить соединение. Это похоже на то, как будто у каждой команды есть поток.
Здесь создается UserLib, потому что он содержит контекст, специфичный для команд.
Также есть небольшой прием для обработки обратного вызова аутентификации GoogleApi. Я вернусь к этому. ☝️
Наконец, таймер для выполнения функции loop
каждую минуту для проверки отправки событий и уведомлений. Подробнее об этом позже.
Взаимодействие с ботом
Botkit предоставляет методы для прослушивания определенных ключевых слов и реагирования на них:
controller.hears(["hello", "hi", "greetings"], ["direct_mention", "mention", "direct_message"], function(bot,message) { bot.reply(message, "Hello! :simple_smile:"); } );
Meekee реагирует на простое общение, но это не чат-бот, поэтому он обрабатывает только простые ключевые слова. Не было необходимости иметь бота со способностью обработки естественного языка, поскольку основные функции - это уведомления о событиях.
Meekee понимает start
и stop
как «включить / выключить уведомления».
Процесс установки
Когда новая команда устанавливает бота, Botkit берет на себя установку и подключение к Slack.
Затем Meekee выполняет дополнительный шаг: запрашивает у пользователей доступ к их Календарю:
Как я уже упоминал во вступлении, было немного сложно понять, как обрабатывать обратный вызов из Google Calendar API. Обычный процесс аутентификации будет проверять связь с конечной точкой, которую вы предоставили с кодом аутентификации.
Однако Meekee не «живет» в браузере. Настроена конечная точка для прослушивания этого кода (controller.webserver.get('/google/auth/callback')
в controller.rtm(on_open)
), поэтому код извлекается. Но поскольку контекст не используется совместно браузером и ботом, невозможно узнать, какому пользователю принадлежит код… Пользователя просят вставить его обратно в Slack. Затем авторизация завершается.
Это была часть, которую я пытался решить со многих сторон. В конце концов я согласился на этот неэлегантный способ обращения с вещами. Meekee изначально задумывался как внутренний инструмент, так что этого было достаточно.
Примечание о токенах Google Calendar API
Meekee один раз устанавливает соединение с Google Calendar. Было бы неприятно, если бы доступ приходилось предоставлять снова каждую неделю или если бы уведомления пропускались из-за истечения срока действия токена, верно? 😅
Вот как сделать токен «липким».
oauth2Client.generateAuthUrl({ access_type: 'offline', approval_prompt: 'force', scope: [ 'https://www.googleapis.com/auth/calendar.readonly' ] });
generateAuthUrl
возвращает URL-адрес, который будет отправлен пользователю для аутентификации в Google API.
Параметры
access_type: 'offline'
иapproval_prompt: 'force'
используются для включенияrefresh_token
. В этом режиме Gcal отправит новый токен по истечении срока действия предыдущего. ✨
При каждом запросе к календарю
updateToken()
подтверждает, что токен Google все еще действителен, или обновляет токен в базе данных, если он был обновлен.
loop
Боковое примечание: Heroku перезапускает экземпляр сервера каждые 24 часа, поэтому при первом запуске meekee проверяет часовые пояса пользователя, при необходимости обновляет. Люди в AQ много путешествуют, и им нравятся их уведомления в правильном часовом поясе!
Ядро Meekee - это цикл, который каждую минуту проверяет события каждого пользователя.
checkCalendar
призван:
- Запросите API Календаря Google для текущего пользователя,
- Фильтровать события,
- Форматировать уведомления,
- Обновите базу данных (ведите подсчет отправленных уведомлений),
- Обратный вызов в цикл и отправка уведомлений.
checkCalendar = function(user_object, callback){ var notifications = []; var error = null; updateToken(user_object); var args = calendar.getEventArgs(oauth2Client); google.calendar('v3').events.list(args, function(err, res){ if (err) error = calendar.handleQueryError(err, args); else notifications = calendar.filterEvents(args, res, user_object); if (notifications.length){ user_object.notifications += notifications.length; saveUser(user_object, function(){ if (err) console.error(err); }, false); } } callback(user_object, notifications, error); }); }
Анатомия запроса к календарю Конечная точка событий:
auth
содержит идентификацию для Gcal, такую как токен пользователя, идентификатор клиента, секрет клиента, а также URL-адрес перенаправления для обратного вызова.calendarId: primary
Сейчас просматривается только главный календарь.maxResults: 5
Предполагается, что одновременно происходит не более 5 событий.singleEvents: true
Включает повторяющиеся события регулярные события.orderBy: startTime
Событие, начинающееся первым, отображается первым в результатах.timeZone: utc
События обрабатываются по часовому поясу UTC. При форматировании уведомлений применяется часовой пояс пользователя.- _22 _, _ 23_ Выберите все события, которые начинаются через 3 минуты или происходят в настоящее время (включая события «Весь день»).
События, которые фактически не начинаются в это время, будут отфильтрованы.
Если не произойдет ошибка en, этот запрос вернет набор событий.
Отфильтровать события и форматировать уведомления
Вызов tocalendar.filterEvents
затем отфильтровывает события, которые соответствуют этим критериям и формату как уведомления:
- События, которые фактически начинаются через 3-4 минуты.
- События, на которые приглашены другие лица, кроме пользователя (и ответили «да» или «возможно»). Meekee не сообщает о событиях, в которых больше никого нет.
Отправлять уведомления!
Вернувшись в цикл, отправьте уведомление через обратный вызов.
Slack предоставляет множество опций для форматирования сообщений столбцами, изображениями и т. Д.
Meekee также имеет 3 контрольных точки, чтобы помочь каждому пользователю в процессе. Цикл также используется для их отправки.
2 часа: предоставил ли пользователь доступ к Календарю Google?
Если нет, напомним.
1 неделя: пользователю рекомендуется оставить отзыв в нашей учетной записи Twitter @aqbots и сообщить своим коллегам о боте.
4 недели: Meekee отправляет небольшое сообщение, чтобы поблагодарить их, сообщить, сколько уведомлений они получили на данный момент и кто использует бота в их команде.
Что я выучил
Я узнал, что «крошечный бот» может потребовать много работы, особенно если он чувствителен ко времени, находится между двумя API и имеет несколько пользователей и команд.
Выявление ошибки и устранение неполадок во времени и контексте может быть непросто из-за большого количества асинхронных процессов. Классическое ведение журнала здесь не очень помогает.
Я не реализовал полноценные тесты, потому что, как я уже сказал, это внутренний инструмент, и имитировать API-интерфейсы было бы довольно сложно.
Но это еще раз доказало, что написание тестов никогда не бывает пустой тратой времени. Это почти всегда сэкономит вам время позже. И это особенно когда сложно проверить код вручную.
Auth - это сложно. Несмотря на существующие стандарты, бывает сложно понять, как установить соединение и поддерживать его в течение долгого времени.
Небольшие проекты могут стать прекрасной возможностью внести свой вклад в Open Source.
Мне потребовались функции, которых не было в botkit-storage-mongo. Я закодировал их и сделал небольшой запрос на вытягивание, который позже был добавлен в исходный код. Это действительно должны быть большие изменения. Если вам это нужно, изменения могут быть внесены и кем-то другим.
Вот и все!
Спасибо, что прочитали этот пост. Надеюсь, вы нашли в этом какую-то ценность.
Вы можете найти meekee на meekee.io, @aqbots в твиттере и просмотреть источник на Github meekee-bot.
Вы можете написать мне в Twitter @oiorain.
У меня также есть тамблер, где я иногда публикую полезные советы и рекомендации: script-it-friday.tumblr.com