railsでslackとOAuth + メッセージ投稿

# 内容

本記事はrailsアプリから、ユーザのSlackと連携するためのアクセストークンをOAuthで取得して、メッセージを送る処理を書いています。
なので、自分の所属するチームにメッセージを送るというより、見知らぬ誰かのチームに送るサービスをイメージしています。

Incomming webhooks使えばOAuthいらんやん、とか言われそうですが
ユーザ側で設定しなきゃいけないのは面倒くさいと思うので。
（普段slackにブラウザでアクセスする人ってあんまいないんじゃ、、、とかは気にしない）

# 環境
- macOS High Sierra 10.13.3
- ruby 2.5.0
- rails 5.1.4

# 事前準備編
## Slackにチーム登録
Slackにはチームがすでに登録されている前提です。


## Slackにアプリ登録

まずは、SlackAPIを叩くためにSlackにアプリを登録しておきます。

ブラウザでslackにログインしてから、こちらのurlにアクセス
https://api.slack.com/

右上に表示される"Your Apps"をクリック
![your_apps.png](https://qiita-image-store.s3.amazonaws.com/0/97730/13de2200-5b21-7227-ac0f-e20e1234c2d3.png)

"Create an App"クリック 
![create_an_apps.png](https://qiita-image-store.s3.amazonaws.com/0/97730/5381e7e4-b6cd-7b59-2511-5826a8b62b1e.png)

表示されるフォームで適当にアプリの名前を入力して、アプリを登録したいチームを選択
![create_app.png](https://qiita-image-store.s3.amazonaws.com/0/97730/19c71289-f5ee-1b47-9354-b105c5dbcc30.png)

Basic InformationのApp CredentialsでClient IDとClient Secretを控えておきます。
![credentials.png](https://qiita-image-store.s3.amazonaws.com/0/97730/6a7142e5-59c4-c47e-1a11-1175b9662ab7.png)

ちなみにClient Secretは関係者以外に見せてはいけないので、管理には注意してください。
git載せるダメ、絶対

下にVerification Tokenというものもありますが、メッセージを送るだけならば不要です。


続いて、OAuth連携をするための設定を行います。
左サイドのOAuth & Permissionsをクリックします。

Redirect URLsはslackで認証後に自分のアプリに戻るためのurlです。
後でいくらでも変更可能なので、適当に登録しておきます。
ここでは認証後にhttp://localhost:3000/slack/auth へ戻してもらうことにしましょう。
意外に気づかないのが、"Add a new Redirect URL"で記入した後、"Save URLs"を押さないと登録されないので注意。
![redirect_uri.png](https://qiita-image-store.s3.amazonaws.com/0/97730/b1240fa2-8ddb-be6b-ec6b-e2e5cddf8a1f.png)

次は下にスクロールしてScopeを設定します。
このアプリが認証されたユーザに対して行うことができる操作をここで設定します。
twitterとかでアプリ連携する時に、「このアプリは〇〇を行うことができます」みたいな感じで表示されるアレ

今回はメッセージが送れれば良いので、"Send messages as hogehoge (chat:write:bot)"のみ、
権限は最小限にするのが鉄則だから不用意になんでもかんでも設定しない
これもSave Changesをお忘れなく。
![scopes.png](https://qiita-image-store.s3.amazonaws.com/0/97730/2ec36c7e-e09d-7e49-3f98-e2762309fc05.png)

これでslack側の設定は完了！！:tada:

# rails
## 前準備
細かい所は気にしないでslack連携を行う場所を適当に作っていきます。

```
rails new slack_auth
rails generate controller slack index
```

```ruby:config/routes.rb

Rails.application.routes.draw do
  resources :slack do
    collection do
      get :auth
    end
  end
end
```



今回はslackへのメッセージ送信用にslack-apiというgemを利用します。
slackで提供されるapiのラッパーなので、
必要なパラメータはcurlなどで叩く場合と基本的に同じなはずです。

Gemfileに以下を追記してbundle install実行

```ruby:Gemfile
gem 'slack-api'
```

```
bundle install
```

使用するときはrequireしてあげる必要があるので、initializersにそれ用のファイルを追加します。

```ruby:config/initializers/gems.rb
require "slack"
```

## Slackの承認ページアクセス
Slackの承認ページへアクセスするための処理を作りましょう。
今回はnewアクションでリダイレクトさせるようにします。
クエリパラメータには要求するscopeと、先程取得したclient idをつけてください。
実際に利用する際は環境変数を使うなどして、直接書かないことをおすすめします。

```ruby:app/controllers/slack_controller.rb
class SlackController < ApplicationController
  def index
  end

  def new
    redirect_to ("https://slack.com/oauth/authorize\?scope\=chat:write:bot\&client_id\=" + "取得したclient_id")
  end

  def auth
  end
end
```

```erb:app/views/slack/index.html.erb
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>

<h1>Slack#index</h1>
<%= button_to 'Slack OAuth', new_slack_path, :method => :get %>
```

![index.png](https://qiita-image-store.s3.amazonaws.com/0/97730/155ca4b3-da30-fa23-dd9c-45e18ce0a979.png)

ボタンをクリックすると、Slackの承認ページが表示されます。

![slack.png](https://qiita-image-store.s3.amazonaws.com/0/97730/098cf8e1-3845-07bd-772a-e2017404fd7a.png)

## アクセストークン要求
Authorizeを押すと、
先程redirect_urlで設定した指定したurl(/slack/auth)へ
codeというクエリパラメータが付加された状態でリダイレクトされます。

アクション側ではこのcodeを使って、アプリからSlackへアクセストークンを要求することになります。
slack-apiでSlackクラスが用意されるため、これでアクセストークンを取得します。
例によってclient_secretは直接書かないことをおすすめします。

```ruby:app/controllers/slack_controller.rb
  def auth
    auth_result = Slack.oauth_access({client_id: "取得したclient_id", client_secret: "取得したclient_secret", code: params[:code], scope: "chat:write:bot"})

    if auth_result["ok"]
      redirect_to slack_index_path, notice: "テスト"
    else
      redirect_to slack_index_path, alert: "error: " + auth_result["error"]
    end
  end
```

auth_resultの中身はこんな感じ

```
# 成功した時
{"ok"=>true,
 "access_token"=>"xxxx-xxxxx-xxxxx-xxxxx-xxxxx",
 "scope"=>"identify,chat:write:bot",
 "user_id"=>"XXXXXXXXX",
 "team_name"=>"slaaaaack",
 "team_id"=>"XXXXXXXXX"}

# 失敗した時
{"ok"=>false, "error"=>"code_already_used"}
```

このアクセストークンがSlackへの連携処理を行う際に必要な情報になります。
現状トークンに有効期限はなく、ユーザが再生成しない限り
同じトークンを使い続けられるそうです。
https://qiita.com/subarunari/items/3e4c6060fcefd4c65257

## メッセージ投稿
取得したアクセストークンを使って、Slackにメッセージを投稿します。
メッセージ投稿はユーザの表示名と投稿先のチャンネルを指定して行います。

```ruby:app/controllers/slack_controller.rb
  def auth
    auth_result = Slack.oauth_access({client_id: "取得したclient_id", client_secret: "取得したclient_secret", code: params[:code], scope: "chat:write:bot"})

    if auth_result["ok"]
      Slack.client({token: auth_result["access_token"]})
           .chat_postMessage(text: "グッド!!", username: "test_auth", channel: "#random")

      redirect_to slack_index_path, notice: 'slackにメッセージを送りました'
    else
      redirect_to slack_index_path, alert: "error: " + auth_result["error"]
    end
  end
```

Slackの承認ページでAuthorizeを押すと

[画面]

![index_2.png](https://qiita-image-store.s3.amazonaws.com/0/97730/c23bdc35-a0de-82e6-7b93-1043c57b6828.png)

[Slack]

![slack_message.png](https://qiita-image-store.s3.amazonaws.com/0/97730/76658132-6f4c-a8d7-b0aa-7b9d98892734.png)

無事メッセージが送られました:tada: