Поэтому мы создаем этот сервис, который позволит пользователям создавать отчеты из своих отчетов Google Analytics. Основная функциональность заключается в доступе к данным путем авторизации в Google, отправке серии или запросов в Analytics API, создании графиков, их объединении в один файл и представлении пользователю.
Помимо отображения файла на веб-странице, мы рассматриваем различные другие способы доставки отчета. Отправка отчета по электронной почте — это, конечно, один из способов. Одной из экспериментальных функций, с которой мы экспериментируем, может быть интеграция приложения со Slack — системой обмена мгновенными сообщениями и совместной работы, которую, кажется, используют в наши дни на каждом рабочем месте или в школьной группе. Это определенно один из основных каналов коммуникации Craft Academy.
Итак, идея состоит в том, чтобы добавить функциональность, которая отправляет уведомление в экземпляр Slack пользователя с прикрепленным отчетом, созданным визуализатором.
Как пользователь
Чтобы распространять отчет GA Visualizer среди широкой публики
Я хочу, чтобы отчет был опубликован в моем канале Slack
После некоторых первоначальных исследований я понял, что есть два основных способа отправки уведомлений в Slack.
- с помощью вебхука
- с помощью пользователя-бота
Ради своего Спайка я хотел изучить оба этих метода, прежде чем решить, какой из них использовать в своей реализации.
Пользователь бота
Я начал искать метод API и нашел обёртку ruby, которая выглядела многообещающе — Slack Ruby Client.
Я добавил зависимость к своему Gemfile
и связал.
source 'https://rubygems.org' ruby '2.3.1' gem 'rails', '~> 5.0.0', '>= 5.0.0.1' # other gems... gem 'slack-ruby-client'
Следующим шагом было создание интерфейса для запроса учетных данных пользователя и присоединения некоторого действия javascript для отправки данных в контроллер приложений.
[снимок экрана]
Разметка HAML для этого представления выглядит следующим образом:
.large-4.columns{style: 'margin-top: 50px;'} %h4 Share report %p Looking good. Why not sharing this report with people in your organization? #api %h5 Slack - using a Bot-user %input#api-input{type: 'text', placeholder: 'Slack API Token'} = button_tag 'Check API', id: 'slack-api-button', class: 'button', disabled: true .api_response .api_form
И JavaScript, связанный с этим методом?
$('#slack-api-button').on('click', function () { var input = $('#api-input'); $.ajax({ url: '/check_api', type: 'post', dataType: 'json', data: {api_token: input.val()} }) .done(function (data) { $('#api').hide(); $('.api_response').html('<img src="' + data.icon + '">'); $('.api_response').append('<p><strong>' + data.team_name + '</strong></p>'); $(function () { $('.api_form') .append('<form id="notify-api-form" data-remote="true"></form>'); $('#notify-api-form') .attr('action', '/notify_with_api').attr('method', 'post') .append('<input type="hidden" name="image_path" value="' + $('#report-img').attr('src') + '" />') .append(appendSelect(data.channels)) .append($('<input type="submit" value="Post notification" name="submit" class="button" />')); }); }) .fail(function (data) { $('.api_response').html('<strong>' + data.responseJSON.message + '</strong>'); }); })
Функция appendSelect
, вызываемая в приведенном выше коде, выглядит так:
function appendSelect(channels) { var select = $("<select name='channel'></select>"); channels.forEach(function (value, i) { select.append($("<option></option>") .attr("value", value) .text('#' + value)); }); return select; }
Действия контроллера:
class SubscriptionsController < ApplicationController skip_before_action :verify_authenticity_token, only: [:notify_with_api] def check_api begin session[:api_token] = params[:api_token] client = Slack::Web::Client.new(token: params[:api_token]) team = client.team_info channels = client.channels_list.channels render json: {team_name: team[:team][:name], icon: team[:team][:icon][:image_88], channels: channels.map { |c| c[:name] }} rescue => ex render json: {message: 'An unknown error occurred'}, status: 400 end end def notify_with_api SlackNotifier.api_notify(session[:api_token], params[:channel], params[:image_path], 'Visualizer report') @message = {message: "Notification sent to ##{params[:channel]}"} respond_to do |format| format.js end end end
Вы заметили, что я вызываю SlackNotifier
, чтобы фактически выполнить вызов API. Давайте посмотрим на этот модуль:
module SlackNotifier extend ActiveSupport::Concern def self.api_notify(token, channel, file, message) client = Slack::Web::Client.new(token: token) client.files_upload( channels: channel, as_user: true, file: Faraday::UploadIO.new("public/#{file}", 'image/png'), title: 'Report' ) end end
Я также добавил шаблон js, который будет отображаться после отправки уведомления.
// app/views/subscriptions/notify_with_api.js.erb $(".api_form").html("<%=j render html: @message[:message] %>");
Итак, с точки зрения UX это выглядит так.
Пользователь вводит свой токен API для бота и нажимает кнопку «Проверить API». В этот момент вызывается действие check_api
, и происходит следующее:
- Токен хранится в файле cookie сеанса (он понадобится нам для дальнейших действий)
- клиент Slack открывается с использованием токена API
- Получена информация о команде
- Получена информация о доступных каналах
- Объект json с именем команды, значком и доступными каналами создается и отправляется обратно в представление.
И у нас есть сообщение в нашем канале Slack.
Веб-перехватчики
Второй подход заключается в настройке веб-перехватчика с помощью панели инструментов Slack (см. инструкции по ссылке выше) и реализации способа отправки сообщения с отчетом в виде вложения.
Для этого метода я буду использовать гем под названием slack-notifier. Как обычно, я удостоверяюсь, что он добавлен в мой Gemfile
и включен приложением.
source 'https://rubygems.org' ruby '2.3.1' # other gemsgem 'slack-notifier', require: 'slack-notifier'
Я изменяю свой интерфейс с полем ввода, заключенным в div с идентификатором webhook
:
.large-4.columns{style: 'margin-top: 50px;'} %h4 Share report %p Looking good. Why not sharing this report with people in your organization? #webhook %h5 Slack - using a webhook %small Experimental %input#webhook-input{type: 'text', placeholder: 'Slack webhook URL'} = button_tag 'Post with Webhook', id: 'slack-webhook-button', class: 'button', disabled: true #api %h5 Slack - using a Bot-user %input#api-input{type: 'text', placeholder: 'Slack API Token'} = button_tag 'Check API', id: 'slack-api-button', class: 'button', disabled: true .api_response .api_form
И добавьте некоторые действия JavaScript для обработки отправки:
$('#slack-webhook-button').on('click', function () { var input = $('#webhook-input'); var message = '<p><strong>Not a valid Slack Webhook</strong></p>' if (input.val().length && parseUrl(input.val()).authority !== "hooks.slack.com") { if (input.parent().has('p').text().length == 0) { input.parent().append(message); } } else { input.parent().find('p').remove(); $.ajax({ url: '/notify_with_webhook', type: 'post', dataType: 'json', data: {hook: input.val(), image_path: $('#report-img').attr('src')} }) .done(function (data) { $('#webhook').hide(); $('#api').hide(); $('.api_response').html('<strong>' + data.message + '</strong>'); }) .fail(function (data) { // TODO: There is no error handler in the controller atm. $('.api_response').append('<strong>' + data.responseJSON.message + '</strong>'); }); } });
Что я хочу сделать здесь, так это добавить простую проверку того, что веб-перехватчик указывает на Slack, прежде чем я отправлю это действие контроллера (отсюда и проверка parseUrl(input.val()).authority !=="hooks.slack.com"
), чтобы у нас не было проблем.
Функция parseUrl()
— это пользовательская функция, которая выглядит так:
function parseUrl(url) { var pattern = new RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?"); var matches = url.match(pattern); return { scheme: matches[2], authority: matches[4], path: matches[5], query: matches[7], fragment: matches[9] }; }
Новый метод в SubscriptionsController
обрабатывает запрос:
class SubscriptionsController < ApplicationController # omitted code def notify_with_webhook image_path = "#{request.protocol}#{request.host_with_port}#{params[:image_path]}" SlackNotifier.notify_hook(params[:hook], image_path, 'Visualizer report ') render json: {message: 'Notification sent'} end end
А добавление к SlackNotifier
` делает запрос к URL вебхука:
module SlackNotifier extend ActiveSupport::Concern def self.notify_hook(hook, image_path, message) client = Slack::Notifier.new hook client.ping 'Google Analytics Visualizer report', attachments: [{ image_url: image_path, initial_comment: message }] end # omitted code end
И результат в Slack — это новое сообщение с отчетом в виде вложения.
Это поток, который мы сейчас используем в прототипе VISUALIZER. Есть много возможностей для рефакторинга, и мы отойдем от выполнения ajax-вызовов с использованием jQuery и перейдем к более рельсовому способу. Но это доказательство концепции уже есть, и у нас есть два способа интеграции Slack в наше приложение.
Хороший материал.