飲食店を応援したい！テイクアウト仕組みの構築＆LINEで注文状況を照会

#どんな事にチャレンジしたのか紹介
飲食店でのテイクアウトの自動化にチャレンジ
最近、LINEの勉強をしているという事で色々なコンサルの方から質問が来ます。
その流れで、飲食店のコンサルをされている方と最近テイクアウトの流れを一緒に考えているので、「オンライン注文をより便利なものに」を目標に、Firebaseを利用してどんな事が出来るのか試してみました。

今回私の考えた仕組み
1.LINEからもWEBからも予約が出来る
2.予約情報は一元管理されている
3.LINEで過去の予約状況を照会

上記の３工程
これをvue.js・node.js・Firebase・LINE Messaging APIを使って完全自動化！にチャレンジしました。

#完成品フロー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820683/b3741f1d-4740-2116-5879-4dae70c972dd.png)

webアプリ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820683/dc4c6742-bef2-3da6-9e1f-f31829d411b4.png)

LINEbot
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820683/45c36661-e240-39e2-7fd8-80a72fc71d9f.png)


#環境
・Visual Studio Code
・Firebase[https://firebase.google.com/](https://firebase.google.com/)
・Vue.js
・node.js

#下準備
##Firebaseでデータベースを構築
アプリ新規作成でSDKを発行し、データベースを構築
Firebaseでのデータベース構築方法は別記事に書きました↓↓
[Firebase CloudFirestoreの使い方を初心者が解説してみた](https://qiita.com/misa_m/items/0afbd063ff13b49f2729)

作成したコレクションは【takeout】
ドキュメントは
・利用日
・名前
・メールアドレス
・電話番号
・商品名
・コメント

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820683/267ba528-6ef1-630c-2c04-0ff404108192.png)

##LINE Messaging API作成
LINE Messaging APIを1つ作成
・チャンネルアクセストークン
・シークレット
取得

#レシピ
Visual Studio Codeで作業を進めました。
##Vue.jsを使ってwebアプリ側を作成

htmlファイルを作成し、FirebaseのSDK名とコレクション名を入力します。

```vuejs


<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>予約入力フォーム</title>
</head>

<body>
    <div id="app">
        <p>商品サービス：<input v-model="service" placeholder="サービス"><br> 名前：
            <input v-model="name" placeholder="名前"><br> メールアドレス： <input v-model="mail" placeholder="test@test.com"> <br>電話番号： <input v-model="tel" placeholder="090xxxxxxxx"> <br>利用日時： <input v-model="date" placeholder="2020年12月20日"><br> コメント： <input v-model="message"
                placeholder="自由テキスト入力欄"><br>
            <button v-on:click='post'>送信</button>
        </p>
    </div>

    <script src="https://unpkg.com/vue"></script>
    <script src="https://www.gstatic.com/firebasejs/7.2.3/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.2.3/firebase-firestore.js"></script>
    <script>
        // Your web app's Firebase configuration
        const firebaseConfig = {
            apiKey: "ForebaseSDKを入力します",
            authDomain: "ForebaseSDKを入力します",
            projectId: "ForebaseSDKを入力します",
            storageBucket: "ForebaseSDKを入力します",
            messagingSenderId: "ForebaseSDKを入力します",
            appId: "ForebaseSDKを入力します",
            measurementId: "ForebaseSDKを入力します"
        };
        // Initialize Firebase
        firebase.initializeApp(firebaseConfig);
        const db = firebase.firestore();

        const app = new Vue({
            el: '#app',
            data: {
                allData: [],
                service: '',
                name: '',
                mail: '',
                tel: '',
                date: '',
                message: ''
            },
            mounted: async function() {
                db.collection('takeout').onSnapshot(snapshot => {
                    // データベースに変更があった場合実行される
                    console.log('on snapshot!');
                    // 更新されたデータだけでなく、全て取得される
                    this.allData = [];
                    snapshot.forEach(doc => {
                        // console.log(doc);
                        this.allData.push(doc.data());
                    });
                });
            },
            methods: {
                //データ追加
                post: async function() {
                    await db.collection("Firebaseコレクション名を入力").add({
                        service: this.service,
                        name: this.name,
                        mail: this.mail,
                        tel: this.tel,
                        date: this.date,
                        message: this.message
                    });
                }
            }
        })
    </script>

</body>

</html>
```
以下のフロント部分を作成しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820683/460b8b41-aa2e-b31b-53e0-12f922fe21f3.png)

入力するとForebaseにデータが格納されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820683/f5d9da41-ca01-1d60-81da-151f6d7a433b.png)

##LINEのリッチメニューを利用して、LINEから予約
リッチメニューを作成しました。
2分割で、
・予約確認　⇒　テキストで【予約確認】と返答
・予約　⇒　予約webアプリへのリンク(LIFFを利用しても良い)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820683/d2d74560-9559-2ad1-6052-7dafc4a096ff.png)

##過去の予約状況をLINEのチャットで取得
リッチメニューで作成した予約確認バナーをクリックすると【予約確認】をテキストが返るようにしているので、このテキストで条件分岐してFirebaseからデータを取り出すようにしました。

node.jsのパッケージを利用するので

```
npm i express
npm i @line/bot-sdk
npm i firebase
```
をインストールします。

```node.js
'use strict'; 

// ########################################
//               初期設定など
// ########################################

// パッケージを使用します
const express = require('express');
const line = require('@line/bot-sdk');
const firebase = require("firebase/app");
require("firebase/firestore");

const firebaseConfig = {
            apiKey: "ForebaseSDKを入力します",
            authDomain: "ForebaseSDKを入力します",
            projectId: "ForebaseSDKを入力します",
            storageBucket: "ForebaseSDKを入力します",
            messagingSenderId: "ForebaseSDKを入力します",
            appId: "ForebaseSDKを入力します",
            measurementId: "ForebaseSDKを入力します"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
const db = firebase.firestore();

// ローカル（自分のPC）でサーバーを公開するときのポート番号です
const PORT = process.env.PORT || 3000;

// Messaging APIで利用するクレデンシャル（秘匿情報）です。
const config = {
    channelSecret: 'LINE Messaging APIシークレットキー',
    channelAccessToken: 'LINE Messaging APIアクセストークン'
};

// ########################################
//  LINEサーバーからのWebhookデータを処理する部分
// ########################################

// LINE SDKを初期化します
const client = new line.Client(config);

// LINEサーバーからWebhookがあると「サーバー部分」から以下の "handleEvent" という関数が呼び出されます
async function handleEvent(event) {
    // 受信したWebhookが「テキストメッセージ以外」であればnullを返すことで無視します
    if (event.type !== 'message' || event.message.type !== 'text') {
        return Promise.resolve(null);
    }
    //予約確認というテキストを受け取ったらお名前を教えてくださいと返します
    if (event.message.text == '予約確認') {
        const message = {
            type: 'text',
            text: 'お名前を教えてください'
        };
        client.pushMessage(event.source.userId, message);
    }
    const name = event.message.text;
    console.log(name);

    //入力してもらった名前をfirebaseから検索
    const onoStapshot = await db.collection("takeout").where('name', '==', name).get();
    const str = [];
    onoStapshot.forEach(ono => {
        console.log(ono.data());
        str.push(ono.data());
    });

    console.log(str);
    let msg = '';
    console.log(str.length);
    for (let i = 0; i < str.length; i++) {
    for (let i = 0; i < str.length; i++) {
        msg += "名前:" + str[i].name "\n" + "予約日:" + str[i].date + "\n" + "商品:" + str[i].service + "\n" + "メールアドレス:" + str[i].mail "\n" + "電話番号:" + str[i].tel + "\n" + "メッセージ:" + str[i].message + "\n";
    }
    }
    console.log(msg);
    const message = {
        type: 'text',
        text: msg
    };
    client.pushMessage(event.source.userId, message);

  
}

// ########################################
//          Expressによるサーバー部分
// ########################################

// expressを初期化します
const app = express();

// HTTP GETによって '/' のパスにアクセスがあったときに 'Hello LINE BOT! (HTTP GET)' と返事します
// これはMessaging APIとは関係のない確認用のものです
app.get('/', (req, res) => res.send('Hello LINE BOT! (HTTP GET)'));

// HTTP POSTによって '/webhook' のパスにアクセスがあったら、POSTされた内容に応じて様々な処理をします
app.post('/webhook', line.middleware(config), (req, res) => {
    // Webhookの中身を確認用にターミナルに表示します
    console.log(req.body.events);

    // 空っぽの場合、検証ボタンをクリックしたときに飛んできた"接続確認"用
    // 削除しても問題ありません
    if (req.body.events.length == 0) {
        res.send('Hello LINE BOT! (HTTP POST)'); // LINEサーバーに返答します
        console.log('検証イベントを受信しました！'); // ターミナルに表示します
        return; // これより下は実行されません
    }

    // あらかじめ宣言しておいた "handleEvent" 関数にWebhookの中身を渡して処理してもらい、
    // 関数から戻ってきたデータをそのままLINEサーバーに「レスポンス」として返します
    Promise.all(req.body.events.map(handleEvent)).then((result) => res.json(result));
});

// 最初に決めたポート番号でサーバーをPC内だけに公開します
// （環境によってはローカルネットワーク内にも公開されます）
app.listen(PORT);
console.log(`ポート${PORT}番でExpressサーバーを実行中です…`);
```

Firebaseから検索をして情報を取り出す部分

```javascript
    const nameSnapshot = await db.collection("takeout").where('name', '==', name).get();
    const str = [];
    nameSnapshot.forEach(name => {
        console.log(name.data());
        str.push(name.data());
    });
```

無事、実装成功です！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820683/c1fe0833-ceb0-5c78-b839-5c43f72937ef.png)


#まとめ・考察
今回のチャレンジで、LINE Messaging APIの仕組みをだいぶ理解できました。Firebaseの方はマニュアル通りに動かしているに留まっています。

飲食店のテイクアウトを想定して自動化の仕組みを作りましたが、様々なジャンルのweb予約、予約状況照会に使える機能だなと思いました。

また、Firebaseの管理画面だと情報の管理はしにくいので、iPaaSサービスを利用してスプレッドシートに接続したいなと思いました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820683/a7c38f0d-0c87-0bbd-9c2b-c9cd6c246e39.png)

その後、Firebaseとスプレッドシートを接続してみたのですが、思うような値を取得できませんでした。
[Firebaseとスプレッドシートを繋ぐ](https://qiita.com/misa_m/items/b339e788a91bb7d08993)