Create a Slack App that Sends SMS with Twilio and Laravel PHP

September 27, 2019
Written by
Ankit Jain
Contributor
Opinions expressed by Twilio contributors are their own

Create a Slack App that Sends SMS with Twilio and Laravel PHP.png

Laravel is one of the most famous PHP MVC frameworks, largely due to its commitment to modern architecture and an active community. Notably, it provides all the features that are required to create your project, whether for personal or enterprise-level usage.

Slack is an online instant messaging and collaboration system that enables users to centralize notifications. From sales to tech support, social media and more, Slack becomes one searchable place where your team can discuss ideas and take action.

In this tutorial, we’ll be combining Slack, Twilio and Laravel to learn how to send an SMS from Slack. This would be a great feature for situations such as notifying users on the phone regarding an urgent meetup.

After we’re finished, you will have a running Laravel application that allows you to send SMS to tagged users in a Slack channel.

Requirements

  1. PHP development environment with Laravel
  2. Composer globally installed
  3. Twilio account
  4. Slack Account
  5. Ngrok
  6. Passion :D

Set Up a New Laravel Project

If you don’t have Laravel installed in your system, a quick download from the Laravel Official Documentation will get you started. A guide to getting started from Laravel can be found in the Laravel docs. Let’s start with setting up a new Laravel project named send-sms-from-laravel. Remember, you can give your project any name you want.

$ laravel new send-sms-from-laravel 

This will install Laravel v6.0 along with all the dependencies required by it. Composer post-create scripts automatically create the .env file for us and set the APP_KEY environment variable. Add the APP_KEY in the .env file by running this command, if it is not configured automatically.

$ php artisan key:generate

Create a Slack App to Send SMS

Log in to your Slack account and navigate to the create Slack App page.

Create a Slack App

Enter the name for the app, select the workspace and click on Create App button.

Add the Slash Commands Feature

After clicking on the Slack app, we will be redirected to the settings page for the app. Let’s add additional functionalities to our newly created app. We want to send SMS to the user whenever someone tags them with the keyword in which our app listens/responds.

Keep in mind, Slash Commands are initiated from the message box in Slack, but they aren't messages. Upon submit, a Slash Command will transfer a payload of data from Slack to an app, allowing the app to automatically respond in whatever way it has been programmed. Read more here.

On the settings page, click on Slash Commands.

Add features and functionality

After clicking on the Slash Commands card, we will be redirected to the page to define the Commands which enable users to interact with your app from within Slack.

Screen Shot 2019-09-27 at 11.25.15 AM.png

I have chosen the command /ping for my app. Upon execution, it will send an HTTP request to our Laravel project.

NOTE: Please note that the Request URL is required to save your command. Because our Laravel application is only accessible locally, we will need DNS software like ngrok to assign a publicly accessible URL over the Internet. Laravel is pre-loaded with ngrok and can be activated using the artisan serve command. Read more about ngrok here.

Create a local PHP server to run our Laravel application using the following command.

# Development Server
$ php artisan serve

We can expose the webserver running on our local machine to the internet with ngrok using following commands. This will provide the environment we need to define our Request URL.

$ ./ngrok http 8000

ngrok screen

Now, we can enter NGROK URL + /webhook as the requested URL. Be sure to add the short description and check the “Escape channels, users, and links sent to your app” setting so that we can capture user and channel id in the data from Slack. Once those values are updated, click on the Save button to save the configuration.

Slash Commands

We have saved the app which will trigger our Laravel application every time we send a message with the /ping command. Click on the “Basic Information” link from the Sidebar settings to display options similar to the below image.

Basic Information

Install the App to Slack Workspace

Now that we have created our Slack App, let’s install it into our Slack workspace. Click the “Install App to Workspace” button and allow permission to the app. Once we install our Slack App, we will see something similar as shown in the image below.

Add features and functionality

We have created a new Slack app and successfully installed it in our Slack workspace. At this point, our application has no route with the name /webhook so it won’t work. Let’s still test whether or not we are receiving the request from the slack.

Ngrok comes with its own dashboard where we can inspect any request to our application. The ngrok dashboard is running at http://127.0.0.1:4040. Open the dashboard of your Slack workspace and type /ping helloworld in any channel.

Sending the message “helloworld” with /ping will throw this error in slack

Slackbot

Let’s inspect the request from the ngrok dashboard.

ngrok requests

A POST to /api/webhook shows that our Slack app is triggering our Laravel application as expected. We’ll now add the functionality to send an SMS when the /ping command is triggered from Slack.

Create the Webhook in our Laravel Application

In Laravel, routes are defined in our route files, which are located in the routes directory. Open api.php file and add a new route /webhook as we defined in our Slack app.

NOTE: Routes defined in api.php are automatically prepended with the /api prefix and do not need to be added to the route.

Route::post('/webhook', 'IncomingController@index');

Add the above code at the end of the api.php file. Because Slack app makes a POST request, we have created a route /api/webhook which accepts POST requests and passes the data to the associated IncomingController.

Create the Controller in our Laravel Application

In Laravel, controllers are defined in the app/Http/Controllers directory and work with routes to process the incoming request and return the response. Let’s create a listener class using the Artisan CLI:

$ php artisan make:controller IncomingController

A new file named IncomingController.php will be created in the app/Http/Controllers directory. We will create methods under the IncomingController class to add our required functionality. Let’s understand our requirement before writing the code directly.

First, we want to send an SMS to the user tagged in the slash command. To send an SMS, we need two things:

  1. User mobile number.
  2. Twilio Account for sending SMS

Setting up the Twilio SDK

To communicate with Twilio’s API, we need to install the Twilio SDK for PHP in our project. In your terminal, navigate to the project directory and run the following command:

$ composer require twilio/sdk

Because each request to the Twilio API is authenticated, we will need Twilio Credentials in order to make use of Twilio SDK. Open the Twilio Dashboard (create an account if you don’t have one) and Grab Account SID and Auth token as shown in the image below.

Twilio Dashboard

As mentioned before, your application will need a phone number in order to send messages from. If you already purchased a Twilio Number, open the Phone Number section to get your SMS enabled phone number. If not, purchase a new phone number.

Once, we have the phone number, populate the .env file with the credentials as follows:

TWILIO_ACCOUNT_SID="Insert Account SID here"
TWILIO_AUTH_TOKEN="Insert Auth Token here"
TWILIO_SMS_NUMBER="Insert Phone Number in E.164 format"

Finding the User’s Mobile Number

Slack has profiles for every user which includes basic information such as Name, Display name, and Mobile number as shown in the image below.

Edit your profile

In order to expose our mobile number in the request, we have to enter it here with the country code. Whenever we tag a user with our /ping command, it sends the tagged user with their username along with the user_id. We can use the Slack API to fetch the user details.

Data payload from Slack

Slack has some Methods to get additional information using the Slack API. The users.info method specifically gets information about a user as shown in the image below.

users.info

For accessing user details using the above method, we need access to the workspace token. 

Meet the tokens

Create Slack Workspace Token

Open the Legacy Tokens page, scroll down and create a token for the workspace in which we are installing the app. We can also re-issue the token any number of times.

Workspace token

Generate the token and save it in the .env file as follows:

SLACK_WORKSPACE_TOKEN=xoxp-*******************

Implement the Logic in our Controller

Open IncomingController.php in the app/Http/Controllers directory that we created in the previous steps.

We will create a function with the name index() which will get the response from the Slack POST request. Inside this function, we will first capture the user_id from the POST request and validate whether we have tagged any user or not. If we haven’t tagged any user, we will return the response back to the slack. Read more about Responding to Commands.

NOTE: The full code for the controller is at the end of this section.

index()

If we have tagged the user, we will assign the user_id to the $user variable.

Next we will create another function named computeSlackUserInfo() to fetch the user mobile number using Slack API.

computeSlackUserInfo()

Notice that we are making a GET request to the Slack API https://slack.com/api/users.info with the SLACK_WORKSPACE_TOKEN and user_id parameters to fetch the user information and return the valid response back to the index function as shown in the image above. If the Slack user profile is incorrect or we don’t get a valid response, we will return the response back to the Slack channel with the message “Issue with Slack user profile”.

In the case we get a valid response of the user with his complete details, we can send the SMS to the user on their mobile number. We’ll create another function named sendSMS() to send the SMS to the user.

sendSMS()

We are using the Twilio SDK to send SMS using Twilio. If the message is successfully sent, we will return the response back to Slack with a success message, else we will respond with the error message.

Our controller will require Guzzle in order to make HTTP requests to the Slack API. Install this dependency in your application by running the following command in your terminal:

$ composer require guzzlehttp/guzzle

Let’s see our final IncomingController.php file with all the logic.


<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
use Twilio\Rest\Client as TwilioClient;
use Twilio\Exceptions\RestException;

class IncomingController extends Controller
{

    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index(Request $request)
    {
        $input = $request->all();
        // Getting user_id
        preg_match("/<@(\w+)|/", $input['text'], $user);

        $response = [
            "response_type" => "in_channel"
        ];
        if (!count($user) || count($user) == 1) {

            if ($input['text'] == "") {
                $response['text'] = 'Looks good.. :blush:';
            }
            return response()->json($response);
        }

        $data = $this->computeSlackUserInfo($user);

        if (!$data['status'] || !$data['data']['ok']) {
            return response()->json([
                "response_type" => "in_channel",
                "text" => 'Issue with slack user profile :zipper_mouth_face:'
            ]);
        }
        $data = $data['data'];

        $message = $this->sendSMS($data, $input, $user);
        if (!$message['status']) {
            return response()->json([
                "response_type" => "in_channel",
                "text" => 'Error in sending sms :x:'
            ]);
        }
        $message = $message['data'];


        return response()->json([
            "response_type" => "in_channel",
            "text" => 'Message has been successfully sent to the user :heavy_check_mark:'
        ]);
    }

    public function computeSlackUserInfo($user)
    {
        try {
            $client = new Client(); //GuzzleHttp\Client
            $result = $client->request('GET', 'https://slack.com/api/users.info', [
                'query' => [
                    'token' => env('SLACK_WORKSPACE_TOKEN'),
                    'user' => $user[1],
                ]
            ]);

            $data = json_decode($result->getBody()->getContents(), true);
            return ['status' => true, 'data' => $data];
        } catch (GuzzleException $e) {
            // echo $e->getResponse()->getBody()->getContents();
            // echo "\n";
            return ['status' => false];
        }
        return ['status' => false];
    }

    public function sendSMS($data, $input, $user)
    {
        $accountSid = env('TWILIO_ACCOUNT_SID');
        $authToken  = env('TWILIO_AUTH_TOKEN');
        $from  = env('TWILIO_SMS_NUMBER');
        $twilio = new TwilioClient($accountSid, $authToken);
        $body = str_replace('<@'.$user[1].'|', '@', $input['text']);
        $body = str_replace('@'.$data['user']['name'].'>', '@'.$data['user']['name'], $body);
        try {
            $message = $twilio->messages->create($data['user']['profile']['phone'], [
                "body" => $body,
                "from" => $from
            ]);
            return ['status' => true, 'data' => $message];
        } catch (RestException $e) {
            // echo "Error: " . $e->getMessage();
            return ['status' => false];
        }
        return ['status' => false];
    }
}

Awesome! Let’s test our application and see whether it’s sending our messages or not.

Testing

Our application is already running and ngrok is also configured previously. Let’s open the ngrok dashboard and Slack workspace again and type /ping @Ankit helloworld in any channel.

Slack channel message

As we can see that message has been sent successfully. Let’s check our mobile device for the message.

SMS message

Let’s check the logs on the Twilio Dashboard.

Twilio log

Conclusion

In this article, we learned how to send SMS using Twilio SDK, as well as how we can use it to ping users from Slack.

You could also use Laravel Queues to queue your messages and use the Slack Delayed Response feature to scale your application. To learn more about Laravel Queues, check out this article.

The complete code for the tutorial can be found on GitHub repo → send-sms-from-slack.

Feel free to comment and ask me anything. You can reach me at ankitjain28may77@gmail.com and follow me on Twitter @ankitjain28may. You can also read more of my articles on Medium @ankitjain28may.

Thanks for reading!