LINE の Echo Bot を Google Cloud Functions に作る

LINE Bot を Cloud Functions で作りたくて、試しに echo bot を書いてみようとしたんですが、意外とまとまってる資料が無かったので残しておきます。

全体のリポジトリは[ココ](https://github.com/matsubara0507/cloud-function-echo-line-bot)にあります。

## Cloud Functions

Google Cloud Platform 版の AWS Lambda ですね。
まだ Beta 版で、正直 Lambda の方が機能豊富な気がします(ちゃんと比較はしてないけど)。
特に Lambda と比べて以下の点が今回は引っかかりました。

- Javascript (Node.js) しか使えない
- 環境変数を追加する方法が無い

## Node.js の LINE Messaging API 

Cloud Functions は Node.js しか使えないので、副作用的に LINE Messaging API も Node.js しか使えません。
昔は Node.js の SDK が無かったせいか、Cloud Functions で LINE Bot の記事があっても、SDK を使わずにやっていて、直接的に参考にならず苦労しました...

Node.js の SDK は [GitHub](https://github.com/line/line-bot-sdk-nodejs) にあります。
サンプルコードもここから借りることにします。

## 作る

(ココからが本題)
今回はコンソールではなく、`gcloud` コマンドでデプロイを行いますので、適当に調べてインストールしておいてください。

### メインとなる関数

まずは、SDK のリポジトリにある[サンプルコード](https://github.com/line/line-bot-sdk-nodejs/blob/fc86d6654dcd9c025f7073a313298741b2f5bd79/examples/echo-bot/index.js)を参考にして、メインとなる関数を実装します。
サンプルコードでは `express` というパッケージを使っていますが、Cloud Functions の場合は export するだけで良いので、要りません。
結果として次のような `index.js` を書きます(ファイルの名前は別に何でも良い)。

```Javascript
'use strict';

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

const config = {
  channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN,
  channelSecret: process.env.CHANNEL_SECRET,
};

const client = new line.Client(config);

function handleEvent(event) {
  console.log(event);
  var message = 'Please push text message.';

  if (event.type === 'message' && event.message.type === 'text') {
    message = event.message.text;
  }

  const echo = { type: 'text', text: message };
  return client.replyMessage(event.replyToken, echo);
}

exports.handler = function echoBot (req, res) {
  Promise
    .all(req.body.events.map(handleEvent))
    .then(result => res.status(200).send(`Success: ${result}`))
    .catch(err => res.status(400).send(err.toString()));
};
```

### 環境変数をどうするか

ココで問題なのは(前述したとおり)環境変数です。
いくつか LINE Bot とは関係ない Cloud Functions のコードを見てみたら `dotenv` を使っていたので、ぼくもそうすることにしました。
`index.js` の頭に書き加えます。

```Javascript
'use strict';

require('dotenv').config();
const line = require('@line/bot-sdk');

...
```

そして次のような `.env` ファイルを作っておきます(GitHub などにあげないように気を付けて)。

```
CHANNEL_ACCESS_TOKEN="XXXXX"
CHANNEL_SECRET="YYYYY"
```

ちなみに、`CHANNEL_ACCESS_TOKEN` も `CHANNEL_SECRET` も LINE Developers のサイトの Bot の管理ページで分かります(アクセストークンは最初に再発行をクリックする必要がある)。

### `package.json` を書く

次は `package.json` を書きます。

```json
{
    "name": "echo-bot", 
    "version": "0.1.0", 
    "main": "index.js", 
    "dependencies": {
        "@line/bot-sdk": "^5.0.0",
        "dotenv": "^4.0.0"
    }
}
```

`main` のところに関数を書いた JS ファイルを指定してあげます。

### デプロイをする

ココまで書けたら、デプロイをします。

```
$ gcloud beta functions deploy echo-bot --stage-bucket hoge-bucket --trigger-http --entry-point handler
```

`--entry-point` で実行する関数を指定します。
ちなみに、これらのコードを保存する場所として `--stage-bucket` オプションで GCP のバケットを指定していますが、もしまだ作っていなかったら次のようなコマンドで事前に作っておく必要があります(今回の場合は
 `[BUCKET_NAME]` が `hoge-bucket` になる)。

```
$ gsutil mb -p [PROJECT_ID] gs://[BUCKET_NAME]
```

### Webhook を設定する

あとは URL を Webhook に指定するだけです。
以下のコマンドで URL を取得できるはずです。

```
$ gcloud beta functions describe echo-bot | grep url
```

注意点が一つ。
LINE Developers のサイトで Webhook を指定した後 `接続確認` というボタンを押したいと思いますが、**このコードだと必ずエラーとなる** のでやらなくていいです(笑)
`console.log(err.originalError.response.data)` というコードを埋め込んで、レスポンスを見てみた結果「ReplyToken が間違ってる」と言われます。
これはどうしようもない...(たぶん)

## 試す

いい感じ

<img width="300px" src="https://qiita-image-store.s3.amazonaws.com/0/143424/d41c2c91-6a44-50ab-29bc-8349056bc0d6.png">