Lento con forza

大学生気分のIT系エンジニアが色々書いてく何か。ブログ名決めました。

SlackでクイズができるSlack Appsを作った

こんばんは! id:kouki_dan です!
この記事は、はてなエンジニア Advent Calendar 2019の8日目の記事です。

昨日は id:hokkai7go さんのはてなSREチームにおけるスクラムの実践というタイトルでJuly Tech Festa2019に登壇しますでした。July Tech Festa2019はちょうど今日行われているみたいなので、どんな発表が行われるか楽しみですね。

今日はSlackで遊ぶ話をします。

僕とSlack

僕はSlackで遊ぶのが好きで、これまでSlack上で将棋ができるようにしたりSlack上でボンバーマンで遊べるようにしたりしてきました。今日はSlackで遊ぼうシリーズ第三弾として、最近作ったものの紹介と技術的な話をします。

ところで、僕は最近クイズにハマっています。みんなで早押しクイズという早押しクイズのアプリでは、Aランクまでやりましたし、YouTubeにあるQuizKnockの動画は全部見てます。ナナマルサンバツも全部読んでいます。

何かにハマるとSlackで遊べるようにしたくなりますよね。僕はそうなるので、今回は早押しクイズをSlackで遊べるようにします。 思えば、将棋を作った時も将棋にハマっていましたし、ボンバーマンを作った時もボンバーマンがやりたくて仕方がなかったです。

まずは作ったものはこちらからインストールできます!と、言いたいところなのですが、Slack Appsを開発用ワークスペース以外にインストールするためには審査が必要で、その審査をまだ受けてないためインストールできません。

審査に通れば、こちらからインストール可能になる予定です!

Add to Slack

実際にクイズで遊んでいる様子はこちらの動画でご確認ください!


Quiz Meister あそびかた

ギガが足りない方は画像で雰囲気を f:id:kouki_dan:20191208010956p:plain

Slack Apps

SlackにはSlack Appsという仕組みがあり、いろいろな会社やユーザーが作ったアプリをインストールする仕組みがあります。今回はこれを使って実装していきます。

slack.com

Slack Appsの作り方はとても簡単で、Webサービスを作ることができれば誰でも作ることができます。

基本的には

  • Slackで何かしらのイベントが発生する
  • イベントの情報を含んだHTTP リクエストが事前に指定したサーバーに送られる
  • それに対して何かしらの処理を行う

これだけです。メッセージの追加や、ボタンのタップなどは全てSlack側でHTTPリクエストに変換され、事前に指定したURLへとリクエストが行われます。

今回作ったクイズでは

  • Slash Commands
  • Event Subscriptions app_mentionイベント
  • Interactive Components

を使っています。

Slack Apps 開発の流れ

Slack Apps を作るには、HTTPサーバーを作れば良いです。Slack社が作っている、Slack Apps用のフレームワークであるBoltがあり、これを使うのが簡単です。

slack.dev

このチュートリアルは日本語もあり、Slack Appsの作り方を学ぶことができるのでおすすめです。

僕は最近Golangにハマっているので、今回作ったアプリはGolangで作成したのですが、新しめの機能はライブラリに機能が足りなかったりして、自分で記述しなければいけない部分が多かったり、少し大変でした。基本はHTTPリクエストを受けて、SlackのAPIを叩くだけなので、フレームワークやライブラリを使わなくても作成することは可能ですが、やはりあるものは使っていけると良いですね。

Slack Appsの開発のためには、まずはSlackの開発者用サイトから開発用ワークスペースを指定してAppを作る必要があります。

f:id:kouki_dan:20191207235305p:plain

Appを作り、いくつかの設定をすると、開発用ワークスペースに対してアプリをインストールすることができます。

f:id:kouki_dan:20191207235448p:plain

そうすることで、 OAuth Access TokenBot User OAuth Access Tokenが作成されるので、開発時にはこれをアプリに埋め込んで使うことができます。

f:id:kouki_dan:20191207235539p:plain

実際にSlack Appsを他のワークスペースにインストール可能にするためには、OAuthによるワークスペースごとのアクセストークンの取得が必要になりますが、開発用はこのトークンを直接使うのがお手軽です。また、社内や個人のみの利用など、限定的なユースケースにおいてはこのトークンを使ってSlack Appsを動作させることができるようになっています。

HTTPリクエストに応答する

Slack Appsの設定画面で受け付けるイベントと、それを受け付けるHTTPエンドポイントを指定できます。これに応答するプログラムを書きます。

普通のウェブサーバーを作れば良いのですが、ここで指定するエンドポイントはSlackからのリクエストを受け付けるために、インターネットに公開されている必要があります。このままだと悪意のあるユーザーがリクエストを偽装して送ることが可能なので、それを防ぐための仕組みが用意されています。
事前に共有しているTokenを一致しているかどうかを確かめる方法(deprecated)と、リクエストの署名を検証する方法が用意されています。前者の方が簡単で単純なのですが、後者の署名を使う方法が推奨されていています。

api.slack.com

仕様はここで公開されているため、この通りに実装することで任意の言語で実装することができます。 Golangの場合はライブラリに実装されていたので、それを利用しました。 https://github.com/nlopes/slack/blob/9c98723e5a0030d32bfcc55f62cb4db619aac1ed/security.go#L21-L25

以下のような関数を定義し、ハンドラーの最初で呼び出しています。デバッグ時とテスト時に検証をスキップしたかったので、スキップ可能なように作りましたが、その辺りはお好みで。

func VerifyRequest(req *http.Request, signingSecret string, skipVerify bool) error {
    if skipVerify {
        return nil
    }

    secretVerifier, err := slack.NewSecretsVerifier(req.Header, signingSecret)
    if err != nil {
        return err
    }

    body, err := ioutil.ReadAll(req.Body)
    if err != nil {
        return err
    }

    req.Body = ioutil.NopCloser(bytes.NewBuffer(body))

    _, err = secretVerifier.Write(body)
    if err != nil {
        return err
    }

    err = secretVerifier.Ensure()
    if err != nil {
        return err
    }

    return nil
}

UIを作る

僕がSlackで動くアプリを作るのが好きな理由の一つとして、UIを簡単に作れることがあります。絵文字や文字で作らなくてはいけないという制約のおかげで自由にレイアウトを作ることができるWebやアプリよりも、自分でUIを考えやすいと思っています。

SlackでのUIは文字ベースのものになります。Block KitというUIフレームワークをSlackが提供してくれていて、これを使うことで複雑なレイアウトも簡単にドラッグ&ドロップで作ることが可能になります。

https://api.slack.com/tools/block-kit-builder?

これを使い、テキストやボタンを配置してレイアウトを作成します。ここで作ったJSONをプログラムから利用し、SlackのAPIリクエストを行うことでユーザーに表示されます。

ユーザーからの入力を受け付ける

Slackでは基本はテキストやボタンでメッセージを受け付けますが、それ以外の方法も用意されています。それは、先ほど紹介したBlock Kitを使う方法です。

Block Kitでモーダルのレイアウトを作ることができます。そのモーダルではテキスト入力を受け付けることができ、サーバーで受け取ることができます。

クイズアプリの例では、新しいクイズを作るUIとしてこれを利用しました。

f:id:kouki_dan:20191208004524p:plain

一部のSlackからのリクエストに付与されているtrigger_idを付与し、3秒以内に views.openAPIを呼び出すことで、このモーダルを表示できます。

この仕組みを使うことでユーザーからの入力を受け付けるフォームをことも簡単に行うことができます。

まとめ

ここで説明したような仕組みを使ってSlackで動くアプリを作成してみました!Slackで動くアプリはUIに制約がある分デザインで悩むことが多いエンジニアでも手が出しやすく、また、Block Kitにより複雑なレイアウトやユーザーからの入力を受け付けることも可能になります。

現在Betaで提供中のHome Tab機能を使うことで、ユーザーに対して常時情報を表示するようなアプリを作ることもできるようになりました。 api.slack.com

皆さんもSlackで動くアプリを作って公開していきましょう!!僕も今回作ったQuiz Meisterを審査に提出して、リリースまで持っていきたいと思っています。
300ミリ秒に1度Editすることで早押しクイズの問読みを表現しているこのアプリをSlack社の人に見せる勇気がなくて審査に出すのを躊躇っていたのですが、ブログも公開したことなので近いうちに審査に提出しようと思っています。もし審査通過したらこのリンクからあなたのSlackワークスペースでもクイズを楽しんでもらいたいと思っています!また、現在クイズは僕が作成しているため、4問しか登録されていません。クイズを考えて、クイズの追加に協力してくれる方もお待ちしております!

Add to Slack

終わりに

明日のはてなエンジニア Advent Calendar 2019id:astj さんです!お楽しみに!!