はじめに
Messaging APIを使ったLINEbotでNASAの毎日更新される天文写真を送ってくれるものを作ってみました。さらに、飽き足らずに日付を選んでその日の天文写真も見られるようにしました。
完成形
前提、環境など
こちらの記事のサンプルコードが動かせることが前提です。
また、こちらの記事の手順1でNASA APIキーを生成してください。
- Windows10
- Ruby
3.1.0
- Sinatra
- Heroku
- Message API
- NASA apod API
手順
0. コードを理解する
以下はこちらの記事の最後にあるサンプルコードを少し変えたものです。
require 'bundler/setup'
require 'sinatra'
require 'line/bot'
require 'nasa-api'
require 'net/http'
require 'json'
require 'date'
get '/' do
'hello world!'
end
def client
@client ||= Line::Bot::Client.new { |config|
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)
halt 400, {'Content-Type' => 'text/plain'}, 'Bad Request'
end
events = 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
# 1. ユーザからメッセージが送られた時の処理
end
when Line::Bot::Event::Postback
# 2. ポストバックアクションが返ってきた時の処理
end
end
"OK"
end
ポイントは、when Line::Bot::Event::MessageType::Text
と when Line::Bot::Event::Postback
です。インテンドに注意してください。
前者はユーザがメッセージを送信したときの処理を書きます。後者にはポストバックイベントが返ってきたときの処理を書きます。
ポストバックイベントは、テンプレートメッセージのボタンテンプレート、確認テンプレート、カルーセルテンプレートや、日付選択アクション、クイックリプライなどの機能を使ったときに返ってくるアクションです。
1. HerokuにNASAのAPIキーの環境変数を追記する
Herokuのこのアプリのsettingを開き、Reveal Config Vars
をクリックします。
すでに、LINE_CANNEL_SECRET
と LINE_CHANNEL_TOKEN
の環境変数は設定されているはずなので(まだの場合は、こちらの記事の手順を踏んでください)、NASA_API_KEY
という環境変数も設定します。valueは自身のNASAのAPIキーです。生成していない場合はこちらの記事の手順1を踏んでください。
2. とりあえずコードを書いてみる
sample.rb に以下を追記していきます。
require 'bundler/setup'
require 'sinatra'
require 'line/bot'
require 'nasa_apod'
require 'net/http'
require 'json'
require 'date'
get '/' do
'hello world!'
end
def client
@client ||= Line::Bot::Client.new { |config|
config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
}
end
# ここから追記----
def nasa(date)
api_key = ENV["NASA_API_KEY"]
if date == 0
uri = URI.parse("https://api.nasa.gov/planetary/apod?api_key=#{api_key}")
else
uri = URI.parse("https://api.nasa.gov/planetary/apod?api_key=#{api_key}&date=#{date}")
end
json = Net::HTTP.get(uri)
data = JSON.parse(json)
return data["url"], data["date"], data["title"]
end
# ここまで追記----
post '/callback' do
body = request.body.read
signature = request.env['HTTP_X_LINE_SIGNATURE']
unless client.validate_signature(body, signature)
halt 400, {'Content-Type' => 'text/plain'}, 'Bad Request'
end
events = 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
# ここから追記----
if event.message['text'] == "今日の天文写真は?"
data = nasa(0)
url = data[0]
date = data[1]
title = data[2]
reply_image(event, url, date, title)
elsif event.message['text'] == "あの日の天文写真は?"
today = Date.today
client.reply_message(event['replyToken'], select_date(today))
end
# ここまで追記----
end
when Line::Bot::Event::Postback
# ここから追記----
user_date = event['postback']['params']['date']
data = nasa(user_date)
url = data[0]
date = data[1]
title = data[2]
reply_image(event, url, date, title)
# ここまで追記----
end
end
"OK"
end
# 以下を追記----
def select_date(today)
{
"type": "template",
"altText": "this is a buttons template",
"template": {
"type": "buttons",
"title": "Please select a date",
"text": "いつの天文写真を見ますか?",
"actions": [
{
"type": "datetimepicker",
"label": "select date",
"mode": "date",
"data": "action=datetemp&selectId=1",
"max": today,
"min": "1995-06-20"
}
]
}
}
end
def reply_image(event, url, date, title)
if url =~ /jpg/
space_image = {
type: 'image',
originalContentUrl: url,
previewImageUrl: url
}
else
space_image = {
type: 'text',
text: url
}
end
message = {
type: 'text',
text: "#{date}\n#{title}"
}
client.reply_message(event['replyToken'], [space_image, message])
end
最後にHerokuへのデプロイを忘れずに。
$ git add .
$ git commit -m "xxx"
$ git push heroku master
これで、LINE画面から「今日の天文写真は?」と送ると、今日の天文写真とタイトルが返ってきます!
解説
場合を分けて考えてみる
大きく分けて、場合分けは以下の2通りです。
- ユーザから「今日の天文写真は?」と送られてくる
- ユーザから「あの日の天文写真は?」と送られてくる
1 は今日の天文写真を返します。2 は日付を選んでその日の天文写真を返します。
では、1つずつ見ていきましょう。
1. ユーザから「今日の天文写真は?」と送られてくる
まず、if event.message['text'] == "今日の天文写真は?"
の処理に入ります。nasaメソッドで今日の天文写真のurl、date、titleが返ってきます。その後、reply_image メソッドで応答メッセージとして、天文写真、日付、タイトルがトーク画面に表示されます。
client.reply_message(event['replyToken'], [space_image, message])
と応答メッセージを配列にすることで、吹き出しが2個になります。
2. ユーザから「あの日の天文写真は?」と送られてくる
まず、elsif event.message['text'] == "あの日の天文写真は?"
の処理に入ります。select_date メソッドは日付選択アクションです。日付選択アクションは日付を選択できます。時間まで指定することもできます。詳しくはこちら。
こちらはiOS版での表示です。Androidでは1か月ごとのカレンダーが表示されるようです。
日付が選択されると、ポストバックイベントが返ってきます。user_date = event['postback']['params']['date']
には選択された日付のデータが入っています。JSON形式で以下のようになっています。詳しくはこちらです。
あとは 1 と同様です。
{
"destination": "xxxxxxxxxx",
"events": [
{
"replyToken": "b60d432864f44d079f6d8efe86cf404b",
"type": "postback",
"mode": "active",
"source": {
"userId": "U91eeaf62d...",
"type": "user"
},
"timestamp": 1513669370317,
"webhookEventId": "01FZ74A0TDDPYRVKNK77XKC3ZR",
"deliveryContext": {
"isRedelivery": false
},
"postback": {
"data": "storeId=12345",
"params": {
"date": "2017-12-25"
}
}
}
]
}
Messaging APIリファレンス-日付選択アクション
Messaging APIリファレンス-ポストバックイベント
2022/05/23 追記 RubyにおけるMessaging APIのポストバックイベントの返り値
postbackイベントのcase whenの記述の中で、p event
を追記し、event には何が入っているのか確かめてみました。結果は以下になります。改行はありませんでしたが、見やすいように適宜改行を入れています。また、...
となっているところは文字が続きます。
#<Line::Bot::Event::Postback:0x0000561e.....
@src = {
"type"=>"postback",
"postback"=>{"data"=>"action=datetemp&selectId=1","params"=>{"date"=>"2020-02-23"}},
"webhookEventId"=>"01G3RJ9KE9.....",
"deliveryContext"=>{"isRedelivery"=>false},
"timestamp"=>165331.....,
"source"=>{"type"=>"user", "userId"=>"U53ff33106....."},
"replyToken"=>"9ef9a......",
"mode"=>"active"
}
>
こうして見ると、項目は公式リファレンスにあったJSONの内容と同じで、Rubyではハッシュを使っていることがわかりました。(しかもハッシュの中にハッシュ...?)
event['postback']['params']['date']
の記述がありましたが、これはハッシュのキーで値を取得しているということのようです。。ハッシュの中のハッシュ(?)なので、配列の二次元配列のようにキーを並べて値を取得しているということでしょうか。
さらに、応答メッセージを送る際、必ずclient.reply_message(event['replyToken'],...
という記述が必要なのですが、これもハッシュの中のreplyToken
というキーから値を取得しているということがわかりました。
今までMessaging APIのRubyのGithubのサンプルコードから試行錯誤して動けば正しい精神で書いていたので、ポストバックイベントの返り値を確かめたことで少しすっきりしました。
LINEbotの作成にあたって、Rubyの出力p
やputs
を書いてもLINEのトーク画面に表示されることがないことに注意してください。それらの出力は、ターミナルで以下のコマンドを実行すると見ることができます。
$ heroku logs --tail
--tail
のあとにHerokuで設定したアプリ名(heroku create
のときに決めたもの。忘れたらHerokuの自分のページに行くとアプリ一覧が見られます)を追記することで、アプリを指定して見ることもできます。ない場合は現在の階層にあるプログラムになります。
このコマンドはリアルタイムでログを確認するものです。p
やputs
の出力結果が見られるほか、エラーがあればエラー文も表示されます。LINEbotが動かなかったり、変数の中身を確認したいときなどに使います。
リッチメニューを設定する
LINE Official Account Manager でリッチメニューを設定します。リッチメニューの設定の仕方はこちらを参考にしてください。
ここで2つのメニューを作り、タップすると「今日の天文写真は?」と「あの日の天文写真は?」というテキストが送信されるようにしました。
最後に
ちなみに、Herokuの設定で地域がUSになっているため、時間がUSの標準時間になっています。しかし、NASAの天文写真もUSの時間で更新されるので丁度良かったかなと思っています。
また、今回のNASAのAPIから情報を取得するとき、1時間あたりの制限は1,000リクエストです。こちらの記事と取得方法が違うので制限回数が違います。
というわけで、天文写真が送信されるLINEbotを作ることができました。毎日ワンタップするだけで綺麗な天文写真が送られてきます。自分の誕生日などの天文写真を調べてみるのもおすすめです!(私の誕生日は美しい星空などではなく、よくわからない写真でした(笑))