LINE Messaging APIのテンプレートメッセージでチャットBOTを作る

この記事は室蘭工業大学データサイエンス研究室の　[DSL Advent Calendar 2019](https://qiita.com/advent-calendar/2019/muroran-it-dsl)　16日目の記事です。M1の @romorimori が担当します。
#はじめに
LINEが提供している[Messaging API](https://developers.line.biz/ja/services/messaging-api/)には様々なメッセージタイプのテンプレートが用意されていて、個人でも簡単にチャットBOTを作ることができます。

この記事では確認テンプレートやポストバックアクションなどの機能を使って、晩ご飯をレコメンドしてくれるチャットBOTを作っていきます。

##完成品
<img width="375" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403459/39053030-0c1e-f5a5-ee30-be2aa487d613.png">
チャットBOTに"晩ご飯"とメッセージを送ると、室蘭工業大学から徒歩圏内のお店をレコメンドしてくれます。
レコメンド機能に関しては作り込んでいないので、リストからランダムに選ばれるだけです。

##構成図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403459/e315d169-c0c6-4ad2-5c41-07c6852e6c4e.png)
（引用：[LINE API ドキュメント](https://developers.line.biz/ja/docs/messaging-api/overview/) )


##環境＆使うもの
今回はNode.jsでBOTサーバーを開発していきます。
また、開発したBOTサーバーは[ngrok](https://ngrok.com/)で外部公開してテストします。
ngrokの解説はわかりやすい日本語記事がありましたので、こちらを引用させていただきます。（[テストサーバーへのアップが面倒なときはngrokでローカル環境を外部公開してみよう](https://liginc.co.jp/web/programming/156484)）

MacOS Catalina 10.15.1
node v11.14.0
npm 6.7.0
LINEアカウント（普段使いのものでOK）
ngrok version 2.3.34

#開発
以下のような流れで開発していきます。

- チャットBOTのChannelの開設＆設定
- チャットサーバーを開発
- ngrokで公開＆Webhook設定
- スマホアプリからメッセージを送ってみる

##Channelの開設
チャットBOT用のLINEアカウントを作成します。アカウントはLINE Developer コンソールではChannelとして管理されます。
まず[LINE Developer コンソール](https://developers.line.biz/ja/)で自分の普段使いしているLINEアカウントでログインします。初回は開発者登録が求められるので画面に従って必要事項を入力し登録します。

登録が済んだら、最初にプロバイダーを作成します。プロバイダーとはこれから作成するチャットBOTの提供元として表示される情報です。

**プロバイダーのChannel管理画面**
<img width="1239" alt="スクリーンショット 2019-12-15 15.07.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403459/8f365ab0-76a8-ab5b-0bdf-45dfa44eb67f.png">

次にChannelを作成します。今回はMessaging APIを選択します。
アプリ名などの必要項目を入力していき、Channelを作成します。
<img width="1239" alt="スクリーンショット 2019-12-15 15.18.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403459/bbe23433-5c77-761c-5d89-fdafbbf715a2.png">

これでChannelが開設できました。
次に、BOTサーバーの開発に必要な設定を行っていきます。

##アクセストークンとChannel Secretの発行
Messaging API設定タブに移動し、チャネルアクセストークンを発行します。このトークンは自分で開発したチャットサーバーからMessaging APIを呼び出すのに必要になります。
<img width="1239" alt="アクセストークン発行.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403459/02e84643-720f-28f0-d55a-425b756f61f2.png">

Channel　Secretも同様に、API呼び出しに必要になります。基本設定タブにあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403459/1e035317-4d0c-f4a0-39ea-979c3068a18d.png)

##アクセストークンとChannel Secretを環境変数へセット
MacOS環境の場合、`export`コマンドで環境変数をセットできます。

```console
$ export BANGOHAN_RECOMMEND_ACCESS_TOKEN="xxxxxxxxxxxxx"
$ export BANGOHAN_RECOMMEND_SECRET_KEY="xxxxxxxxxxxx"
```

##LINE公式アカウント機能の設定
**自動応答メッセージ**と**友達追加時あいさつ**は使わないので、「利用しない」を選択しておきます。

これでChannelの設定はほぼ完了です。これからチャットBOTの開発をしていくのですが、Webhook URLの設定をあとから行うので、このページは開いたまま開発を進めていきます。

##チャットBOTサーバーを開発
今回はNode.jsを使って開発していきます。
プロジェクトディレクトリを作って`npm init`します。

```console
$ mkdir button-message
$ cd button-message/
$ npm init --yes
```
開発に必要なnpmパッケージをインストールしておきます。

```console
$ npm install --save express @line/bot-sdk
```

- **Express** : Node.js向けのWebアプリケーションフレームワーク。リクエストの処理が楽になります。
- **@line/bot-sdk** : [公式](https://developers.line.biz/ja/docs/messaging-api/line-bot-sdk/)が提供しているLINE Messaging APIのSDK（Software Development Kit）のNode.js版。Messaging APIを組み込んだBOTアプリの開発が簡単にできるようになります。


次に、プログラムの雛形を記述します。

```javascript
const express = require('express'); // expressインポート
const app = express();
const line = require('@line/bot-sdk');// sdkインポート

const config = {
 channelAccessToken: process.env.BANGOHAN_RECOMMEND_ACCESS_TOKEN, // 環境変数からアクセストークンをセット
 channelSecret: process.env.BANGOHAN_RECOMMEND_SECRET_KEY // 環境変数からChannel Secretをセット
};
const client = new line.Client(config); // APIコールのためのクライアントインスタンスを作成

const port = process.env.PORT || 3000; // ポートを環境変数or3000で設定
app.listen(port, () => console.log(`Listening on port ${port}...`) );

// WebHookのエンドポイント
app.post('/hook', line.middleware(config), (req, res) => lineBot(req, res));

// line bot 本体
function lineBot(req, res) {
  res.status(200).end(); // すぐにLINE側にステータスコード200でレスポンス
// チャットボットの処理をかいていく
}
```

- `config`には、先ほどLINE DevelopersのChannel管理画面で取得したアクセストークンとChannel Secretを設定します。これらはセキュリティの面から環境変数に設定してそこから取得しています。
- Webhookを受け取るためのエンドポイントは`/hook`にします。 WebhookはLINEでBotに関連するイベントが発生した場合に、そのイベントをチャットBOTに通知してもらうためのアクセスポイント（URL）です。
- チャットBOTのメインの処理を行う関数では、[大量メッセージが来ても安心なLINE BOTサーバのアーキテクチャ](https://qiita.com/yoichiro6642/items/6d4c7309210af20a5c8f)を参考に、リクエストを受け取るとすぐにステータスコード200でレスポンスを行います。



##チャットBOT本体のメソッド
今回実装したい機能は

- Botに”晩ご飯”とメッセージを送ると、お店のリストからランダムに確認テンプレート形式のメッセージで返信する。
- Botの返信に対して"NO"ボタンをタップすると、`postback`アクションを実行して別の案を返信する。
- Botの返信に対して”YES”ボタンをタップすると、`message`アクションを実行する。

LINE APIのメッセージオブジェクトのメッセージタイプは９種類あり、今回はテンプレートメッセージのひとつである確認テンプレートを使っていきます。テンプレートメッセージには事前定義されたレイアウトがいくつか用意されています。また、アクションを使うことでユーザとのやりとりができたりします。
確認テンプレートは下のような形式です。（引用元：[LINE Developersドキュメント](https://developers.line.biz/ja/docs/messaging-api/message-types/#confirm-template)）
<img width="375" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403459/b0d4f6ab-93f3-ab64-d754-3a294279da9e.png">

本体の処理を以下のように記述します。

```javascript
// 室蘭工業大学の学生の晩ご飯一覧
const bangohanList = ["SAINO","章吉","もっちゃん","なかよし","学食","夕月庵","チャイナ","コンビニ","自炊"];
// line bot 本体
function lineBot(req, res) {
  res.status(200).end(); // すぐにLINE側にステータスコード200でレスポンス
  const promises = []; // すべてのイベント処理のpromiseを格納する配列
  const events = req.body.events; // イベントオブジェクト

  // イベントオブジェクトを処理
  events.forEach((event) => {
    // イベントタイプが"message"で、typeが"text"だった場合
    if(event.type === "message" && event.message.type == "text"){
      // メッセージの内容が”晩ご飯”なら晩ご飯をレコメンドする
      if(event.message.text === "晩ご飯"){
        const bangohan = bangohanList[Math.floor(Math.random() * bangohanList.length)];// 晩ご飯リストからランダムに要素を取得
        promises.push(client.replyMessage(event.replyToken, {
          "type": "template",
          "altText": "晩ご飯をレコメンドします",
          "template": {
            "type": "confirm",
            "text": `今日の晩ご飯は${bangohan}でどう？`,
            "actions": [
                {
                  "type": "postback", //"NO"が押されたらpostbackアクション
                  "label": "NO",
                  "data": JSON.stringify({"action":"no"})
                },
                {
                  "type": "message", //"YES"が押されたらmessageアクション
                  "label": "YES",
                  "text": `今日の晩ご飯は${bangohan}で決まり！`
                }
            ]
          }
        }));
      }
    }
    // イベントタイプが"postback"だった場合
    else if(event.type === "postback"){
      // noボタンが押されていた場合
      if(JSON.parse(event.postback.data).action === "no"){
        const bangohan = bangohanList[Math.floor(Math.random() * bangohanList.length)];
        promises.push(client.replyMessage(event.replyToken, {
          "type": "template",
          "altText": "晩ご飯をレコメンドします",
          "template": {
            "type": "confirm",
            "text": `それなら${bangohan}はどう？`,
            "actions": [
                {
                  "type": "postback",
                  "label": "NO",
                  "data": JSON.stringify({"action":"no"})
                },
                {
                  "type": "message",
                  "label": "YES",
                  "text": `今日の晩ご飯は${bangohan}で決まり！`
                }
            ]
          }
        }));
      }
    }
  });
  // 全プロミスを処理したらログを出力
  Promise.all(promises).then(console.log(`promise completed\n`));
}

```

この関数では、ユーザが送ってきたメッセージやアクションのリクエストに対してメッセージを作成し、返信します。

具体的には、リクエストからイベントオブジェクトを取り出し、イベントのタイプが`message`か`postback`かを判別して、それぞれ別のメッセージオブジェクトを返信します。
`@line/bot-sdk`のクライアントインスタンスを使うことで、メッセージイベントに対しての返信を`client.replyMessage( "リプライトークン",　{メッセージオブジェクト} )`というように書くことができます。

###**メッセージオブジェクト**
今回使用したメッセージオブジェクトを表にまとめてみます。メッセージタイプによって必須のプロパティなどは変わってきます。詳しくは[LINE Developersドキュメント](https://developers.line.biz/ja/docs/messaging-api/message-types/#template-messages)に乗っています。

| プロパティ | 説明 |
|:-----------|------------|
| type | メッセージタイプを設定する。今回はテンプレートメッセージが使いたいので"template"を設定 |
| altText | 代替テキスト、通知とかで表示される文。 |
| template |　*テンプレートオブジェクト* |


###**テンプレートオブジェクト**
次に、今回使ったテンプレートオブジェクトのプロパティを表にまとめます。今回は確認テンプレートをつかいましたが、こちらもテンプレートの種類によってプロパティは変わります。

| プロパティ | 説明 |
|:-----------|------------|
| type | テンプレートタイプを設定する。今回は確認テンプレートが使いたいので"confirm"を設定 |
| text | テンプレートの本文。今回はレコメンドの内容を設定しました。 |
| actions | *アクションオブジェクト* 。今回使った確認テンプレートには2つのボタンがあり、それぞれが押された時のアクションを設定できます。 |

###**アクションオブジェクト**
アクションのタイプは`"type"`プロパティで設定できます。今回使ったもの以外にも日時選択アクションやカメラアクションなど、様々な機能があります。

**・postbackアクション**
このアクションが関連づけられたコントロールがタップされると、`data`プロパティに指定された文字列を含むポストバックイベントが、Webhookを介して返されます。
今回の場合は、NOボタンをタップすると文字列`{"action":"no"}`が返されます。JSON文字列で送信することで、そのあとのイベント解析が楽になります。

**・messageアクション**
このアクションが関連づけられたコントロールがタップされると、`text`プロパティの文字列がユーザーからのメッセージとして送信されます。

##ngrokで公開する
チャットBOTサーバーを実行します。
ローカル環境の3000ポートでサーバーが動いているので、3000ポートをngrokを使って公開します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403459/f5cc0b27-22c6-dca2-281e-1a2caa288077.png)
HTTPSのアドレスが取得できるので、Channel管理画面を開き、これをWebhook設定の部分に設定し、**Webhookの利用**をONにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403459/81c17623-1449-746b-7804-0f846ac6e0a9.png)

#完成したもの
Channel管理画面でQRコードを読み取ることでチャットBOTを友達追加できるので、追加してメッセージを送ってみます。
<img width="375" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403459/cc5d934b-dfca-7550-b53a-e293b132a388.png">

うまく動いてくれました。

- ”晩ご飯”とメッセージを送ると、”今日のご飯は〜でどう？”と確認テンプレートを使った提案をしてくれています。
- 写真では伝わりにくいですが、NOボタンをタップすると”それなら〜はどう？”と再提案してくれます。
- YESをタップすると`message`アクションが実行されて、ユーザ側から”今日の晩ご飯は〜で決まり！”と提携文が返されます。
- また、スタンプは`text`タイプではないので何も実行されません。

#おわりに
簡単にですが、LINE APIを使ったチャットBOT開発の流れを書かせていただきました。LINE APIには、ここで紹介した以外にもたくさんの機能があり、おもしろいWEBサービスを作ることができるとおもいます。

今年もあと少しなので、がんばりましょう！！！！！！！！！！！！！！！！


#参考
[LINE Developersドキュメント](https://developers.line.biz/ja/docs/messaging-api/message-types/#template-messages)
[大量メッセージが来ても安心なLINE BOTサーバのアーキテクチャ](https://qiita.com/yoichiro6642/items/6d4c7309210af20a5c8f)
[Webhookとは？](https://qiita.com/soarflat/items/ed970f6dc59b2ab76169)
[テストサーバーへのアップが面倒なときはngrokでローカル環境を外部公開してみよう](https://liginc.co.jp/web/programming/156484)