読者です 読者をやめる 読者になる 読者になる

十を聞いて一を知る

要領の悪い高専生がプログラミングをします。

RailsでLINE Botのサーバー側の処理をしてみる

LINE Bot 2016年度春休み連載 修正予定

こんばんは。
前回に引き続きLINE Botを弄ります。


まずrails newします。

$ rails new line_bot_test

サーバーを起動。

$ bundle exec rails s

http://localhost:3000/を開く。   f:id:taira1999120:20170302004120p:plain:w500
この画面が出たら成功。
相変わらずビックリするほど簡単ですね。

続いてお手軽scaffold

$ rails generate scaffold event request_body:text
$ bundle exec rake db:migrate

http://localhost:3000/eventsを開く。
f:id:taira1999120:20170302013149p:plain
この画面が出たら成功

WebhookのHTTPS POSTリクエストに対しては、常にstatus code 200を返してください。

とのことなのでevents_controller.rbを書き換えます。
よくないんでしょうけどCSRF対策を無効にしておきます。(参考 Rails4のCSRF対策で「Can’t verify CSRF token authenticity」エラー

events_controller.rb

  protect_from_forgery with: :null_session
def create
  @event = Event.new(request_content)

  respond_to do |format|
    if @event.save
      format.html { redirect_to @event, notice: 'Event was successfully created.' }
      format.json { render :show, status: :ok, location: @event }
    else
      format.html { render :new }
      format.json { render json: @event.errors, status: :ok }
    end
  end
end
def request_content
  body = request.raw_post

  content = {"request_body"=>body}
end




正常に動くかテストします。(参考 curl を使って JSONデータをPOSTする - Λ Takuya71 の日記 Λ

$ curl -X POST -H "Accept: application/json"
  -H "Content-type: application/json"
  -d '{"event":{"request_body":"hoge"}}'
  http://localhost:3000/events.json

これが返ってきたら成功です。

{
    "id": 5,
    "request_body": "{\"event\":{\"request_body\"=>\"hoge\"}}",
    "created_at": "2017-02-28T17:00:35.340Z",
    "updated_at": "2017-02-28T17:00:35.340Z",
    "url": "http://localhost:3000/events/5.json"
}




今回はHerokuを使うのでpostgresqlをgemに追加(参考 herokuでRailsアプリを公開する手順をゆっくり丁寧に。 | Workabroad.jp

Gemfile

gem 'sqlite3', :group => [:development, :test]
gem 'pg', :group => [:production]

Herokuへのデプロイは先ほどのページを参考に行ってください。
デプロイしたら

$ heroku run rake db:migrate

画像のWebhook URLの欄に https://アプリ名.herokuapp.com/events を入力してSAVE。 f:id:taira1999120:20170302004130p:plain

VERIFYを押してSUCCESSになるのを確認します。 f:id:taira1999120:20170302004128p:plain

次は署名検証です。
Signature validation

リクエストの送信元がLINEであることを確認するために署名検証を行わなくてはなりません。 各リクエストには X-Line-Signature ヘッダが付与されています。 X-Line-Signature ヘッダの値と、request body と Channel secret から計算した >signature が同じものであることをリクエストごとに 必ず検証してください。

検証は以下の手順で行います。
1. Channel secretを秘密鍵として、HMAC-SHA256アルゴリズムによりrequest bodyのダイジェスト値を得る。
2. ダイジェスト値をBASE64エンコードした文字列が、request headerに付与されたsignatureと一致することを確認する。

公式ドキュメントより

CHANNEL_SECRET = ... # Channel Secret string
http_request_body = request.raw_post # Request body string
hash = OpenSSL::HMAC::digest(OpenSSL::Digest::SHA256.new, CHANNEL_SECRET, http_request_body)
signature = Base64.strict_encode64(hash)

乗るべきレールが見えないためやっつけです。
ごめんなさい。いつか修正します。

events_controller.rb

def request_content
  x_line_signature = request.headers['X-Line-Signature']
  body = request.raw_post
  channel_secret = '自分のキー'

  hash = OpenSSL::HMAC::digest(OpenSSL::Digest::SHA256.new, channel_secret, body)
  signature = Base64.strict_encode64(hash)

  if x_line_signature == signature
    content = {"request_body"=>body}
  else
    content = nil
  end
end

event.rb

class Event < ApplicationRecord
  validates :request_body, presence: true
end

「自分のキー」のところには自分のチャンネルのBasic Informationに書いてあるChannnel Secretを入れてください。
右側にあるSHOWをクリックすれば表示されます。 f:id:taira1999120:20170302004112p:plain



Herokuにプッシュしたあと、もう一回VERIFYしてSUCCESSってなることを確認してください。 Herokuの方を見ると、 f:id:taira1999120:20170302004126p:plain
こんなのが来てるはずです。

自分のLINEアカウントでBotを友達登録します。
LINE@ MANAGERにアクセスしてアカウント一覧からBotを選びます。
画面左側のアイコンの下にID(?)があるので、
f:id:taira1999120:20170302004118p:plain
それで検索して登録します。
f:id:taira1999120:20170302004125p:plain



適当なメッセージを送ると、
f:id:taira1999120:20170302004116p:plain

来ました。 f:id:taira1999120:20170302004114p:plain



次は手動で返信します。(参考 リファレンス

curl -X POST 
-H 'Content-Type:application/json'
-H 'Authorization: Bearer チャンネルアクセストークン'
-d '{
    "replyToken": "リプライトークン",
    "messages": [
        {
            "type": "text",
            "text": "Hello, user"
        },
        {
            "type": "text",
            "text": "May I help you?"
        }
    ]
}'
https://api.line.me/v2/bot/message/reply 

チャンネルアクセストークンはここにあります。
f:id:taira1999120:20170302004109p:plain
Bearerも必要なので忘れずに。
リプライトークンはHerokuにとんできたJSONに含まれています。
一定秒間以内に使わないと無効になるそうなので注意してください。

$ curl -X POST
-H 'Content-Type:application/json'
-H 'Authorization: Bearer チャンネルアクセストークン'
-d '{
    "replyToken": "新鮮なリプライトークン",
    "messages": [
        {
            "type": "text",
            "text": "Hello, user"
        },
        {
            "type": "text",
            "text": "May I help you?"
        }
    ]
}'
https://api.line.me/v2/bot/message/reply

来ました。
f:id:taira1999120:20170302004124p:plain

力尽きたので今日はここまでです。実はこの時点までで二日かかってます。
自分の能力の低さ、知識の少なさを実感させられますが、問題点が見えるようになって良かったということにしておきましょう。

最終的にはおうむ返しBotを作ります。