슬래시 명령

사용자 정의 슬래시 명령을 사용하면 사용자가 메시지 입력란에 /something을 입력하여 서비스를 호출할 수 있습니다. Antimatter는 명령을 URL로 전송하고, 답변을 해당 사용자만 볼 수 있게 하거나 전체 채널에 표시합니다.

슬래시 명령 작동 방식

  1. 누군가가 /weather Paris를 입력하고 전송합니다. 텍스트는 게시되지 않습니다.
  2. Antimatter는 명령의 URL로 HTTP 요청을 보내며, 누가 입력했는지, 어디서 입력했는지, 트리거 뒤에 있는 텍스트를 포함합니다.
  3. 서비스는 요청 타임아웃(기본값 30초) 내에 메시지로 답변합니다.
  4. Antimatter는 답변을 표시합니다: 해당 사용자만 볼 수 있는 에페메럴(ephemeral) 또는 모든 사람이 볼 수 있는 채널 내 답변.

명령은 커뮤니티에 속하며, 트리거 단어는 커뮤니티 내에서 고유해야 합니다. /join, /msg 또는 /away와 같은 내장 명령은 대체할 수 없습니다. 앱(플러그인)도 명령을 등록할 수 있습니다. 서버 부분을 참조하세요.

명령 생성

커스텀 명령어는 기본적으로 활성화되어 있습니다(시스템 콘솔 › 통합 › 통합관리의 커스텀 명령어, ServiceSettings.EnableCommands). 커뮤니티 관리자와 시스템 관리자는 기본적으로 커스텀 명령어를 만들 수 있습니다.

  1. 오른쪽 상단의 아바타를 클릭한 뒤 통합 › 슬래시 명령어, 그리고 슬래시 명령어 추가를 선택하세요.
  2. 제목과 설명(통합 페이지에 표시됨)을 입력합니다.
  3. 슬래시 기호와 공백을 제외한 명령어 트리거 단어를 입력하세요. 예: weather.
  4. 요청 URL을 입력하고 요청 메소드를 선택하세요: POST(기본값) 또는 GET.
  5. 선택적으로 응답 유저 이름과 응답 아이콘을 설정할 수 있습니다. 서버가 통합이 이름과 아이콘을 덮어쓰도록 허용하는 경우에만 사용됩니다(자세한 내용은 이름과 아이콘 참고).
  6. 자동완성을 선택하면 사람들이 /를 입력할 때 명령어가 목록에 표시됩니다. 자동완성 제안(예: [city]과 같은 인자)과 자동완성 설명을 함께 설정할 수 있습니다.
  7. 저장을 선택하고 표시된 토큰을 복사합니다. 서비스는 이 토큰을 사용하여 요청이 서버에서 온 것을 확인합니다.

API를 통해: POST /api/v4/commands에 {"team_id", "trigger", "url", "method": "P", "display_name", "description", "auto_complete": true, "auto_complete_hint", "auto_complete_desc", "username", "icon_url"}를 전달합니다. method는 POST의 경우 P, GET의 경우 G입니다. PUT /api/v4/commands/{command_id}/regen_token을 호출하면 새로운 토큰이 발급됩니다.

서비스가 수신하는 요청

POST 명령은 form-encoded 본문(application/x-www-form-urlencoded)을 전송하고, GET 명령은 동일한 필드를 쿼리 문자열로 전송합니다. 토큰은 또한 Authorization: Token <token> 헤더에도 포함됩니다.

필드값
token명령의 토큰. 모든 요청에서 이를 확인해야 합니다.
command슬래시가 포함된 트리거, 예: /weather.
text트리거 이후의 모든 내용, 예: Paris.
user_id, user_name명령을 실행한 사용자.
channel_id, channel_name명령이 실행된 위치.
team_id, team_domain커뮤니티의 ID와 URL 이름.
root_id글타래의 답글 입력창에서 실행된 경우 해당 글타래.
trigger_id답변으로 대화상자를 열 수 있게 합니다(참조: Dialogs). 유효 기간이 매우 짧습니다.
response_url나중에 추가 답변을 게시하기 위한 URL(참조: 지연된 답변).
user_mentions, user_mentions_ids, channel_mentions, channel_mentions_ids텍스트에 언급된 사람과 공개 채널이 있는 경우입니다. 채널은 #channel-name 또는 ~channel-name로 작성된 경우 모두 포함됩니다.
커스텀 명령어가 실행되려면 서버의 사이트 URL이 설정되어 있어야 합니다. Antimatter는 이 URL을 기반으로 response_url을 생성합니다.

응답

JSON(Content-Type: application/json)으로 200을 반환합니다:

{
  "response_type": "in_channel",
  "text": "Paris: 18 °C, light rain",
  "attachments": []
}
필드의미
response_typeephemeral(기본값): 명령을 실행한 사용자만 답변을 볼 수 있으며, 새로고침 후에도 유지되지 않습니다. in_channel: 답변이 채널에 게시됩니다.
text, attachments웹훅과 마찬가지로 메시지입니다: Markdown 텍스트와 첨부 파일, 버튼, 메뉴를 포함합니다.
username, icon_url대체(override)가 허용되는 경우 표시할 이름과 이미지. 명령 자체의 설정이 이 값보다 우선합니다.
channel_id사용자가 멤버인 다른 채널에 답변을 게시합니다.
goto_location명령 실행 후 한 번만 열 수 있는 URL.
props, type웹훅과 마찬가지로 추가 속성과 사용자 정의 게시 유형.
extra_responses첫 번째 답변 이후에 게시되는 추가 답변. 각 답변은 동일한 필드를 가집니다.
skip_slack_parsingtrue를 설정하면 Slack 스타일의 링크와 멘션을 변환하지 않고 텍스트를 그대로 유지합니다.

JSON이 아닌 응답은 본문이 텍스트로 사용되는 임시(ephemeral) 메시지로 표시됩니다. 204 No Content는 아무것도 표시하지 않습니다. 200~299 범위를 벗어난 상태 코드는 사용자에게 오류를 표시합니다.

예시 서비스

표준 라이브러리만 사용하는 Python 기반 최소 명령 서비스:

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

서비스가 비공개 네트워크 주소에서 실행되는 경우, 시스템 관리자가 신뢰할 수 없는 내부 연결을 허용 설정(ServiceSettings.AllowedUntrustedInternalConnections)에서 해당 주소를 허용해야 합니다.

지연된 응답

타임아웃보다 더 오래 걸리는 작업의 경우, 즉시 임시 "처리 중…" 같은 응답을 보낸 뒤, 실제 응답을 response_url로 POST해야 합니다. 이 URL은 응답과 동일한 JSON을 받으며, 명령 실행 후 30분 이내에 최대 5회까지 사용할 수 있습니다.

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

API를 사용하여 명령 실행

스크립트와 봇은 사용자가 직접 입력한 것처럼 내장 명령이나 사용자 정의 명령을 실행할 수 있습니다:

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는 커뮤니티에서 사용자가 자동완성할 수 있는 명령 목록을 반환합니다.

자동완성

통합 페이지에서 생성한 명령은 트리거, 힌트, 설명으로 자동완성됩니다. 앱이 등록한 명령은 AutocompleteData를 사용하여 서브명령과 인자를 자세히 설명할 수 있으며, model.NewAutocompleteData(trigger, hint, helpText)를 통해 생성합니다:

  • AddCommand(sub)는 서브명령을 추가하며, 이 서브명령 자체도 AutocompleteData입니다.
  • AddTextArgument(helpText, hint, pattern)는 자유 텍스트 인자를 추가합니다.
  • AddStaticListArgument(helpText, required, items)는 AutocompleteListItem{Item, Hint, HelpText}의 고정 목록을 제공합니다.
  • AddDynamicListArgument(helpText, url, required)는 사용자가 입력하는 동안 앱에 제안 사항을 요청합니다. URL은 앱의 경로, 예: /plugins/com.example.weather/cities입니다. 이 경로는 사용자, 채널, 커뮤니티 정보를 수신하고 {"Item", "Hint", "HelpText"}의 JSON 목록을 반환합니다.

Named… 계열 함수(AddNamedTextArgument 등)는 --name value 플래그를 추가합니다. 두 웹 인터페이스 모두 이러한 제안 사항을 표시합니다.

누군가 명령어를 입력하는 동안, Fusion은 명령어와 각 인자를 블록으로 표시하며, 현재 입력 중인 인자는 도움말 텍스트와 함께 강조 표시됩니다. 인자는 AutocompleteData에서 가져오거나, 없으면 제안 문구에서 가져옵니다. @[username]... #[channel]와 같은 제안 문구에서 @로 시작하는 단어는 사람 인자로, #로 시작하는 단어는 채널 인자로 분류되며 순서와 무관합니다. ...는 인자가 반복됨을 의미하며, 마지막 인자는 나머지 텍스트를 모두 가져갑니다.

인자 내 채널

명령 인수에서 @은 사람을 제안하고, #은 채널을 제안하며 #channel-name으로 완성합니다. ~은 ~channel-name으로 완성합니다. Antimatter의 웹 인터페이스와 모바일 앱은 채널 링크를 #으로 작성하므로, 채널을 받는 명령은 두 가지 형식을 모두 받아들이야 합니다. 내장 명령인 /join, /mute, /invite는 이를 지원하며, 그 힌트에는 #[channel]라고 표시됩니다.