[GoogleAppsScript] YES/NOチャート LINE-BOTを作成する

# はじめに
LINE を使用して YES/NOチャートを行うBOTを作成しました。
質問にYES/NOで答えると次の質問に進みます。最終的に結果が表示されます。
![faq_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50563/202e49b4-3779-a8a9-fa9a-18cb66977eaf.png)

![faq_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50563/0dc7c9a6-0bbf-04f1-0ef8-52ba7879f87f.png)

![faq_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50563/38849d9f-3ca1-f331-be8e-f42d2606a417.png)

スプレッドシートでは、IDと質問内容、YES選択時のID、NO選択時のIDを指定します。
YES選択時のIDが次の質問内容を表示するIDとなります。
（typeの`confirm`は質問、`message`は結果メッセージ）

![faq.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50563/7cfb24fd-8c8d-55ff-5086-b6a7f8037722.png)

# 事前準備
[詳しくはこちら](https://qiita.com/yun_bow/items/33879bca8f82d1ac6cfa)

1. GoogleDriveにてGoogleAppsScriptを開いて、以下のスクリプトを実装します。

- `LINE_CHANNEL_TOKEN`にLINE Messaging APIで発行したトークンを指定します。
- `SSID`にスプレッドシートのIDと`SSN_USER`にシート名を指定します。

```JavaScript:main.gs
/**
 * YES/NOチャート
 */
const LINE_CHANNEL_TOKEN = '*****'; // LINE NOTIFYのアクセストークン
const SSID = '*****';
const SSN_USER = 'user';
const SSN_FAQ = 'faq';
```

# フォロー登録
[詳しくはこちら](https://qiita.com/yun_bow/items/33879bca8f82d1ac6cfa)

# フォロー解除
[詳しくはこちら](https://qiita.com/yun_bow/items/33879bca8f82d1ac6cfa)

# リプライ
ユーザーがYES/NOを選択すると`LINE Messaging API`からGASにPOSTが通知されます。GASでは、IDが通知されます。そのIDから質問または結果を`LINE Messaging API`にPOSTで返します。

```plantuml
@startuml
actor ユーザー
ユーザー -> LINEアプリ: YES/NO選択
activate LINEアプリ
LINEアプリ -> "LINE Messaging API"
deactivate LINEアプリ
activate "LINE Messaging API"
  "LINE Messaging API" -> GoogleAppsScript: ...POST (type: message)
deactivate "LINE Messaging API"
  activate GoogleAppsScript
    GoogleAppsScript -> GoogleAppsScript: 次の質問を取得
    GoogleAppsScript -> "LINE Messaging API": POST /message/reply
  deactivate GoogleAppsScript
activate "LINE Messaging API"
"LINE Messaging API" -> LINEアプリ
deactivate "LINE Messaging API"
activate LINEアプリ
LINEアプリ -> LINEアプリ: 質問を表示
deactivate LINEアプリ
@enduml
```

```JavaScript:main.gs
/**
 * メッセージイベント
 * @param {Object} reqEvent 
 */
function executeMessage(reqEvent) {
    let msgList = [];
    let user = getUser(reqEvent.source.userId);
    if (user) {
        if (reqEvent.message.type === 'text') {
            let msg = createMsg(reqEvent.message.text);
            if (msg) {
                msgList.push(msg);
                sendLineReply(reqEvent.replyToken, msgList);
            }
        }
    }
}

/**
 * FAQ IDからメッセージを生成する
 * @param {String} faqId 
 */
function createMsg(faqId) {

    let msg = null;
    let faq = getFaq(faqId);
    if (faq) {
        switch (faq.type) {
            case 'message':
                msg = {
                    'type': 'text',
                    'text': String(faq.text),
                }
                break;
            case 'confirm':
                msg = {
                    'type': 'template',
                    'altText': String(faq.text),
                    'template': {
                        'text': String(faq.text),
                        'type': 'confirm',
                        'actions': [{
                                'type': 'message',
                                'label': String(faq.yes.text),
                                'text': String(faq.yes.id)
                            },
                            {
                                'type': 'message',
                                'label': String(faq.no.text),
                                'text': String(faq.no.id)
                            }
                        ],
                    },
                }
                break;
        }
    }
    return msg;
}

/**
 * FAQを取得する
 * @param {String} faqId 
 */
function getFaq(faqId) {
    let faqList = getFaqList();
    for (let i in faqList) {
        let faq = faqList[i];
        if (faq.id == faqId) {
            return faq;
        }
    }
    return null;
}

/**
 * FAQ一覧を取得する
 */
function getFaqList() {
    let faqList = [];
    let lastRow = faqSheet.getLastRow();
    if (2 < lastRow) {
        faqList = faqSheet.getRange(2, 1, lastRow, 7).getValues();
        faqList = faqList.map((row) => {
            return {
                id: row[0],
                type: row[1],
                text: row[2],
                yes: {
                    id: row[3],
                    text: row[4],
                },
                no: {
                    id: row[5],
                    text: row[6],
                },
            }
        });
    }
    return faqList;
}

/**
 * LINEに応答メッセージを送信する
 * @param {String} replyToken リプライトークン
 * @param {Object} msgList メッセージリスト
 */
function sendLineReply(replyToken, msgList) {
    let url = 'https://api.line.me/v2/bot/message/reply';
    let options = {
        'method': 'post',
        'headers': {
            'Content-Type': 'application/json; charset=UTF-8',
            'Authorization': `Bearer ${LINE_CHANNEL_TOKEN}`
        },
        'payload': JSON.stringify({
            replyToken: replyToken,
            messages: msgList
        })
    };
    let response = UrlFetchApp.fetch(url, options);
    return JSON.parse(response.getContentText('UTF-8'));
}
```
- `execute`関数では、LINE Messaging APIからの通知内容により、処理を振り分けしています。`message`の場合は、YES/NO選択内容を解析します。
- 質問または結果メッセージを作成して`sendLineReply`関数でLINE Messaging APIにPOSTします。

# 参考リンク
- [【30分でやる】Google Spread Sheetで管理するLINE BOTの作り方](https://qiita.com/WdknWdkn/items/b78ae572e7cb5c9dfdca)
- [Google Spreadsheetで診断ボットをつくった](https://qiita.com/cgetc/items/67fcc0de611b78376171)

# さいごに
ソースコードをGitHubに公開しています。

- [ソースファイルはこちら](https://github.com/yunbow/GoogleAppsScript-LINE-FAQ)

以上です。