AIが入ったBotの作り方を学ぼう - Part3 Bot本体のプログラムを開発する


> この記事はやや古くなっています。下記の新しいチュートリアルがベターです。
http://qiita.com/nkjm/items/38808bbc97d6927837cd

# 概要

この記事はLINEで動作する栄養士Botを開発するチュートリアルのPart3です。

前回までの記事はこちら。

- Part1 http://qiita.com/nkjm/items/0e9d24b2f3429bd33c8d
- Part2 http://qiita.com/nkjm/items/daa4e34b26ef937446c6

今回はLINEでBot本体のプログラムを開発していきます。

# 所要時間

60分

# Bot本体を開発する

いよいよBot本体のプログラムを作っていきます。今回はNode.jsおよびExpressフレームワークを使ってこのプログラムを開発します。

まずプログラムを格納するディレクトリを作成します。今回はdietitianというディレクトリ名としています。

```
$ cd $HOME
$ mkdir dietitian
$ cd dietitian/
```

次にnpm initでpackage.jsonファイルを対話的に作成します。

```
$ npm init
name: (dietitian) 
version: (1.0.0) 
description: dietitian bot for LINE
entry point: (index.js) 
test command: 
git repository: 
keywords: 
author: Kazuki Nakajima
license: (ISC) MIT

{
  "name": "dietitian",
  "version": "1.0.0",
  "description": "dietitian bot for LINE",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Kazuki Nakajima",
  "license": "MIT"
}

Is this ok? (yes) 
```

expressフレームワークをインストールします。

```
$ npm install --save express
// 出力省略
```

ではこのプログラムの核となるindex.jsファイルをまず下記の通り作成します。

```javascript:/index.js
// -----------------------------------------------------------------------------
// モジュールのインポート
var express = require('express');
var app = express();

// -----------------------------------------------------------------------------
// Webサーバー設定
var port = (process.env.PORT || 3000);
var server = app.listen(port, function() {
    console.log('Node is running on port ' + port);
});

// -----------------------------------------------------------------------------
// ルーター設定
app.get('/', function(req, res, next){
    res.send('Node is running on port ' + port);
});

```

これで一度アプリが起動するかテストします。

```
$ node index.js
Node is running on port 3000

```

上記の通りエラーが出ずに「Node is running on port 3000」のメッセージがでればOKです。

ブラウザで http://localhost:3000 にアクセスすると、同じメッセージが表示されるはずです。

## プログラムをHerokuにデプロイする

Heroku CLIを使ってHerokuにログインします。

```
$ heroku login
Enter your Heroku credentials.
Email: あなたのEmail
Password (typing will be hidden): 
Logged in as あなたのEmail
```

続けてHeroku CLIでHeroku上にアプリケーションを作成します。

```
$ heroku apps:create あなたのアプリ名
Creating ⬢ あなたのアプリ名... done
https://[あなたのアプリ名].herokuapp.com/ | https://git.heroku.com/あなたのアプリ名.git
```


Gitレポジトリを初期化します。

```
$ git init
Initialized empty Git repository in /Users/nkjm/dietitian/.git/
```

リモートレポジトリとしてHerokuを追加します。

```
$ git remote add heroku https://git.heroku.com/あなたのアプリ名.git
```

.gitignoreファイルを作成し、Gitの管理対象外にしたいファイルを一行一ファイルで列挙します。

```text:/.gitignore
.DS_Store
.gitignore
npm-debug.log
node_modules
```

Herokuにプログラムの起動方法を教えるための設定ファイル、Procfileを作成します。

```text:/Procfile
web: node index.js
```

これまでに作成したコードをレポジトリにコミットします。

```
$ git add .
$ git commit -m "first commit"
```

Herokuへデプロイします。

```
$ git push heroku master
// 途中出力省略
remote: Verifying deploy.... done.
To https://git.heroku.com/dietitian-nkjm.git
 * [new branch]      master -> master
```

https://あなたのアプリ名.herokuapp.com にアクセスして先ほどと同じく「Node is running on port [ポート番号]」が表示されることを確認します。 *＊ポート番号は動的に割り当てられます。*

これでプログラムを開発し、Herokuへデプロイするまでの流れが確認できました。


## Webhookを設定する

WebhookはLINEでBotに関連するイベントが発生した場合に、そのイベントを現在開発しているプログラムに通知してもらうためのアクセスポイントです。

このURLは開発者が指定することができ、そのURLに対してLINEからPOSTリクエストが送信されることになります。今回は下記のURLをWebhookとして設定します。

https://あなたのアプリ名.herokuapp.com/webhook

LINE Developersにアクセスします。LINE Developersはまず[LINE Business Center](https://business.line.me/ja/)にアクセスし、アカウントリストタブを選択、作成したビジネスアカウントの「LINE Developers」ボタンをクリックすることで遷移できます。
![スクリーンショット_2016-11-28_15_15_38.png](https://qiita-image-store.s3.amazonaws.com/0/26079/56fab9a5-b810-b67e-cec9-5b7d6cda1f0c.png "スクリーンショット_2016-11-28_15_15_38.png")

ここで画面下部の「EDIT」ボタンをクリックし、Webhook URLを下記の通り設定します。
![スクリーンショット_2016-11-28_15_18_23.png](https://qiita-image-store.s3.amazonaws.com/0/26079/d2723ff9-7d15-775f-5e04-f475f7dfda01.png "スクリーンショット_2016-11-28_15_18_23.png")

https://あなたのアプリ名.herokuapp.com/webhook

これでLINE側の設定は完了です。つづけてBot本体に該当するコードを加えていきます。index.jsファイルを編集してルーター設定のセクションに下記のコードを追加します。

```javascript:/index.js
app.post('/webhook', function(req, res, next){
    res.status(200).end();
    console.log(req.body);
});
```

これでまたHerokuにコードをデプロイします。

```
$ git add . && git commit -m "added webhook" && git push heroku master
```

ログをリアルタイムで出力させます。

```
$ heroku logs --tail
```

この状態でスマホのLINEアプリからQRコードでBotを友達に追加します。
![IMG_0206.PNG](https://qiita-image-store.s3.amazonaws.com/0/26079/8cbb60c9-4819-1b13-4922-5978bd216621.png "IMG_0206.PNG")

![26758202-c1df-dab6-f378-86bfa4762508.png](https://qiita-image-store.s3.amazonaws.com/0/26079/a75947db-46ef-d298-9d95-310e9090f1b9.png "26758202-c1df-dab6-f378-86bfa4762508.png")


友達に追加したら、下記のようなログがHeroku上で出力されているはずです。これはLINEからHerokuで稼働しているNodeアプリに「友達追加イベント」の通知が届いたことを意味しています。

```
2016-11-28T06:35:42.578258+00:00 heroku[router]: at=info method=POST path="/webhook" host=あなたのアプリ名.herokuapp.com request_id=47b39641-54ea-4cc7-a86f-323b33c601d1 fwd="203.104.146.155" dyno=web.1 connect=1ms service=6ms status=200 bytes=117
2016-11-28T06:35:42.576932+00:00 app[web.1]: undefined
```

ログの最後に「undefied」となっていますが、これは本来先ほど追加したコードのconsole.log(req.body)が出力されるべき部分です。

これはPOSTリクエストのbody部分を出力させることを意図していますが、req.bodyオブジェクトはデフォルトでは存在していないのでundefinedとなっています。このreq.bodyオブジェクトからbodyデータにアクセスするためにはbody-parserモジュールを使用する必要があります。

下記のように追加でbody-parserモジュールをインストールします。

```
$ npm install body-parser --save
```

次にindex.jsを下記のように更新します。

```javascript:/index.js
// -----------------------------------------------------------------------------
// モジュールのインポート
var express = require('express');
var bodyParser = require('body-parser'); // 追加
var app = express();


// -----------------------------------------------------------------------------
// ミドルウェア設定
app.use(bodyParser.json()); // 追加
```

これでリクエストに含まれるbody部分のデータはreq.bodyとしてアクセス可能になりました。
Herokuに更新したコードをデプロイしてもう一度ログを確認してみます。

```
$ git add . && git commit -m "added body-parser" && git push heroku master
// 出力は省略
$ heroku logs --tail
```

もうBotは友達に追加されていると思いますので、今度はメッセージを送信してみます。（本来は同じイベントで差異をテストすべきですが、友達に追加・削除を繰り返すのは面倒なのでメッセージイベントでテストします）
![e034bd98-c097-1cc8-f440-0183540caaec.png](https://qiita-image-store.s3.amazonaws.com/0/26079/17bfb32f-7da7-0858-7137-bd8236f1869b.png "e034bd98-c097-1cc8-f440-0183540caaec.png")


```
2016-11-28T07:29:58.804025+00:00 heroku[router]: at=info method=POST path="/webhook" host=あなたのアプリ名.herokuapp.com request_id=27f54e62-3334-4d5d-8317-32482c7f4007 fwd="203.104.146.155" dyno=web.1 connect=0ms service=40ms status=200 bytes=117
2016-11-28T07:29:58.792131+00:00 app[web.1]: { events: 
2016-11-28T07:29:58.792148+00:00 app[web.1]:    [ { type: 'message',
2016-11-28T07:29:58.792150+00:00 app[web.1]:        replyToken: 'f82d408e5a4c448889f9c301af97fdff',
2016-11-28T07:29:58.792150+00:00 app[web.1]:        source: [Object],
2016-11-28T07:29:58.792152+00:00 app[web.1]:        timestamp: 1480318198095,
2016-11-28T07:29:58.792153+00:00 app[web.1]:        message: [Object] } ] }
```

今度はreq.bodyが出力されているのがわかります。
あとはイベントに応じてこのreq.bodyの中身を処理すれば、イベントのハンドリングが可能になります。

> 本来はここで署名検証の処理を入れるのですが、本チュートリアルでは割愛しています。

## メッセージを受信する

先ほどまでのステップでメッセージの受信はほぼ完了しています。あとはreq.bodyからメッセージを抜き出すだけです。

index.jsのWebhookのルーター設定を下記のように修正します。

```javascript:/index.js
app.post('/webhook', function(req, res, next){
    res.status(200).end();
    for (var event of req.body.events){
        if (event.type == 'message'){
            console.log(event.message);
        }
    }
});
```

LINEからのイベント通知におけるデータは、eventオブジェクトの配列によって構成されています。したがってこのeventオブジェクトの配列をループ処理し、イベントタイプ（event.type）がmessageだった場合にそのメッセージ（event.message）を出力するようにしました。

このコードをHerokuにデプロイし、ログを見ながらBotに同じメッセージを送ってみます。

```
$ git add . && git commit -m "added body-parser" && git push heroku master
// 出力は省略
$ heroku logs --tail
```

すると下記のようにログが出力されるはずです。

```
2016-11-28T07:42:21.072770+00:00 app[web.1]: { type: 'text', id: '5270345585694', text: 'ハロー' }
```

これでメッセージを受信することができるようになりました。


## ユーザーに返信してみる

LINEにAPIコールを発行するため、requestモジュールをインストールします。

```
$ npm install request --save
```

index.jsに下記2点の修正を加えます。

1. requestモジュールをインポートする
+ LINEへのAPIコールに必要なChannel Access Tokenを定数として設定する
+ Webhookのルーター設定にLINE Messaging APIへのコールアウトを追加する。

まず1と2の変更です。

```javascript:/index.js
// -----------------------------------------------------------------------------
// 定数の設定
const LINE_CHANNEL_ACCESS_TOKEN = 'あなたのChannl Access Token'; // 追加

// -----------------------------------------------------------------------------
// モジュールのインポート
var express = require('express');
var bodyParser = require('body-parser');
var request = require('request'); // 追加
var app = express();
```

次に3の変更です。

```javascript:/index.js
app.post('/webhook', function(req, res, next){
    res.status(200).end();
    for (var event of req.body.events){
        if (event.type == 'message' && event.message.text == 'ハロー'){
            var headers = {
                'Content-Type': 'application/json',
                'Authorization': 'Bearer ' + LINE_CHANNEL_ACCESS_TOKEN
            }
            var body = {
                replyToken: event.replyToken,
                messages: [{
                    type: 'text',
                    text: 'こんにちはー'
                }]
            }
            var url = 'https://api.line.me/v2/bot/message/reply';
            request({
                url: url,
                method: 'POST',
                headers: headers,
                body: body,
                json: true
            });
        }
    }
});
```

このコードをHerokuにデプロイし、Botに同じ「ハロー」というメッセージを送ってみます。

```
$ git add . && git commit -m "added reply" && git push heroku master
```

するとBotから「こんにちはー」と返信があるはずです。逆に「ハロー」以外のメッセージを送っても返信はありません。
![11a31678-5037-c105-a965-9cb08afd5210.png](https://qiita-image-store.s3.amazonaws.com/0/26079/02440c1d-3cfa-69f5-3726-0c199bc02282.png "11a31678-5037-c105-a965-9cb08afd5210.png")

まだまだ原始的な状態ですが、これでBotは受信したメッセージに応じて返信する能力を身につけました。

# 次のステップ

http://qiita.com/nkjm/items/d46bd91e1784adf1434b

次回はBotがメッセージの中から食品を特定し、その栄養を取得する機能を追加していきます。