Commandes slash

Une commande slash personnalisée permet aux utilisateurs de taper /quelque_chose dans la zone de message pour appeler votre service. Antimatter envoie la commande à votre URL et affiche votre réponse, soit uniquement à la personne concernée, soit à tout le canal.

Fonctionnement d'une commande slash

  1. Quelqu'un tape /weather Paris et l'envoie. Le texte n'est pas publié.
  2. Antimatter envoie une requête HTTP à l'URL de la commande, avec l'identité de la personne, l'emplacement et le texte suivant le déclencheur.
  3. Votre service répond dans le délai de la requête (30 secondes par défaut) avec un message.
  4. Antimatter affiche la réponse : uniquement à cette personne (éphémère) ou dans le canal pour tout le monde.

Les commandes appartiennent à une communauté, et un mot déclencheur est unique au sein de celle-ci. Les commandes intégrées telles que /join, /msg ou /away ne peuvent pas être remplacées. Les applications (plugins) peuvent également enregistrer des commandes ; voir La partie serveur.

Créer une commande

Les commandes slash personnalisées sont activées par défaut (Activer les commandes slash personnalisées dans Console système › Intégrations › Gestion des intégrations, ServiceSettings.EnableCommands). Les administrateurs de la communauté et les administrateurs système peuvent les créer par défaut.

  1. Ouvrez votre avatar en haut à droite, puis Intégrations › Commandes slash, puis Ajouter une commande slash.
  2. Renseignez le Titre et la Description (affichés sur les pages d'intégrations).
  3. Saisissez le Mot-clé de déclenchement, sans barre oblique et sans espaces, par exemple weather.
  4. Saisissez l'URL de requête et choisissez la Méthode de requête : POST (par défaut) ou GET.
  5. Facultatif : définissez un Utilisateur affiché dans la réponse et une Icône de la réponse. Ils ne sont utilisés que si le serveur autorise les intégrations à remplacer les noms et les icônes (voir Nom et icône).
  6. Cochez Auto-complétion pour lister la commande lorsque les utilisateurs tapent /, avec un Indice de l’auto-complétion (ses arguments, tels que [city]) et une Description de l'auto-complétion.
  7. Choisissez Enregistrer et copiez le jeton affiché. Votre service l'utilise pour vérifier que les requêtes proviennent de votre serveur.

Via l'API : POST /api/v4/commands avec {"team_id", "trigger", "url", "method": "P", "display_name", "description", "auto_complete": true, "auto_complete_hint", "auto_complete_desc", "username", "icon_url"}. La méthode est P pour POST ou G pour GET. PUT /api/v4/commands/{command_id}/regen_token émet un nouveau jeton.

La requête reçue par votre service

Une commande POST envoie un corps encodé en formulaire (application/x-www-form-urlencoded) ; une commande GET envoie les mêmes champs dans la chaîne de requête. Le jeton est également présent dans l'en-tête Authorization: Token <token>.

ChampValeur
tokenLe jeton de la commande. Vérifiez-le à chaque requête.
commandLe déclencheur avec sa barre oblique, par exemple /weather.
textTout ce qui suit le déclencheur, par exemple Paris.
user_id, user_nameQui l'a exécutée.
channel_id, channel_nameOù elle a été exécutée.
team_id, team_domainL'identifiant et le nom d'URL de l'Espace de travail.
root_idLe fil de discussion, lorsqu'elle a été exécutée dans la zone de réponse d'un fil.
trigger_idPermet à votre réponse d'ouvrir une boîte de dialogue (voir Boîtes de dialogue). Il n'est valable que pour une courte durée.
response_urlUne URL pour publier d'autres réponses plus tard (voir Réponses différées).
user_mentions, user_mentions_ids, channel_mentions, channel_mentions_idsLes personnes et les canaux publics mentionnés dans le texte, s'il y en a. Les canaux sont comptés qu'ils soient écrits #channel-name ou ~channel-name.
L'URL de site du serveur doit être définie pour que les commandes personnalisées fonctionnent : Antimatter construit response_url à partir de celle-ci.

La réponse

Répondez avec 200 en JSON (Content-Type: application/json) :

{
  "response_type": "in_channel",
  "text": "Paris: 18 °C, light rain",
  "attachments": []
}
ChampSignification
response_typeephemeral (par défaut) : seule la personne qui a exécuté la commande voit la réponse, et elle n'est pas conservée après un rechargement. in_channel : la réponse est publiée dans le canal.
text, attachmentsLe message, comme pour les webhooks : texte Markdown et pièces jointes, y compris les boutons et les menus.
username, icon_urlLe nom et l'image à afficher, lorsque les substitutions sont autorisées. Les paramètres propres à la commande priment sur ces valeurs.
channel_idPubliez la réponse dans un autre canal, dont la personne doit être membre.
goto_locationUne URL à ouvrir une fois la commande exécutée.
props, typeDes propriétés supplémentaires et un type de publication personnalisé, comme pour les webhooks.
extra_responsesD'autres réponses, chacune avec les mêmes champs, publiées après la première.
skip_slack_parsingtrue conserve le texte tel quel, sans convertir les liens et les mentions au format Slack.

Une réponse qui n'est pas au format JSON est affichée comme un message éphémère avec le corps de la réponse comme texte. 204 No Content n'affiche rien. Tout code de statut hors de la plage 200–299 affiche une erreur à la personne.

Service d'exemple

Un service de commande minimal en Python, utilisant uniquement la bibliothèque standard :

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()

Si le service fonctionne sur une adresse réseau privée, un administrateur système doit l'autoriser dans Autoriser les connexions internes non approuvées à (ServiceSettings.AllowedUntrustedInternalConnections).

Réponses différées

Pour les tâches qui dépassent le délai d’attente, répondez immédiatement (par exemple avec un message éphémère « En cours de traitement… »), puis envoyez la réponse réelle à response_url. Elle utilise le même JSON que la réponse et fonctionne jusqu’à 5 fois dans les 30 minutes suivant la commande.

curl -X POST "$RESPONSE_URL" -H 'Content-Type: application/json' \
  -d '{"response_type": "in_channel", "text": "Report ready: https://reports.example.com/42"}'

Exécuter une commande via l’API

Les scripts et les bots peuvent exécuter n’importe quelle commande, intégrée ou personnalisée, comme s’ils l’avaient tapée :

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 liste les commandes que les utilisateurs peuvent compléter dans une communauté.

Autocomplétion

Les commandes créées sur les pages d’intégrations s’accompagnent de leur déclencheur, de leur indice et de leur description. Les commandes enregistrées par les applications peuvent décrire en détail leurs sous-commandes et leurs arguments avec AutocompleteData, à partir de model.NewAutocompleteData(trigger, hint, helpText) :

  • AddCommand(sub) ajoute une sous-commande, elle-même un AutocompleteData.
  • AddTextArgument(helpText, hint, pattern) ajoute du texte libre.
  • AddStaticListArgument(helpText, required, items) propose une liste fixe de AutocompleteListItem{Item, Hint, HelpText}.
  • AddDynamicListArgument(helpText, url, required) demande à votre application des suggestions au fur et à mesure que les utilisateurs tapent. L’URL est une route de votre application, par exemple /plugins/com.example.weather/cities. Elle reçoit l’utilisateur, le canal et la communauté, et renvoie une liste JSON de {"Item", "Hint", "HelpText"}.

Les variantes Named… (AddNamedTextArgument et autres) ajoutent des drapeaux --name value. Les deux interfaces web affichent ces suggestions.

Lorsqu'une personne saisit une commande, Fusion affiche la commande et chacun de ses arguments sous forme de blocs, celui en cours de saisie étant mis en évidence avec son texte d'aide. Les arguments proviennent de AutocompleteData ou, à défaut, de l'indice : dans un indice tel que @[username]... #[channel], les mots commençant par @ sont attribués à l'argument « personnes » et ceux commençant par # à l'argument « canaux », quel que soit leur ordre ; ... signifie que l'argument se répète, et le dernier argument reçoit le reste du texte.

Canaux dans les arguments

Dans les arguments d'une commande, @ suggère des personnes, # suggère des canaux et les complète sous la forme #channel-name, et ~ les complète sous la forme ~channel-name. L'interface web et l'application mobile d'Antimatter écrivent les liens de canaux avec #, donc une commande qui prend un canal en paramètre doit accepter les deux formes : les commandes intégrées /join, /mute et /invite le font, et leurs indices indiquent #[channel].