LoginSignup
92
90

More than 5 years have passed since last update.

Nodejs+LINE BOTでレストラン検索

Last updated at Posted at 2016-04-11

はじめに

2016年4月7日にLINEより「BOT API Trial Account」の無償提供(先着1万名限定)が始まりました。
既に様々な記事が出ていますがExpressを使ったものが見つからなかったので、
試しにサービス開始時のNEWSにあった、レストランBOTを作ってみました。

※これを書いている時に酷似した記事を見つけましたが全然気にしてません。まったく。

今回はHerokuにデプロイしてますが、ググれば先人たちの記録がたくさん見つかるので、その辺りの話はしません。
参考:Node.js+Express+EJS+HerokuでWebアプリケーション開発 その1

環境

  • OS X El Capitan 10.11.3
  • node v5.4.1
  • npm 3.5.3

準備

  • Herokuを使えるようにしておく
  • BOT API Trial Accountに登録して次の3つを取得
    • Channel ID
    • Channel Secret
    • MID
  • Herokuのアドオン(Fixie)用にクレジットカード ※無料
  • ぐるなび Webサービスからアクセスキーを取得

さっそくですが

とりあえず動いたものを。

index.js
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var request = require('request');
var async = require('async');

app.set('port', (process.env.PORT || 5000));
app.use(bodyParser.urlencoded({extended: true}));  // JSONの送信を許可
app.use(bodyParser.json());                        // JSONのパースを楽に(受信時)
// app.get('/', function(request, response) {
//     response.send('Hello World!');
// });

app.post('/callback', function(req, res){

    async.waterfall([
        // ぐるなびAPI
        function(callback) {

            var json = req.body;

            // 受信テキスト
            var search_place = json['result'][0]['content']['text'];
            var search_place_array = search_place.split("\n");

            //検索キーワード
            var gnavi_keyword = "";
            if(search_place_array.length == 2){
                var keyword_array = search_place_array[1].split("");
                gnavi_keyword = keyword_array.join();
            }

            // ぐるなびAPI レストラン検索API
            var gnavi_url = 'http://api.gnavi.co.jp/RestSearchAPI/20150630/';
            // ぐるなび リクエストパラメータの設定
            var gnavi_query = {
                "keyid":"<ぐるなびのアクセスキー>",
                "format": "json",
                "address": search_place_array[0],
                "hit_per_page": 1,
                "freeword": gnavi_keyword,
                "freeword_condition": 2
            };
            var gnavi_options = {
                url: gnavi_url,
                headers : {'Content-Type' : 'application/json; charset=UTF-8'},
                qs: gnavi_query,
                json: true
            };

            // 検索結果をオブジェクト化
            var search_result = {};

            request.get(gnavi_options, function (error, response, body) {
                if (!error && response.statusCode == 200) {
                    if('error' in body){
                        console.log("検索エラー" + JSON.stringify(body));
                        return;
                    }

                    // 店名
                    if('name' in body.rest){
                        search_result['name'] = body.rest.name;
                    }
                    // 画像
                    if('image_url' in body.rest){
                        search_result['shop_image1'] = body.rest.image_url.shop_image1;
                    }
                    // 住所
                    if('address' in body.rest){
                        search_result['address'] = body.rest.address;
                    }
                    // 緯度
                    if('latitude' in body.rest){
                        search_result['latitude'] = body.rest.latitude;
                    }
                    // 経度
                    if('longitude' in body.rest){
                        search_result['longitude'] = body.rest.longitude;
                    }
                    // 営業時間
                    if('opentime' in body.rest){
                        search_result['opentime'] = body.rest.opentime;
                    }

                    callback(null, json, search_result);

                } else {
                    console.log('error: '+ response.statusCode);
                }
            });

        },
    ],

    // LINE BOT
    function(err, json, search_result) {
        if(err){
            return;
        }

        //ヘッダーを定義
        var headers = {
            'Content-Type' : 'application/json; charset=UTF-8',
            'X-Line-ChannelID' : '<Your Channel ID>',
            'X-Line-ChannelSecret' : '<Your Channel Secret>',
            'X-Line-Trusted-User-With-ACL' : '<Your MID>'
        };

        // 送信相手の設定(配列)
        var to_array = [];
        to_array.push(json['result'][0]['content']['from']);


        // 送信データ作成
        var data = {
            'to': to_array,
            'toChannel': 1383378250, //固定
            'eventType':'140177271400161403', //固定
            "content": {
                "messageNotified": 0,
                "messages": [
                    // テキスト
                    {
                        "contentType": 1,
                        "text": 'こちらはいかがですか?\n【お店】' + search_result['name'] + '\n【営業時間】' + search_result['opentime'],
                    },
                    // 画像
                    {
                        "contentType": 2,
                        "originalContentUrl": search_result['shop_image1'],
                        "previewImageUrl": search_result['shop_image1']
                    },
                    // 位置情報
                    {
                        "contentType":7,
                        "text": search_result['name'],
                        "location":{
                            "title": search_result['address'],
                            "latitude": Number(search_result['latitude']),
                            "longitude": Number(search_result['longitude'])
                        }
                    }
                ]
            }
        };

        //オプションを定義
        var options = {
            url: 'https://trialbot-api.line.me/v1/events',
            proxy : process.env.FIXIE_URL,
            headers: headers,
            json: true,
            body: data
        };

        request.post(options, function (error, response, body) {
            if (!error && response.statusCode == 200) {
                console.log(body);
            } else {
                console.log('error: '+ JSON.stringify(response));
            }
        });

    });

});

app.listen(app.get('port'), function() {
    console.log('Node app is running');
});

なにをやっているか

/callbackにリクエスト(ユーザが送信するメッセージ)が来たら、

  • 一行目:住所 例)岩手県盛岡市
  • 二行目:検索キーワード 例)焼き肉、冷麺…

を取り出して、ぐるなびのAPIに投げる。
検索結果から、

  • お店の名前
  • 画像
  • 住所
  • 緯度・経度
  • 営業時間

を取り出し、ここからようやくLINE BOTから送信したい情報をセットしてPOSTする。
送信したいものが1つであれば Sending messages、複数ならSending multiple messagesを使う。

つまづいたところ

LINE BOT をとりあえずタダで Heroku で動かすを見ると、

LINE BOT API の呼び出しには Server IP Whitelist に接続元 IP の指定が必要とのことで、Heroku だと難しいかなー、と思いましたがアドオンで解決できました。

Fixie

なんと!!!ありがたい。
Fixieを使うときにfreeでもクレジットカードの登録が求められるので、渋々必要情報を入力します。
そして、次の作業を忘れていたため、かなり時間を取られました。

Herokuの管理画面から、Resources > Fixie > account と進み、
Proxy URLを、LINE BOTにPOSTする際に必要になります。

すると、ようやくそれっぽいものができました。

IMG_9307.PNG

まとめ

ツッコミあればよろしくお願いします!
(BOT API Trial Account、まだ10,000名達してないのかな。。。)

92
90
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
92
90