Slack Custom Commands and Laravel to Make an American to British Translater

Posted: 2016-01-17 13:36:19

in use

Laravel or anything you are comfortable with making a Route and Response will work fine really.

This "messaging as a platform" is a new world for me, challenging my way of thinking in many ways. Some things just do not need a authentication of their own, a UI, etc they just need a place to get info from, or do something with some info etc.

In this case I made a really simple "Custom Slash Command" to translate British Slang to American and American to British (so I can sound cooler!)

Later I will look into bots etc but for now here is a look at a custom slash commands.

Getting API Endpoint Setup

I still do not have a great local workflow for this. There are articles about this online with secure tunnels etc. My next step is to use my wildcard cert or using https://letsencrypt.org/ to have a local url so I can make this workflow a ton easier.

But to get going I had to setup a domain with valid HTTPS. As linked above, Let's Encrypt might be a great way to setup a play area and solutions like Forge https://forge.laravel.com/ Heroku https://www.heroku.com/ are great ways to get going quickly on getting a "POSTABLE" API in place.

What I mean by POSTABLE is that your API needs to take a POST from Slack (it can take a GET but not sure the advantage to that) but consider App you are setting up the foundation to so many cool integrations you can do with Slack all using this one server your are setting up, until of course something needs it's own space. One POSTABLE API can be like your own "If This Then That" https://ifttt.com/ solution in my opinion. Fun for some of us that is for sure.

Once that was setup I could start playing around.

Setting up the Slack Endpoint

Once I log into the slack.com website and read up on the API went over to Custom Integrations. Making a Slack App will come later but for not this was more than enough to have some fun.

In there I added a configuration.

There are several settings here that matter.

example image

  • Command: /b2a
  • URL: https://foo.com/api/v1/brit_to_us
  • Method: POST
  • Token: TOP_SECRET
  • Customize Name: brit2american
  • Custom Icon: speaking_head_in_silhouette
  • Help Text:
  • Description: Translate British slang to American Slang
  • Usage hint: Throw A Spanner In The Works
  • Descriptive Label: Brit to American Slang

I made two of them since I also need to turn my boring American into cool British Slang

  • Command: /a2b
  • URL: https://foo.com/api/v1/us_to_brit
  • Method: POST
  • Token: TOP_SECRET
  • Customize Name: american2brit
  • Custom Icon: speaking_head_in_silhouette
  • Help Text:
  • Description: Translate American to Cool British Slang
  • Usage hint: Throw A Spanner In The Works
  • Descriptive Label: Good Morning All!

Once these are setup we are ready to build our Routes!

Setting Up the Receiving API

This part could have gone better if I had a local environment setup to receive these Slack commands and next time I will, I just did not realize how easy it was to just setup my own local environment with a good signed cert which Slack requires until I started writing this article.

This was for fun so I did not go crazy with design. All of it could have been in the route file really. But I do want this domain and Laravel install I made to allow me to plug in other Custom Commands and later Slack App.

Slack Payload

Note this page on Slack https://api.slack.com/slash-commands you will get a payload like this

token=gIkuvaNzQIHg97ATvDxqgjtO
team_id=T0001
team_domain=example
channel_id=C2147483705
channel_name=test
user_id=U2147483697
user_name=Steve
command=/weather
text=94070
response_url=https://hooks.slack.com/commands/1234/5678

For me that meant keeping some things in the .env file as needed to look for a specific Token, User etc BUT later I can see making a Provider class and Config file to setup up for each Slash Command or App I make so that I keep the settings as needed in one grouped place and not in a huge file.

For now I just use the text and the response_url to my advantage the rest I am not worried about right now.

This is also I good example of an easy Webhook workflow. Give me your object and where to send the results back to.

Keeping the routing simple my routes.php file is just this

Route::post('/api/v1/brit_to_us', 'BritToUSController@britToUs');
Route::post('/api/v1/us_to_brit', 'BritToUSController@usToBrit');

And that controller is just this

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Goutte\Client as GoutteClient;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Response;

class BritToUSController extends Controller
{

    protected $url = 'http://www.translatebritish.com/';

    /**
     * @var GoutteClient
     */
    protected $client;


    public function __construct(GoutteClient $client)
    {
        $this->client = $client;
    }

    public function usToBrit(Request $request)
    {
        $this->validate($request, [ 'token' => 'required']);

        Log::info($request->input());

        $crawler = $this->client->request('GET', $this->url . 'reverse.php');

        $form    = $crawler->siblings()->filterXPath('//*[@id="content-area"]/div/div[1]/div[1]/div[1]/div[1]/form')->form();

        $crawler    = $this->client->submit($form,
            array('p' => $request->input('text')));

        $results = $crawler->siblings()->filter('.translation-text')->text();

        return Response::json($this->respondToSlack($results, $request->input('text'), 'in_channel'));

    }

    public function britToUs(Request $request)
    {
        $this->validate($request, [ 'token' => 'required']);

        Log::info($request->input());

        $crawler = $this->client->request('GET', $this->url);

        $form    = $crawler->siblings()->filterXPath('//*[@id="content-area"]/div/div[1]/div[1]/div[1]/div[1]/form')->form();

        $crawler    = $this->client->submit($form,
            array('p' => $request->input('text')));

        $results = $crawler->siblings()->filter('.translation-text')->text();

        return Response::json($this->respondToSlack($results, $request->input('text'), 'in_channel'));

    }

    protected function respondToSlack($message, $original_message, $type = 'in_channel')
    {
        return ['response_type' => 'in_channel', 'text' => trim($message), 'attachments' => ['text' => $original_message]];
    }

    /**
     * @return string
     */
    public function getUrl()
    {
        return $this->url;
    }

    /**
     * @param string $url
     */
    public function setUrl($url)
    {
        $this->url = $url;
    }
}

The gist of it is to take the incoming text and pass it to the Translation Site, and return it to the response_url using the Response::json Facade.

Pretty easy. Later I can validate tokens, validate users or query info based on a user eg "Get me my tickets from JIRA" for example

That is some of my goals later, how to use Slack to automate some of my, and my teams, day to day needs and workflows.

UPDATE Make sure you exclude this route from the "app/Http/Middleware/VerifyCsrfToken.php" middleware

    protected $except = [
        'api/v1/imports'
    ];

Tags:

laravel php slack