[Mattermost Integrations] Slash Command 発展編
Mattermost 記事まとめ: https://blog.kaakaa.dev/tags/mattermost/
本記事について
Mattermost の統合機能アドベントカレンダーの第 7 日目の記事です。
前回の記事では、Mattermost で/
で始まるコマンドを実行することで、特定の処理を実行することができる Slash Command の機能について紹介しました。また、Custom Slash Command により外部アプリケーションにリクエストを送信する方法を紹介しました。本記事では、Custome Slash Command のリクエストが送信された外部アプリケーション側から Mattermost 側へ返却するレスポンス内容について紹介します。
Slash Command のレスポンスとして投稿を作成する
Slash Command によるリクエストを受け付けた外部アプリケーションから、レスポンスとして Mattermost に投稿を作成する方法について紹介します。
以下のコードは昨日のコードにレスポンスを返すコードを追加したものです。
package main
import (
"fmt"
"io"
"log"
"net/http"
)
const (
WebHookToken = "8w7foap4ufrsfczda8uez51yxo"
)
func main() {
http.HandleFunc("/command", func(w http.ResponseWriter, r *http.Request) {
log.Printf("%#v", r.Form)
r.ParseForm()
log.Printf("token: %v", r.Form.Get("token"))
// (1) Tokenをチェック
if r.Form.Get("token") != WebHookToken {
log.Printf("received an invalid request with token: %s", r.Form.Get("token"))
return
}
// (2) レスポンスとしてMattermostへ投稿を作成
w.Header().Add("Content-Type", "application/json")
io.WriteString(w, fmt.Sprintf(`{"text": "**%s**"}`, r.Form.Get("text")))
})
http.ListenAndServe(":8080", nil)
}
Outgoing WebHook の時とほぼ同じですが、(1)
でトークンを利用したリクエストの検証を行い、(2)
で Slash Command のレスポンスとして投稿を作成しています。レスポンスヘッダーへのContent-Type: application/json
の設定は必須です。text
フィールドに指定したメッセージが Mattermost に投稿されます。
このサーバに対して/サンプルコマンド TEST
という Slash Command を送信した時の画面が下記になります。
作成された投稿には、BOT
というラベルに加え、(あなただけが見ることができます)
というメッセージがついています。Slash Command のレスポンスとして作成した投稿は、デフォルトでは Slash Command を実行したユーザーしか見ることができない投稿になり、他のユーザーからは見ることができません。チャンネルの全員が閲覧可能な投稿を作成するには、後述のresponse_type
パラメータを設定します。
Slash Command レスポンスパラメータ
text
投稿されるメッセージであり、後述のattachments
を指定しない場合は必須のパラメータです。Markdown 記法や、@
によるメンションを投稿することもできます。
io.WriteString(w, `{"text": "**TEST**"}`)
response_type
Slash Command のレスポンスにより作成される投稿の種別を指定します。デフォルトではephemeral
であり、Slash Command を実行したユーザーにしか見えない投稿になります。チャンネル全員が見えるようにするにはresponse_type
にin_channel
を指定します。
io.WriteString(w, `{"text": "**TEST**", "response_type": "in_channel"}`)
username
, icon_url
Slash Command のレスポンスとして作成される投稿のユーザー名とアイコンの表示を変更することができます。デフォルトでは、ウェブフックを作成したユーザーになります。
このパラメータを使用する場合、システムコンソールから下記の設定をそれぞれ有効
にする必要があります。
- システムコンソール > 統合機能管理 > 統合機能によるユーザー名の上書きを許可する
- システムコンソール > 統合機能管理 > 統合機能によるプロフィール画像アイコンの上書きを許可する
(この設定を有効にすると、username
とicon_url
を指定しない場合の投稿の作成者がデフォルトのアイコン、ユーザー名(webhook
)に変更されます)
io.WriteString(w, `{
"text": "**TEST**",
"username": "new-bot",
"icon_emoji": "http://www.mattermost.org/wp-content/uploads/2016/04/icon_WS.png"
}`)
channel_id
投稿を作成するチャンネルを指定できます。ここで指定するチャンネル ID は、チャンネルメニュー > 情報を表示するから確認できます。
io.WriteString(w, `{
"text": "**TEST**",
"channel_id": "u6ijofzg8bnsie5u8c1eumtc1h"
}`)
goto_location
Slash Command 特有のパラメータです。goto_location
フィールドに URL を指定しておくと、Slash Command が実行された際に指定された URL に移動します。この時text
などで指定した投稿も作成されます。
io.WriteString(w, `{
"text": "**TEST**",
"goto_location": "https://github.com/mattermost"
}`)
goto_location
にはhttps
だけでなく、ssh
やmailto
のプロトコルも使用できるため、例えばスラッシュコマンドを実行した場合に「メーラーでメール作成画面を開く」というような動作をさせることも可能です。
io.WriteString(w, `{
"text": "**TEST**",
"goto_location": "mailto:test@example.com"
}`)
attachments
attachments
は、Mattermost にリッチな投稿を作成することができるMessageAttachmentsを指定できる機能です。MessageAttachmentsについては、別途紹介記事を用意する予定です。
type
type
は、Mattermost の投稿の種別を指定するパラメータですが、基本的に使用することはありません。Plugin(Webapp)にてプラグイン独自の投稿種別を指定した場合のみに使用するものかと思います。Plugin(Webapp)については、別途紹介記事を用意する予定です。
extra_responses
extra_responses
配列としてレスポンスの JSON データを記述することで複数の投稿を作成することができます。
extra_responses
が導入される Mattermost v5.6 以前は、Slash Command のレスポンスとして複数の投稿を作成したい場合は REST API を実行するなどをしなくてはなりませんでしたが、extra_responses
によって REST API を実行するためのコードなどが必要なくなりました。
Slash Command のリクエストに対して下記のレスポンスを返却すると、「チャンネル全員が閲覧できる投稿を作りつつ、コマンド実行ユーザーのみにしか見えない投稿も合わせて作成し、さらに、別のチャンネルにコマンドが実行されたことを通知する」ということができるようになります。
io.WriteString(w, `{
"text": "**TEST**",
"response_type": "in_channel",
"extra_responses": [
{
"text": "**Secret Info**"
},
{
"text": "Notify in other channel",
"channel_id": "u6ijofzg8bnsie5u8c1eumtc1h"
}
]
}`)
extra_responses
内にさらにextra_responses
を指定することはできず、また、goto_location
も使用できません。
skip_slack_parsing
Mattermost の統合機能は Slack 記法との互換性を意識して作成されていますが、skip_slack_parsing
にtrue
を指定すると、レスポンスとして返却したtext
を Slack 互換の記法として解析しないようになります。あまり使うことは無いかと思いますが、レスポンスが想定どおりに投稿されない場合などに利用を検討してみても良いかもしれません。
このパラメータが追加されたきっかけとなったのは下記の Issue です。
slack-compatibility-layer seems to break results of MM-only integration · Issue #12702 · mattermost/mattermost-server
props
Incoming WebHook などと同様、投稿のメタデータを格納するフィールドです。Incoming WebHook と同様、card
も利用することができます。
Slash Command で時間のかかる処理を実行する場合
単に Slash Command のレスポンスとして投稿を作成できるだけだと、外部アプリケーションで時間のかかる処理を実行し、その結果を返したいという場合、Mattermost 上のユーザーはコマンドを実行してから結果が返るまで何の反応も得ることができません。それを回避するため、Slash Command 実行時に送信されるリクエストには、レスポンスを一度返した後で投稿を作成ことができる URL であるresponse_url
フィールドを持っています。
response_url
を使ったコードは下記のようになります。
package main
import (
"io"
"log"
"net/http"
"strings"
"time"
)
const (
WebHookToken = "8w7foap4ufrsfczda8uez51yxo"
)
func main() {
http.HandleFunc("/command", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
if r.Form.Get("token") != WebHookToken {
log.Printf("received an invalid request with token: %s", r.Form.Get("token"))
return
}
w.Header().Add("Content-Type", "application/json")
io.WriteString(w, `{"text": "Request is recieved. Results will return later."}`)
// (1) 5秒後に "response_url" に対してリクエストを送信
go func(url string) {
time.Sleep(5 * time.Second)
body := strings.NewReader(`{"text": "This is the result.", "response_type": "in_channel"}`)
http.Post(url, "application/json", body)
}(r.Form.Get("response_url"))
})
http.ListenAndServe(":8080", nil)
}
今までと同じようにレスポンスを返した後、5 秒 Sleep した後にresponse_url
の URL に対してリクエストを送信しています
response_url
に送信するリクエストには、goto_location
は使えませんでしたが、response_type
やextra_responses
などは使えるようです。
また、response_url
が有効なのは、元の Slash Command が実行されてから 30 分間のみで、5 つまでの投稿を作成することができます。処理が 30 分以上かかる場合や、5 つ以上の投稿を作成したい場合は、REST API を利用するか、Plugin を利用する必要がありそうです。
さいごに
本日は、Slash Command の詳細な使い方について紹介しました。
明日は、REST API の使い方について紹介していきます。
Discussion