LoginSignup
23
12

More than 3 years have passed since last update.

Slack ソケットモードの最も簡単な始め方 (Go 編)

Last updated at Posted at 2021-01-19

slack-go/slack がソケットモードに対応 :tada:

広く使われている Go SDK の github.com/slack-go/slack がソケットモード対応しました :tada: @mumoshu さんと @kanata2 さんの素晴らしい仕事です :clap:

ソケットモードアプリ起動までの手順

この記事では、Slack ソケットモードの最も簡単な始め方で使ったサンプルアプリと同じものを、この Go SDK を使って動かす方法を解説します。

プロジェクトをつくる

ここで紹介するサンプルは v0.8.0 以上で動作します。

go mod init socket-mode-app
go get github.com/slack-go/slack@v0.8.0

main.go を用意

とりあえず以下のソースコードをそのままコピペしてみてください。

package main

import (
    "fmt"
    "github.com/slack-go/slack/socketmode"
    "log"
    "os"
    "strings"

    "github.com/slack-go/slack"
    "github.com/slack-go/slack/slackevents"
)

func main() {
    webApi := slack.New(
        os.Getenv("SLACK_BOT_TOKEN"),
        slack.OptionAppLevelToken(os.Getenv("SLACK_APP_TOKEN")),
        slack.OptionDebug(true),
        slack.OptionLog(log.New(os.Stdout, "api: ", log.Lshortfile|log.LstdFlags)),
    )
    socketMode := socketmode.New(
        webApi,
        socketmode.OptionDebug(true),
        socketmode.OptionLog(log.New(os.Stdout, "sm: ", log.Lshortfile|log.LstdFlags)),
    )
    authTest, authTestErr := webApi.AuthTest()
    if authTestErr != nil {
        fmt.Fprintf(os.Stderr, "SLACK_BOT_TOKEN is invalid: %v\n", authTestErr)
        os.Exit(1)
    }
    selfUserId := authTest.UserID

    go func() {
        for envelope := range socketMode.Events {
            switch envelope.Type {
            case socketmode.EventTypeEventsAPI:
                // イベント API のハンドリング

                // 3 秒以内にとりあえず ack
                socketMode.Ack(*envelope.Request)

                eventPayload, _ := envelope.Data.(slackevents.EventsAPIEvent)
                switch eventPayload.Type {
                case slackevents.CallbackEvent:
                    switch event := eventPayload.InnerEvent.Data.(type) {
                    case *slackevents.MessageEvent:
                        if event.User != selfUserId && strings.Contains(event.Text, "こんにちは") {
                            _, _, err := webApi.PostMessage(
                                event.Channel,
                                slack.MsgOptionText(
                                    fmt.Sprintf(":wave: こんにちは <@%v> さん!", event.User),
                                    false,
                                ),
                            )
                            if err != nil {
                                log.Printf("Failed to reply: %v", err)
                            }
                        }
                    default:
                        socketMode.Debugf("Skipped: %v", event)
                    }
                default:
                    socketMode.Debugf("unsupported Events API eventPayload received")
                }
            case socketmode.EventTypeInteractive:
                // ショートカットのハンドリングとモーダル起動
                payload, _ := envelope.Data.(slack.InteractionCallback)
                switch payload.Type {
                case slack.InteractionTypeShortcut:
                    if payload.CallbackID == "socket-mode-shortcut" {
                        socketMode.Ack(*envelope.Request)
                        modalView := slack.ModalViewRequest{
                            Type:       "modal",
                            CallbackID: "modal-id",
                            Title: slack.NewTextBlockObject(
                                "plain_text",
                                "タスク登録",
                                false,
                                false,
                            ),
                            Submit: slack.NewTextBlockObject(
                                "plain_text",
                                "送信",
                                false,
                                false,
                            ),
                            Close: slack.NewTextBlockObject(
                                "plain_text",
                                "キャンセル",
                                false,
                                false,
                            ),
                            Blocks: slack.Blocks{
                                BlockSet: []slack.Block{
                                    slack.NewInputBlock(
                                        "input-task",
                                        slack.NewTextBlockObject(
                                            "plain_text",
                                            "タスク",
                                            false,
                                            false,
                                        ),
                                        // multiline is not yet supported
                                        slack.NewPlainTextInputBlockElement(
                                            slack.NewTextBlockObject(
                                                "plain_text",
                                                "タスクの詳細・期限などを書いてください",
                                                false,
                                                false,
                                            ),
                                            "input",
                                        ),
                                    ),
                                },
                            },
                        }
                        resp, err := webApi.OpenView(payload.TriggerID, modalView)
                        if err != nil {
                            log.Printf("Failed to opemn a modal: %v", err)
                        }
                        socketMode.Debugf("views.open response: %v", resp)
                    }
                case slack.InteractionTypeViewSubmission:
                    // モーダルからの送信をハンドリング
                    if payload.CallbackID == "modal-id" {
                        socketMode.Debugf("Submitted Data: %v", payload.View.State.Values)
                        socketMode.Ack(*envelope.Request)
                    }
                default:
                    socketMode.Debugf("Skipped: %v", payload)
                }

            default:
                socketMode.Debugf("Skipped: %v", envelope.Type)
            }
        }
    }()

    socketMode.Run()
}

起動する

前の記事と同様、環境変数を設定した上で起動してみましょう。

export SLACK_APP_TOKEN=xapp-<自分のトークンの値>
export SLACK_BOT_TOKEN=xoxb-<自分のトークンの値>
go run main.go

以下のようなメッセージが表示されていれば、接続できています!

$ go run main.go
api: 2021/01/19 15:41:48 slack.go:125: Challenging auth...
sm: 2021/01/19 15:41:48 socket_mode_managed_conn.go:241: Starting SocketMode
sm: 2021/01/19 15:41:48 main.go:133: Skipped: connecting
api: 2021/01/19 15:41:48 socket_mode.go:30: Using URL: wss://wss-primary.slack.com/link/?ticket=xxx&app_id=yyy
sm: 2021/01/19 15:41:48 socket_mode_managed_conn.go:249: Dialing to websocket on url wss://wss-primary.slack.com/link/?ticket=xxx&app_id=yyy
sm: 2021/01/19 15:41:49 socket_mode_managed_conn.go:78: WebSocket connection succeeded on try 0
sm: 2021/01/19 15:41:49 socket_mode_managed_conn.go:422: Starting to receive message
sm: 2021/01/19 15:41:49 main.go:133: Skipped: connected
sm: 2021/01/19 15:41:49 socket_mode_managed_conn.go:464: Incoming WebSocket message: {
  "type": "hello",
  "num_connections": 1,
  "debug_info": {
    "host": "applink-xxx-yyy",
    "build_number": 10,
    "approximate_connection_time": 18060
  },
  "connection_info": {
    "app_id": "A111"
  }
}

sm: 2021/01/19 15:41:49 socket_mode_managed_conn.go:476: Finished to receive message
sm: 2021/01/19 15:41:49 socket_mode_managed_conn.go:422: Starting to receive message
sm: 2021/01/19 15:41:49 socket_mode_managed_conn.go:319: Received WebSocket message: {"type":"hello","num_connections":1,"debug_info":{"host":"applink-xxx-yyy","build_number":10,"approximate_connection_time":18060},"connection_info":{"app_id":"A111"}}
sm: 2021/01/19 15:41:49 main.go:133: Skipped: hello
sm: 2021/01/19 15:41:51 socket_mode_managed_conn.go:544: WebSocket ping message received: Ping from applink-xxx-yyy

次のステップ

オフィシャルのサンプルコードは以下の場所にありますので、チェックしてみてください。

23
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
12