Mattermost integrations: Requesting data with slash commands

Slash commands are the easiest way to send requests from channels and direct messages to other programs

(Originally published at controlaltdeliet.be)

In the first two installments in this series, you learned you learned how to send alerts with incoming webhooks and request data with outgoing webhooks. In this article, you will learn how to set up a slash command.

Slash commands are very similar to outgoing webhooks and even a little more powerful. To show their power in action, let’s find out how to use slash commands to request the temperature of a specific refrigerator.

Create the slash command in four steps

Follow these four steps to create a slash command.

1. Go to the Menu and choose Integations

Integrations

2. At the following screen, choose Slash Commands

Slash commands

3. Next, click the Add Slash Command button on the right-hand side of the screen

Slash commands

4. Now it’s time to set up the slash command

Here’s how you can do that:

  1. Give the slash command a title.
  2. Give the slash command a description.
  3. Specify the trigger word used to send the request (not including the slash).
  4. Give the URLs where the POST-request will be sent to. Go here if you are planning to send to internal IP addresses!
  5. We will be using HTTPS. If you have a self-signed certificate, you need to enable unsecure connections. Here is how you do that.
  6. Define if it will be a POST or a GET request.
  7. Give the username that will be displayed on the response.
  8. Give the URL for the profile picture that will be displayed on the response.
  9. If you check the Autocomplete box, your slash command will be displayed in the list of commands when a user types /
  10. You can give a hint with the command and its values, and you can also give a description that will be displayed when users browse your command.
  11. Click the save button.

At this point, you’ll get confirmation that the slash command has been created. You will also get a very important token that we will be needing in our next step.

As we are using our slash command to retrieve the temperature from our fridges (see the first part of this series), we named our slash command Ice Ice Baby, described it as the coolest command, and configured it so it gets triggered with /vanilla_ice.

slash commands

Write some code

You made a slash command—congratulations!

Now, we’ll write some code that can listen to your requests.

First things first: We will send encrypted HTTPS requests. It’s a good habit.

Next, let’s make a self-signed certificate!

openssl req -nodes -new -x509 -keyout server.key -out server.cert
cat server.key server.cert > server.pem

Now, the magical Python code (below is a NodeJS example).

The slash command sends the token you generated. Check if the token matches your generated token, otherwise anyone can activate your slash command.

The variables that the sender adds to the slash command are sent in a variable named text and are whitespace separated.

!/usr/bin/python3
import time
import sys
import os
import ssl
import bottle
from bottle import Bottle, post, run, ServerAdapter,response,request
###CHANGE THE FOLLOWING LINES TO YOUR CONFIGURATION###
hostname = "The IP address of the device running your code"
serverport = 443
token="yourbrandnewtoken"
certfile="/path/to/your/just/created/server.pem"

@post("/freeze")
def hello():
    if request.forms.get('token')==token:
        variables=request.forms.get('text').split() #
        response.content_type = 'application/json'
        return '{"text":"your first variable is '+str(variables[0])+' your second variable is '+str(variables[1])+'"}'
    else:
        return HTTPResponse(status=403)



class SSLWSGIRefServer(ServerAdapter):
    def run(self, handler):
        from wsgiref.simple_server import make_server, WSGIRequestHandler
        import ssl
        if self.quiet:
            class QuietHandler(WSGIRequestHandler):
                def log_request(*args, **kw): pass
            self.options['handler_class'] = QuietHandler
        srv = make_server(self.host, self.port, handler, **self.options)
        srv.socket = ssl.wrap_socket (
         srv.socket,
         certfile='server.pem',  # path to certificate
         server_side=True)
        srv.serve_forever()

if __name__ == "__main__":
   '''UNCOMMENT IF YOU WANT TO RUN THE SCRIPT IN THE BACKGROUND###
   try:
   # Store the Fork PID
      pid = os.fork()

      if pid > 0:
        print ('PID: %d' % pid)
        os._exit(0)

   except :
     print ('Unable to fork. Error: ')
     os._exit(1)
     '''
srv = SSLWSGIRefServer(host="0.0.0.0", port=serverport)
run(server=srv)

Important note: Your response has to be JSON-formatted:

const https = require('https');
    const fs = require('fs');
    const qs = require('querystring');
    
    //MODIFY THE NEXT 4 LINES
    const token='yoursecrettoken';
    const serverport=4443;
    const key="/path/to/your/server.key";
    const cert="/path/to/your/server.cert";
    //
    
    var post;
    const options = {
      key: fs.readFileSync(key),
      cert: fs.readFileSync(cert)
    };
    
    https.createServer(options, (req, res) => {
      console.log(req.headers.authorization)
    
      if (req.headers.authorization == "Token "+token){
         if (req.method == 'POST') {
            var body = '';
    
            req.on('data', function (data) {
                body += data;
    
                // Too much POST data, kill the connection!
                // 1e6 === 1 * Math.pow(10, 6) === 1 * 1000000 ~~~ 1MB
                if (body.length > 1e6)
                    req.connection.destroy();
            });
    
            req.on('end', function () {
                post = qs.parse(body);
               console.log(post.text)
    
      variables=post.text.split(" ");
      res.writeHead(200, {'Content-Type': 'application/json'});
      res.write('{"text":"your first variable is '+variables[0]+' your second variable is '+variables[1]+'"}');
      res.end();
          });
        }
    }
  }).listen(serverport);

More options

It’s worth noting that your response isn’t limited to just text and you have some options.

Changing the username

If you want to use another username to appear as the sender, you can add the username variable. This setting is default disabled and must be enabled in the Managment Console.

{"text":"All is going very well, thank you for your kind interest.","username":"The Coolest Fridge"}

Changing the profile picture

If you want to change the profile picture, add the icon_url in the request.

'{"icon_url":"https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/120/apple/198/freezing-face_1f976.png","text": "The Fonz says the fridge is cool enough"}'

Adding emoticons

If you want to add an emoticon in the text, add the shortcode like :tada: in the text.

'{"text": "Wow, this works :tada:"}'

Adding more lay-out

If you are not familiar with Markdown layout, you can find an introduction in the Mattermost documentation and in this blog post.

'{"text": "| Left-Aligned  | Center Aligned  | Right Aligned |
| :------------ |:---------------:| -----:|
| Left column 1 | this text       |  $100 |
| Left column 2 | is              |   $10 |
| Left column 3 | centered        |    $1 |"}'

Allow untrusted internal connections

If you want to send your requests to your internal network, you have to specify to which hosts you want to send. Mattermost can only send to the specified internal hosts or hostnames! You can adjust this setting by navigating to System Console -> Environment -> Developer.

slash commands

Unsecure outgoing connections

If you are using self-signed certificates like in our example, you need to allow outgoing HTTPS connections with self-signed unverified certificates. You can find this setting here: System Console -> Environment -> Web Server.

Debugging

Is something not working as expected? Go to the System Console and under Environment you find the Logging section.

Logging

In the Logging section, enable Webhook Debugging and set Log Level to Debug. You’ll find in the logfiles (or in the console if you enabled logging to the console) all the relevant information.

My most common mistake is a typo in the request. It gives me an Unable to parse incoming data error.

Debugging

More info

For more information on how to use slach commands in Mattermost, check out the docs. You can also always ask for help on the Mattermost forum.

Share this article:

mm

Tom De Moor

Tom De Moor is the official reviewer of the Dutch translation of Mattermost. He is a technology lover with a broad outlook who uses PHP, Python, NodeJS, MariaDB, MongoDB, and Raspberry Pis on a daily basis. He is also an official drone pilot.

To get future blog posts to your inbox, subscribe below.

We use cookies for advertising, social media and analytics purposes. Read about how we use cookies here. By continuing to use this site, you consent to our use of cookies.