SlackのSlash CommandsをApp Engineで稼働させる
こんにちは、pairs事業部のkaneshinです!
GWなので自己研鑽として勉強しようと意気込んでいる人も多いと思いますが何を勉強していいのかわからない方もいると思うので、本日はそんな方へSlackのIntegrationの一つ「Slash Commands」をGoogle App Engineで動かす簡単なサンプルを紹介しようと思います。
Slack – Slash Commands
SlackではSlash Commandsという機能を各自が追加することが可能です。
Real Time Messaging APIを利用したHubotをBotとしてSlackと連携させることは最近では珍しくありませんが、簡単な機能追加ならばSlash Commandsだけで実現可能です。
使い方(実装方法)
Slash Commandsの使い方は「https://api.slack.com/slash-commands」に記載がありますが、大きく分けると下記のようになります。
1. コマンドの定義
/hello
のように、コマンドを定義します。既にSlackが用意しているものや別のIntegrationで定義されているものを上書きすることはできません。
2. コマンド実行
仮に、/hello
コマンドが定義されたとして、これを実行されると指定しているURLにPOSTのリクエストが下記のようなデータと共に送信されてきます。
token=gIkuvaNzQIHg97ATvDxqgjtO
team_id=T0001
team_domain=example
channel_id=C2147483705
channel_name=test
user_id=U2147483697
user_name=Steve
command=/weather
text=94070
response_url=https://hooks.slack.com/commands/1234/5678
※これらのデータを使って処理を実装しますが、データを完全無視してもPOSTのリクエストをハンドリングしてSlash Commandsを実装することも可能です。
3. トークンを検証
不正なリクエストでないかを token
を用いて行います。 token
自体は無視しても可能ですが、無闇に叩かれたら困る場合は検証するロジックを実装しておいたほうが無難です。
4. コマンドにレスポンスを返却
レスポンスに下記のようなデータを追記して返却することでSlackにメッセージが表示されます。
{
"response_type": "in_channel",
"text": "It's 80 degrees right now.",
"attachments": [
{
"text":"Partly cloudy today and tomorrow"
}
]
}
response_type
にephemeral
を指定すると自分だけしか見えないメッセージになりますが、あまり使用しないと思います。
attachments
に指定できるJSONはこちらを参考にしてください。
Slash Commands + App Engine

Slash CommandsのIntegrationについては説明したので早速実装に入りたいと思います。
App EngineでHTTPをハンドリングするために今回はGoCon 2016 Spring でvvakameさんが紹介していたuconというフレームワークを使用します。
実装
HTTPリクエスト(for App Engine)
ucon
はnet/http
の世界観を崩さないで利用できるためとても素晴らしいです。
package app
import (
"net/http"
"github.com/favclip/ucon"
)
func init() {
ucon.Orthodox()
ucon.HandleFunc("POST", "/command", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World!"))
})
ucon.DefaultMux.Prepare()
http.Handle("/", ucon.DefaultMux)
}
今回は下記のように/command
というパスにPOST
メソッドでリクエストが来ることを想定したハンドリングを用意します。
ucon.HandleFunc("POST", "/command", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World!"))
}
つまり、 https://[project-id].appspot.com/command
でPOSTリクエストを受け取ることを想定しています。
App Engineへデプロイ
appdir
にApp Engineの設定となるapp.yaml
が存在していると仮定すれば下記のようなコマンドでデプロイが可能です。
$ goapp deploy -application=[project-id] /path/to/appdir
Slash Commandsの設定
URLが定まったので、Slash CommandsのIntegrationを設定します。今回は/yakisoba
というコマンドを作成します。


これでSlack上で /yakisoba
コマンドが定義されました。コマンドのヘルプを表示したい場合は下記のように設定することでヘルプを表示することが可能になります。


ね、簡単でしょう?
Slackへのレスポンス
さて、早速定義されコマンドを叩いてもまだ Hello World!
しか返却していないので下記のように自分にしか見えないメッセージが返却されます。

では、まずは全員に見えるメッセージとして「オープンソース!」と発言するように実装します。
ucon.HandleFunc("POST", "/command", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
data := map[string]string{
"response_type": "in_channel",
"text": "オープンソース!",
}
var buf bytes.Buffer
json.NewEncoder(&buf).Encode(data)
w.Write(buf.Bytes())
})
このように実装して、/yakisoba
を実行すると下記のようにメッセージが表示されます。

これだけだと少しつまらないので、こちらがコマンドを叩くときに「オープンソース」という文字を含めたら「焼きそば!」/yakisoba オープンソース
をメッセージングするようにします。
ucon.HandleFunc("POST", "/command", func(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
w.Write([]byte(err.Error()))
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
data := map[string]string{
"response_type": "in_channel",
}
if t := r.PostFormValue("text"); strings.Contains(t, "オープンソース") {
data["text"] = "焼きそば!"
} else {
data["text"] = "オープンソース!"
}
var buf bytes.Buffer
json.NewEncoder(&buf).Encode(data)
w.Write(buf.Bytes())
})

今回のサンプルプロジェクトはこちらにて公開しています。
また、今回かなり省きましたが、前述したとおりtoken
は各自で検証した方が良いです。
おわりに
Slash Commandsの触りだけを紹介しましたが、あとは各自のアイディア次第でいろいろ拡張することができると思います。例えば、サブコマンドを定義する(/yakisoba echo
)しておいて、それに反応するようにしたり、Incoming Webhooksと連携してコマンド経由で何かメッセージを投稿したりなど。
実際、社内Slackにはkaonashi
が定義されていたりします。
App Engineでホスティングしていれば開発だけに集中できるので、非常にオススメです。
GWにやることが決まっていない方は簡単なSlash Commandsを作成するのはいかがでしょうか。
エウレカでは、一緒に働いていただける方を絶賛募集中です。募集中の職種はこちらからご確認ください!皆様のエントリーをお待ちしております!
こんにちは、pairs事業部のkaneshinです!
GWなので自己研鑽として勉強しようと意気込んでいる人も多いと思いますが何を勉強していいのかわからない方もいると思うので、本日はそんな方へSlackのIntegrationの一つ「Slash Commands」をGoogle App Engineで動かす簡単なサンプルを紹介しようと思います。
Slack – Slash Commands
SlackではSlash Commandsという機能を各自が追加することが可能です。
Real Time Messaging APIを利用したHubotをBotとしてSlackと連携させることは最近では珍しくありませんが、簡単な機能追加ならばSlash Commandsだけで実現可能です。
使い方(実装方法)
Slash Commandsの使い方は「https://api.slack.com/slash-commands」に記載がありますが、大きく分けると下記のようになります。
1. コマンドの定義
/hello
のように、コマンドを定義します。既にSlackが用意しているものや別のIntegrationで定義されているものを上書きすることはできません。
2. コマンド実行
仮に、/hello
コマンドが定義されたとして、これを実行されると指定しているURLにPOSTのリクエストが下記のようなデータと共に送信されてきます。
token=gIkuvaNzQIHg97ATvDxqgjtO team_id=T0001 team_domain=example channel_id=C2147483705 channel_name=test user_id=U2147483697 user_name=Steve command=/weather text=94070 response_url=https://hooks.slack.com/commands/1234/5678
※これらのデータを使って処理を実装しますが、データを完全無視してもPOSTのリクエストをハンドリングしてSlash Commandsを実装することも可能です。
3. トークンを検証
不正なリクエストでないかを token
を用いて行います。 token
自体は無視しても可能ですが、無闇に叩かれたら困る場合は検証するロジックを実装しておいたほうが無難です。
4. コマンドにレスポンスを返却
レスポンスに下記のようなデータを追記して返却することでSlackにメッセージが表示されます。
{ "response_type": "in_channel", "text": "It's 80 degrees right now.", "attachments": [ { "text":"Partly cloudy today and tomorrow" } ] }
response_type
にephemeral
を指定すると自分だけしか見えないメッセージになりますが、あまり使用しないと思います。
attachments
に指定できるJSONはこちらを参考にしてください。
Slash Commands + App Engine
Slash CommandsのIntegrationについては説明したので早速実装に入りたいと思います。
App EngineでHTTPをハンドリングするために今回はGoCon 2016 Spring でvvakameさんが紹介していたuconというフレームワークを使用します。
実装
HTTPリクエスト(for App Engine)
ucon
はnet/http
の世界観を崩さないで利用できるためとても素晴らしいです。
package app import ( "net/http" "github.com/favclip/ucon" ) func init() { ucon.Orthodox() ucon.HandleFunc("POST", "/command", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello World!")) }) ucon.DefaultMux.Prepare() http.Handle("/", ucon.DefaultMux) }
今回は下記のように/command
というパスにPOST
メソッドでリクエストが来ることを想定したハンドリングを用意します。
ucon.HandleFunc("POST", "/command", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello World!")) }
つまり、 https://[project-id].appspot.com/command
でPOSTリクエストを受け取ることを想定しています。
App Engineへデプロイ
appdir
にApp Engineの設定となるapp.yaml
が存在していると仮定すれば下記のようなコマンドでデプロイが可能です。
$ goapp deploy -application=[project-id] /path/to/appdir
Slash Commandsの設定
URLが定まったので、Slash CommandsのIntegrationを設定します。今回は/yakisoba
というコマンドを作成します。
これでSlack上で /yakisoba
コマンドが定義されました。コマンドのヘルプを表示したい場合は下記のように設定することでヘルプを表示することが可能になります。
ね、簡単でしょう?
Slackへのレスポンス
さて、早速定義されコマンドを叩いてもまだ Hello World!
しか返却していないので下記のように自分にしか見えないメッセージが返却されます。
では、まずは全員に見えるメッセージとして「オープンソース!」と発言するように実装します。
ucon.HandleFunc("POST", "/command", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) data := map[string]string{ "response_type": "in_channel", "text": "オープンソース!", } var buf bytes.Buffer json.NewEncoder(&buf).Encode(data) w.Write(buf.Bytes()) })
このように実装して、/yakisoba
を実行すると下記のようにメッセージが表示されます。
これだけだと少しつまらないので、こちらがコマンドを叩くときに「オープンソース」という文字を含めたら「焼きそば!」/yakisoba オープンソース
をメッセージングするようにします。
ucon.HandleFunc("POST", "/command", func(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { w.Write([]byte(err.Error())) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) data := map[string]string{ "response_type": "in_channel", } if t := r.PostFormValue("text"); strings.Contains(t, "オープンソース") { data["text"] = "焼きそば!" } else { data["text"] = "オープンソース!" } var buf bytes.Buffer json.NewEncoder(&buf).Encode(data) w.Write(buf.Bytes()) })
今回のサンプルプロジェクトはこちらにて公開しています。
また、今回かなり省きましたが、前述したとおりtoken
は各自で検証した方が良いです。
おわりに
Slash Commandsの触りだけを紹介しましたが、あとは各自のアイディア次第でいろいろ拡張することができると思います。例えば、サブコマンドを定義する(/yakisoba echo
)しておいて、それに反応するようにしたり、Incoming Webhooksと連携してコマンド経由で何かメッセージを投稿したりなど。
実際、社内Slackにはkaonashi
が定義されていたりします。
App Engineでホスティングしていれば開発だけに集中できるので、非常にオススメです。
GWにやることが決まっていない方は簡単なSlash Commandsを作成するのはいかがでしょうか。
エウレカでは、一緒に働いていただける方を絶賛募集中です。募集中の職種はこちらからご確認ください!皆様のエントリーをお待ちしております!