Ruby + SinatraでLINE Botを作ろう – Part 1

2020年9月12日Webサービス開発

こんにちは。
この連載では、複数回に渡りRubyとSinatraを使って

  • 本の裏にあるISBNコードを送信すると検索して本の画像を表示してくれる
  • その本を記録しておいて、あとから参照できる

という機能を搭載した「ほんめも!」というLINE Botを作ってみたいと思います。

この記事で作るもの

この記事では、オウム返しするLINE Botを作っていきます。
プログラムの流れは下記の通りです。( サービス が今回作るプログラムです)

プロジェクトを準備する

まずはプロジェクトの雛形を作りましょう。既に雛形がある場合や、既存のコードに組み込む場合は次の章に進んでください。

$ bundle init

bundle initコマンドでGemfileを生成します。
そして、下記のgemを追記していきましょう。

# 中略
gem "sinatra"
gem "sinatra-contrib"
gem "dotenv"
gem "line-bot-api"

記述が完了したらbundleコマンドでインストールしましょう。

$ bundle

続いて、コアのファイルを作っていきます。今回はapp.rbというファイル名にします。

require 'bundler/setup'
Bundler.require
require 'sinatra/reloader' if development?

get '/' do
    "Hello world!"
end

さて、実行して動作テストをしてみましょう。

$ ruby app.rb -o 0.0.0.0

とコマンドを入力して、プログラムを起動します。
Sinatraのデフォルトのポート番号は4567なので、http://localhost:4567/ にアクセスしてみましょう。
「Hello world!」と表示されれば、Sinatraの環境構築は完了です!(スクリーンショットのポート番号が違うのはDockerを噛ませている為なのであまり気にしないでください><)

LINE Developersに登録しよう

ログインしよう

https://developers.line.biz/ja/
にアクセスしてログインしましょう。普段LINEを使っていればQRコードでログイン出来ます。

ログインすると下のような画面に移動するはずです。とりあえず右下のボタンから言語を変更出来るので、日本語にしておくと楽です。

プロバイダーとチャネル?

LINE Botの開発をすると必ず「プロバイダー」と「チャネル」という単語が出てきます。
これらは、ざっくり説明すると

  • プロバイダー: 開発者アカウント
  • チャネル: Botアカウント

という意味になります。
プロバイダーは会社や個人、開発者のグループ等の単位で作成し、チャネルは1つのプロバイダーに属します。

プロバイダーを作ろう

というわけで、まずはプロバイダーを作成しましょう。
「新規プロバイダー作成」をクリックすると、プロバイダー名を入力する画面が出てくるので、好きなプロバイダー名を入力しましょう。 

ここで入力したプロバイダー名はBotの作者として 公開 されます。本名等は控えたほうが良いでしょう。
作成ボタンを押すとプロバイダーが作成されます。

チャネルを作ろう

続いて、Botのアカウントとなるチャネルを作りましょう。
Botで使うチャネルは「Messaging API」なので、Messaging APIをクリックします。

新しく作るチャネルの設定画面が出てきます。必要な内容を埋めていきましょう。

項目内容
チャネルの種類Messaging API
プロバイダー先程作ったもの
チャネルアイコン今は設定しなくてOK
チャネル名Bot名
チャネル説明Botの説明
大業種Botのジャンル
小業種Botの詳細ジャンル
メールアドレスBotに関する連絡が来るメールアドレス。連絡が受けられるメールアドレスにしておくと良いです。
プライバシーポリシーURL今は設定しなくてOK
サービス利用規約URL今は設定しなくてOK

必要な規約に同意した後、作成ボタンを押すとチャネルが作成されます。

Botに必要なキーを.envファイルに書き込もう

Botに必要なチャネルの準備が出来たので、キーやシークレットを.envファイルに書き込んでいきましょう。
プログラム内に直接書き込むことも出来ますが、セキュリティの事を考えるとあまりよろしくありません。

LINE_CHANNEL_ID=xxxxxxxxxx
LINE_CHANNEL_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
LINE_CHANNEL_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

チャネルIDとチャネルシークレットは「チャネル基本設定」
チャネルトークンは「Messaging API設定」の中に「チャネルアクセストークン(長期)」という名前で記載されています。
表示されていない場合は発行ボタンを押して発行しましょう。

プログラム内で.envに記述した変数が利用出来るようにするためDotenv.loadをrequireの下に書いておきます。

require 'bundler/setup'
Bundler.require
require 'sinatra/reloader' if development?
Dotenv.load # <- 追記

get '/' do
  "Hello world!"
end

Webhookを受け付けるようにしよう

Webhookって何?

Webhookとは、相手のサービス(この記事ではLINE)でイベントが発生したら、事前に設定しておいたURLにイベントの内容をPOSTしてもらう仕組みのことです。

例えば、自分の作ったサービスの/callbackというURLをLINEのチャネルに設定しておけば、チャネルにメッセージが来た時に/callbackにメッセージの内容をPOSTしてもらう事が出来ます。

ただし、LINEのサーバーからアクセス出来るURLである必要があるので、サービスを実際に公開していないときちんと動作しません。例えば開発中のlocalhost:4567は自分のPCからしかアクセス出来ないURLなので、localhost:4567/callbackを設定した状態でメッセージが来てもlocalhost:4567/callbackは呼び出されません。

この仕様があるため、LINE Botを開発する時は基本的に毎回デプロイする必要があります。
ポート開放をすればいちいちデプロイする手間は省けますが、セキュリティ上のリスクもあるのでここでは紹介しません。

Webhookを受け取って返信しよう

基本的にGitHubのREADMEと一緒ですが、画像の処理を抜いてシンプルなコードにしています。
https://github.com/line/line-bot-sdk-ruby

require 'bundler/setup'
Bundler.require
require 'sinatra/reloader' if development?
Dotenv.load

# ====== 追記ここから ======

def client
  @client ||= Line::Bot::Client.new { |config|
    config.channel_id = ENV["LINE_CHANNEL_ID"]
    config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
    config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
  }
end

post '/callback' do
  body = request.body.read

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

  events = client.parse_events_from(body)
  events.each do |event|
    if event.is_a?(Line::Bot::Event::Message)
      if event.type === Line::Bot::Event::MessageType::Text
        message = {
          type: 'text',
          text: event.message['text']
        }
        client.reply_message(event['replyToken'], message)
      end
    end
  end

  "OK"
end

# ====== 追記ここまで ======

get '/' do
  "Hello wolrd!"
end

コードの解説をこの下でしていきます。

コード解説

def client
  @client ||= Line::Bot::Client.new { |config|
    config.channel_id = ENV["LINE_CHANNEL_ID"]
    config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
    config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
  }
end

これはLINE Botを操作するための「client」を使えるようにするためのコードです(これはline-bot-apiの機能です)。
Line::Bot::Client.newでclientを作る事が出来ますが、1つのサービスで1つのclientがあれば十分なのでこのような実装になっています。
||=演算子を使う事で、@clientが空の時はLine::Bot::Client.newしてそれを渡す。@clientが既に入ってる時はそれを渡す。という処理を実現しています。

post '/callback' do
  body = request.body.read

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

post '/callback' doのブロックは少し長いので分けて説明します。
body = request.body.readは送られてきたデータをbody変数に代入しているだけです。

signature以降では、送られてきたデータが本当にLINEサーバからのものかをチェックしています。
LINEサーバから送信されるデータには必ず「HTTP_X_LINE_SIGNATURE」というものが含まれており、その中身を見るとLINEサーバから送られてきたデータかどうかを確認する事が出来ます。
LINEサーバかどうかの確認はline-bot-apiに実装されており、先程作ったclientを通して利用する事が出来ます。
確認の処理はclient.validate_signature(body, signature)という部分で実行されています。

悪意のある人がLINEサーバをなりすましてメッセージを送り込んできていないかをチェックするという重要なコードです。

  events = client.parse_events_from(body)
  events.each do |event|
    if event.is_a?(Line::Bot::Event::Message)
      if event.type === Line::Bot::Event::MessageType::Text
        message = {
          type: 'text',
          text: event.message['text']
        }
        client.reply_message(event['replyToken'], message)
      end
    end
  end

  "OK"
end

events = client.parse_events_from(body)では送られてきたデータをrubyで扱いやすい形に変換してもらっています。
変換した結果はeventsという名前からも分かるように、イベントの配列になります。

events.each do |event|では複数あるイベントを1件ずつ処理しています。これは一度に複数のイベントが同時に送信される事がある為です。

if event.is_a?(Line::Bot::Event::Message)ではイベントの種類がMessageかどうかを確認しています。メッセージ以外のイベントには、「友だち追加」や「ブロック解除」などがあります。

if event.type === Line::Bot::Event::MessageType::Textではメッセージの種類がテキストであることを確認しています。テキスト以外のメッセージの種類には、画像や動画、スタンプなどがあります。

つまり、上から4行までのコードで「送信されたデータを解析して、テキストメッセージのみを絞り込む」という処理をしています。


続いてif文の中のコードを見てみましょう

message = {
  type: 'text',
  text: event.message['text']
}
client.reply_message(event['replyToken'], message)

上4行でLINEサーバに送るメッセージを組み立てて、最後の1行で返信を送信しています。
event[‘replyToken’]はイベントに含まれている返信用のトークンです。


最後に"OK"と書いていますが、処理が正常に成功したら正しいレスポンスを返す必要があるというLINE Bot APIのルールに則ったものです。何を返してもOKです。

デプロイしよう

さあ、コードは完成したので実行してみましょう!と言いたい所ですが、先程解説したとおり残念ながらローカルでの実行では動作しません。
なので、今回はHerokuへデプロイすることにします。

herokuへのデプロイの詳細は割愛しますが、Procfileだけ作成しておきましょう。

web: bundle exec ruby app.rb -o 0.0.0.0 -p $PORT
$ git init
$ git add -A
$ git commit -m "first commit"
$ heroku create
$ git push heroku master

デプロイできたらアプリケーションを開いてみましょう。「Hello wolrd!」と表示されればデプロイ完了です!

Webhookを設定しよう

LINE Developersのサイトに行きチャネル設定の画面に移動しましょう。
「Messaging API設定」を開き、「Webhook URL」の編集をクリックします。
URLを入力する枠が出てくるので、先程デプロイしたURL+/callbackと入力して更新ボタンを押しましょう。

例えばデプロイしたURLが
https://xxxxxxx-yyyyyy-zzzzz.herokuapp.com
だったら
https://xxxxxxx-yyyyyy-zzzzz.herokuapp.com/callback`
となります。

その後、Webhookの利用にチェックを入れておきましょう。

検証ボタンを押すとサーバーの動作チェックが出来ます。成功と表示されれば問題ありません。


また、現時点では自動応答メッセージが有効になっているためBotのメッセージが送信出来ません。なので、自動応答を無効にします。

Messaging API設定の中の「応答メッセージ」の編集ボタンをクリックします。
すると、応答設定というページが表示されるので、下にある詳細設定の

  • 応答メッセージを オフ に
  • Webhookを オン に

設定します。

これでWebhookを利用する準備が出来ました。

試してみよう

さて、準備は全て整いました!Botを友だちに追加して実際にメッセージを送信してみましょう!
「Messaging API設定」の中にQRコードがあるので、これをLINEで読み込んでみましょう。
作ったBotと友達になれるはずです。

友達になれたら何かメッセージを送信してみましょう。
送ったメッセージと同じメッセージが帰ってきたら成功です!おつかれさまでした!

IMG_1505A6F48E6C-1.jpeg

まとめ

この記事では送られてきたメッセージをオウム返しするBotを作りました。
とてもシンプルなものですが、Bot作りに欠かせない大切なコードが沢山含まれているのでよく理解しておくことをおすすめします!

次の記事はこちら

連載記事一覧

  1. Ruby + SinatraでLINE Botを作ろう - Part 1 - オウム返しするBotを作ろう ← イマココ
  2. Ruby + SinatraでLINE Botを作ろう - Part 2 - 書籍を検索して情報を返そう
  3. Ruby + SinatraでLINE Botを作ろう - Part 3 - 検索した書籍を記録しよう

Webサービス開発 LINE Bot, Ruby, Sinatra

Posted by mizucoffee