この記事は Slack Advent Calendar 2016 - Qiita の3日目の記事です。
昨日は Kinoppyd さんの「今そこにあるSlack」でした。
さて、今回、この記事では golang で Slack bot を実装する方法を紹介しようと思います。
世に蔓延る Slack bot
これから bot を世に放とうとしている人は、是非、1日目と2日目の記事を読み、事前知識を頭に叩き込んでおくと良いと思います。
基本的に、自身で作成した bot はもちろん好きになると思いますが、人によっては理解不能な bot や、意味不明な場面で反応したりと、「邪魔だな」と思われてしまうことがあります。そのため、bot を開発する人は「謙虚・尊敬・信頼」(Team Geekより)を持った上で、世に放ちましょう。
golang で bot を開発
Golang の開発環境は整っているとします。
bot の新規作成
Slack の bot の仕様については下記のページに色々と書かれています。
アプリケーションに統合された bot の開発も可能ですが、今回は簡単に実装するために Slack Bots からトークンを取得します。
Slack Bots の新規作成はこちらから作成ができ、下記のようなページで新規作成となります。
作成が完了すると、下記の画像の設定ページに遷移するので、ここにあるトークンを使用して bot を稼働させます。
実装
Go で開発をすすめます。 今回、簡単に Slack API 連携するために github.com/nlopes/slack を先に go get しておきます。
$ go get -u github.com/nlopes/slack
コード全体
package main import ( "log" "os" "github.com/nlopes/slack" ) func run(api *slack.Client) int { rtm := api.NewRTM() go rtm.ManageConnection() for { select { case msg := <-rtm.IncomingEvents: switch ev := msg.Data.(type) { case *slack.HelloEvent: log.Print("Hello Event") case *slack.MessageEvent: log.Printf("Message: %v\n", ev) rtm.SendMessage(rtm.NewOutgoingMessage("Hello world", ev.Channel)) case *slack.InvalidAuthEvent: log.Print("Invalid credentials") return 1 } } } } func main() { api := slack.New("[YOUR-API-TOKEN]") os.Exit(run(api)) }
順を追って解説します。
slack.Client の生成
func main() { api := slack.New("[YOUR-API-TOKEN]") os.Exit(run(api)) }
slack.New
関数に先ほど取得したAPIトークンを渡して、 slack.Client
を生成します。これは Slack API のクライアントになるので、bot 以外でももちろん API を叩くことが可能になります。
api := slack.New("[YOUR-API-TOKEN]")
Real Time Messaging API
func run(api *slack.Client) int { rtm := api.NewRTM() go rtm.ManageConnection() for { select { case msg := <-rtm.IncomingEvents: switch ev := msg.Data.(type) { case *slack.HelloEvent: log.Print("Hello Event") case *slack.MessageEvent: log.Printf("Message: %v\n", ev) rtm.SendMessage(rtm.NewOutgoingMessage("Hello world", ev.Channel)) case *slack.InvalidAuthEvent: log.Print("Invalid credentials") return 1 } } } }
Real Time Messaging を使用して、 Websocket から情報を取得します。
rtm := api.NewRTM()
go rtm.ManageConnection()
コネクションを張り次第、ひたすらループして情報を取得します。情報はチャネルの IncomingEvents
に enqueue されるので、それを dequeue して型によって振る舞いを変えます。
for { select { case msg := <-rtm.IncomingEvents: switch ev := msg.Data.(type) { case *slack.HelloEvent: log.Print("Hello Event") case *slack.MessageEvent: log.Printf("Message: %v\n", ev) rtm.SendMessage(rtm.NewOutgoingMessage("Hello world", ev.Channel)) } } }
今回の例では誰かがこの bot が招待されているチャネルに投稿すると、slack.MessageEvent
が発火して、 "Hello world" をそのチャネルに投稿しています。
rtm.SendMessage(rtm.NewOutgoingMessage("Hello world", ev.Channel))
実行
上に記述したコードを main.go という名前で実装していると仮定すると。
$ cd /path/to/slack-bot $ go run main.go 2016/12/03 06:32:53 Hello Event 2016/12/03 06:32:53 Message: &{{message XX XX Hello world 1480746760.000010 false [] [] <nil> false <nil> [] <nil> false <nil> 1 []} <nil>}
のように出力されれば動いています。
デーモン化
bot が死んでいたら悲しいので、デーモン化をちゃちゃっとしちゃいます。
まず、作成した bot をコンパイルしてバイナリにしておきます。
$ go build -o /path/to/bot main.go
次に、デーモン化ですが、今回は supervisor での例を載せておきます。
[program:bot] command=/path/to/bot user=someone autostart=true autorestart=true stderr_logfile=/var/log/bot.err.log stdout_logfile=/var/log/bot.out.log
上記を conf に用意して、 supervisord を再起動させましょう。これでデーモン化された bot の完成です。
おわりに
慣れている言語でちゃちゃっと bot は実装したいと思うので、各言語での bot 実装法はもっと出回っていいのではと思っています。最近、 Crystal にハマっているので、今度機会があれば Crystal バージョンを書こうと思います。