【Slack】テキトーなテキストメッセージへの返信方法
はじめに
こんにちは!大ちゃんの駆け出し技術ブログです。
今回もまた!Slack APIです笑
もうどんだけ書くのよという感じですが、今はこれに奮闘しすぎてSlackのネタがたくさん出てきてしまいます笑
今回はテキトーなメッセージを送信した時にヘルプメッセージを送信する方法を紹介します。基本的にはヘルプメッセージはslashコマンドの一覧を返します。
読者対象
メッセージタブでのやりとりのフロー
この記事を深く理解するための事前知識として、「メッセージタブ」の存在に触れておきます。メッセージタブとは、Slack Appのボットとメッセージのやりとりができるスペースのことです。下の図で言うところの「Home」、「Messages」、「About」のMessagesにあたる箇所です。
Slackアプリはこのメッセージタブでイベントを発生させることでユーザとのやりとりを可能にします。メッセージタブでのイベントの種類は下記のような感じです。
- ユーザがメッセージタブを開いた時
- ユーザがメッセージタブにメッセージを送信した時
今回は後者のメッセージタブにメッセージを送信した時にアプリ側にリクエストが送られ、それに対してヘルプメッセージを送信するフローになります。
- ユーザが「hogehoge」とメッセージタブに打ち込む
↓
- Slackが指定のURLにリクエストを送信
↓
- アプリ側でヘルプメッセージを送信する処理
↓
- メッセージタブにヘルプメッセージが送られる
方法がわからなかったのはSlackが指定のURLにリクエストを送信する方法です。「hogehoge」とメッセージタブに送信を行った場合、Slack側ではどのようにそれをアプリ側に送信するのでしょうか。
Event Subscriptions
SlackではEvent SubscriptionsというSlack上でのユーザの動作に対してアプリにリクエストを送ることができる機能があります。
どこで設定するのかというと、Slack App開発画面のFeatures > Event Subscriptionsで設定できます。
ここで「ユーザがメッセージタブにメッセージを送った時にアプリにリクエストを送る」と言う設定を行っていきます。ユーザがメッセージタブにメッセージを送るイベントをアプリ側に送るためには、message.im event
をEvent Subscriptionsで設定します。
Event reference: message.im event
少し大雑把に説明しましたが、ある程度の理解で構いません。今はEvent Subscriptionsの設定でmessage.im event
を使用することでメッセージタブのイベントをリクエストとして送信することができるんだなと思っておいてください。
実装手順
それでは実際に設定していきます。
まず、先ほどの設定画面で右上にあるボタンをONにしましょう。
すると、いろいろな設定項目が出てきます。
まずRequest URLから設定していきます。これはEvent Subscriptionsで有効にしたイベントが発生した時に送信するURLのことです。Railsアプリ側でそのURLを受け取るためのルーティングを設定しましょう。下記の例ですとアプリURL/slack/events
のリクエストを受け取るようにします。
# routes.rb namespace :slack do post 'events', to: 'events#respond' end
よってRequest URLで設定するURLもこれに合わせて設定します。今回はngrokを使用してHTTPS側のURLを設定します。
すると、下記のようにエラーメッセージ が表示されたはずです。
**URLがchallengeパラメータの値で応答しませんでした。**
Event SubscriptionsのURLを受け取るためにはアプリ側で一度challenge
パラメータの値に応答しておく必要があります。これはurl_verification
というイベントが発生していて、Slack側で発行されるchallenge
パラメータをRequest URLのレスポンスでJSONでレンダーすることで、アプリのリクエストURLが本人のものということを認証します。
Event reference: url_verification event
このRequest URLはどのようなHTTPSのURLも設定できてしまいます。例えばYouTubeのURLも設定できます。
https://www.youtube.com/
しかし、当然ですがYouTubeは自分が開発したものではないですよね。何の認証もなしにRequest URLの値に好きなURLを入れられたら、たくさんのリクエストが知らないところから飛んできてしまいます。
そのため、Request URLに最初にchallenge
パラーメーターを送り、同じ値をレンダーすることでそのアプリが本人のものということを認証するのです。よってchallenge
パラメータ をレンダーするようにします。
def respond render json: params[:challenge], status: 200 end
これで同じリクエストURLに認証を行うと、下記画面のように認証が通ったというメッセージが表示されます。
ちなみにですが、このchallenge
パラメーターへの処理は認証が通ればそれ以上は必要ないので削除します。もしこれを残したまま本番環境にデプロイすると、他のslackアプリからのRequest URLにも同じURLが指定された時にchallenge
パラメーターを返却してしまいます。あくまで自分のアプリであることを証明するためのフローです。
それでは次にmessage.im event
を追加します。Subscribe to bot eventsの「Add Bot User Event」ボタンからmessage.im
を検索しましょう。見つけたら追加して右下にある「Save Changes」ボタンをクリックします。
補足すると、im:history
というscopeがアプリ側に自動で追加されているかと思います。これはこのイベントを追加するためにはこのim:history
のscopeが必要になるということです。
これでGUI画面での設定は終わりです。実際にパラメータを受け取れるようになったはずです。binding.pryで実際にパラメータが飛んでいるか確認します。
def respond binding.pry
アプリ側で一度「hogehoge」というメッセージを送ってみましょう。すると処理が止まるはずですのでそこでparamsを確認します。するとすごく長いパラメーターを取得できたと思います。
params => <ActionController::Parameters {"token"=>"xxxxxxx", "team_id"=>"xxxxxx", "api_app_id"=>"xxxxx", "event"=><ActionController::Parameters {"client_msg_id"=>"xxxxx", "type"=>"message", "text"=>"hogehoge", "user"=>"xxxxx",
あとはユーザに対してヘルプメッセージを送信する処理のみです。
自分が作成した処理を貼っておきます。
def respond if params[:event][:type] == 'message' && params[:event][:text].present? send_help_msg end end def send_help_msg team = Team.find_by(workspace_id: params[:team_id]) user = User.find_by(uid: params[:event][:user]) return if team.nil? || user.nil? || team.workspace_id != user.team.workspace_id access_token = set_access_token(user.authentication.access_token) encoded_text = get_encoded_help_text encoded_msg = get_encoded_help_block_msg access_token.post("api/chat.postMessage?channel=#{user.uid}&blocks=#{encoded_msg}&text=#{encoded_text}&pretty=1").parsed end def get_encoded_help_text text = "ヘルプメッセージを送信しました" encoded_text = ERB::Util.url_encode(text) return encoded_text end def get_encoded_help_block_msg msg = "[ { 'type': 'divider' }, { 'type': 'section', 'text': { 'type': 'mrkdwn', 'text': '本アプリでは以下のコマンドがSlack内で利用できるよ:hamster:' } }, { 'type': 'section', 'fields': [ { 'type': 'mrkdwn', 'text': ':information_source: `/prof_help` \nDMでヘルプメッセージを送るよ' }, { 'type': 'mrkdwn', 'text': ':postbox: `/prof_random_block` \n DMでランダムにブロックを1つ送るよ' } ] }, { 'type': 'section', 'fields': [ { 'type': 'mrkdwn', 'text': ':ok_hand: `/prof_activate_share` \n毎日18時の投稿を有効するよ' }, { 'type': 'mrkdwn', 'text': ':raised_back_of_hand: `/prof_inactivate_share` \n 毎日18時の投稿を止めるよ' } ] }, { 'type': 'divider' } ]" encoded_msg = ERB::Util.url_encode(msg) return encoded_msg end
まずパラメータの種類がmessageであること、textが含まれていることでユーザからのDMだということがわかるので条件分岐を張り付けます。
if params[:event][:type] == 'message' && params[:event][:text].present?
DMであることがわかれば、そのユーザからアクセストークンと取り出します。その手前でuserとteamを取り出している処理ですが、これはアプリにユーザが登録しているかどうかを検証するためです。ユーザでなければ基本的にはヘルプメッセージを送信する必要がないので。
team = Team.find_by(workspace_id: params[:team_id]) user = User.find_by(uid: params[:event][:user]) return if team.nil? || user.nil? || team.workspace_id != user.team.workspace_id access_token = set_access_token(user.authentication.access_token)
あとはchat.postMessageメソッドでエンコードしたメッセージを送信しています。ここれへんの説明はSlack APIをよく知っていればわかるはずです。