Rails HerokuでLINE BOTを試してみた

# <i class="fa fa-exclamation-triangle" style="font-size:1em; color=red;"></i> 追記
この記事はトライアル版時代のものになります。すでに正式版が出ているので、ご注意ください。


Railsで今話題のLINE BOTを作ってみました。
とりあえず、送ったメッセージをそのまま返すものです。

# LINEへの登録
https://business.line.me から登録できます。

デベロッパー登録が済めば、次のような画面が出てくるので、

* Channel ID
* Channel Secret
* MID

の値を控えておきます。

![スクリーンショット 2016-04-09 14.43.14.png](https://qiita-image-store.s3.amazonaws.com/0/30931/ce6a4e46-6c7e-0036-dd1c-f92d3248f17a.png)

# HerokuにRailsアプリを作成

LINE BOT API の呼び出しには Server IP Whitelist に接続元 IPを登録する必要があるんですが、Herokuの[Fixie](https://elements.heroku.com/addons/fixie)というアドオンを使うと、プロキシを経由してくれるのでIPを固定できます。
参考) [LINE BOT をとりあえずタダで Heroku で動かす](http://qiita.com/yuya_takeyama/items/0660a59d13e2cd0b2516#comment-e6737f3166be32597681)

とりあえず空のRailsアプリをHerokuにデプロイして、下記コマンドでFixieアドオンを追加してください。

```
$ heroku addons:create fixie:tricycle
```

Heroku上で作ったアプリの設定画面から Fixie の画面を開くと次のような画面になるので、

* Proxy URL
* Outbound IPs

を控えます。

![スクリーンショット 2016-04-09 15.06.11.png](https://qiita-image-store.s3.amazonaws.com/0/30931/4e5dba64-5683-a9c2-9c39-bc43c1097e9f.png)

そして、LINE DevelopersのServer IP Whitelistに控えたIPが2つあると思うので2つとも登録しておきます。

![スクリーンショット 2016-04-09 14.43.51.png](https://qiita-image-store.s3.amazonaws.com/0/30931/231dabd5-4e4d-25e4-8aaa-833882eaf3f5.png)

# CallbackURLの設定
BOTに対してメッセージを送るとWebhookでcallback先に情報が飛んでくるのでcallbackURLを設定します。
HTTPSしか許可してくれないんですが、HerokuはもともとHTTPSなのでこの点はクリアです。
自分のサーバでやるときはSSLとかを頑張らないといけないです。
(証明書を使っても、無料のLet's Encryptだとうまく動かないなどの罠があるみたいです。[参考](http://qiita.com/masuidrive/items/1042d93740a7a72242a3)）

下記のようにBasic Informationのところに設定します。ポート番号の443も必須みたいです。

![スクリーンショット 2016-04-09 14.43.35.png](https://qiita-image-store.s3.amazonaws.com/0/30931/f5681995-2a26-3d4a-6f02-5cb8444a8637.png)

自分の場合、CallbackURLを設定してから反映するまで結構時間がかかりました。
1時間以上経ってからコールバックしてきたので、なにか間違ったかといろいろいじり倒してしまいました。
自分を信じて気長に待つ事をオススメします。

# Railsアプリ

本題?のRailsアプリです。

まず、Basic Informationに設定したCallbackURLを受け付けるようroute.rbを書きます。

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

次にコントローラです。
WebhookなのでCSRF対策を無効化してます。
is_validate_signatureメソッドは[LINE公式ドキュメント](https://developers.line.me/bot-api/getting-started-with-bot-api-trial#signature_validation)のコピペです。

```rb:app/controllers/webhook_controller.rb
class WebhookController < ApplicationController
  protect_from_forgery with: :null_session # CSRF対策無効化

  CHANNEL_ID = ENV['LINE_CHANNEL_ID']
  CHANNEL_SECRET = ENV['LINE_CHANNEL_SECRET']
  CHANNEL_MID = ENV['LINE_CHANNEL_MID']
  OUTBOUND_PROXY = ENV['LINE_OUTBOUND_PROXY']

  def callback
    unless is_validate_signature
      render :nothing => true, status: 470
    end
    result = params[:result][0]
    logger.info({from_line: result})
    text_message = result['content']['text']
    from_mid =result['content']['from']

    client = LineClient.new(CHANNEL_ID, CHANNEL_SECRET, CHANNEL_MID, OUTBOUND_PROXY)
    res = client.send([from_mid], text_message)

    if res.status == 200
      logger.info({success: res})
    else
      logger.info({fail: res})
    end
    render :nothing => true, status: :ok
  end

  private
  # LINEからのアクセスか確認.
  # 認証に成功すればtrueを返す。
  # ref) https://developers.line.me/bot-api/getting-started-with-bot-api-trial#signature_validation
  def is_validate_signature
    signature = request.headers["X-LINE-ChannelSignature"]
    http_request_body = request.raw_post
    hash = OpenSSL::HMAC::digest(OpenSSL::Digest::SHA256.new, CHANNEL_SECRET, http_request_body)
    signature_answer = Base64.strict_encode64(hash)
    signature == signature_answer
  end
end
```

次にLINE BOT APIを呼ぶ自作ライブラリです。
[Faraday](https://github.com/lostisland/faraday)を使うので、Gemfileに

```:Gemfile
gem "faraday"
gem "faraday_middleware"
```
を追加しておいてください。


```rb:lib/line_client.rb
require "faraday"
require "faraday_middleware"
require "json"
require "pp"

class LineClient
  module ContentType
    TEXT = 1
    IMAGE = 2
    VIDEO = 3
    AUDIO = 4
    LOCATION = 7
    STICKER = 8
    CONTACT = 10
  end
  module ToType
    USER = 1
  end

  END_POINT = "https://trialbot-api.line.me"
  TO_CHANNEL = 1383378250 # this is fixed value
  EVENT_TYPE = "138311608800106203" # this is fixed value

  def initialize(channel_id, channel_secret, channel_mid, proxy = nil)
    @channel_id = channel_id
    @channel_secret = channel_secret
    @channel_mid = channel_mid
    @proxy = proxy
  end

  def post(path, data)
    client = Faraday.new(:url => END_POINT) do |conn|
      conn.request :json
      conn.response :json, :content_type => /\bjson$/
      conn.adapter Faraday.default_adapter
      conn.proxy @proxy
    end

    res = client.post do |request|
      request.url path
      request.headers = {
          'Content-type' => 'application/json; charset=UTF-8',
          'X-Line-ChannelID' => @channel_id,
          'X-Line-ChannelSecret' => @channel_secret,
          'X-Line-Trusted-User-With-ACL' => @channel_mid
      }
      request.body = data
    end
    res
  end

  def send(line_ids, message)
    post('/v1/events', {
        to: line_ids,
        content: {
            contentType: ContentType::TEXT,
            toType: ToType::USER,
            text: message
        },
        toChannel: TO_CHANNEL,
        eventType: EVENT_TYPE
    })
  end
end
```

環境変数に、

* LINE_CHANNEL_ID
* LINE_CHANNEL_SECRET
* LINE_CHANNEL_MID
* LINE_OUTBOUND_PROXY

を使っているので、控えた値をもとに設定しておいてください。
LINE_OUTBOUND_PROXYの値はポート番号付きで最後の:80を忘れないようにしてください。
Herokuだと、

```
$ heroku config:add LINE_CHANNEL_ID="XXXXXXXXXXXX"
$ heroku config:add LINE_CHANNEL_SECRET="XXXXXXXXXXXX"
$ heroku config:add LINE_CHANNEL_MID="XXXXXXXXXXXX"
$ heroku config:add LINE_OUTBOUND_PROXY="XXXXXXXXXXXX"
```

という感じになります。

これで、BOTと友達になって、メッセージを送りつけると、そのまま返してくるはずです。

Basic InformationのところのQRコードを使ったら、友達になれます。
まだトライアルなので50人までしか友達増やせないみたいです。