Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Writing a Slack Plugin is One of the Easiest Things to Do... Sort of

DZone's Guide to

Writing a Slack Plugin is One of the Easiest Things to Do... Sort of

See how you one developer made a Slack slash command to display images of Nicholas Cage in your Slack chat.

· Java Zone
Free Resource

Get the Edge with a Professional Java IDE. 30-day free trial.

Slack is the little chat app that could. It’s had a meteoric rise to becoming one of the most popular productivity tools amongst developers. Whilst it’s great as a chat app on its own, one of the reasons it’s become so popular is the huge plugin ecosystem that has grown around it. Slack has integration with calendar systems, weather systems, log scrapers, deployment tools and much more. You can really do anything from it.

Where this really gets exciting is in creating your own Slack integrations. You can hook your Slack system up to your internal processes, and best of all it’s exceptionally easy to do. Slack has created a simple yet powerful API- all you need is a REST service. In this tutorial I’ll be showing you how to do just that to create your own slash command to display images of Nicholas Cage in your Slack chat, taking advantage of the PlaceCage API.

Creating a Custom Slash Command

In slack, a slash command is a forward slash, followed by the command, and then some optional parameters. For example, “/remind me in ten minutes to get a drink” will remind you in ten minutes to get a drink. We can create our own slash commands to do almost anything we want.

Creating our REST service

Whenever someone calls our custom slash command our REST service will be called; we’ll tell slack the name of our command later on and where exactly to send the call. First we need to create our service. I’ve chosen to use Sparkjava for this, but feel free to use whatever your framework of choice is.

  public static void main(String[] args) {
        String port = Optional.ofNullable(System.getenv("PORT")).orElse("8080");
        port(Integer.parseInt(port));
        get("/cage", (req, res) -> {
            res.type("application/json");
            return "{" +
                    "" +
                    "    \"text\": \"Here is a calm image of Nic Cage.\"," +
                    "    \"attachments\": [" +
                    "        {" +
                    "            \"title\": \"Nic Cage\",\n" +
                    "            \"image_url\": \"https://www.placecage.com/200/300\",\n" +
                    "            \"color\": \"#764FA5\"" +

                    "        }" +
                    "    ]" +
                    "}";
        });
    }

I’ve used an optional to get the port, as I’m using Heroku to deploy my application.

I have a single “get” exposed (you can choose whether to use get or post when setting up in slack) which returns some hardcoded JSON. There are tons of options you can use which are fully documented here, But I’ve gone with a simple message and attachment. If you’re only sending text then you don’t need to even both with JSON, and can just respond with the text you want to see in the chat.

To support message plus attachments, there must be some message text followed by the attachment field, which is an array of objects (you can have multiple attachments). Your attachment MUST have a title, which isn’t documented anywhere but caused me quite a bit of frustration to figure out. The image_url field points to whatever picture url you want to use: in my case I’m using placecage to get pictures. The color I’ve just added in for fun, and changes the color of the little bar along the left of the attachment.

That’s literally all there is to it. Push your code up to somewhere that Slack can reach and take a note of your URL.

Setting up the command

Head over to https://api.slack.com/custom-integrations and click on "Set up a slash command" just over half way down the page. Make sure that you’ve got the right team selected if you’re in multiple teams

On the next page, choose a name for your command. Because we’ll be putting pictures of Nicolas Cage, I’ve gone for “/cage”. Continue on, and on the next page put the URL of your REST service. Hit save and your command is live in your chat.

Accepting parameters

Chances are that normally you will want to accept parameters after your command to affect the response. I’ve expanded /cage to allow you to select the type of picture you’d like; calm, crazy, black and white or an animated GIF.

The message sent over from Slack contains a number of useful fields, including “token” so that you can validate the source of the call for security. In this scenario we’re interested in “text”, which simply contains all of the text after the command which we can handle however we choose.

  private static UrlFormatter urlFormatter;

    public static void main(String[] args) {
        String port = Optional.ofNullable(System.getenv("PORT")).orElse("8080");
        port(Integer.parseInt(port));
        get("/cage", (req, res) -> {
            res.type("application/json");
            String params = req.queryParams("text");

            urlFormatter = new UrlFormatter(params);
            String formattedUrl = urlFormatter.getFormattedUrl();

            return "{" +
                    "\"response_type\": \"in_channel\"," +
                    "    \"text\": \"Here is a " + urlFormatter.imageType() + " image of Nic Cage.\"," +
                    "    \"attachments\": [" +
                    "        {" +
                    "            \"title\": \"Nic Cage\",\n" +
                    "            \"image_url\": \"" + formattedUrl + "\",\n" +
                    "            \"color\": \"#764FA5\"" +

                    "        }" +
                    "    ]" +
                    "}";
        });
    }

    public class UrlFormatter {
    static Map<String, String> commandMap = new HashMap<String, String>() {{
        put("calm", "");
        put("gray", "/g");
        put("crazy", "/c");
        put("gif", "/gif");
    }};
    static String formatUrl = "https://www.placecage.com%s/200/300";
    private String params;

    public UrlFormatter(String params) {
        this.params = params;
        if (!commandMap.containsKey(params)) {
            System.out.println("Text did not match a mood. Text was: " + params);
            this.params = "calm";
        }
    }

    public String getFormattedUrl() {
        return String.format(formatUrl, commandMap.get(params));
    }

    public String imageType() {
        return params;
    }
}


We pull the text param from the call to pass to our formatter. The code simply takes the command and maps it to the relevant placecage URL, and returns the formatted URL (or defaults to calm).

A Word of Warning

Although the Slack API is easy in theory, it’s actually quite horrible to debug. There’s no way to run slack locally to validate your code, and there’s no test harnesses provided to validate. This means that you don’t know for sure if the code is working until you’ve deployed and checked it. This is immensely frustrating and time consuming. For my cage plugin I spent ages trying to understand why my attachments weren’t displaying until I discovere the attachment title was a mandatory field which wasn’t documented anywhere. Each time I’d have to change something, commit it, wait for my CI to deploy it, then try the command out. Hopefully the Slack API crew will release some tooling to make this easier going forward, but I can imagine this would lead to severe frustration on any serious plugin development .

You can checkout the source code from GitHub at https://github.com/samberic/placeholder-slack.

If you’ve any questions or want to know more, follow me on twitter via @SambaHK.

Get the Java IDE that understands code & makes developing enjoyable. Level up your code with IntelliJ IDEA. Download the free trial.

Topics:
java ,spark ,slack ,rest

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}