FirebaseでLINE botを作ってみた

#前置き
Firebaseの時代が来そうな予感がしたので何か作ってみようと思い、LINE botを作成してみる事にしました。

#LINE Developersでの準備
[https://developers.line.biz/](https://developers.line.biz/)にアクセスしてログインします。

##① チャネルを作成
新規チャネルを作成
Messaging APIを選択

##② 設定
[こちら](https://qiita.com/n0bisuke/items/909881c8866e3f2ca642)を参考にしながら設定
ざっくり言うと
**Channel Secretをメモる**
**アクセストークンを再発行してメモる**
**Webhook送信を「利用する」に変更する**
です。
Channel Secretとアクセストークンは、後ほどソースコードに記載します。

注意しないといけないのが最後のWebhook送信。
Webhook送信を**<font color="Red">「利用する」に変更して「更新」を押す！</font>**
「利用する」にチェック入れただけでは反映されません。
Firebase側へ通信が来ずにあれ？あれ？ってハマっていたらこれのせいでした。

##③ 友達になる
チャネルの基本設定画面下部にあるQRコードから、作ったアカウントと友達になっておきます。

(いったんLINE Developerではここまで。開発環境での準備が整ったら⑤に戻ってきます)

##④ Webhook URLを設定(デプロイ後)
開発環境からFirebaseにデプロイしてURLが発行されたらチャネルの基本設定画面から下記になるように設定します。
https://(発行されたURL)/webhook

#Firebaseでの準備
[https://console.firebase.google.com](https://console.firebase.google.com)にアクセスしてログインします。

##① プロジェクトを作成
よく分からないので(爆
とりあえずAWSのリージョンとかのノリで近いとこに設定
アナリティクスの地域：日本
Cloud Firestoreのロケーション：asia-northeast1
また今度調べようかな、また今度

##② プランを変更
<font color="Red">重要</font>
**無料のSparkのままでは外部APIを実行出来ない**ので、
定額のFlame
従量のBlaze
のどちらかにする必要があります。

これをしないと、通信時に
getaddrinfo EAI_AGAIN api.line.me:443
というエラーが出ます。
LINEからのリクエスト内容はきちんと受信出来ているのに、よく分からないエラーが出てるぞ？？ってハマります。

[このへん](https://yutakami.work/?p=625)を参考にさせていただきながら、無料枠が適用されるBlazeにしました。
個人ならたぶん無料枠を超えない…はず…

#開発環境での準備
こちらの内容はほぼほぼ[参考サイト](https://qiita.com/n0bisuke/items/909881c8866e3f2ca642)と同じです。
流れとコマンドだけメモ程度に記載します。

##① Firebase CLI インストール
```
npm i firebase-tools
```

##② firebaseにログインする
```
firebase login
```
※ アカウント変えたいときはfirebase logoutしてからloginしたらいけました

##③ functionsの初期化
```
firebase init functions
```

##④ 必要なモジュールのインストール
```
cd functions
npm i --save @line/bot-sdk express
```

##⑤ ソースの修正
functions/index.js
firebase.json
functions/package.json
の3ファイルです。

```javascript:functions/index.js
'use strict';

const functions = require('firebase-functions');
const express = require('express');
const line = require('@line/bot-sdk');

const config = {
    channelSecret: '', // LINE Developersでの準備②でメモったChannel Secret
    channelAccessToken: '' // LINE Developersでの準備②でメモったアクセストークン
};

const app = express();

app.post('/webhook', line.middleware(config), (req, res) => {
    console.log(req.body.events);
    Promise
      .all(req.body.events.map(handleEvent))
      .then((result) => res.json(result))
      .catch((result) => console.log('error!!!'));
});

const client = new line.Client(config);

async function handleEvent(event) {
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }

  return client.replyMessage(event.replyToken, {
    type: 'text',
    text: event.message.text + 'を受け取りました。'
  });
}

exports.app = functions.https.onRequest(app);
```
エラーが出たときに、Promiseのエラーをハンドリングしてないぞって怒られたのでcatchを追加しています。
ただ何かしらレスポンスしないとLINEからの通信がタイムアウトになるまで残ってしまうみたいなので、もうちょい何かしないといけないんだろうなぁ(遠い目

```json:firebase.json
{
    "hosting": {
        "public": "./",
        "rewrites": [{
            "source": "/webhook",
            "function": "app"
        }],
        "ignore": [
            "firebase.json",
            "**/.*",
            "**/node_modules/**"
        ]
    }
}  
```
これをコピペ

```json:functions/package.json
{
  "engines": {"node": "8"}
}
```
こちらは追記なので注意。
これをしないと、デプロイ時にasyncのところでSyntaxErrorが出ます。

##⑥ デプロイ

```
firebase deploy --only functions,hosting
```

デプロイが完了したら1番下らへんに
Hosting URL: https://xxxxx-xxxxx.firebaseapp.com
のような感じでURLが発行されるので、これをLINE DevelopersのWebhook URLへ追加してあげます。(LINE Developersでの準備④を参照)

これで完了！

#おわりに
LINE Developersでの準備③で友達になっておいたbotへ話しかけます。
Firebaseでの準備②で書いたプランの問題が解消されていれば、ちゃんと返信がくるはず！

以前AWS Lambdaでもbotを作った事ありましたが
[SINoALICEスタンプに反応するLINE botをLambdaで作った話](https://qiita.com/dq-ny/items/bdf3a371710a7b6df09a)
今の所は正直どちらも手間とかさほど変わらない印象。(まぁ返信するだけのbotだし…)
Firestoreとか使ったりもうちょっと作り込んでいくと変わるのか…？
今後もいろいろ使ってみようと思います。

#おまけ(開発環境での検証)
ngrokというモジュールを使うとLINE DevelopersからアクセスするためのURLを発行してくれるみたいです。
これは便利。
ただし発行されるURLは起動する毎に異なるようなので、都度LINE DevelopersのWebhook URLの設定を変更しないといけないのが難点。

ngrokインストール

```
npm i ngrok
```

ngrokを5000ポート指定で起動

```
./node_modules/ngrok/bin/ngrok http 5000
```

ローカルでfunctionsを起動

```
firebase serve --only functions,hosting
```

#おまけ(文字化け)
レスポンスに日本語を渡したら文字化け。
nkfでindex.jsを見てみたらISO-2022-JPになってました。
UTF-8へ変換して解決！

```
nkf -w --overwrite functions/index.js
```

#参考

FirebaseでNode.jsを動かしてLINE BOTを作る
https://qiita.com/n0bisuke/items/909881c8866e3f2ca642

1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 #nodefest
https://qiita.com/n0bisuke/items/ceaa09ef8898bee8369d

【Dialogflowメモ】FirebaseのBlazeプランにはいる
https://yutakami.work/?p=625