LINE botでレストラン検索してみた
LINE botが先日公開されました。
公式プレスリリースはこちら。
linecorp.com
LINE CONFERENCE TOKYO 2016で LINE bot オープンAPIを公開する告知があり、
2016.04.07日からトライアルとして1万人限定でAPIの提供が開始されました。
それに伴ってweb界隈では続々とエンジニアの方々が開発を始めています。
技術情報共有サイトQiitaにもLINE botのタグがついた記事がここ一週間程度で30記事ほど上がってきています。
ちょっぴり紹介
こんな記事も見つけた。
blogs.itmedia.co.jp
2016年はbotが一気にくるぞ!やばい!的な記事が結構バズってて、
大げさだな〜とか思いつつ。せっかくだからやってみるか!ってノリで開発してみた。
作ってる最中に似たようなものを作ってる人の記事がリアルタイムで上がってきてて、
先手を取られた〜。と思いつつも、せっかく作ったので記事にしてまとめておく。
なおネタが被ってしまったことに関しては全く気にしていない。
ただ、少し参考にさせてもらった。
構成
構成は、以下でやってみた。
Ruby 2.3.0
Heroku
LINE bot API
ぐるなびAPI
PHPでの開発記事を初めに目にしたので、せっかくだしPHPで開発してみるか〜と思ってたんですが。
Ruby界で有名な風呂グラマーのmasuidrive(増井)さんがRubyで実装した記事を後々見つけて、
こっちの方がすぐ出来そうやんって思ったので速さをとってRubyで実装しました。(慣れてるからね)
オウムbotを作ってみる
ウォーミングアップ
参考にした記事がこちら。
qiita.com
とりあえず動かしてみて感覚を掴みたかったので同じようにオウムbotを作りました。
初めはHerokuだとセキュリティの問題で使えない的な話がありました。
が、後にHerokuにプロキシを貼るサービス(Fixie)を入れれば使えることが判明したため、
下手にサーバを立てなくても簡単にデプロイできるようになりました。
Herokuにデプロイする記事がこちら。
qiita.com
Herokuデプロイ運用について記事にまとめてくれた方に感謝を申し上げたいです。
基本的に記事通りにやれば動くと思います。
レストランを探すbotを作ってみる。
本題に入ります。
構成は上の通りです。
まずLINE でDevelopers登録を行い、以下を取得します。
・Channel ID
・Channel Secret
・MID
これらは登録すればユーザページで確認することができます。
ぐるなびのAPIを使います。
ぐるなび Web Service - トップページ
使うにはユーザ登録が必要です。
登録を済ませAPIを使うためのアクセスキーを取得します。
Gemは以下を使いました。
source 'https://rubygems.org' ruby '2.3.0' gem 'sinatra' gem 'rest-client' gem 'faraday'
Rubyで実装したソースコードを貼っておきます。
とりあえず動きますがエラー処理まで手が伸びていないためこれから実装、改修していきます。
コードも汚いため要リファクタリング。ツッコミ大歓迎です!
require 'sinatra/base' require 'faraday' require 'json' require 'rest-client' class App < Sinatra::Base post '/linebot/callback' do # ぐるなびに渡すキーワードの取得 params = JSON.parse(request.body.read) # APIのオブジェクト生成 conn = Faraday::Connection.new(url: 'http://api.gnavi.co.jp/RestSearchAPI/20150630/') do |builder| builder.use Faraday::Request::UrlEncoded builder.use Faraday::Response::Logger builder.use Faraday::Adapter::NetHttp end if params['result'][0]['content']['location'].nil? send_data = keyword_seach(params, conn) p 'keyword側' p send_data send(params, send_data) else send_data = location_seach(params, conn) p 'location側' p send_data send(params, send_data) end end def keyword_seach(params, conn) search_place = params['result'][0]['content']['text'] search_place_array = search_place.split("\n") if search_place_array.length == 2 keyword_array = search_place_array[1].split("、") gnavi_keyword = keyword_array.join() end # GETでAPIを叩く response = conn.get do |req| req.params[:keyid] = ENV['GURUNAVI_API_KEY'] req.params[:format] = 'json' req.params[:address] = search_place_array[0] req.params[:hit_per_page] = 1 req.params[:freeword] = gnavi_keyword req.params[:freeword_condition] = 2 req.headers['Content-Type'] = 'application/json; charset=UTF-8' end json = JSON.parse(response.body) result = {} result['name'] = json['rest']['name'] if json['rest'].include?('name') result['shop_image1'] = json['rest']['image_url']['shop_image1'] if json['rest'].include?('image_url') result['address'] = json['rest']['address'] if json['rest'].include?('address') result['latitude'] = json['rest']['latitude'] if json['rest'].include?('latitude') result['longitude'] = json['rest']['longitude'] if json['rest'].include?('longitude') result['opentime'] = json['rest']['opentime'] if json['rest'].include?('opentime') return result end def location_seach(params, conn) # 緯度取得 latitude = params['result'][0]['content']['location']['latitude'] # 経度取得 longitude = params['result'][0]['content']['location']['longitude'] # GETでAPIを叩く response = conn.get do |req| req.params[:keyid] = ENV['GURUNAVI_API_KEY'] req.params[:format] = 'json' req.params[:hit_per_page] = 1 req.params[:latitude] = latitude.to_f req.params[:longitude] = longitude.to_f req.params[:range] = 1 req.headers['Content-Type'] = 'application/json; charset=UTF-8' end json = JSON.parse(response.body) result = {} result['name'] = json['rest']['name'] if json['rest'].include?('name') result['shop_image1'] = json['rest']['image_url']['shop_image1'] if json['rest'].include?('image_url') result['address'] = json['rest']['address'] if json['rest'].include?('address') result['latitude'] = json['rest']['latitude'] if json['rest'].include?('latitude') result['longitude'] = json['rest']['longitude'] if json['rest'].include?('longitude') result['opentime'] = json['rest']['opentime'] if json['rest'].include?('opentime') return result end # send LINE BOT def send(params, result) message = { # テキスト messageNotified: 0, messages: [ { contentType: 1, text: "お店を見つけました!\n[お店]" + "#{result['name']}" + "\n営業時間" + "#{result['opentime']}" }, # 画像 { contentType: 2, originalContentUrl: result['shop_image1'], previewImageUrl: result['shop_image1'] }, # 位置情報 { contentType: 7, text: result['name'], location: { title: result['address'], latitude: result['latitude'].to_f, longitude: result['longitude'].to_f } } ]} to_array = [] to_array.push(params['result'][0]['content']['from']) request_content = { to: to_array, toChannel: 1383378250, # Fixed value eventType: '140177271400161403', # Fixed value content: message } endpoint_uri = 'https://trialbot-api.line.me/v1/events' content_json = request_content.to_json RestClient.proxy = ENV['FIXIE_URL'] if ENV['FIXIE_URL'] RestClient.post(endpoint_uri, content_json, { 'Content-Type' => 'application/json; charset=UTF-8', 'X-Line-ChannelID' => ENV["LINE_CHANNEL_ID"], 'X-Line-ChannelSecret' => ENV["LINE_CHANNEL_SECRET"], 'X-Line-Trusted-User-With-ACL' => ENV["LINE_CHANNEL_MID"] }) end end
今回RubyでAPIを叩く際に'net/http'ではなく、'faraday'というGemを使ってみました(新しい挑戦)
faraday | RubyGems.org | your community gem host
使い方は以下を参考
Ruby の faraday をつかって Parse の REST API 叩いてみる - セロリ食べたい
Path API を叩く PHP / Python / Ruby のサンプル - Qiita
使ってみた感想は、めっちゃ使いやすかったです。そして何よりわかりやすかった。