コネヒト開発者ブログ

コネヒト開発者ブログ

物覚えが悪いのでSlackに色々と覚えてもらうappを作った話

こんにちは!「M-1グランプリやるなら教えてほしかったんだけど?」と思っている金城(@o0h_)です。
ちょっと寂しいので、最近読みました「ここから先は倫理です。」のリンクを貼ります。

ここは今から倫理です。 1 (ヤングジャンプコミックスDIGITAL)

ここは今から倫理です。 1 (ヤングジャンプコミックスDIGITAL)

さて昨日は、英語が聴ける・話せるようになりたい・・・・と思っているところにガツンとくるトミーさんの記事でした。
引き続いてのAdvent Calendar Day16、わたくしからはSlackのbotの話でもいたします。

https://cdn.mamari.jp/authorized/5a34f7a1-bc6c-46ad-b6f5-0019ac120002.jpg

標準の「自動レスポンス」を再発明してリッチにする

皆さんも、自動レスポンス(という名前でいいのかな・・)は利用しているのではないかと思います。
設定画面で「キーワード」と「反応」を入れると反応してくれるアレですね。 f:id:o0h:20171216185417g:plain

コネヒトではSlackにいろいろを集約していく風潮がありますので、こういう機能は「よく使うけど忘れがちなコマンド」や「使いたくなったときにすぐ引き出したいURL」を放り込むなどして活用しています。

しかし、標準の機能に対して「もっとこうできたらいいのにな!」という課題が段々と浮かび上がってきておりました。
具体的には以下の3点です

  1. Slackでの権限が強い人しか設定を行えず、「私の作業効率を上げたい」と願う全ての人の要望に応えにくい
  2. 設定画面を開いて登録!という手間がある
  3. 「登録済みキーワード」の検索や分類もできればいいのに・・・

これらの問題に対して過剰に対応を行いましたので、今回はそれについて技術的観点からの話題を中心にお話したいと思います。

全体概要

実現したいこと

ざっと整理して、こんな要求があるものと設定しました。

  1. 登録・編集・削除を全てSlack上で完結させるようにする
    • Slackアカウントの権限を問わず利用が可能になる
    • 手軽にアップデートを行える状態にすることによって、「少し便利にする」のハードルを下げる
  2. 登録済みキーワードないし本文から、「検索」をできるようにする
    • トリガーとなるキーワードを覚えてないといけない!という問題の解消
  3. 登録したキーワードを「分類」できるようにして、関連語を一覧できるようにする
    • 2と同様に「探したいものがわからない」問題に対するアプローチ

実現すること

これらに対して、以下の方法をとって実現をしていくものとします。 普通に作ってもつまらない ので、 折角だからSlackの色んな機能を使っちゃれ というエゴを丸出しにしています!*1

機能面

  1. チャットによる簡易なコンテンツ登録
  2. チャットによる登録コンテンツの呼び出し
  3. 同じくチャットで「検索」「カテゴリ呼び出し」を行い、直感的かつ邪魔にならない形でのコンテンツの呼び出し
  4. 専用UIを用いた、フォーム形式で詳細なコンテンツ登録

技術面

  1. Events APIを用いて、RTMクライアントを伴わない形でチャットへのリアクションを行う(機能面の1,2,3)*2
  2. Interactive Messageを用いて、検索やカテゴリ指定でのコンテンツ群の呼び出しを行う(機能面の3)
  3. Custom Command + Dialogを用いた、コンテンツの登録・編集(機能面の4)

完成イメージ

ここからゴタゴタと述べていくので、その前にまずは絵面をごらんください・・・!

コンテンツの登録

登録用フォーマットを決めて、それに従う形でトリガーワード・コンテンツを放り込みます。
社内ミームとして「いでよ!○○」が以前から存在し、標準の自動レスポンス機能の利用にも散見されていたので、これを利用します。

機能-1です。

新しくいでよ!{{キーワード}}
{{コンテンツ}}
{{複数行OK}}

f:id:o0h:20171216202229g:plain

コンテンツの表示

機能-2です。

いでよ!{{キーワード}}

f:id:o0h:20171216202622g:plain

コンテンツの検索

機能-3(1)です。

探して!{{検索語}}

f:id:o0h:20171216203420g:plain

カテゴリの表示

機能-3(2)です。

{{カテゴリ}}!ください

f:id:o0h:20171216203742g:plain

コンテンツの登録(詳細)

機能-4です。 チャットベースでの登録は手軽なのですが、長い文章を打ちにくかったりするので、「詳細にやりたいぜ」って時です。

/ideyo-add

f:id:o0h:20171216204739g:plain

コンテンツの編集

「詳細登録」では、既存コンテンツの編集もできます。
さっき登録した 英語で1-5 にtypoがあります。これを編集したい!といった場合に。

/ideyo-add {{キーワード}}

f:id:o0h:20171216205122g:plain 便利ですね。

実装ポイント

ここから先は技術です。

appの準備

Slack API: Applications | Slackから独自appを作成します

events APIの利用

ざっくり言うと、「特定のイベントが起きた時に、指定したURLをSlackが叩いてくれる」というものです。
「Slackが」というのが重要です。

背景

初期の実装は、heroku上で動かしているnodeJSのbot(botkit)をフロントエンドとし、特定キーワード(ex: いでよ!)を含むchatを→バクエンドに構えているビジネスロジックを持つサーバーに転送するという構成をとっていました。キーワードをリッスンする仕組みが必要だからです。これはpassiveな「webサーバー」では難しいと思われました。*3
botkitでのRTMクライアントは非常に安定させやすいのと、稼働率・運用に関して(既にその他の機能も相乗りさせているため)「ちゃんと動かしている」という状態にあったという点は魅力でした。
しかしながら、

  1. 機能追加時には「バックエンドに転送する条件」をメンテナンスしないといけないこと
  2. そもそもbotkitで動かしているのはSlack Appでの作成でなかったため、Interactive Message等の機能が利用できない

という問題がありました。特に2点目は致命的です。新たなbotアカウントが必要です。作り直し・リプレイスは免れません。

そこで、当初は「バックエンド」の位置づけであったサービスに「フロンドエンド」たる責務を負わせられないか?と考えます。
調べてみたら、今までとは違う仕組みを利用すると、リアルタイムに近いリアクションを実現する事が出来そうだと分かりました。

Event APIです。

利用の準備

api.slack.com

「どのイベントを購読するか」を選択しておくことで、該当のイベントが来たときに「予め指定されたendpointにpostでメッセージがくる」状態を実現できます。
今回は「チャットメッセージがチャンネルに投稿されたとき」に知らせてほしいので、 message.channels を有効にしましょう。

(作成したアプリの設定画面で) > Basic Information > Add features and functionality > Event Subscriptions > Enable Events > Add Workspace Event f:id:o0h:20171216211839p:plain f:id:o0h:20171216211545p:plain

チャレンジ!Event API

先に述べたように「イベントが発生したら通知する先」をアプリごとに1つ指定する必要があります。
その設定を行うのに、 検証が必要です。

api.slack.com

  1. Event Subscriptionsの画面で、リクエスト URLに登録したいエンドポイントを入力する
  2. challenge というフィールドを持つリクエストが飛んできます
  3. これに対して、 200 OKで challenge の値を返すように応答してください
  4. うまくいくと、「検証済み ✓」と表示されるようになります(先のキャプチャを参照してください)

※ このエンドポイントがエラーを頻発するようだと、自動的にイベントの購読が停止されます。

Interactive Message / Components

ざっくりいうと、

  1. chat.postMessage に「メニュー」を添付して
  2. 添付されたメニューに対するリアクションは、「予め指定された、interactive component用エンドポイント」に送られ
  3. そこから、slackに対して応答を返す

という流れになります。

f:id:o0h:20171216220604p:plain

interactive messageのフォーマットは、Slackが提供しているプレビューツールを利用して遊んでみるのがおすすめです。 message builder

interactive component用エンドポイントはappの設定メニューから登録してください。
ユーザーが(Slack上で)選択したメニューや押したボタンの内容とともに、こちらのエンドポイントにPOSTリクエストが飛んで来るようになります。 f:id:o0h:20171216221234p:plain

あとは、必要に応じて返答その他の処理をアプリケーションから行ってください。

Ideyo-app

「バックエンド」です。 events apiからポストされたメッセージの内容を読み取り、「データをDBに永続化する」「DBから読み出す」といった処理を実装しています。
この記事ではSlackの機能や利用法の紹介を目的としているので、詳細を割愛します。

Slash Command(custom command)

さっくりいうと、

  1. 予め登録しておいたコマンドをユーザーが入力すると
  2. 予め登録されていたURLに対して、リクエスト内容が送信され
  3. そこから、slackに対して応答を返す

という流れになります。

custom command用エンドポイントはappの設定メニューから登録してください。
f:id:o0h:20171216221052p:plain

Dialog

Dialog機能は比較的新しいものになります。
こちらのエントリーにて紹介されているので、一読されるのをおすすめします。夢が広がる!

medium.com

ざっくりいうと、

  1. Custom Commandからからの送信内容に含まれる trigger_id を取り出して
  2. open.Dialog へ、trigger_id と供に展開内容を送信すると
  3. ユーザーに対してダイアログが展開される
  4. 選択内容が、「予め指定された、interactive component用エンドポイント」に送られ
  5. (以下、interactive messagesのときと同様)

という流れになります。

f:id:o0h:20171216222724p:plain

これで「ダイアログ」機能が展開されます。

まとめ

チャットのみをUIとした機能はかなり前から社内で利用していたのですが、特にコンテンツ編集や検索のユーザービリティの低さがネックでした。
今回、Slackの機能をリッチに利用することで、これらの問題を解消できたと思っています。 特にナレッジ領域の課題を解消するサービスのおいては、「いかにユーザー自身の「引き出し」にたよらず機能を引き出せるか」というのが、UXに関してかなり大きな価値をもつと思います。その点で言えば、「かんたんに検索できる」「グループに属する一覧を引き出せる」というのは、個人的にもお気に入り機能です。

今後は、細かいチューニングや社内のコミュニケーションをより円滑にする方面にも活用できたら面白いかな?と妄想しています。

次回予告

さて、明日はついに!!我らがCTO @tatsushimの登場です・・・!
全く内容を聞いていないので、私も楽しみ・・・w

qiita.com

*1:開発合宿でベースを開発したため、力技も用いて「自分でやりたいことをやる!」というふうに振っています。ので、実装方式としては筋が良いわけではなさそう。遊び心・・・!

*2:本来的には、リアルタイム性を要するアプリケーションはRTMを利用すべきです。実際、botkit等で作られた「自動返信システム」に比べて今回の実装には応答遅延等が見られやすいです

*3:場合によっては、Legacy IntegrationのOutgoing Webhookで十分に実現可能なものと考えます。実際に試してもみて、一部の要件についてはコレで十分にワークしました