Slack アプリをRailsで作って公開するまでの流れ


知り合いから「Spotifyで流している曲をSlackのチャンネルに投稿してくれるボットが欲しい！」とリクエストをもらい、実際に作って公開してみました。

社内共有として、例えばユーザー登録があった時に通知を出すようなものは何度か実装してきたのですが、公開アプリとして出すのは初めてでした。公開アプリを出すためには、ただアプリを実装するだけでなく、作ったアプリをSlack App Directoryに申請して審査に通過しなければいけません。ここまでやるのが案外大変で、情報も多くなく正直困りました。

「Slackアプリとは！」という点につきましては、ドキュメントや、[@muky96rockさんの記事](https://qiita.com/munky69rock/items/3342c7b7c642fd2c68f3)で分かりやすく説明してくれているので、実際にRailsでアプリを実装して、審査に提出してApp Directoryに反映されるまで一連の流れを説明していきます！

![qiita.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116375/1a665b25-9e13-9b2b-3cba-0dc5333c7a69.png)

できることとしては、Spotify認証をして現在ユーザーが聴いている音楽を取得し、その度にSlackチャンネルに通知を流してくれるアプリ。今のところ、それだけしかできません。ちなみに、今回の記事でSpotify APIの取り扱い部分は省きますが、Webhookの用意がなく、10分おきにAPIを叩きにいってるような仕様にしています。

本当は曲の長さ的に、4分とか、3分おきに取りにいけた方が理想に近いと思うのですが、Heroku Schedulerが10分おきからなので、まあとりあえず無料アプリだしいっかな、とここまでで実装しました。需要があれば、もっと改良します！

## 登録、実装してSlack App Directoryに公開されるまで

ざっくり、こんな感じで進めていきます。

1. Slackアプリを登録する
2. Rails アプリを実装する
3. Slackアプリを審査に提出する
4. Slack App Directoryに公開する

### Slackアプリを登録する
<img width="1440" alt="build_slack_app.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116375/901978ba-0406-0602-4135-e2703454a3a4.png">

大前提として、Slackのワークスペースを持ち合わせておく必要があります。分からないですが、おそらくそのワークスペースが潰れると、そこで作ったSlackアプリも消えてしまうので自分がオーナーとなるワークスペースを用意すると良いでしょう。

[APIのページ](https://api/slack.com)からSlackアプリを作っていきます。後から必要になる準備としてやっておくべきことは下記あたりになります。作りたいアプリによっても変わってくるのですが、今回はユーザーが自分のSlackワークスペースを認証して、チャンネルを選択してそこに通知を送るという仕様を想定しています。

* App Credentialsで`Client ID`と、`Client Secret`をメモしておく
* PermissionsのOauth部分にリダイレクトURLを追加する
* PermissionsのScopeで`channels:read`と `chat:write:bot`を追加する

### Railsアプリを実装する

#### Omniauth認証を実装する

認証のgemで有名な`omniauth`を使いました。まずは、ライブラリをインストールして、`omniauth.rb`で設定を行ないます。先ほど取得したClient IDと、Client SecretはCredentialsに設定しておきます。

```Ruby:Gemfile
gem 'omniauth'
gem 'omniauth-slack'
```

```Ruby:config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :slack,
    Rails.application.credentials[:slack][:consumer_id],
    Rails.application.credentials[:slack][:consumer_secret],
    scope: 'channels:read,chat:write:bot'
end
```

#### Sessionコントローラーを用意する

アプリでのログイン/ログアウト周りを実装していきます。Deviseを使うまでもない仕様だったので、今回は自前で作っていきました。Sessionコントローラーと、Userモデルを一緒に用意します。

```
$ rails g controller sessions new destroy
$ rails g model Users name email provider uid token channel
```

Userモデルには、Slackのuid、tokenと、channelを保存するカラムを忘れないようにしましょう。`fitsy_or_initialize.tap` を使用して、既にユーザーが存在している場合にはそのままセッションを作成し、そうでない場合はユーザーを作成するようにして実装します。

```Ruby:app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
  def create
    user = User.from_omniauth(request.env["omniauth.auth"])
    if user.save
      session[:user_id] = user.id
      redirect_to root_path
    else
      redirect_to root_path
    end
  end
 
  def destroy
    session[:user_id] = nil
    redirect_to root_path
  end
end  
```

```Ruby:app/models/user.rb
class User < ApplicationRecord
  def self.from_omniauth(auth)
    where(provider: auth.provider, uid: auth.uid).first_or_initialize.tap do |user|
      user.name = auth.info.name
      user.email = auth.info.email
      user.token = auth.credentials.token
      return user
    end
  end
end
```

```Ruby:config/routes.rb
Rails.application.routes.draw do
  get 'auth/:provider/callback', to: 'sessions#create'
  get 'auth/failure', to: redirect('/')
  get 'signout', to: 'sessions#destroy', as: 'signout'
 
  resources :sessions, only: [:create, :destroy]
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
```

ここまで作って、`http://localhost:3000/auth/slack`で下記のような画面が表示されて、ユーザーが作成されればSlack認証は出来上がったことになります。

<img width="1439" alt="build_slack_app_1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116375/bbaac937-cf83-b094-f457-212720b7c665.png">

#### slack-ruby-clientをインストール

Slackチャンネルへの通知や、操作には`slack-ruby-client`ができることが多いです。今回も、このライブラリを使って通知を実装していきます。

```Ruby:Gemfile
gem 'slack-ruby-client'
```

#### Slack API Clientを作成する

Slackの情報をtokenから取得してくれるモデルを作成します。

```Ruby:app/models/manager/slack_api_client.rb
class Manager::SlackApiClient
  attr_reader :client, :slack_user
 
  def initialize(slack_user)
    @slack_user = slack_user
    @client = Slack::Web::Client.new(token: slack_user.token)
    @client.auth_test
  rescue => e
    Rails.logger.warn(<<~LOG)
      Error @Manager::SlackApiClient#initialize
      Msg: #{e.class} #{e.message}
    LOG
 
    @client = nil
  end
 
  def valid?
    client
  end
end
```

#### チャンネル情報を取得する

通知を受け取るチャンネルをユーザーが選択できるようにします。先ほど作成したAPI Clientのモデルでチャンネル取得を追記。

```Ruby:app/models/manager/slack_api_client.rb
 def channels
   client.channels_list.channels
 end
```

Slackチャンネルを選択する画面に合わせて変数を追加します。今回の場合は、Settingsコントローラーを用意していたのでここに記していきます。これでユーザーが認証したワークスペースのチャンネル情報を取得できるはずです。

```Ruby:app/controllers/settings_controller.rb
class SettingsController < ApplicationController
  def index
    @user = current_user
    client_manager = Manager::SlackApiClient.new(@user)
    if client_manager.valid?
      @slack_channels = client_manager.channels
    else
      flash[:danger] = 'Slack token expired. Please link Slack app again.'
    end
  end
end
```

#### 再生された時に通知を行なう

Spotifyで現在再生されている音楽をSlackに通知します。SpotifyのAPIを叩く部分は今回省きますが（需要があれば別で書きます）、一応、音楽の曲名、URL、アーティスト名、アルバム名をそれぞれ取得して通知するようにしています。

```Ruby:play_spotify.rb
slack_client_manager = Manager::SlackApiClient.new(user)
slack_client_manager.post_song_message(@current_playing) if slack_client_manager.valid?
```

```Ruby:app/models/manager/slack_api_client.rb
 def post_song_message(song)
    attachments = {
      text: "🎧 #{song.user.name} is currently playing #{song.name}",
      "fields": [
        {
          "title": "Artist",
          "value": song.artist,
          "short": true
        },
        {
          "title": "Album",
          "value": song.album,
          "short": true
        },
        {
          "title": "Spotify url",
          "value": song.url,
          "short": false
        }             
      ],      
      color: "#f9da00"
    }
    client.chat_postMessage(channel: "##{slack_user.channel}", attachments: [attachments])
  end
```

通知する部分は、通常の`text`で文言だけ飛ばすこともできるのですが、ちょっと格好良くしたいのであれば`attachment`を使うのがオススメです。attachmentの装飾方法については、[こちら](https://api.slack.com/docs/message-attachments)に詳しく書いてあるので参考にしてみてください。

ここまでで一連のSlackに認証して、通知を受けるチャンネルを選択して、実際に通知を受けるところまでの実装が完了したことになります。

### Slackアプリを審査に提出する

自前のワークスペースでアプリを使う分にはここまでで問題ないのですが、どのワークスペースにも導入できる公開アプリとして配信するためには、Slackの審査に通過しなくてはいけません。

APIページの、Manage distributionの Distribute app から審査に向けた準備を行っていくことができるようになります。アプリがSlackの利用規約に違反していないか、ちゃんとユーザーにとって有意義なアプリであるかの確認作業がここで行なわれます。基本はチェックボックスを確認していくだけではありますが、他アプリとの連携がある場合はそのログイン情報も提出しなければなりません。今回の場合は、Spotifyのアカウントを作成して記しました。

<img width="1440" alt="build_slack_app_2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116375/638c48a4-50af-aff1-de2a-5b546f180e10.png">

<img width="1440" alt="build_slack_app_3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116375/db2ad64d-67ec-ee1f-05d4-00dec805f4b3.png">

提出が完了すると、Slack App Directoryの担当者がレビューしてくれるまで待ちます。だいたい4〜5営業日はかかります。そして、結構審査も厳しいです。Slackアプリに関する説明がきちんとランディングページで記載があるかどうか、イメージ画像もそれに準じたものであるかどうかなど、見た目に関するところで3,4回落とされてしまいました。昔はもっと緩かったみたいな話があるので、やはり上場してしまってそのあたり注意が払われているのかもしれないですね。

### Slack App Directoryに公開する

審査が通過すると、晴れて公開できるようになります。やっぱり、こういうストア系に自分のアプリが立ち並ぶのはテンション上がります！

<img width="1436" alt="buildapp.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116375/2192cae4-36d0-b8ab-9b1c-4fd8f2a1f66c.png">

## さいごに

今年、Slackは晴れてIPOを果たし、日本にもSlack Japanが発足したりと今後社内コミュニケーションとしてSlackが当たり前になる時代が既にきています。今後、SaaSやビジネスツールで勝負するなら、一時期スマホアプリにこぞってみんなが挑戦したように、Slackアプリで勝負をしていくというのも一つありそうだなと思います。

Google Driveや、Trelloのような既存の大きいアプリがSlackで使われるだけでなく、Slackアプリから発足するような例えば、Donutsとか、Lunch Train、Geekbootのようなものも生まれてきています。そのまま世界で使われるような展望も見えるので結構ありですよね！

今回作ったアプリ「OfficeDJ」は本日（11月13日付）でProduct Huntにてレース参加中なので、もしよろしければアップボートしていってください！

<a href="https://www.producthunt.com/posts/officedj?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-officedj" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=174335&theme=light" alt="OfficeDJ - Share what's currently playing on Spotify to Slack channel | Product Hunt Embed" style="width: 250px; height: 54px;" width="250px" height="54px" /></a>