Mattermost 記事まとめ: https://blog.kaakaa.dev/tags/mattermost/
本記事について
Mattermost の統合機能アドベントカレンダーの第 15 日目の記事です。
本記事では、Mattermost の投稿にユーザーが操作できるセレクトボックスを追加する Interactive Message Menu の機能について紹介します。
Interactive Message Menu 概要
Interactive Message Menu は、昨日紹介した Interactive Message Buttont と同様の機能です。
Interactive Message Button は、Mattermost の投稿にボタンを表示する機能でしたが、Interactive Message Menu では、Mattermost の投稿にセレクトボックスを表示することができます。
Interactive Message に関する公式ドキュメントは下記になります。
作成
Interactive Message Menu を含む投稿を作成する方法は Interactive Message Button の時とほぼ同じです。
Incoming Webhook(内向きのウェブフック)を使って作成する例は下記のようになります。
BODY='{
"attachments": [{
"title": "Echo text",
"actions": [{
"id": "echo",
"name": "Would you like to echo?",
"integration": {
"url": "http://localhost:8080/actions/echo",
"context": {
"text": "sample text"
}
},
"type": "select",
"options": [{
"text": "Echo",
"value": "echo"
}, {
"text": "Reject",
"value": "reject"
}]
}]
}]
}'
curl \
-H "Content-Type: application/json" \
-d "$BODY" \
http://localhost:8065/hooks/ucw5qjw86jgeum77o1uw8197jr
上記のリクエストを実行すると、下記のようなセレクトボックスを含む投稿が作成されます。
options
に指定したオプションがセレクトボックスから選べるようになっています。
セレクトボックスから要素を選択すると、対応するオプションのvalue
に指定した値を含むリクエストがIntegration
に指定した URL へ送信されることになります。
パラメータ
actions
フィールドに指定できるパラメータは、Interactive Message Button と同じPostAction
構造体として宣言されています。
https://github.com/mattermost/mattermost-server/blob/master/model/integration_action.go#L37
昨日の記事にも載せましたが、再掲します。
id
: 1 投稿内でユニークとなるボタンの ID を指定します。指定しない場合、Mattermost 側で一意の ID が付与されます。type
: Interactive Message の種別を指定します。select
かbutton
を指定でき、何も指定しないとbutton
が指定されます。name
: Mattermost 内でボタンのラベルとして表示される文字列を指定します。disabled
: ボタンを無効化するオプションです。true
を指定するとボタンが押せなくなります。style
: ボタンの色を指定できます。default
,primary
,success
,good
,warning
,danger
, もしくは任意の Hex color("#FF9900"
など)を指定できます。 Outgoing WebHook を利用するには、システムコンソール > 統合機能管理 > 外向きのウェブフックを有効にする の設定が有効
になっている必要があります。data_source
: Interactive Message Buttonでは使用しません。Interactie Message Menuでのみ使用します。options
: Interactive Message Buttonでは使用しません。Interactie Message Menuでのみ使用します。default_option
: Interactive Message Buttonでは使用しません。Interactie Message Menuでのみ使用します。integration
: アクションを実行した際に送信されるリクエストに関する情報を指定します。url
: リクエスト送信先の URL を指定します。context
: 送信されるリクエストに含まれる追加情報を指定しますintegration
に指定した情報はリクエスト送信時にしか読み出されないため、(https などで通信経路のセキュリティが担保されていれば)アクセストークンのような秘密情報なども含めることができます。
この中で、Interactive Message Menu のみで使用できるパラメータはdata_source
, options
, default_option
になります。
options
は、先ほどの例にも示したようにセレクトボックスで選択する要素を定義するフィールドです。
default_option
は、デフォルトで選択されているオプションを指定することができます。options
内のいずれかのオプションのvalue
の値を指定します。
data_source
は特殊なオプションです。users
かchannels
を指定でき、これを指定するとセレクトボックスから選択できるオプションがそれぞれ Mattermost 上のユーザーか Mattermost 上の公開チャンネルになります。data_source
を指定した場合、options
に指定されたオプションは無視されます。
BODY='{
"attachments": [{
"title": "Echo text",
"actions": [{
"id": "echo",
"name": "Would you like to echo?",
"integration": {
"url": "http://localhost:8080/actions/echo",
"context": {
"text": "sample text"
}
},
"type": "select",
"data_source": "channels"
}]
}]
}'
curl \
-H "Content-Type: application/json" \
-d "$BODY" \
http://localhost:8065/hooks/ucw5qjw86jgeum77o1uw8197jr
実行
今回は、slash command の引数に指定したテキストをセレクトボックスで選択されたチャンネルへ Echo するような Interactive Message Menu を紹介します。
slash command、Interactive Message Menu からのリクエストを受け取るサーバーアプリのサンプルコードは下記のようになります。
(今回の例では slash command、Interactive Message Button 共に、Mattermost からlocalhost
に対してリクエストが送信されるため、システムコンソール > 開発者 > 信頼されていない内部接続を許可するの設定に、リクエスト送信先のサーバーを記述しておく必要があります。詳しくは公式ドキュメントを参照ください。)
package main
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"github.com/mattermost/mattermost-server/v5/model"
)
func main() {
http.HandleFunc("/command", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
response := model.CommandResponse{
ResponseType: model.COMMAND_RESPONSE_TYPE_IN_CHANNEL,
Attachments: []*model.SlackAttachment{{
Title: "Echo server",
Actions: []*model.PostAction{{
// (1) Echoメニュー
Name: "Echo",
Integration: &model.PostActionIntegration{
URL: "http://localhost:8080/actions/echo",
Context: map[string]interface{}{
"text": r.Form.Get("text"),
},
},
Type: model.POST_ACTION_TYPE_SELECT,
Options: []*model.PostActionOptions{{
Text: "Echo",
Value: "echo",
}, {
Text: "Reject",
Value: "reject",
}},
}},
}},
}
// Need to set header. if not, just json string will be posted.
w.Header().Add("Content-Type", "application/json")
io.WriteString(w, response.ToJson())
})
// (2) Echo Buttonが押されたときの処理
http.HandleFunc("/actions/echo", func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
// (3) リクエストデータの読み出し
b, _ := ioutil.ReadAll(r.Body)
var payload model.PostActionIntegrationRequest
json.Unmarshal(b, &payload)
text, ok := payload.Context["text"].(string)
if !ok {
resp := &model.PostActionIntegrationResponse{EphemeralText: "invalid request. Context['text'] is not found."}
fmt.Fprint(w, resp.ToJson())
return
}
selected, ok := payload.Context["selected_option"].(string)
if !ok {
resp := &model.PostActionIntegrationResponse{EphemeralText: "invalid request. Context['selected_option'] is not found."}
fmt.Fprint(w, resp.ToJson())
return
}
// (4) レスポンスの構築
response := &model.PostActionIntegrationResponse{}
switch selected {
case "echo":
response.Update = &model.Post{
Message: text,
Props: model.StringInterface{},
}
case "reject":
response.Update = &model.Post{Props: model.StringInterface{}}
response.EphemeralText = "Echoing was rejected"
default:
response.EphemeralText = "invalid operation"
}
w.Header().Add("Content-Type", "application/json")
io.WriteString(w, string(response.ToJson()))
})
http.ListenAndServe(":8080", nil)
}
(1)
では Interactive Message Menu の内容を定義しています。Integration.URL
にリクエスト送信先としてhttp://localhost:8080/actions/echo
を、Integration.Context
に、slash command の引数として指定された文字列を保持しています。また、Echo
とReject
のオプションをoptions
に定義しています。
(2)
で、セレクトボックスからオプションが選択された時に送信されるリクエストを処理しています。(3)
で送信されたリクエストを読み出しています。送信されるリクエストの形式は、Interactive Message Menu から送信されるリクエストも Interactive Message Button と同じくPostActionIntegrationRequest
として定義されています。
type PostActionIntegrationRequest struct {
UserId string `json:"user_id"`
UserName string `json:"user_name"`
ChannelId string `json:"channel_id"`
ChannelName string `json:"channel_name"`
TeamId string `json:"team_id"`
TeamName string `json:"team_domain"`
PostId string `json:"post_id"`
TriggerId string `json:"trigger_id"`
Type string `json:"type"`
DataSource string `json:"data_source"`
Context map[string]interface{} `json:"context,omitempty"`
}
今回はContext
からtext
をキーとして格納されている slash command 実行時の引数と、selected_option
をキーとして格納されている選択されたオプションを読み出しています。
そして、(4)
で読み出したselected_option
の値に応じてレスポンスを構築しています。レスポンスの形式も Interactie Message Button と同じくPostActionIntegrationResponse
として定義されています。
type PostActionIntegrationResponse struct {
Update *Post `json:"update"`
EphemeralText string `json:"ephemeral_text"`
SkipSlackParsing bool `json:"skip_slack_parsing"` // Set to `true` to skip the Slack-compatibility handling of Text.
}
さいごに
本日は、Interactive Message Menu の使い方について紹介しました。 明日は、Interactive Dialog について紹介します。