Получение Уведомлений От Внешних Сервисов, Или Зачем Сделали Hooksler

В последнее время сервис для командного общения стал очень популярен.

Слабый .

«Из коробки» имеет немалое количество интеграций.

с различными услугами + довольно удобное внешнее API. Но при всем этом на бесплатных аккаунтах действует ограничение в 5 интеграций.

Прикрепили github, newrelic + пару плат с trello и всё, они закончились.

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

Но программист не был бы программистом, если бы не решил эту задачу.

Решение простое, как молоток.

Мы сами принимаем хуки от сервисов, обрабатываем их и отправляем в Slack в нужном нам виде.

Помимо интеграций в список вошли: гамак , который написан на PHP и имеет некоторые набор плагинов , но мне это решение не особо понравилось.

Хоть и есть готовые интеграции, но нужных, увы, нет, а так как я знаком с PHP на уровне чтения кода и «исправления чего-то из справочника», писать свою у меня не возникло желания.

Вот почему я решил написать свой собственный сервис.

Я решил построить его полностью по модульному принципу: ядро и отдельные модули «ввода» и «выхода».

В качестве языка я использовал Ruby; его динамичный характер очень помог в реализации моих планов.

Итак, знакомьтесь



Хукслер!

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

Для настройки используйте собственный DSL:

  
  
  
  
  
  
   

require 'hooksler/slack' require 'hooksler/newrelic' require 'hooksler/trello' require 'dotenv' Dotenv.load Hooksler::Router.config do secret_code 'very_secret_code' host_name ' http://example.com ' endpoints do input 'simple', type: :simple input 'newrelic', type: :newrelic input 'trello', type: :trello, create: false, public_key: ENV['TRELLO_KEY'], member_token: ENV['TRELLO_TOKEN'], board_id: ENV['TRELLO_ID1'] output 'black_hole', type: :dummy output 'slack_out', type: :slack, url: ENV['SLACK_WEBHOOK_URL'], channel: '#test' end route 'simple' => 'slack_out' route 'trello' => ['black_hole', 'slack_out'] route 'newrelic' => ['black_hole', 'slack_out'] end

Вначале объявляются точки ввода/вывода, каждая имеет свое имя и тип, а также может содержать дополнительные параметры для инициализации.

Ниже приведены маршруты.

Его можно указывать в разных формах: один к одному, один ко многим и наоборот. Вы также можете прикрепить к каждому маршруту фильтры, которые могут как модифицировать сообщение, так и фильтровать его.

Таким образом мы получаем достаточно гибкое ядро для маршрутизации сообщений из точки А в точку Б.

Сообщения внутри передаются во внутреннем представлении, и известно от какого сервиса (его типа) оно было получено + исходное сообщение.

При получении заполняются типовые поля: пользователь, текст, заголовок, ссылка, уровень.

В будущем их можно будет использовать для генерации уведомлений.

На данный момент ядро полностью реализовано, протестировано и покрыто тестами.

Также реализовано несколько интеграций: trello, newrelic, slack. Очень легко написать свою собственную интеграцию.



Немного практики



Получение сообщений

Например, мы создадим модуль, который позволит вам разместить тело POST-запроса в поле сообщения.



class DummyInput extend Hooksler::Channel::Input register :dummy def initialize(params) @params = params end def load(request) build_message({}) do |msg| msg.message = request.body.read end end end

Давайте объявим класс и расширим его соответствующим модулем.

Тогда мы зарегистрируем его имя.

Все, после этого мы готовы принимать и обрабатывать входящие данные.

Обработка запроса осуществляется в методе нагрузка , который принимает только один параметр — объект класса Стойка::Запрос .

Какая-то сложная обработка нам не нужна, поэтому сразу создаем сообщение и заполняем поле.

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

Для отправки можно создать сразу несколько сообщений, т.е.

метод нагрузка вернет массив.

В дальнейшем каждый объект обрабатывается отдельно.



Отправка сообщений

Столь же просто создать модуль отправки, который позволит нам видеть полученные сообщения в консоли:

class DummyOutput extend Hooksler::Channel::Output register :dummy def initialize(params) @params = params end def dump(message) puts "-- #{message.title} : #{message.level} --" puts message.user puts message.message puts message.url end end

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

Отправка, в нашем случае вывод на консоль, осуществляется в методе свалка .

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

Теперь соберем все это и опишем маршруты:

Hooksler::Router.config do secret_code 'very_secret_code' host_name ' http://example.com ' endpoints do input 'in', type: :dummy output 'out', type: :dummy end route 'in' => 'out' end

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

Запустим и все готово.

Конечные пути вы можете просмотреть, обратившись http://example.com/_endpoints_ , ответ будет в формате JSON. Более подробный пример можно найти в ДЕМО-приложении: github.com/hooksler/hooksler-demo Таким образом, без особых усилий вы можете настроить пересылку сообщений одновременно в разные точки: получать изменения из Trello, пересылать их в Slack или отправлять особо важные (например, содержащие ключевые слова или теги) push-уведомлением на телефон.

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



Более практичный пример

На днях передо мной встала задача автоматизировать процесс приглашения пользователей в Slack. Добавлять всех вручную долго и нудно, а открытую регистрацию из коробки не создать.

В интернете есть готовая форма на nodejs. А потому что у меня уже есть рабочий хукслер и я решил на нем сделать.

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

Далее создаем почтовый ящик, настраиваем Webhook и пишем наш получатель:

require 'hashie' module Hooksler module Mandrill class Input extend Hooksler::Channel::Input register :mandrill def initialize(params) @params = Hashie::Mash.new(params) end def load(request) return unless request.content_type == 'application/x-www-form-urlencoded' action, payload = request.POST.first return unless action == 'mandrill_events' payload = MultiJson.load(payload) payload.map do |event| build_message(event) do |msg| begin method_name = "for_#{event['event']}" self.send method_name, msg, event if respond_to? method_name rescue end end end end def for_inbound(msg, event) msg.message = event['msg']['text'] || event['msg']['html'] msg.title = event['msg']['subject'] msg.user = event['msg']['headers']['From'] end end end end

Принимаем события, оборачиваем их в Message и идем дальше.

Теперь нам нужен код, который будет приглашать пользователей:

class SlackInviteOutbound extend Hooksler::Channel::Output register :slack_invite def initialize(params) @params = params end def dump(message) return unless message.source == :mandrill email = message.raw['msg']['from_email'] url = " https://#{@params[:team]}.

slack.com/api/users.admin.invite " HTTParty.post url, body: { email: email, token: @params[:token], set_active: true } end end

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

В качестве последнего штриха настройте маршрутизацию:

endpoints do input 'slack_invite', type: :mandrill output 'slack_invite', type: :slack_invite, team: 'myteam', token: 'mysupersecrettoken' end route 'slack_invite' => 'slack_invite'

Запускаем и наслаждаемся процессом.



Окончательно

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

Единственное, не все дела обрабатываются в Trello; у них слишком много разных типов уведомлений.

Также мы сделали свои модули форматирования для Slack, кому интересно может посмотреть пример здесь .

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

Я надеюсь, что это решение будет полезно кому-то еще.

Критика и предложения приветствуются, об ошибках в тексте пишите мне.

Сам Hooksler и адаптеры доступны на Github: github.com/hooksler Теги: #hooksler #ruby #Slack #Trello #agile #notifications #ruby

Вместе с данным постом часто просматривают: