LINEで、返答を返してくれるチャットボット(DBからのデータ表示、スクレイピング)を実装しました。前準備のLINEアカウント作成手順については、LINE Developersなどに丁寧に記載されているため割愛。
概要
私事ですが、通勤に電車と会社のバスを使っています。
そこで、自分用に次に乗れそうなバスの時刻とスクレイピングで電車の運行状況を返してくれるLINE Botを実装してみました。
※ スクレイピングはスクレイピング先に負荷をかける恐れがあるため、実際の運用はせず、勉強のための実装です。
手順
- LINE Developersにボット作成のためのアカウントを作成(割愛)。
- 実装中に動きを確認する目的で、ローカルサーバー3000で動かせるよう、Ngrokを導入。
-
gem 'line-bot-api'
で、実装。
Ngrok
- localhostで動くサーバーを、LAN外からアクセスを可能にする。
- ローカルPC上のサービスを外部公開することが可能。
今回は、ローカル環境で立ち上げたポート番号3000番を外部からアクセスできるようにしたい。
使用方法
① ダウンロード
$ brew cask install ngrok
② ngrokを起動
今回はport番号:3000を指定して、ngrokを起動する。
$ ngrok http 3000(port番号)
立ち上がると、ターミナルに表示された下記のhttps://乱数.ngrok.io
の部分をコピーしておく。
※ 乱数の部分は、サーバーを起動する度に変わるので、その度に②以降の作業が必要。
:
Forwarding https://乱数.ngrok.io -> http://localhost:3000
:
ブラウザでhttp://localhost:3000/
に行くと、railsサーバーが立ち上がること確認できる。※もちろん、アプリもrails s
で起動しておく必要がある。
③ LineBotのWebhookの設定
LINE DevelopersのBotチャネルのWebhook URLの箇所を、上記②のURL + /callback( 例 https://乱数.ngrok.io/callback
)に変更する。
※ Webhook : 一言で言うと、Botにイベントが発生した時、Botインスタンスに通知するためのアクセスポイント(URL)の事。そのURLを指定して、LINEからPOSTリクエストが送信される。
④ 接続確認
あとは、Botを友達追加し、動作確認ができる。
コード
DBはmysqlを使用。
$ rails new アプリ名 -d mysql
dbを作成し、シャトルバスの時刻表テーブルを作成。
$ rails db:create
gemを導入 → bundle install
gem 'line-bot-api'
gem 'nokogiri'
ルーティング
post '/callback', to: 'linebot#callback'
コントローラー
# gem 'line-bot-api'を使えるように宣言
require 'line/bot'
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :validate_signature, except: [:new, :create]
def validate_signature
body = request.body.read
signature = request.env['HTTP_X_LINE_SIGNATURE']
unless client.validate_signature(body, signature)
error 400 do 'Bad Request' end
end
end
def client
@client ||= Line::Bot::Client.new { |config|
# ローカルで動かすだけならベタ打ちでもOK。
config.channel_secret = "your channel secret"
config.channel_token = "your channel token"
}
end
end
linebotコントローラーを作成
$ rails g controller linebot
class LinebotController < ApplicationController
protect_from_forgery except: :sort
# ルーティングで設定したcallbackアクションを呼び出す
def callback
body = request.body.read
events = client.parse_events_from(body)
events.each { |event|
require "date"
require 'nokogiri'
require 'open-uri'
#時刻表示を 時:分 に指定
now = DateTime.now
nowTime = now.strftime("%H:%M")
:
# 下記に記載
:
case event
when Line::Bot::Event::Message
case event.type
when Line::Bot::Event::MessageType::Text
message = {
type: 'text',
text: response
}
client.reply_message(event['replyToken'], message)
when Line::Bot::Event::MessageType::Image, Line::Bot::Event::MessageType::Video
response = client.get_message_content(event.message['id'])
tf = Tempfile.open("content")
tf.write(response.body)
end
end
}
"OK"
end
end
:
# 1 を入力した時のアクション(DBからデータ取得)
if event.message["text"].include?("1")
nextBus = BusTimetableKaiseiSt.all
nextBusKaisei = []
nextBus.each do |nextBus|
time = nextBus.time.strftime("%H:%M")
if time >= nowTime
nextBusKaisei << time
end
end
#DBから現在時刻を起点に直近の3つのバスの時刻を出力
response =
"開成発"+nextBusKaisei[0]+"\n
Next "+nextBusKaisei[1]+"\n
"+nextBusKaisei[2]+"\n\n\n
↓↓番号を選択↓↓\n
1. 開成駅→会社(シャトルバス)\n
2. 会社→開成駅(シャトルバス)\n
3. 電車の運行状況\n
4. 会社周辺の天気\n
5. 東京の天気\n\n
※半角数字でお願いします。"
# 2 を入力した時のアクション(DBからデータ取得)
# 流れは上記と同様なので、割愛
# 3 を入力した時のアクション(スクレイピングでデータ取得)
elsif event.message["text"].include?("3")
urlOdakyu = 'https://www.odakyu.jp/cgi-bin/user/emg/emergency_bbs.pl'
charset = nil
htmlOdakyu = open(urlOdakyu) do |f|
charset = f.charset
f.read
end
docOdakyu = Nokogiri::HTML.parse(htmlOdakyu, nil, charset)
docOdakyu.xpath('//div[@id="pagettl"]').each do |node|
#スクレイピング情報の出力
response =
node.css('p').inner_text+"\n\n\n
↓↓番号を選択↓↓\n
1. 開成駅→会社(シャトルバス)\n
2. 会社→開成駅(シャトルバス)\n
3. 電車の運行状況\n
4. 会社周辺の天気\n
5. 東京の天気\n\n
※半角数字でお願いします。"
end
# 4 を入力した時のアクション(スクレイピングでデータ取得)
# 方法は上記と同様なので、割愛
# 5 を入力した時のアクション(スクレイピングでデータ取得)
# 方法は上記と同様なので、割愛
# 上記以外を入力した時のアクション
else
response =
"↓↓番号を選択↓↓\n
1. 開成駅→会社(シャトルバス)\n
2. 会社→開成駅(シャトルバス)\n
3. 電車の運行状況\n
4. 会社周辺の天気\n
5. 東京の天気\n\n
※半角数字でお願いします。"
end
: