Firebase CloudFirestore X LINE X webアプリを接続

#やりたかった事
LINEから送られてくるメッセージをFirebase CloudFirestoreに保存！

#材料
・Firebase CloudFirestore　・・・　１プロジェクト
・LINE Messaging API　・・・　１アカウント
・Visual Studio Code
・node.js

#下準備
##Firebase CloudFirestoreの設定
・【SDN】のメモ
・格納するデータの設定

こちらば別記事で詳しく解説しています
[Firebase CloudFirestoreの使い方を初心者が解説してみた](https://qiita.com/misa_m/items/0afbd063ff13b49f2729)

##LINE Messaging APIの設定
・シークレットキー発行
・アクセストークン発行
１つ作成しておきます

##jsファイル作成
Visual Studio Codeで作業をします。
indexファイル１つ
jsファイルを１つ
作成しておきます。

#作り方
Visual Studio Codeで作業をしていきます。

##npm初期化
ターミナルを立ち上げてnpmを初期化します。
【ターミナル】＞【新しいターミナル】

```
npm i
```

##LINEのライブラリインストール
以下のコマンドでLINEのライブラリをインストールします。

```
npm i @line/bot-sdk express
```

##Firebase CloudFirestoreのライブラリインストール
以下のコマンドでFirebase CloudFirestoreのライブラリをインストールします。

```
npm i firebase
```
##Vue.jsでwebのフロント側を作成
htmlファイルに

```html

<!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">
        <ul v-for="data in allData" :key="data.id" class="menu-list">
            <li>
                {{data.name}} / {{data.age}}
            </li>
        </ul>

        <p>
            名前：<input v-model="name" placeholder="名前"> 所在地：
            <input v-model="address" placeholder="所在地">
            <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: "ご自身の情報を入力",
            authDomain: "ご自身の情報を入力",
            projectId: "ご自身の情報を入力",
            storageBucket: "ご自身の情報を入力",
            messagingSenderId: "ご自身の情報を入力",
            appId: "ご自身の情報を入力",
            measurementId: "ご自身の情報を入力"
        };
        // Initialize Firebase
        firebase.initializeApp(firebaseConfig);
        const db = firebase.firestore();

        const app = new Vue({
            el: '#app',
            data: {
                allData: [],
                name: '',
                address: ''
            },
            mounted: async function() {
                db.collection('memos').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 CloudFirestoreコレクション名").add({
                        name: this.name,
                        address: this.address
                    });
                }
            }
        })
    </script>

</body>

</html>
```

Firebase CloudFirestoreコレクション名というのは
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820683/84744bee-eaba-6c66-954d-079790b364d6.png)

その下の

```html

 name: this.name,
 address: this.address
```
も作成したデータベースの内容に変えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820683/3d36d752-41b4-b15c-af92-82bc3a99d298.png)


##webアプリ側完成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820683/56d2aa9d-2e16-c5b1-c2e3-e688cc60216e.png)

入力した内容は保存されています
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820683/18c36ad7-0b5b-3e93-7e05-149e48762e35.png)

##LINE接続

jsファイルに

```javascript
'use strict'; // おまじない

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

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

const firebaseConfig = {
    apiKey: "ご自身の情報を入力",
    authDomain: "ご自身の情報を入力",
    projectId: "ご自身の情報を入力",
    storageBucket: "ご自身の情報を入力",
    messagingSenderId: "ご自身の情報を入力",
    appId: "ご自身の情報を入力",
    measurementId: "ご自身の情報を入力"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
const db = firebase.firestore();

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

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

// ########################################
//  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);
    }

    // データベースにデータを追加
    await db.collection("コレクション名に変更").add({
        name: event.message.text,
        address: "所在地"
    });

    // 「テキストメッセージ」であれば、受信したテキストをそのまま返事します
    return client.replyMessage(event.replyToken, {
        type: 'text',
        text: event.message.text // ← ここに入れた言葉が実際に返信されます
    });
}

// ########################################
//          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サーバーを実行中です…`);
```

##LINE接続用コードを実行

```
node ファイル名
```

localが実行されるので、もう１つターミナルを立ち上げて

```
npx ngrok http 3000
```
実行してwebhook用のURLを生成します。
LINE Developwerのwebhookへ入力して完成。

##LINEからFirebase CloudFirestoreへのデータ格納成功！
実際にLINEbotからメッセージを送ると、Firebase CloudFirestoreへデータが格納させていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820683/aae59b85-b179-e54f-d866-383ecedbff5c.png)

#LINEとwebアプリ両方からの情報を保持
LINEとwebアプリ、両方から入力された情報を1つのFirebase CloudFirestoreで管理されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820683/ad6437a1-f6ee-5b04-31ac-f1c1d93cd49c.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/820683/b4d7d776-01ba-9c09-134a-aa47d8186d11.png)

#今後の展望
LINEから予約を受け付けたり、電子カルテの仕組みも作ってみたいと思いました。
LINEからメッセージが送られてきた内容を、どのように複数のデータに保持していくのかを解決しないといけないと感じています。

また、最後にはiPassを利用してスプレッドシートにデータを保存出来たら良いなと考えています！