💬
Goで作るSlack Modal Applicationサンプルコード 4/4
STEP4 完了メッセージを送信
では、いよいよ最後です。注文をうけつけたメッセージを送信します。
- リクエストの認証(自分のWorkspaceからのリクエストかどうか)
- リクエストをStructにマッピング
- 注文内容を取得
- Chipをバリデーション(数値かどうか)
- メッセージを作成、送付
の5つ。だいたいやることがテンプレ化されてきましたね。
上3つは、前のSTEPとかぶるので、説明から省きます。
handlerのソースはこちらです。(各関数の実装含めた完全版は、GitHub参照)
func handleConfirmationModalSubmissionRequest(message slack.InteractionCallback) (events.APIGatewayProxyResponse, error) {
// Validate a message.
if err := validateChip(message); err != nil {
// Create validation failed response.
errors := map[string]string{
"block_id_chip": "[ERROR] Please enter a number.",
}
resAction := slack.NewErrorsViewSubmissionResponse(errors)
bytes, err := json.Marshal(resAction)
if err != nil {
return events.APIGatewayProxyResponse{StatusCode: 200}, fmt.Errorf("failed to marshal a validation failed message: %w", err)
}
return events.APIGatewayProxyResponse{
StatusCode: 200,
IsBase64Encoded: false,
Headers: map[string]string{
"Content-Type": "application/json",
},
Body: string(bytes),
}, nil
}
// Get private metadata
var privateMeta privateMeta
if err := json.Unmarshal([]byte(message.View.PrivateMetadata), &privateMeta); err != nil {
return events.APIGatewayProxyResponse{StatusCode: 200}, fmt.Errorf("failed to unmarshal private metadata: %w", err)
}
// Send a complession message.
// - Create message options
option, err := createOption(message, privateMeta)
if err != nil {
return events.APIGatewayProxyResponse{StatusCode: 200}, fmt.Errorf("failed to create message options: %w", err)
}
// - Post a message
api := slack.New(tokenBotUser)
if _, _, err := api.PostMessage(privateMeta.ChannelID, option); err != nil {
return events.APIGatewayProxyResponse{StatusCode: 200}, fmt.Errorf("failed to send a message: %w", err)
}
return events.APIGatewayProxyResponse{StatusCode: 200}, nil
}
Chipをバリデーション(数値かどうか)
まずはChipに「数値以外」が入力されていないかを確認します。数値以外が入力されていた場合は、エラーメッセージが出るようにしてみましょう。
今回はParseFloatでerrorを返さないかで判断してみましょう。以下のような関数としてみました。
func validateChip(message slack.InteractionCallback) error {
// Get an input value.
chip := message.View.State.Values["block_id_chip"]["action_id_chip"].Value
// Chech if the value is number or not.
if _, err := strconv.ParseFloat(chip, 64); err != nil {
return err
}
return nil
}
バリデーションに失敗した場合は、
- どのフィールドになんのエラーメッセージを表示するか定義したmapを作成
- NewErrorsViewSubmissionResponseでレスポンスを生成
- レスポンスとして返す
の流れでエラーメッセージを表示します。
// Validate a message.
if err := validateChip(message); err != nil {
// Create validation failed response.
errors := map[string]string{
"block_id_chip": "[ERROR] Please enter a number.",
}
resAction := slack.NewErrorsViewSubmissionResponse(errors)
bytes, err := json.Marshal(resAction)
if err != nil {
return events.APIGatewayProxyResponse{StatusCode: 200}, fmt.Errorf("failed to marshal a validation failed message: %w", err)
}
return events.APIGatewayProxyResponse{
StatusCode: 200,
IsBase64Encoded: false,
Headers: map[string]string{
"Content-Type": "application/json",
},
Body: string(bytes),
}, nil
}
メッセージを作成、送付
さて、いよいよこれで最後です。注文完了のメッセージを作成して送信します。
Private Metadataに格納して引き継ぎ続けてきた「チャンネルID」、「注文内容」はここで使います。
まずは、Private Metadataを取得しましょう。
// Get private metadata
var privateMeta privateMeta
if err := json.Unmarshal([]byte(message.View.PrivateMetadata), &privateMeta); err != nil {
return events.APIGatewayProxyResponse{StatusCode: 200}, fmt.Errorf("failed to unmarshal private metadata: %w", err)
}
つづいてメッセージの見た目を生成。もう慣れたものですね。
// Send a complession message.
// - Create message options
option, err := createOption(message, privateMeta)
if err != nil {
return events.APIGatewayProxyResponse{StatusCode: 200}, fmt.Errorf("failed to create message options: %w", err)
}
func createOption(message slack.InteractionCallback, privateMeta privateMeta) (slack.MsgOption, error) {
// Text section
titleText := slack.NewTextBlockObject("mrkdwn", ":hamburger: *Thank you for your order !!*", false, false)
titleTextSection := slack.NewSectionBlock(titleText, nil, nil)
// Divider
dividerBlock := slack.NewDividerBlock()
// Text section
sMenuText := slack.NewTextBlockObject("mrkdwn", "*Menu*\n"+burgers[privateMeta.Menu], false, false)
sMenuTextSection := slack.NewSectionBlock(sMenuText, nil, nil)
// Text section
sSteakText := slack.NewTextBlockObject("mrkdwn", "*How do you like your steak?*\n"+privateMeta.Steak, false, false)
sSteakTextSection := slack.NewSectionBlock(sSteakText, nil, nil)
// Text section
sNoteText := slack.NewTextBlockObject("mrkdwn", "*Anything else you want to tell us?*\n"+privateMeta.Note, false, false)
sNoteTextSection := slack.NewSectionBlock(sNoteText, nil, nil)
// Text section
amount, err := strconv.ParseFloat(privateMeta.Amount, 64)
if err != nil {
return nil, fmt.Errorf("failed to convert amount to float64: %w", err)
}
chip, err := strconv.ParseFloat(message.View.State.Values["block_id_chip"]["action_id_chip"].Value, 64)
if err != nil {
return nil, fmt.Errorf("failed to convert amount to float64: %w", err)
}
amountText := slack.NewTextBlockObject("mrkdwn", "*Total amount :moneybag:*\n$ "+strconv.FormatFloat(amount+chip, 'f', 2, 64), false, false)
amountTextSection := slack.NewSectionBlock(amountText, nil, nil)
// Blocks
blocks := slack.MsgOptionBlocks(
titleTextSection,
dividerBlock,
sMenuTextSection,
sSteakTextSection,
sNoteTextSection,
dividerBlock,
amountTextSection,
)
return blocks, nil
}
最後に送信は、PostMessageです!
// - Post a message
api := slack.New(tokenBotUser)
if _, _, err := api.PostMessage(privateMeta.ChannelID, option); err != nil {
return events.APIGatewayProxyResponse{StatusCode: 200}, fmt.Errorf("failed to send a message: %w", err)
}
長かったですが、これがModalアプリのシンプルな例。
ソースの全文は、[GitHub](https://github.com/nicoJN/slack-modal-examples)に置いてあるので、適宜改良して使ってみてください。
:::message
本記事は、[著者のブログ](https://jimon.info/slack-modal-example-go/)からの転載です
長くなったので、STEP別に4つの記事に分割しています
- **STEP1:** [作るものの紹介+Botを呼び出して、トリガーメッセージを送付](https://zenn.dev/jimon/articles/d4bc599285e9aa79b0fb)
- **STEP2:** [お店情報のModalを送付](https://zenn.dev/jimon/articles/f948664f3968882d4afc)
- **STEP3:** [注文確認画面のModalを送付](https://zenn.dev/jimon/articles/93dccdc70cd0a8ce8d31)
- **STEP4:** 完了メッセージを送付**←今ココ!**
:::
Discussion