LINE Bot （LINE MESSAGING API）の個人的開発7Tips


# はじめに
<a id="a1"></a>
この記事は、9/23実施の [Arumon](https://www.arumon.net/) Lightning Talk 向けの記事です。

* この記事で書くこと
    * LINE Bot をいくつか開発して思ったTips（ほぼ当たり前のこと）の紹介
    * （ケーススタディとして）実際に @kodomoEBINA が作った LINE Bot の紹介

* この記事に書かないこと
    * LINE Bot のハンズオン的な開発手順の説明
    * push通知の実装など（いずれ他の記事で）

* この記事読んでためになりそうな人
    * LINE Bot でどんなことができるかのイメージが湧いていない人 
    * LINE Bot 開発してみたいけど、どうすればいいかのイメージが沸かない人
    * LINE Bot 開発駆け出しの人

# 開発に必要なもの
<a id="b1"></a>

### LINE Bot （LINE MESSAGING API） とは
基本的には、**右から左へ受け流す~~ムーディ勝山的な~~存在**
できることは大きく以下の二つ

* **リプライ （←今回の説明対象）**
    * メッセージ受け取って、そのまま自作のプログラムに受け流す
    * 自作のプログラムでの実行結果を受け取って、ユーザにメッセージとして送る

　　→ **複雑そうな処理は全部裏側を自作する必要がある**


* **プッシュ通知**
    * ユーザに一方的にメッセージを送りつける
    * ただし、無料/低価格プランの場合、回数制限あり

### LINE Bot の基本構成
![基本構成イメージ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258791/ea5cd082-09f2-3382-e62f-c543cb48ac63.png)

### 登場人物たちの解説
* LINE Bot （LINE MESSAGING API） 
    * メッセージをひたすら受け流す
    * 開設には、LINE公式アカウントの登録が必要
    * 作り方は、[LINE BOTの作り方（外部リンク）](https://first-contact.jp/blog/article/linebot/) 参照

* Google Apps Script （GAS）
    * メッセージを受け取って処理するための自作プログラム
    * Google Cloud Functions （GCF） や Lambda などでも代用可
    * ちなみに、GASの場合、言語はJavascript（node.js）

* Firebase Realtime Database / Firestore
    * ユーザ登録などで必要
    * 一つの処理に複数回往復が必要になる場合は必要（＝ほぼ絶対必要）
    * 他のDBでももちろん代用可。今回はGASとの親和性の高さを重視

# 開発の流れ
<a id="c"></a>

1. 何をやらせるか（＝持たせたい機能）を考える
2. 処理フローをざっくり考える
3. 環境を準備する
4. 細かいメッセージタイプとか考えつつ、開発→デプロイ→テスト のサイクルを回す

### 0.ケーススタディの前座 - 「投資王」を例に
* 「投資王」ってなんだ？
    * 一言で言うと、「仮想通貨/投資信託の売買シミュレーションLINEbot」
    * 機能概要
        * 登録すると、擬似的に100万円がプレゼントされる
        * 仮想通貨は30分に1回、投資信託は1日に1回チャートが変動
        * 売買のたびに売買手数料も取られる
        * と言う、中で金融商品（仮想通貨/投資信託）を売り買いを擬似体験するもの
    * 詳しい説明は、 [こちら](https://github.com/kodomoEBINA/secretaline/wiki/%E6%8A%95%E8%B3%87%E7%8E%8B) 
    * 実際に触ってみたい方は、[こちら](https://lin.ee/gAk0Xtl)から


* 「投資王」の構成概要
![投資王の構成概要](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258791/8537efc3-fdc3-d7a3-faa7-ade488f4f44f.png)

### 1. 何をやらせるか（＝持たせたい機能）を考える
<a id="c1"></a>
細かくは割愛

* 投資王における機能一覧
    * ユーザ登録
    * 金融商品の現在価格表示（リクエストベース）
    * 金融商品の売買
    * 売り時タイマー：特定の商品がとある価格より上回った時のお知らせ機能


### 2. 処理フローをざっくり考える
<a id="c2"></a>
「投資王」の例のように、どんなやりとりにするかのシナリオをざっくりイメージする

**用例**

* →　<処理内容>　　　：*ユーザからBotへ*
* ←　<処理内容>　　　：*Botからユーザへ*
* （--　<処理内容>　--）　：*Bot内処理*

**例1：金融商品の現在価格表示**

* →　金融商品の現在価格表示リクエスト
* （--　価格情報が乗っているDBの情報を取得　--）
* ←　価格一覧を返す

**例2：ユーザ登録**

* →　登録リクエスト
* ←　登録名を聞く
* →　登録名を入力/回答
* （--　DBに情報を登録＆所持金として100万円を付与　--）
* ←　登録完了の旨をリプライ

**例3：金融商品の売買**

* →　売り/買い リクエスト
* ←　売買対象を聞く
* →　対象を回答
* ←　数量を聞く
* →　数量を回答
* （--　売買処理　--）
* ←　処理結果を返す


### 3. 環境を準備する
<a id="c3"></a>
  
Tips参照のため、割愛


### 4. 細かいメッセージタイプとか考えつつ、開発する
<a id="c4"></a>

#### 座学：受け渡されるメッセージの体型を理解する

とりあえず、以下のソースをGASにはっつけてそのままデプロイすると
何を言っても「こんにちは」で返してくれるBotが作れる。
> [LINE BOTの作り方（外部リンク）](https://first-contact.jp/blog/article/linebot/)  より引用

``` sample.js
function doPost(e) {
  var replyToken= JSON.parse(e.postData.contents).events[0].replyToken;
  if (typeof replyToken === 'undefined') {
    return;
  }

  var url = 'https://api.line.me/v2/bot/message/reply';
  var channelToken = 'ここにアクセストークン（ロングターム）を貼り付け';

  var messages = [{
    'type': 'text',
    'text': 'こんにちは',
  }];

  UrlFetchApp.fetch(url, {
    'headers': {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer ' + channelToken,
    },
    'method': 'post',
    'payload': JSON.stringify({
      'replyToken': replyToken,
      'messages': messages,
    }),
  });
  return ContentService.createTextOutput(JSON.stringify({'content': 'post ok'})).setMimeType(ContentService.MimeType.JSON);
}
```

まず、```doPost(e)```の```e```の中身を理解する。

```json
{
  "destination": "xxxxxxxxxx",
  "events": [
    {
      "replyToken": "0f3779fba3b349968c5d07db31eab56f",
      "type": "message",
      "mode": "active",
      "timestamp": 1462629479859,
      "source": {
        "type": "user",
        "userId": "U4af4980629..."
      },
      "message": {
        "id": "325708",
        "type": "text",
        "text": "Hello, world"
      }
    },
    {
      "replyToken": "8cf9239d56244f4197887e939187e19e",
      "type": "follow",
      "mode": "active",
      "timestamp": 1462629479859,
      "source": {
        "type": "user",
        "userId": "U4af4980629..."
      }
    }
  ]
}
```

基本的には、

* ユーザ識別子 : ```e.events.source.userID```
* メッセージ : ``` e.events.message.text ```

あたりを抑えておくと良い

#### 各やりとりにおけるユーザ操作を考える
botにメッセージを送るやり方はいくつかあるので、チョイス。
細かくはTips集に記載。

* テキスト入力
* リッチメニューから選択
* クイックリプライから選択
* テンプレートメッセージから選択

#### （参考）リッチメニュー・メッセージタイプについて

* [リッチメニュー画像の作り方から設定方法まで](https://line-sm.com/blog/howtomakerich-menus/)
* [公式リファレンス：テンプレートメッセージ・クイックリプライ](https://developers.line.biz/ja/docs/messaging-api/message-types/#template-messages)
* [Qiita：LINEのクイックリプライ機能を試してみました](https://qiita.com/kawazu255/items/fd72c4fa9b75a7d02d69)

# Tips集
<a id="d1"></a>

### <font color="Tomato">Tips1：複数"やりとり"まとめて1処理の実装</font>
<a id="tips1"></a>

LINE MESSAGING API は基本的に1メッセージに対して、1処理のため、
例2・例3のような"従前の会話を踏まえた処理"は、自作が必要

* **解決策の例：前回のやりとりをDBに保存**
    * 一番最初に前回やりとりを確認する処理を入れる
    * 前回やりとりの結果を元に受け取った内容を処理

### <font color="Tomato">Tips2：bot環境は本番/開発の二つを用意</font>
<a id="tips2"></a>

* 理由
    * 開発用があったほうが機能追加/修正時に便利なため
    * むしろ、開発用ないととてもじゃないけど、修正ができないため

* 実装のポイント
    * 開発/本番は、基本ソースは同じ
    * アクセストークン（=APIキー）を本番/開発で書き分けるだけにする


* 注意点
    * アクセストークンを修正し忘れるとbotが何も機能しなくなる
    * デプロイ後は必ず基本的な動きを確認

### <font color="Tomato">Tips3：テキスト入力は最小限</font>
<a id="tips3"></a>

* ユーザ操作の"起点"は、リッチメニューで選択


* "やりとり"は、クイックリプライ or テンプレートメッセージにする

* 理由・メリット
    * 直感的で分かりやすいユーザビリティになる
    * 一連の"やりとり"において、エラーになりづらい


=== 「投資王」での例：買う操作 === 

例：リッチメニューから選択させる
![メニュー画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258791/175a0e87-c5ff-2513-9ee9-e3f86f6245f4.jpeg)

例：やりとりは基本的にクイックリプライを活用
![buyfp3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258791/3ccf8287-8a17-0938-2be4-8829b6e766a9.jpeg)




### <font color="Tomato">Tips4：PC版のことは考えない</font>
<a id="tips4"></a>

* 理由：PC版では以下の機能が使えないため
    * リッチメニュー
    * クイックリプライ

→　結局テキストベースとなってしまうため、設計も面倒かつ使いづらいbotになる

### <font color="Tomato">Tips5：メッセージタイプを使い分ける</font>
<a id="tips5"></a>

* クイックリプライの特色を理解する
    * "やりとり"を見つつ、選択肢型でリプライができる
    * 表示領域が限定的


* クイックリプライの実装は、以下を心がける
    * 選択肢の数を少なくする
    * 選択肢自体の文字数も少なくする


* **逆に、選択肢が多い/選択肢の文字数が多い時は、テンプレートメッセージ**

* よくない例：「投資王」の商品選択
**→実際は選択肢が8つもあるのだが、3つしか画面上表示されていない**
（※ちなみに、今のところ直すつもりはない）

![商品選択画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258791/b5273d0e-623e-6215-d4b8-8a337d733ae6.jpeg)


### <font color="Tomato">Tips6：リッチメニューには 「キャンセル」・「ヘルプ」 を</font>
<a id="tips6"></a>

* 実際にこうしたボタンを設けるに至った友人の声
    * キャンセルボタン
        * ユーザがメニューを2回タップしてしまったことによって、エラーが発生
        * 今どの部分のやりとりなのかわからなくなる
    * ヘルプボタン
        * 名前入力のやり方がわからず、登録を諦める

### <font color="Tomato">Tips7:登録時あいさつメッセージに「ヘルプ」を</font>
<a id="tips7"></a>

* 登録時あいさつメッセージとは
    * botを友達追加した時に、bot側からユーザに自動配信されるメッセージ
    * そのメッセージにヘルプページにリンクを貼っておく