Slash commands
A custom slash command lets people type /something in the message box to call your service. Antimatter sends the command to your URL and shows your answer, either to the person alone or to the whole channel.
How a slash command works
- Someone types
/weather Parisand sends it. The text is not posted. - Antimatter sends an HTTP request to the command's URL, with who typed it, where, and the text after the trigger.
- Your service answers within the request timeout (30 seconds by default) with a message.
- Antimatter shows the answer: only to that person (ephemeral) or in the channel for everyone.
Commands belong to a community, and a trigger word is unique within it. Built-in commands such as /join, /msg or /away can't be replaced. Apps (plugins) can register commands too; see The server part.
Create a command
Custom slash commands are on by default (Enable Custom Slash Commands in System Console › Integrations › Integration Management, ServiceSettings.EnableCommands). Community admins and system admins can create them by default.
- Open the community menu, then Community settings, click Integrations at the end of its tabs, then Slash commands and Add Slash Command.
- Fill in the Title and Description (shown on the integrations pages).
- Enter the Command Trigger Word, without the slash and without spaces, for example
weather. - Enter the Request URL and choose the Request Method: POST (the default) or GET.
- Optionally set a Response Username and a Response Icon. They are used only when the server allows integrations to override names and icons (see Name and icon).
- Tick Autocomplete to list the command when people type
/, with an Autocomplete Hint (its arguments, such as[city]) and an Autocomplete Description. - Choose Save, and copy the token shown. Your service uses it to check that requests come from your server.
With the API: POST /api/v4/commands with {"team_id", "trigger", "url", "method": "P", "display_name", "description", "auto_complete": true, "auto_complete_hint", "auto_complete_desc", "username", "icon_url"}. The method is P for POST or G for GET. PUT /api/v4/commands/{command_id}/regen_token issues a new token.
The request your service receives
A POST command sends a form-encoded body (application/x-www-form-urlencoded); a GET command sends the same fields in the query string. The token is also in the Authorization: Token <token> header.
| Field | Value |
|---|---|
token | The command's token. Check it on every request. |
command | The trigger with its slash, for example /weather. |
text | Everything after the trigger, for example Paris. |
user_id, user_name | Who ran it. |
channel_id, channel_name | Where it was run. |
team_id, team_domain | The community's ID and URL name. |
root_id | The thread, when it was run in a thread's reply box. |
trigger_id | Lets your answer open a dialog (see Dialogs). It is valid for a short time only. |
response_url | A URL to post more answers later (see Delayed answers). |
user_mentions, user_mentions_ids, channel_mentions, channel_mentions_ids | The people and public channels mentioned in the text, when there are any. Channels count whether they are written #channel-name or ~channel-name. |
response_url from it.The response
Answer 200 with JSON (Content-Type: application/json):
{
"response_type": "in_channel",
"text": "Paris: 18 °C, light rain",
"attachments": []
}
| Field | Meaning |
|---|---|
response_type | ephemeral (the default): only the person who ran the command sees the answer, and it isn't kept after a reload. in_channel: the answer is posted in the channel. |
text, attachments | The message, as for webhooks: Markdown text and attachments, buttons and menus included. |
username, icon_url | The name and picture to show, when overrides are allowed. The command's own settings win over these. |
channel_id | Post the answer in another channel, which the person must be a member of. |
goto_location | A URL to open once the command has run. |
props, type | Extra properties and a custom post type, as for webhooks. A response posted into a forum channel needs props.am_thread_title, or it is refused. |
extra_responses | More answers, each with the same fields, posted after the first. |
skip_slack_parsing | true keeps the text as it is, without converting Slack-style links and mentions. |
A response that isn't JSON is shown as an ephemeral message with the body as its text. 204 No Content shows nothing. Any status outside 200–299 shows the person an error.
Example service
A minimal command service in Python, with only the standard library:
import hmac
import json
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import parse_qs
TOKEN = "the token shown when you saved the command"
class Handler(BaseHTTPRequestHandler):
def do_POST(self):
body = self.rfile.read(int(self.headers["Content-Length"])).decode()
form = {key: values[0] for key, values in parse_qs(body).items()}
if not hmac.compare_digest(form.get("token", ""), TOKEN):
self.send_response(401)
self.end_headers()
return
answer = {
"response_type": "ephemeral",
"text": f"Hello @{form['user_name']}, you asked for: {form.get('text', '')}",
}
data = json.dumps(answer).encode()
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(data)
HTTPServer(("0.0.0.0", 8080), Handler).serve_forever()
If the service runs on a private network address, a system admin must allow it in Allow untrusted internal connections to (ServiceSettings.AllowedUntrustedInternalConnections).
Delayed answers
For work that takes longer than the timeout, answer at once (for example with an ephemeral "Working on it…"), then POST the real answer to response_url. It takes the same JSON as the response, and works up to 5 times within 30 minutes of the command.
curl -X POST "$RESPONSE_URL" -H 'Content-Type: application/json' \
-d '{"response_type": "in_channel", "text": "Report ready: https://reports.example.com/42"}'
Run a command with the API
Scripts and bots can run any command, built-in or custom, as if they had typed it:
curl -X POST https://chat.example.com/api/v4/commands/execute \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"channel_id": "'$CHANNEL_ID'", "command": "/weather Paris"}'
GET /api/v4/teams/{team_id}/commands/autocomplete lists the commands people can autocomplete in a community.
Autocomplete
Commands created on the integrations pages autocomplete with their trigger, hint and description. Commands registered by apps can describe their subcommands and arguments in detail with AutocompleteData, from model.NewAutocompleteData(trigger, hint, helpText):
AddCommand(sub)adds a subcommand, itself anAutocompleteData.AddTextArgument(helpText, hint, pattern)adds free text.AddStaticListArgument(helpText, required, items)offers a fixed list ofAutocompleteListItem{Item, Hint, HelpText}.AddDynamicListArgument(helpText, url, required)asks your app for suggestions as people type. The URL is a route of your app, such as/plugins/com.example.weather/cities. It receives the user, channel and community, and returns a JSON list of{"Item", "Hint", "HelpText"}.
The Named… variants (AddNamedTextArgument and others) add --name value flags. Both web interfaces show these suggestions. A value with spaces can be quoted so it autocompletes as one argument, for example --name "my remote": double quotes work for every command, and Antimatter's own commands that take flags also accept single or curly quotes.
While someone types a command, Fusion shows the command and each of its arguments as a block, the one being typed highlighted with its help text. The arguments come from AutocompleteData or, without it, from the hint: in a hint such as @[username]... #[channel], words starting with @ go to the people argument and words starting with # to the channel argument, whatever their order; ... means the argument repeats, and the last argument takes the rest of the text.
Channels in arguments
In a command's arguments, @ suggests people, # suggests channels and completes them as #channel-name, and ~ completes them as ~channel-name. Antimatter's web interface and mobile app write channel links with #, so a command that takes a channel should accept both forms: the built-in /join, /mute and /invite do, and their hints say #[channel].