SMS integration in Slack

Create and integrate your own slash command in Slack for sending SMS messages through Routee's API.

TABLE OF CONTENTS

- Overview

- Approach

- Routee Setup

- Slack Setup

- Examples

OVERVIEW

The command will perform a very simple task: take a phone number and a text as parameters and send an SMS message through Routee's API.

It has the following syntax:

/sms +359899000000 Message text

Before you start you need the following:

  • A plain text editor or your favorite IDE
  • A web hosting where you can upload the simple application we're going to write

APPROACH

The general approach is to implement a simple middleware application that will handle the data sent from Slack command and invoke Routee's API to send a text message. The application needs to be accessible under a public URL address.
Whatever text you enter after the command itself will be sent as an HTTP request to your app's URL. The simple app will then handle the data sent by the Slack command, perform some validation and send a text message using Routee's API. It will also return a proper message to Slack whether the sending of the message has been successful or not.

ROUTEE SETUP

Go to www.slack.com and click the "Create workspace" button at the top right corner of the page. After that choose the "Developer's Platform" option and fill in your details in the form which will appear on the next page. When you hit the "Register" button you will be prompted to verify your email address. Check your email for the verification email from Routee, and click on the "ACTIVATE YOUR ACCOUNT NOW" button in it. When your account has been verified, you will be redirected to your dashboard (https://dev.routee.net/#/management/dashboard).

SLACK SETUP

Register your team to Slack

Go to www.slack.com and click "Create workspace" button at the top right corner of the page. Then enter your email address and click "Next". Now go and check your email for the confirmation code from Slack and enter it on the confirmation page. On the next page, you will see a form where you need to enter your first and last names. After you fill in those fields you need to click on the "Continue to Password" button and enter your password in the page which you will see.The next page will be a form where you need to fill the information about your organization and after this is done and you click "Continue to Company Name" button you will be prompted for your Company Name. Click on "Continue to Team URL" and fill in the address which you will use with your team. Click "Create Team" and you are done! On the next page, you can choose teammates which you can invite or you can skip this part.

Set up Slack app

Go to the Slack's API page (https://api.slack.com/) and click on "Your Apps" button at the top left corner of the page and after that click on on "Create an App" button which will appear in the next page. Enter your app details, choose the team which will use this app and click "Create App".

Set up Slack command

Choose your Slack app and go to "Slash Commands" located under the "App Features" section of the right menu. Hit the "Create New Command" button and fill in the form with the following data: for "Command" enter /sms, for "Request URL" add the address of the already uploaded handler, "Short Description" will be something describing what the command will do, and the "Usage hint" filed will be: +359899000000 Message text. Click on the "Save" button and now your command is ready to be used.

EXAMPLES

### Java

#### Set up your config


private String slackVerificationToken = "your-slack-verification-token";
private String slackCommandInputRegex = "^(\\+[0-9]{12})\\s(.*)$"; // expression used to separate phone number and actual text message

private String routeeAppId = "your-routee-application-id";
private String routeeAppSecret = "your-routee-application-secret";


#### Get authenticated through Routee's API


private String routeeAuthenticate(String routeeAppId, String routeeAppSecret) throws MalformedURLException, IOException {

    Base64.Encoder encoder = Base64.getEncoder();
    String authString = routeeAppId + ":" + routeeAppSecret;
    String token = encoder.encodeToString(authString.getBytes());
    String accessToken = "";

    URL url = new URL("https://auth.routee.net/oauth/token");
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();

    connection.setRequestMethod("POST");
    connection.setRequestProperty("Authorization", "Basic " + token);
    connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

    connection.setDoOutput(true);
    DataOutputStream wr = new DataOutputStream (connection.getOutputStream());
    wr.writeBytes("grant_type=client_credentials");
    wr.close();

    BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
    String inputLine;
    StringBuffer response = new StringBuffer();

    while ((inputLine = in.readLine()) != null) {
        response.append(inputLine);
    }
    in.close();

    try {
        JSONParser parser = new JSONParser();
        JSONObject jsonObject = (JSONObject)parser.parse(response.toString());
        accessToken = (String) jsonObject.get("access_token");
    } catch (Exception e) {
        e.printStackTrace();
    }

    return accessToken;
}

#### Send the SMS message


private boolean sendSmsMessage(String authToken, String message, String sender, String receiver) throws MalformedURLException, IOException {

    URL url = new URL("https://connect.routee.net/sms");
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    String requestBody = "{\"body\":\"" + message + "\", \"to\":\"" + receiver + "\", \"from\":\"" + sender + "\"}";

    connection.setRequestMethod("POST");
    connection.setRequestProperty("Authorization", "Bearer " + authToken);
    connection.setRequestProperty("Content-Type", "application/json");

    if (sender.length() > 11) {
        sender = sender.substring(0,11);
    }

    connection.setDoOutput(true);
    DataOutputStream wr = new DataOutputStream (connection.getOutputStream());
    wr.writeBytes(requestBody);
    wr.close();

    int responseCode = connection.getResponseCode();

    return responseCode == 200;
}


#### Perform HTTP request validation and returning a proper message to Slack's user


private void sendSlackResponse(String message, String channelId, String slackResponseUrl) throws MalformedURLException, IOException {

    URL url = new URL(slackResponseUrl);
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    String requestBody = "{\"channel\":\"" + channelId + "\", \"text\":\"" + message + "\"}";
    String requestBodyLength = Integer.toString(requestBody.length());

    connection.setRequestMethod("POST");
    connection.setRequestProperty("Content-Length", "" + requestBodyLength + "");
    connection.setRequestProperty("Content-Type", "application/json");

    connection.setDoOutput(true);
    DataOutputStream wr = new DataOutputStream (connection.getOutputStream());
    wr.writeBytes(requestBody);
    wr.close();
}


// validate request token
if (slackVerificationToken.equals(token)) { 
    String slackResponseMessage = "";

    Pattern pattern = Pattern.compile(slackCommandInputRegex);
    Matcher matcher = pattern.matcher(commandText);
    
    // validate command syntax
    if (matcher.matches()) { 
        String receiver = matcher.group(1);
        String message = matcher.group(2);
        String authToken = this.routeeAuthenticate(routeeAppId, routeeAppSecret);
        boolean success = this.sendSmsMessage(authToken, message, userName, receiver);

        if (success) {
            slackResponseMessage = "Message successfully send to " + receiver;
        } else {
            slackResponseMessage = "Sms message can't be send!";
        }
    } else {
        slackResponseMessage = "Wrong command format! The correct one is - /sms +359899000000 Message text";
    }

    String channelId = request.getParameter("channel_id");
    String responseUrl = request.getParameter("response_url");

    this.sendSlackResponse(slackResponseMessage, channelId, responseUrl);
}


### Python

#### Set up your config


slackVerificationToken = 'your-slack-verification-token'
slackCommandInputRegex = '^(\+[0-9]{12})\s(.*)$'

routeeAppId = 'your-routee-application-id'
routeeAppSecret = 'your-routee-application-secret'


#### Get authenticated through Routee's API


def routeeAuthenticate(routeeAppId, routeeAppSecret):
    token = base64.b64encode(routeeAppId + ':' + routeeAppSecret)
    headers = {"Content-type": "application/x-www-form-urlencoded", "Authorization": "Basic " + token}
    requestBody = "grant_type=client_credentials"

    r = requests.post("https://auth.routee.net/oauth/token", data=requestBody, headers=headers)

    return r.json()['access_token']


#### Send the SMS message


def sendSmsMessage(authToken, message, sender, receiver):
    headers = {"Content-type": "application/json", "Authorization": "Bearer " + authToken}
    requestBody = {"body": message, "from": sender[0:11], "to": receiver}

    r = requests.post("https://connect.routee.net/sms", json=requestBody, headers=headers)

    return r.status_code == 200


#### Perform HTTP request validation and returning a proper message to Slack's user


def sendSlackResponse(message, channelId, slackResponseUrl):
    headers = {"Content-type": "application/json", "Content-Length": len(message)}
    requestBody = {"channel": channelId, "text": message}

    r = requests.post(slackResponseUrl, json=requestBody, headers=headers)


# validate request token
if (matchObj) :
    authToken = routeeAuthenticate(routeeAppId, routeeAppSecret)
    success = sendSmsMessage(authToken, matchObj.group(2), data.getvalue('user_name'), matchObj.group(1))
    # validate command syntax
    if (success):
        slackResponseMessage = "Message successfully send to " + matchObj.group(1)
    else:
        slackResponseMessage = "Sms message can't be send!"
else:
    slackResponseMessage = "Wrong command format! The correct one is - /sms +359899000000 Message text"

sendSlackResponse(slackResponseMessage, data.getvalue('channel_id'), data.getvalue('response_url'))
### PHP

#### Set up your config


<?php
$slackVerificationToken = 'your-slack-verification-token';
$slackCommandInputRegex = '/^(\+[0-9]{12})\s(.*)$/'; // expression used to separate phone number and actual text message

$routeeAppId = 'your-routee-application-id';
$routeeAppSecret = 'your-routee-application-secret';


#### Get authenticated through Routee's API


<?php
function routeeAuthenticate($routeeAppId, $routeeAppSecret) {
    $curl = curl_init();
    $token = base64_encode($routeeAppId . ':' . $routeeAppSecret);

    curl_setopt_array($curl, [
        CURLOPT_URL => "https://auth.routee.net/oauth/token",
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_ENCODING => "",
        CURLOPT_MAXREDIRS => 10,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        CURLOPT_CUSTOMREQUEST => "POST",
        CURLOPT_POSTFIELDS => "grant_type=client_credentials",
        CURLOPT_HTTPHEADER => [
          "authorization: Basic $token",
          "content-type: application/x-www-form-urlencoded"
        ],
    ]);

    $response = json_decode(curl_exec($curl), true);
    curl_close($curl);

    return $response['access_token'];
}


#### Send the SMS message


<?php
function sendSmsMessage($authToken, $message, $sender, $receiver) {
    $requestBody = [
        'body' => $message,
        'to' => $receiver,
        'from' => substr($sender, 0, 11)
    ];

    $json_string = json_encode($requestBody);
    $curl = curl_init();

    curl_setopt_array($curl, [
        CURLOPT_URL => 'https://connect.routee.net/sms',
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_ENCODING => "",
        CURLOPT_MAXREDIRS => 10,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        CURLOPT_CUSTOMREQUEST => "POST",
        CURLOPT_POSTFIELDS => $json_string,
        CURLOPT_HTTPHEADER => [
            "authorization: Bearer $authToken",
            "content-type: application/json"
        ]
    ]);

    $response = json_decode(curl_exec($curl), true);
    $responseInfo = curl_getinfo($curl);
    curl_close($curl);

    return isset($responseInfo['http_code']) && $responseInfo['http_code'] === 200;
}


#### Perform HTTP request validation and returning a proper message to Slack's user


<?php
function sendSlackResponse($message, $channelId, $slackResponseUrl) {
    $requestBody = [
        "channel" => $channelId,
        "text" => $message,
    ];

    $json_string = json_encode($requestBody);
    $curl = curl_init();

    curl_setopt_array($curl, [
        CURLOPT_URL => $slackResponseUrl,
        CURLOPT_CUSTOMREQUEST => "POST",
        CURLOPT_POSTFIELDS => $json_string,
        CURLOPT_CRLF => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            "Content-Type: application/json",
            "Content-Length: " . strlen($json_string)
        ]
    ]);

    $result = curl_exec($curl);
    curl_close($curl);

    return $result;
}

<?php
// validate request token
if ($data['token'] === $slackVerificationToken) {
    // validate command syntax
    if ((preg_match($slackCommandInputRegex, $data['text'], $matches))) {
        $authToken = routeeAuthenticate($routeeAppId, $routeeAppSecret);
        $success = sendSmsMessage($authToken, $matches[2], $data['user_name'], $matches[1]);

        if ($success) {
            $slackResponseMessage = "Message successfully send to $matches[2]";
        } else {
            $slackResponseMessage = "Sms message can't be send!";
        }
    } else {
        $slackResponseMessage = "Wrong command format! The correct one is - /sms +359899000000 Message text";
    }

    sendSlackResponse($slackResponseMessage, $data['channel_id'], $data['response_url']);
}

### Node.js

#### Set up your config

const slackVerificationToken = 'your-slack-verification-token';
const slackCommandInputRegex = /^(\+[0-9]{12})\s(.*)$/;

const routeeAppId = 'your-routee-application-id';
const routeeAppSecret = 'your-routee-application-secret';

#### Get authenticated through Routee's API

function routeeAuthenticate (appId, appSecret) {
    var basicHash = Buffer(appId + ':' + appSecret).toString('base64'),
        options = {
            method: 'post',
            body: "grant_type=client_credentials",
            url: "https://auth.routee.net/oauth/token",
            headers: {
              "authorization": "Basic " + basicHash,
              "content-type": "application/x-www-form-urlencoded"
            }
        },
        promise  = new Promise(function (resolve, reject) {
            request(options, function (err, httpResponse, body) {
                var authData = JSON.parse(body);
                
                resolve(authData.access_token);
            });
        });

    return promise;
};

#### Send the SMS message

function sendSmsMessage (authToken, message, sender, receiver) {
    var requestBody = {
            "body": message,
            "to": receiver,
            "from": sender.substr(0, 11)
        },
        options = {
            method: 'post',
            body: JSON.stringify(requestBody),
            url: "https://connect.routee.net/sms",
            headers: {
              "authorization": "Bearer " + authToken,
              "content-type": "application/json"
            }
        },
        promise  = new Promise(function (resolve, reject) {
            request(options, function (err, httpResponse, body) {
                resolve(httpResponse.statusCode === 200);
            });
        });

    return promise;
};


#### Perform HTTP request validation and returning a proper message to Slack's user

function sendSlackResponse (message, channelId, slackResponseUrl) {
    var requestBody = {
            "channel": channelId,
            "text": message,
        },
        jsonString = JSON.stringify(requestBody),
        options = {
            method: 'post',
            body: jsonString,
            url: slackResponseUrl,
            headers: {
              "content-length": jsonString.length,
              "content-type": "application/json"
            }
        };

    request(options);
};


// validate request token
if (slackData.token == slackVerificationToken) {
    var matches = slackData.text.match(slackCommandInputRegex),
        slackMessage = '';
    // validate command syntax
    if (matches !== null) {
        routeeAuthenticate(routeeAppId, routeeAppSecret).then(function (authData) {
            sendSmsMessage(authData, matches[2], slackData.user_name, matches[1]).then(function (success) {
                if (success) {
                    slackMessage = "Message successfully send to $matches[2]";
                } else {
                    slackMessage = "Sms message can't be send!";
                }
            });
        });
    } else {
        slackMessage = "Wrong command format! The correct one is - /sms +359899000000 Message text";
    }

    sendSlackResponse(slackMessage, slackData.channel_id, slackData.response_url);
}