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
2. At the following screen, choose Slash Commands
3. Next, click the Add Slash Command button on the right-hand side of the screen
4. Now it’s time to set up the slash command
Here’s how you can do that:
- Give the slash command a title.
- Give the slash command a description.
- Specify the trigger word used to send the request (not including the slash).
- Give the URLs where the POST-request will be sent to. Go here if you are planning to send to internal IP addresses!
- We will be using HTTPS. If you have a self-signed certificate, you need to enable unsecure connections. Here is how you do that.
- Define if it will be a POST or a GET request.
- Give the username that will be displayed on the response.
- Give the URL for the profile picture that will be displayed on the response.
- If you check the Autocomplete box, your slash command will be displayed in the list of commands when a user types
/
- 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.
- 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
.
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.
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.
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.
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.