Cloudflare WorkersでLINE botを動かしてみた

# はじめに

Cloudflare WorkersはCloudflare社が提供するCDN（コンテンツデリバリーネットワーク）のエッジでJavaScript(V8)を実行するサーバーレスのサービスです。

https://workers.cloudflare.com/

JavaScriptのコードを世界中の200以上の都市にデプロイし、ユーザーから数ミリ秒以内にコードが実行されるとのことです。

![スクリーンショット 2021-04-11 15.51.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/04e95588-6d0b-7d48-a525-fab77047da1a.png)
![スクリーンショット 2021-04-11 15.56.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/1eba601f-d979-10a2-340c-fb82fac9e342.png)

サーバーレスサービスのコールドスタート問題ですが、Cloudflare Workersでは0msコールドスタートが可能で、コールドスタート時の実行が遅い問題は発生しません。

![スクリーンショット 2021-04-11 15.56.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/39c1b1e9-e5aa-d1b3-d7f4-dcf47b1545f2.png)

無料枠で十分なリソースが利用でき、開発用のサブドメイン名(https://"アプリ名"."アカウント名".workers.dev)が使用可能です。

![スクリーンショット 2021-04-11 15.38.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/e4c1db0f-7832-e8ac-48f4-d331e19fd36a.png)

https://developers.cloudflare.com/workers/platform/limits#worker-limits

## Cloudflare Workersを使用してLINE bot(おうむ返し)を動かしてみる

# 準備

### 1. Cloudflare Workersアカウントの作成

アカウントを作成してログインします。

https://workers.cloudflare.com/

### 2. APIトークンの作成

APIトークンを作成します。

![API_Token.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/d3e4ce7f-7d8e-d165-1649-5edf7d654f61.jpeg)

アクセス許可　→そのまま
アカウントリソース　→すべてを選択
ゾーンリソース　→すべてを選択

![API_Token_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/d2459bf3-5e8a-a71d-b08b-663368335b7f.jpeg)

APIトークンは1度しか表示されないのでメモしておきます。
(もし、メモし忘れた場合は、APIトークンを削除して、もう一度作成します。)

![API_Token_2-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/23d0cc07-86a0-1650-7c63-2147681a8ac6.png)

### 2. Wrangler CLIのインストール

Rust製のWrangler CLIを使って開発・デプロイを行います。

```bash
$ npm install -g @cloudflare/wrangler
```

# 手順

### 1. ログイン

作成したアカウントでログインします。

```bash
$ wrangler login
```

### 2. APIトークンの設定

作成したAPIトークンを入力します。

```bash
$ wrangler config
```

### 3. プロジェクト作成(テンプレートを使用)

Routerテンプレートを使用してプロジェクト(line-bot)を作成します。

```bash
$ wrangler generate line-bot https://github.com/cloudflare/worker-template-router
```

### 4. アカウントIDの設定

アカウントIDを確認します。

```bash
$ wrangler whoami
```

Cloudflare Workersの管理画面でも確認できます。

![アカウントID-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/7bdb3d4f-880a-2241-52c4-61e83002239d.png)

wrangler.tomlファイルのaccount_idの項目にアカウントIDを設定します。

```bash
$ cd line-bot/
$ vi wrangler.toml
```

```text:wrangler.toml
name = "line-bot"
type = "webpack"
account_id = ""　←ここ
workers_dev = true
route = ""
zone_id = ""
```

### 5. シークレット情報の設定

LINE botでMessaging APIを使用するために、チャンネルアクセストークンの情報が必要になります。

![チャンネルアクセストークン-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/6e0e020e-85cd-2afe-ad3b-b90d19aceff2.png)

チャンネルアクセストークンなどのシークレット情報をプログラム内に含めるのはリスクがありますので、環境変数に設定します。

```bash
$ wrangler secret put ACCESS_TOKEN
```

シークレット情報が登録されたことを確認します。

```bash
$ wrangler secret list
```

### 6. プログラムを書く

プログラムはindex.jsに書きます。

```bash
$ vi index.js
```

サンプル(おうむ返し)のソースコードはこちらです。

<details>
<summary>ソースコード</summary>
<div>

```javascript:index.js
const url = "https://api.line.me/v2/bot/message/reply"

function rawHtmlResponse(html) {
  const init = {
    headers: {
      "content-type": "text/html;charset=UTF-8",
    },
  }
  return new Response(html, init)
}

async function readRequestBody(request) {
  const { headers } = request
  const contentType = headers.get("content-type") || ""

  if (contentType.includes("application/json")) {
    const data = await request.json()

    if (data.events[0]) {
      const body = {
        replyToken: data.events[0].replyToken,
        messages: [
          {
             type: "text",
             text: data.events[0].message.text
          }
        ]
      }
      const init = {
        body: JSON.stringify(body),
        method: "POST",
        headers: {
          Authorization: "Bearer "+ACCESS_TOKEN,
          "content-type": "application/json"
          }
      }
      const res = await fetch(url, init)
      
      return JSON.stringify(data)
    }
    else {
      return JSON.stringify(data)
    }
  }
  else if (contentType.includes("application/text")) {
    return await request.text()
  }
  else if (contentType.includes("text/html")) {
    return await request.text()
  }
  else if (contentType.includes("form")) {
    const formData = await request.formData()
    const text = await formData.entries()
    const body = {}
    for (const entry of formData.entries()) {
      body[entry[0]] = entry[1]
    }
    return JSON.stringify(body)
  }
  else {
    const myBlob = await request.blob()
    const objectURL = URL.createObjectURL(myBlob)
    return objectURL
  }
}

async function handleRequest(request) {
  const reqBody = await readRequestBody(request)
  const retBody = `The request body sent in was ${reqBody}`
  //console.log(reqBody)
  return new Response(retBody)
}

addEventListener("fetch", event => {
  const { request } = event
  const { url } = request

  //console.log(JSON.stringify(request))

  if (request.method === "POST") {
    return event.respondWith(handleRequest(request))
  }
  else if (request.method === "GET") {
    return event.respondWith(new Response(`The request was a GET`))
  }
})
```

</div></details>

ソースコードを github にも置きました。
https://github.com/kitazaki/cloudflare_workers_line-bot/blob/main/index.js

### 7. プレビュー実行

プロジェクトをプレビュー実行します。http://127.0.0.1:8787 で起動します。

```bash
$ wrangler dev
Listening on http://127.0.0.1:8787
```

### 7-1. cloudflaredを使用して動作確認

cloudflaredをインストールします。サインアップの必要はありません。

```
$ brew install cloudflare/cloudflare/cloudflared
```

cloudflaredを起動します。

```bash
$ cloudflared tunnel --url localhost:8787
```

(参考)
https://qiita.com/kitazaki/items/b531c80b7d0c0411716e

### 7-2. LINE DevelopersコンソールでWebhook設定

cloudflaredを実行した時に出力される一時的なURLをLINE DevelopersコンソールのWebhookに設定します。

![スクリーンショット 2021-04-11 16.51.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/7f8ea076-7f1d-0866-d489-422146cdb975.png)

動作に問題がなければプロジェクトを公開(本番環境へデプロイ)します。

### 8. プロジェクト公開(本番環境へデプロイ)

```bash
$ wrangler publish
```

### 8-1. LINE DevelopersコンソールでWebhook設定

本番環境のURL(https://"アプリ名"."アカウント名".workers.dev)をLINE DevelopersコンソールのWebhookに設定します。

![webhook.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/3f35d01e-2ae8-7f40-901e-de432aaa077f.png)

# まとめ

Cloudflare WorkersでLINE公式のMessaging API対応SDKを使用することができず、SDKのソースコードとWorkersのサンプルコードやチュートリアルを見比べながらプログラムを書きました。
日本語の情報があまりなかったので、イメージを掴むまで時間がかかりましたが、サンプルコードやチュートリアルが理解の参考になりました。
みなさまも良き Cloudflare Workers ライフを！

### 参考情報

Examples - Read POST

https://developers.cloudflare.com/workers/examples/read-post

Examples - Post JSON

https://developers.cloudflare.com/workers/examples/post-json

Tutorials - Build a Slackbot

https://developers.cloudflare.com/workers/tutorials/build-a-slackbot