Slack slash commands – adding new slash commands with Wunderground API

Post Written by
Marko Marjanović
Last modified on July 1st, 2020 at 11:13 am

Slack slash commands

Before we configure and build our slash command, it's important to cover some basic concepts and requirements first. Slash command integration is a trigger to an external micro service that:

  • is listening to certain type of request
  • generates response into a pre-defined Slack channel

Also, this type of integration requires sending triggers over HTTPS. So, the two things we need to get started are:

  • Hosting place for our integration
  • Domain covered with an SSL certificate (for HTTPS traffic)

For this, we'll use a VPS droplet (Centos 7) with Nginx configured to listen for requests on the https://slack.example.com domain. We'll also need to generate an API key for our external service - in this case, http://api.wunderground.com/weather/api.

Slack Configuration

Configuring the slash command through Slack is easy. Let’s create one by navigating to Browse Apps > Custom Integrations > Slash Command. In this step, we're looking at three important settings:

  • Command - the name of the command prefixed with ‘/’ for triggering request
  • URL - an endpoint that our microservice will use
  • Method - request method, in this case, we are going to use POST

Server Configuration

Nginx

The next step is configuring Nginx. We will:

  • configure Nginx to listen on port 443, and handle request sent to a specific endpoint ( /weather )
  • and then forward the request to a Node instance, listening on port 3000 on the same machine:
server {
    listen  443;

    server_name slack.example.com;
    root  /var/www/slack;
    index  index.js;

    ssl on;
    ssl_certificate_key /path/to/key/key.pem;
    ssl_certificate /path/to/cert/cert.pem;

    access_log /logs/slack.example.access.log;
    error_log /logs/slack.example.error.log;

    location / {
        return 403;
    }

    location /weather {
        proxy_pass http://127.0.0.1:3000;
    }
}

Doing it this way gives us more control over handling many endpoints. Also, if we later decide to add more slash commands, we can do so by changing the configuration in a single vhost file. Also, it’s much easier to use Nginx for terminating SSL connection over Node.

Node

The common way of configuring Node application is by using package.json file.

{
 "name": "weather-slash-command",
 "version": "1.0.0",
 "description": "Weather",
 "main": "index.js",
 "keywords": [
   "slack",
   "forecast"
 ],
 "author": "m4r35",
 "dependencies": {
   "body-parser": "^1.15.2",
   "express": "^4.14.0",
   "request": "^2.79.0"
 }
}

The most important part is specifying the dependencies that the application will use. After that, we can run npm install command from the same directory, and this will install all dependencies.

Node Application

Configuring Node modules

In every Node application, the first step is to define dependencies. To simplify things, we are going to use the "Express" Node web framework to instantiate our application, so that:

  • request module as our HTTP client
  • url as part of core module for parsing url’s
  • body_perser for parsing json payload and url encoding

We will be sending and receiving requests using both formats.

var express = require('express');
var app = express();
var url = require('url');
var request = require('request');
var body_parser = require(‘body_parser’);
var config = require('./config');

You might notice that we are including config file as well. This would be our custom config file for storing Wunderground API key. For security reasons, this file should be protected and stored outside the document root directory. But, for convenience, we are going to place it along with other files in the same directory.

var config = {};
config.wu = 123456789;

In short, we are exporting simple object containing the wu property with the value of 123456789 (API key) which we’ll be using later.

var apikey =  config.wu;

Our "Express" application instance app, provides a method for injecting various middlewares into our application called use(). We need to decompress our request body payload when it comes in (if it’s compressed) and then parse the raw text as URL encoded data. After processing the data, we also need to parse data into JSON format in order for response to be properly sent back to Slack. For parsing, as mentioned above, we are going to use body_parser module which contains middlewares for this type of job.

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

One cool thing with Node is the ability for it to run its own server by calling app.listen() function, and passing it a predefined port number.

app.set('port', (process.env.PORT || 3000));

app.listen(app.get('port'), function() {  
	console.log('Node app is running on port', app.get('port'));
});

Communicating with the API

Now to the fun part. Like with use(), set(), listen(), Express framework provides wrapper methods for handling requests. These methods are asynchronous and accept two parameters; uri and callback function with req and res object. Given that we set our slash command to use POST request, we can use app.post () method to handle it.

app.post('/weather', function(req, res){
	var query = req.body.text;
});

The app listens to the specific endpoint (/weather) and fires up a callback function when request comes in. After handling the requests, the app is exposing the body object containing the parsed payload. We can access the data using req.body.text. Voila! Anything after the initial slash command will be accessible through this parameter. For instance, if we typed /weather rs/belgrade in Slack channel, rs/belgrade part will be sent with the request. That information is used for querying Wunderground API which would be our next step.

Hitting Wunderground API

So far we’ve made great progress by:

  • configuring our web server
  • configuring the Node application
  • managing to receive a request sent from Slack

At this point, the idea is to:

  • parse the data from the request
  • build WU API URL
  • send request to it
  • handle the response
  • build JSON object (Slack formatted message) using gathered information and
  • send it back to Slack channel
var url = 'http://api.wunderground.com/api/' + apikey + '/forecast/q/' + place + '.json';

request(parsed_url, function (error, response, body) {
	var body = JSON.parse(body);
	if(body.hasOwnProperty('current_observation') === false){
		res.send('No cities match your search query');
		return;
	}
	if (!error && response.statusCode == 200) {
		var data = JSON.parse(body);
		var temperature = data.current_observation.temp_c;
		var weatherCondition = data.current_observation.weather
		var icon_url = data.current_observation.icon_url
		var location = data.current_observation.display_location.full
		var body = {
			response_type: "in_channel",
				"attachments": [{
				"author_name": "Orange Cloud",
				"color":"#00BFFF",
				"title": "" + data.current_observation.observation_time,
				"text": "" + location + ", *" + temperature + "°C " 
weatherCondition + "*, _feels like " 
 				  +  data.current_observation.feelslike_c +"°C_\n"
				  +  "Wind: " + data.current_observation.wind_string,
				"image_url": icon_url + "\n",
				"footer": "<https://github.com/m4r35>",
				"mrkdwn_in": [
					"text"
					]
				}]
		};
		res.send(body);
		return;
	}
	res.send('Error ocurred, please try again later!');
	return;
});

Note: “ephemeral” response_type will send message to user whereas “in_channel” to all members. For handling requests and responses from the external API, we are going to use request() method from request module. This method accepts two parameters

  • the URL
  • callback that has its own parameters error, response and body

If there are no errors, we can build our Slack message by constructing the JSON response and sending it back to the Slack channel. Check WU API documentation for various response properties you can use.

Slack Message Formatting

Slack API provides a neat way of formatting messages usist JSON. Advanced formatting can is done by using message attachments

Summary

Building slash command integrations for Slack is fun! Knowing how to handle requests and constructing message responses is all you need to create some cool stuff for your team. Hope you find this article useful.

Contact Us

Fill out the enquiry form and we'll get back to you as soon as possible.