슬래시 명령
사용자 정의 슬래시 명령을 사용하면 사용자가 메시지 입력란에 /something을 입력하여 서비스를 호출할 수 있습니다. Antimatter는 명령을 URL로 전송하고, 답변을 해당 사용자만 볼 수 있게 하거나 전체 채널에 표시합니다.
슬래시 명령 작동 방식
- 누군가가
/weather Paris를 입력하고 전송합니다. 텍스트는 게시되지 않습니다. - Antimatter는 명령의 URL로 HTTP 요청을 보내며, 누가 입력했는지, 어디서 입력했는지, 트리거 뒤에 있는 텍스트를 포함합니다.
- 서비스는 요청 타임아웃(기본값 30초) 내에 메시지로 답변합니다.
- Antimatter는 답변을 표시합니다: 해당 사용자만 볼 수 있는 에페메럴(ephemeral) 또는 모든 사람이 볼 수 있는 채널 내 답변.
명령은 커뮤니티에 속하며, 트리거 단어는 커뮤니티 내에서 고유해야 합니다. /join, /msg 또는 /away와 같은 내장 명령은 대체할 수 없습니다. 앱(플러그인)도 명령을 등록할 수 있습니다. 서버 부분을 참조하세요.
명령 생성
커스텀 명령어는 기본적으로 활성화되어 있습니다(시스템 콘솔 › 통합 › 통합관리의 커스텀 명령어, ServiceSettings.EnableCommands). 커뮤니티 관리자와 시스템 관리자는 기본적으로 커스텀 명령어를 만들 수 있습니다.
- 오른쪽 상단의 아바타를 클릭한 뒤 통합 › 슬래시 명령어, 그리고 슬래시 명령어 추가를 선택하세요.
- 제목과 설명(통합 페이지에 표시됨)을 입력합니다.
- 슬래시 기호와 공백을 제외한 명령어 트리거 단어를 입력하세요. 예:
weather. - 요청 URL을 입력하고 요청 메소드를 선택하세요: POST(기본값) 또는 GET.
- 선택적으로 응답 유저 이름과 응답 아이콘을 설정할 수 있습니다. 서버가 통합이 이름과 아이콘을 덮어쓰도록 허용하는 경우에만 사용됩니다(자세한 내용은 이름과 아이콘 참고).
- 자동완성을 선택하면 사람들이
/를 입력할 때 명령어가 목록에 표시됩니다. 자동완성 제안(예:[city]과 같은 인자)과 자동완성 설명을 함께 설정할 수 있습니다. - 저장을 선택하고 표시된 토큰을 복사합니다. 서비스는 이 토큰을 사용하여 요청이 서버에서 온 것을 확인합니다.
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로 작성된 경우 모두 포함됩니다. |
response_url을 생성합니다.응답
JSON(Content-Type: application/json)으로 200을 반환합니다:
{
"response_type": "in_channel",
"text": "Paris: 18 °C, light rain",
"attachments": []
}
| 필드 | 의미 |
|---|---|
response_type | ephemeral(기본값): 명령을 실행한 사용자만 답변을 볼 수 있으며, 새로고침 후에도 유지되지 않습니다. in_channel: 답변이 채널에 게시됩니다. |
text, attachments | 웹훅과 마찬가지로 메시지입니다: Markdown 텍스트와 첨부 파일, 버튼, 메뉴를 포함합니다. |
username, icon_url | 대체(override)가 허용되는 경우 표시할 이름과 이미지. 명령 자체의 설정이 이 값보다 우선합니다. |
channel_id | 사용자가 멤버인 다른 채널에 답변을 게시합니다. |
goto_location | 명령 실행 후 한 번만 열 수 있는 URL. |
props, type | 웹훅과 마찬가지로 추가 속성과 사용자 정의 게시 유형. |
extra_responses | 첫 번째 답변 이후에 게시되는 추가 답변. 각 답변은 동일한 필드를 가집니다. |
skip_slack_parsing | true를 설정하면 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]라고 표시됩니다.