Delivering effective analytics using Slack and Ruby on Rails

Nowadays, Slack is becoming a central communication bus for companies spread all around the world: a place where every important piece of information gets posted by teammates and by automatic services that build up the companies’ infrastructure. This doesn’t apply to tech companies only. Even our customers in the retail field are using Slack to improve their internal productivity. So, in order to better integrate with their workflow, we’ve delivered a new amazing feature: they can now get shortcuts of Measurence’s daily and weekly reports produced by our analytics platform.

A daily notification from measurence

How can you build and send these notifications? How can you go deeper and create an interactive application on Slack? This post will show you how we’ve implemented a Slack notification system that gave a life and a personality to our analytics platform. At the end you will be able to build up your own simple notification system.

Integrating with Slack

Slack offers a very thorough API which is able to cover many different use cases. There are different modes in which an external service can integrate with Slack, these are called integrations by the API documentation.

Slack integrations

We’ll look in this post at a custom integration, meaning one that is specific to your team and only yours. It’s the perfect way to test a new bot or to experiment while building a Slack integration. Then, when you are ready to open it up to external users it is really easy to move to the next level your custom integration and make it publicly available. How to do so is beyond the scope of this post, but there’s plenty of documentation available online.

If we only want to send notifications to a Slack channel, we’ll simply need to create new incoming webhook, which is also the easiest to use.

Anatomy of a notification

To create the custom integration you’ll need to go to the https://YOURTEAM.slack.com/apps/build/custom-integration page, and click on the “Incoming Webhook” link. You’ll be required to choose a channel to be the default destination of the messages (you can even create a new channel right there.) Notice that you’ll be able to send messages also to other channels by specifying them in the request.

In the following page you’ll get the URL that should be used to send the messages to Slack, you’ll also be able to customize the settings (e.g. choose a nickname and the icon that will appear as the message sender.)

Slack webhook settings

A simple POST request to the Webhook URL with your text in a JSON payload is enough to send a message on Slack!

The simplest message you can send is a JSON object with a single element text which contains the message to publish. Without any additional parameters, the message will be published on the default channel you selected when creating the incoming web hook. For instance, using cURL, you can post a message to the default hook channel with this command:

curl -X POST \
     -H 'Content-type: application/json' \
     --data '{"text":"Hello Slack"}' \
     https://hooks.slack.com/services/T.../B.../...

You can specify a Slack channel by name in the message by adding "channel": "#channel-name" or send a direct message to a user by writing "channel": "@theuser":

curl -X POST \
     -H 'Content-type: application/json' \
     --data '{"channel": "#my-channel", "text":"Hello Slack"}' \
     https://hooks.slack.com/services/T.../B.../...

You’ll probably want to generate messages which are at least a bit more complex. For instance in Measurence we try to convey a lot of informations in a compact way: point int time metrics, trends, timelines. Putting all of this in a plain text message would be ineffective, if not plainly wrong. Luckily Slack offers a good range of formatting capabilities that you can use for specific reasons:

  • Embedded links are ideal to enable the reader to jump from the message to the application that has generated it. You can use it to expand the ability of the message to engage the readers and bring them to your application. In our case every periodic report contains the link to the same report on our web dashboard, where the user can get additional informations and explore the data.
  • Markdown formatting is useful to make some informations stand out in the message. You can format the text, add lists and more. We use it to make the metrics numbers bold to point the attention of the reader to them.
  • Emoji can be added to messages. Thanks to the diffusion of cell phone messaging apps, many people are getting used to this little graphics, and you can use them to enrich the message and convey specific informations in a compact way. We use them as inline graphics to show the trends in the metrics: a smiley means that the number it refers to has been increasing a lot lately.
  • attachments can be used to add fields and data in a table-like format and to include an image in the message. Use them if you need to add data that is semi-structured, or to lay the data in a tabular way. We are using them to organize the metrics display and to include a line graph of the main data.

Publishing from a ruby application

Our web application is built on Ruby on Rails and even if interacting with a webhook would be very easy to build from scratch we decided to use an helper library that incapsulates all the details. For this we have selected the slack-poster gem which we have added to our Gemfile:

gem 'slack-poster'

You’ll need to make the webhook URL available to the overall application. Following the 12 factor model we suggest you use an environment variable to hold this:

export SLACK_HOOK='https://hooks.slack.com/services/...'

To send a message is just a matter of creating a Slack::Poster object (which requires passing the hook URL), creating a message as a Hash object and then calling the send_message:

poster = Slack::Poster.new(ENV['SLACK_HOOK'])
message = { text: "Hello slack!" }
poster.send_message(message)

This is everything you’ll need to get started with sending messages to Slack, but you’ll probably want to do more with them. On top of this simple sender methods we have built a flexible framework that allows our application to deliver a range of reports and notifications to the user.

For instance to send a weekly report about the visits we read the same data structure that are used for our web client (a @weekly_report) and pass it to an object that builds the Slack message using a composable set of methods:

require 'googlecharts'

class Slack::Report::Weekly < Slack::Report::Base

  # prepares a message for the weekly visits
  def visits
    return no_data if @weekly_report.count == 0
    attachment = {
      mrkdwn_in: ["text",
                  "title",
                  "pretext",
                  "fields"],
      text: "Visits",
      image_url: gchart_url([ @weekly_report.weekly_visits_absolute],
                              nil,
                              @weekly_report.week_names_for_period)
    }
    value = description(...)
    {
      text: %{on the week
              #{week_of(@weekly_report.year, @weekly_report.year_week)}
              there have been #{value} *visits*
              for #{@weekly_report.company.name} @
              #{store_name_and_address(@weekly_report.store_reports.first.store)}
              },
      attachments: [ attachment ]
    }
  end
end

We used some tricks to build an engaging message, within the limitations imposed to us by the Slack message formatting rules. For instance, as part of the description we wanted to convey some graphic indication of the trend that a metric has had, that is how it has variated from week to week. This is an important insight for our clients as it can give them timely feedbacks on how their store is behaving (for instance in reaction to a new marketing campaign or as a consequence of some special sales or events). Slack APIs don’t provide any functionality to embed graphic files inline within messages, there’s only the chance to include an attachment. However such a kind of solution seemed overkill for our use case and we already needed to use the attachment to add a line graph. We found a very good compromise by using the smiley emoticons to represent the trend of the data.

Smileys

We’ve chosen five emoticons to represent the range of percentage variations from very bad to very good and mapped the values of the variation to one of them:

def smiley(variation)
  if variation < -15
    ":rage:"
  elsif variation >= -15 && variation <= -2
    ":disappointed:"
  elsif variation > -2 && variation < 2
    ":neutral_face:"
  elsif variation >= 2 && variation <= 15
    ":slightly_smiling_face:"
  else
    ":smile:"
  end
end

Including the resulting emoticon is very easy and the reaction has been very positive from our users. They even said they prefer this way of representing it rather than the usual up/down arrows!

As you can see even if Slack messages have their limitations, with some work, it’s not too hard to create very rich messages from those few basic building blocks.

There are more complex integrations which are possible with Slack, namely the capability to build interactive services that respond to commands given through the chat interface.

Subscribe now to get updates on our IoT technology!