Lineのやり取りをSlackに通知する

この記事は [GLOBIS Advent Calendar 2019 - Qiita](https://qiita.com/advent-calendar/2019/globis) の18日目です。

こんにちは、グロービスでエンジニアをしている @shifumin です。
皆さん、Lineを使っていますか？ 僕は通知オフで使っています。理由は通知で集中力が切れるのが嫌だからです。
しかし、通知オフ設定だと送られたメッセージに長時間気づかずに怒られイベントがたまに発生しますよね？ 僕はたびたび発生しています。
皆さん、Slackを使っていますか？使っていますよね。凝視していますよね。
Slackなら常に見ているから見逃さないんだけどなあ。  

というわけで、多少矛盾を孕んでいますが、通知オフにしたLineで送られたメッセージをSlackに通知させるためのbotを作っていきたいと思います！

## 概要を3行で
- 通知させたいLineグループにLine botをjoinさせてLine Messaging APIでメッセージを取得する
- 上記で取得したメッセージをSlack botとしてSlackに通知する
- botはRailsに乗せてHerokuで動かす

はい、やっていきましょう。

## Line Messaging APIを利用する準備
Lineの送られてきたメッセージを取得するのに [LINE Messaging API](https://developers.line.biz/ja/services/messaging-api/) を利用します。

LINE Developersコンソールでチャンネルの作成とbotの設定を行います。
設定方法は公式のドキュメントが詳しいので、 [Messaging APIを利用するには](https://developers.line.biz/ja/docs/messaging-api/getting-started/) と [ボットを作成する](https://developers.line.biz/ja/docs/messaging-api/building-bot/) を参考にするのが楽です。  

botの作成まで終えた後に各種設定を変更します。

- グループ・複数人チャットへの参加を許可する
    - オン
- あいさつメッセージ（Greeting messages）
    - オフ
- 応答メッセージ(Auto-reply messages)
    - オフ
- Webhook
    - オン

それぞれの設定は下記画像左の `Edit` からいけます。
[![Image from Gyazo](https://i.gyazo.com/40915a131cd02a3f06d242260fa8286e.png)](https://gyazo.com/40915a131cd02a3f06d242260fa8286e)

[![Image from Gyazo](https://i.gyazo.com/012c1fe17940e5b9f0c8cc98951d927e.png)](https://gyazo.com/012c1fe17940e5b9f0c8cc98951d927e)
[![Image from Gyazo](https://i.gyazo.com/c6d8b5e2f57077ea99f167cd8d0de2a7.png)](https://gyazo.com/c6d8b5e2f57077ea99f167cd8d0de2a7)

Webhook設定はURLももちろん設定する必要があるのですが、これは後でHerokuでappを作成後に設定します。
設定後は下記の2つの値が必要になるため、メモります。
発行されていなければ、それぞれ `Issue` , `Reissue` から発行できると思います。

- Channel secret (Basic settingsの中)
- Channel access token (Messaging APi settingsの中)
です。

[![Image from Gyazo](https://i.gyazo.com/6f76edfa96cffa77ba1143ba0ef1ca45.png)](https://gyazo.com/6f76edfa96cffa77ba1143ba0ef1ca45)
[![Image from Gyazo](https://i.gyazo.com/4a5b586b3f810f3dc30f7eca1b84a0a6.png)](https://gyazo.com/4a5b586b3f810f3dc30f7eca1b84a0a6)

### botとフレンドになる
MessagingAPi > QR code にQRコードが表示されているのでbotをフレンド登録します。
また、複数人でのグループチャットを通知させたい場合はグループに招待します。

## Slackの設定
続いて、SlackのApp設定とbot追加を行います。
https://api.slack.com/apps の `Create New App` からAppを作成します
まず、Appの名前と導入するWorkspaceを入力します。

[![Image from Gyazo](https://i.gyazo.com/e4669944be42dd64b90a8b3d2dd14ef2.png)](https://gyazo.com/e4669944be42dd64b90a8b3d2dd14ef2)

その後Appの設定をいくつか行う必要があります。

[![Image from Gyazo](https://i.gyazo.com/b93845521c9e4d86d5943538ff7f37f0.png)](https://gyazo.com/b93845521c9e4d86d5943538ff7f37f0)

### Botの作成
- Botsから `Add a Bot User` をクリックしてbotを作成する

### Permissionsの設定
- PermissionsをクリックしOAuth & Permissions画面へ移動する
- OAuth Tokens & Redirect URLs > `Install App to Workspace` をクリックしてAppをインストールする
- Scopesで下記のScopeを追加する
    - chat:write:bot
    - chat:write:user
    - files:write:user

[![Image from Gyazo](https://i.gyazo.com/2920b61dae7c0b761a75b60ba825eade.png)](https://gyazo.com/2920b61dae7c0b761a75b60ba825eade)

また、下記の値が必要になるため、メモります。

- OAuth Access Token (OAuth & Permissions画面)

[![Image from Gyazo](https://i.gyazo.com/2db29146a8c6c73504103a363678e994.png)](https://gyazo.com/2db29146a8c6c73504103a363678e994)

## Railsアプリ作成
ここからようやくBotの実装に進めていきます。

### rails new
rails newまではよしなにやってください。
とりあえず最速でrails new。 

```ruby
# Railsをインストールまで
bundle init
vim Gemfile # Gemfileの `# gem "rails"` 行のコメントアウトを外す
bundle install
```

`rails new` は今回は画面描画系がいらないのでAPIモードかつ必要ないものを全てスキップしています。
参考: [小さく薄くrails newする(ViewやJSが必要ない場合) - Qiita](https://qiita.com/shifumin/items/f4f4ea68d9963dbe9ca2)

```
bundle exec rails new . --skip-yarn --skip-git --skip-action-mailer --skip-action-mailbox --skip-action-text --skip-active-record --skip-active-storage --skip-action-cable --skip-sprockets --skip-javascript --skip-turbolinks --skip-test --api --skip-bundle
```

### SDKのインストール
Line、Slackそれぞれ公式が出しているSDKのgemを使います。
[line-bot-api](https://github.com/line/line-bot-sdk-ruby) と [slack-ruby-client](https://github.com/slack-ruby/slack-ruby-client) です。  
この2つをGemfileに追加して `bundle install` します。

```ruby
# Gemfile
# add
gem 'line-bot-api'
gem 'slack-ruby-client'
```

```ruby
bundle install
```

### コード実装
#### 各SDKの設定
Slackは `configure` で設定できたので `config/initializers/` 以下に設定を用意し、 `Line::bot::Client` はできなかったので仕方なくApplicationControllerでクライアントを作成する際に設定するようにしました。

```ruby
# config/initializers/slack_ruby_client.rb
require 'slack'

Slack.configure do |config|
  config.token = ENV['SLACK_API_TOKEN']
end

Slack::Web::Client.configure do |config|
  config.user_agent = 'Slack Ruby Client/1.0'
end
```

```ruby
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  private

  def line_client
    @line_client ||= Line::Bot::Client.new do |config|
      config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
      config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
    end
  end

  def slack_client
    @slack_client ||= Slack::Web::Client.new
  end
end
```

#### routes
Webhookはpostでリクエストが来るので、 `/callback` で受けて WebhookController#callbackに渡すようにしました。

```ruby
# config/routes.rb 
Rails.application.routes.draw do
  post '/callback' => 'webhook#callback'
end
```

#### Blocked Hostの設定
Rails6から導入されたBlocked hostの設定は良しなにやってください。
今回はHerokuで`development`で動かすかつ何も考えずに全開放ということで
下記のようにしています。
~~Rails6でrails newするのは今回が初めてでWebhookが届かないなあと記事を書くにあたりここでめっちゃ嵌った。嵌りポイント1~~

```ruby
# config/environments/development.rb
Rails.application.configure do
  # 略
  config.hosts.clear
end
```

#### controller
[line-bot-api](https://github.com/line/line-bot-sdk-ruby) と [slack-ruby-client](https://github.com/slack-ruby/slack-ruby-client) のREADMEとサンプル実装を見ながらbotの本実装である `WebhookController#callback` を書いていきます。 今回は

- メッセージが送られた場合はそのメッセージをそのままSlackに送る
- 写真が送られてきた場合はその写真を保存して(Herokuなのでそのうち消えるけど)、Slackにアップロードする
- ビデオ、スタンプの場合はそれぞれが送られてきた旨のメッセージを送る

ようにしました。

```ruby
# app/controllers/webhook_controller.rb
class WebhookController < ApplicationController
  CHANNEL = '#{botに通知させるチャンネル名: 例： #hoge}'

  def callback
    body = request.body.read

    signature = request.env['HTTP_X_LINE_SIGNATURE']
    unless line_client.validate_signature(body, signature)
      error 400, 'Bad Request'
    end

    events = line_client.parse_events_from(body)
    events.each do |event|
      case event
      when Line::Bot::Event::Message
        case event.type
        when Line::Bot::Event::MessageType::Text
          post_message(event.message['text'])
        when Line::Bot::Event::MessageType::Image
          image_response = line_client.get_message_content(event.message['id'])
          file = File.open("/tmp/#{Time.current.strftime('%Y%m%d%H%M%S')}.jpg", 'w+b')
          file.write(image_response.body)

          slack_client.files_upload(channels: CHANNEL,
                                    file: Faraday::UploadIO.new(file.path, 'image/jpeg'),
                                    as_user: true,
                                    title: File.basename(file.path),
                                    filename: File.basename(file.path),
                                    initial_comment: '写真が送信されました')
        when Line::Bot::Event::MessageType::Video
          post_message('ビデオが送信されました。')
        when Line::Bot::Event::MessageType::Sticker
          post_message('スタンプが送信されました。')
        end
      end
    end

    head :ok
  end

  private

  def post_message(text)
    slack_client.chat_postMessage(channel: CHANNEL, text: text)
  end
end

```

## Herokuのデプロイと設定
あとはHerokuでアプリを作成してデプロイして設定したら完了です。
アプリを作成してデプロイする方法はそこら辺に転がっているので割愛します。

### 環境変数の設定
上の方でメモっておいた下記の環境変数を設定します。

- LINE_CHANNEL_SECRET
    - Lineの `Channel secret`
- LINE_CHANNEL_TOKEN
    - Lineの `Channel access token`
- SLACK_API_TOKEN
    - Slackの` OAuth Access Token`

[![Image from Gyazo](https://i.gyazo.com/207fbc84b65145e315a9d1686e2b93ab.png)](https://gyazo.com/207fbc84b65145e315a9d1686e2b93ab)

### Dynoの起動
Dynoも忘れずに起動させます。
~~Herokuを触るのが久しぶりで起動を忘れていて、動いていないHerokuに対してWebhookを大量に空振りさせていた。嵌りポイント2~~

[![Image from Gyazo](https://i.gyazo.com/55b4b0ec28dd9724fab9adc0fcd72c64.png)](https://gyazo.com/55b4b0ec28dd9724fab9adc0fcd72c64)

### Webhook URLの設定
最後にWebhook URLを設定したら完了です。
Settngs > Domains のURLを

[![Image from Gyazo](https://i.gyazo.com/7718c2f68f611722c9fb793761f161ea.png)](https://gyazo.com/7718c2f68f611722c9fb793761f161ea)

Line DevelopersコンソールのMessaging API > Webhook settingsのURLに設定します。
エンドポイントは `/callback` としているので忘れないようにします。

[![Image from Gyazo](https://i.gyazo.com/696c9be40a1e54e753eedfe5fb38d33c.png)](https://gyazo.com/696c9be40a1e54e753eedfe5fb38d33c)

## 実際の動作
グループにbotを招待してLineからメッセージを送ると下記のようにSlackに通知されます。
(左: Line, 右: Slack)

[![Image from Gyazo](https://i.gyazo.com/86c727a2bd1c46eeb9478a2d1a6da85b.gif)](https://gyazo.com/86c727a2bd1c46eeb9478a2d1a6da85b)

また、Lineの通知をオフにしていても下記のような重要なメッセージを見逃すことが減ります
(なぜならSlackは見ているので)

[![Image from Gyazo](https://i.gyazo.com/d39b7d17bdf8f85367ce5599c453392d.png)](https://gyazo.com/d39b7d17bdf8f85367ce5599c453392d)

## 終わりに
今回はLineのやり取りをSlackに通知するbotを作ってみました。
公式で用意されているIntegration等でSlackとLineを直接繋ぐことはできないですが、それぞれのアプリがAPIとそのSDKを用意してくれているので、少しコードを書けばこの記事のようにそれぞれのアプリを連携させることができます。
個人開発といえばアプリ作成をまず思い浮かべるかと思いますが、APIを利用したbot開発や便利スクリプト作成も手間の割に日々の生活が便利になったりするのでオススメです！！