このスピードについてこれるか！？　echo-botをcloudflareにデプロイする #TS #bun #hono

ハンズオンっぽい記事になります。
本記事ではおうむ返しをするLINE BotをCloudflareにcliでさささっとデプロイするところをやってみます。
テーマはスピード

- Bunだからインストールが速い
- cloudflareはDeployが速い
- そして動作もメチャクチャ速い

というのがメリットです

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2100048/39d61653-bcc2-ca0f-bf0f-88f8baf99db1.jpeg" width="70%" alt="Eicrns8UcAAOAfq.jpeg" />

一方でデメリットもあり

:::note warn
画像を送受信するLINE Botには向いていません
:::

これはcloudflareの処理時間制限に引っかかる可能性があるためです。
気になる方は次の記事を参照ください。

https://zenn.dev/catnose99/articles/d1d16e11e7c6d0

## 0. 準備

### 0.1. cloudflareのアカウント登録

https://www.cloudflare.com/ja-jp/

アクセスして適当に登録するだけです

### 0.2 Bunのインストール

https://bun.sh/

トップページにある`Linux & macOS`または`Windosw`のところに書いてあるコマンドを、ターミナルまたはPowerShellにコピペして実行しましょう

わかる方は、パッケージマネージャー使っていただいても大丈夫です

#### 0.2.1. おすすめのパッケージマネージャー

OSに合わせて選んでください。私はPCに合わせて（個人用がMac、仕事用がWindowなので）両方利用していますが良い感じです。

##### Mac: proto

https://moonrepo.dev/proto

##### Windows: Scoop

https://scoop.sh/

### 0.3. VS Codeのインストール

https://azure.microsoft.com/ja-jp/products/visual-studio-code

オンラインエディタだとcloudflareのログインのところでCORSに引っかかって進まないのでローカルのエディタを利用することをお勧めします。

### 0.4. LINE Developersのアカウント登録と、プロバイダーの作成と設定

https://developers.line.biz/ja/

https://qiita.com/okinakamasayoshi/items/bf9593b2af20148c4d19

この記事の`1.LINE公式アカウント作成`を実施してください
途中で作成される`Channel Secret`, `Channel Access Token`もメモしておいてください

## 1. ハンズオン

### 1.0. デスクトップに空のフォルダーを作成し、VS Codeで開いてターミナルを起動

::: note
初めてVS Codeを使う方向けの項です
普段VS Codeでの開発を実施されている方は、その手順で開始していただいて構いません
:::

<details><summary>折りたたみを開いて参照する</summary>

#### 1.0.1. 空のフォルダーを作成する

- デスクトップに`linebot`などご自身がわかる名前でフォルダを作成してください

#### 1.0.2. VS Codeを開いてフォルダーを開く

- VS Codeを開いてください
- 画像の通り、VS Codeでフォルダーを開いてください

> Open Folderを選ぶ
> <img src="https://i.gyazo.com/77fe0490f1aecf258fb7a1b204b27e2e.png width="75%' alt="img" />
>
> 作ったフォルダーを選ぶ  
> <img src="https://i.gyazo.com/2702dea494760bce6289b818d493f452.png width="75%' alt="img" />
>  
> このようにVS Codeに作成したフォルダーの名前が表示されていれば成功です
> <img src="https://i.gyazo.com/c4999560b9a528c4a9e77a50f5c7cb7b.png width="75%' alt="img" />

#### 1.0.3. VS Codeからターミナルを開く

- この後cliで操作するのでターミナルを開きます
- 下の画像のように進めてください

> ターミナルのボタンからターミナルを開く
> <img src="https://i.gyazo.com/afd46b568f79f72fe3d6e66730fed898.png width="75%' alt="img" />
>  
> VS Codeの下部にターミナルが開いていれば成功です
> <img src="https://i.gyazo.com/1c5b0b9a395a4bae7fd296a94df62867.png width="75%' alt="img" />

</details>

### 1.1. CLIからBunでHonoのプロジェクトを開始してCloudflareにデプロイ

まずはcloudflareにベースとなるサーバーを立てていきます

#### 1.1.1. Bunを使ってHonoのプロジェクトを準備

- ターミナルに次のコマンドをコピペしてEnterを押してください

```bash
bun create hono
```
- 色々と質問されるので、画像のように答えてください

> <img src="https://i.gyazo.com/168b7b9171a2a221e40fef383617c84d.png width="75%' alt="img" />

- 画像の通り

```bash
cd line-server
```

をターミナルにコピペしてEnter

- `package.json`を確認

> <img src="https://i.gyazo.com/c63ea80bb6f8c0fdce3479f5589ad0a4.png width="75%' alt="img" />
>
> scriptsに`deploy`のコマンドがあることを確認

#### 1.1.2. cloudflareへのDeploy

- ターミナルに次のコマンドをコピペしてEnter

```bash
bun run deploy
```

:::note
初回はログインするためにアクセスするURLが表示されると思いますので、そのURLにアクセスして認証を行ってください。私は使ったことがあったので画像が撮れず、申し訳ありません🙇
:::

- 次の画像のようになっていれば成功です
- 画像の位置にあるアドレスへアクセスしてみましょう

> 下線部のURLにアクセス
> <img src="https://i.gyazo.com/73b6f83441644e1f0bb2e1ab5c430823.png width="75%' alt="img" />
>
> hello honoと出てくれば成功です
> <img src="https://i.gyazo.com/4cc81abf24ea3f3c01848616e5490c8d.png width="75%' alt="img" />
>
> cloudflare workersのダッシュボードにもデプロイされていることが確認できます
> <img src="https://i.gyazo.com/923c4c12b7eb45db54d3130ffc233d80.png width="75%' alt="img" />

### 1.2. Bot serverの起動

次にLINE Botのサーバーとして動かすための準備をしていきます

#### 1.2.1. コードのコピペ

- `index.ts`に次のコードをコピペしてください

```ts
import { 
  messagingApi,
  webhook,
} from '@line/bot-sdk'
import { Hono } from 'hono'
import HmacSHA256 from "crypto-js/hmac-sha256";
import Base64 from "crypto-js/enc-base64";

type Bindings = {
  CHANNEL_ACCESS_TOKEN: string,
  CHANNEL_SECRET: string,
}

const app = new Hono<{ Bindings: Bindings }>()

app.get('/', (c) => {
  return c.text('Hello hono & line!')
})

app.post('/webhook', async (c) => {
  const body = await c.req.text() // JSONではなくテキストで取得
  const channelAccessToken = c.env.CHANNEL_ACCESS_TOKEN || process.env.CHANNEL_ACCESS_TOKEN || ''
  const channelSecret = c.env.CHANNEL_SECRET || process.env.CHANNEL_SECRET || ''

  // シグネチャの取得
  const signature = c.req.header('x-line-signature')
  if (!signature) {
    return c.text('Missing signature', 400)
  }

  // HMACを使ってシグネチャを生成
  const hash = await generateHmac(channelSecret, body);

  // シグネチャの検証
  if (signature !== hash) {
    return c.text('Invalid signature', 403)
  }

  const events = JSON.parse(body).events
  const promises = events.map((event: webhook.Event) => handleEvent(event, channelAccessToken))
  await Promise.all(promises)

  return c.text('OK')
})

const handleEvent = async (
  event: webhook.Event,
  accessToken: string,
) => {
  if (event.type !== 'message' || event.message.type !== 'text') return;
  if (!event.replyToken) return;
  const { text } = event.message;
  fetch('https://api.line.me/v2/bot/chat/loading/start', {
    method: 'POST',
    headers: {
      "Authorization": `Bearer ${accessToken}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({"chatId": event.source?.userId})
  })

  const responseBody: messagingApi.ReplyMessageRequest = {
    replyToken: event.replyToken,
    messages: [
      {'type': 'text', 'text': text}
    ] 
  }
  
  return fetch('https://api.line.me/v2/bot/message/reply', {
    method: 'POST',
    headers: {
      "Authorization": `Bearer ${accessToken}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify(responseBody)
  })
}

export default app

const generateHmac = async (secret: string, message: string) => {
  const hmac = HmacSHA256(message, secret);
  return Base64.stringify(hmac);
}
```

> 現在のindex.ts。全て削除してコピペしてください
> <img src="https://i.gyazo.com/a8d322c3031ae062112f7c356f56d83b.png width="75%' alt="img" />
>
> ライブラリをインストールするように促されます
> <img src="https://i.gyazo.com/7ba7e9d412870c62d644c86cd3299a79.png width="75%' alt="img" />

#### 1.2.2. ライブラリのインストール

- ターミナルに次のコマンドを入力してください

```bash
bun add @line/bot-sdk crypto-js @types/crypto-js
```

- 画像のような感じでインストールが進みます

> <img src="https://i.gyazo.com/8096953ea2b9421e5496ecd5b2120369.png width="75%' alt="img" />
>
> 赤線が消えれば成功です
> <img src="https://i.gyazo.com/99ecd5af3bebe271b0e730092c31d007.png width="75%' alt="img" />

#### 1.2.3. 新しいコードでDeploy

- もう一度次のコマンドをターミナルにコピペしてdeployします

```bash
bun run deploy
```

デプロイ成功したら先ほどと同じURLにアクセスしてみます

> Hello hono & line!になっていればひとまず大丈夫です
> <img src="https://i.gyazo.com/f981b1710b31172d87c87567b8ce553e.png width="75%' alt="img" />

### 1.3. 環境変数とwebhook URLの設定

最後に認証周りの設定を行います

#### 1.3.1. cloudflareへの環境変数の設定

LINE Developersにあるチャネルシークレットとチャネルアクセストークンをcloudflareに設定していきます

- まずは**チャネルシークレット**を設定します
- 次のコマンドをターミナルに入力してEnterを押してください

```bash
bunx wrangler secret put CHANNEL_SECRET
```

> セットする値を聞かれるので`チャネルシークレット`をコピペしてください
> <img src="https://i.gyazo.com/6b35abba3c5a59bf820de3b0bebe74fc.png width="75%' alt="img" />
>
> 成功するとセットされます
> <img src="https://i.gyazo.com/5e9e375abdadf4118863621b9ff23a68.png width="75%' alt="img" />

- 同様の手順を**チャネルアクセストークン**に対して実施します
- 次のコマンドをターミナルに入力してEnterを押してください

```bash
bunx wrangler secret put CHANNEL_ACCESS_TOKEN
```

> セットする値を聞かれるので`チャネルアクセストークン`をコピペしてください
> <img src="https://i.gyazo.com/04061d0cce7b532e2da0055fff3cc51d.png width="75%' alt="img" />
>
> 成功するとセットされます。長いので注意してコピペしましょう
> <img src="https://i.gyazo.com/0dfe1d2784cb3cd98d07e9cf923b6458.png width="75%' alt="img" />

これでcloudflare側のセットは完了です

#### 1.3.2. LINE Developersへのwebhook URLの設定

次はLINE Developers側の設定となります

- これまで使ってきたURL（Hello hono & line!が表示されるURLです）をコピペして、**末尾に/webhook**を入力します
- `更新`ボタンを押したのち、`検証`を押して成功のダイアログが表示されれば成功です

> Webhook設定の項目にあるwebhook URLに入力します
> <img src="https://i.gyazo.com/3a18856a6c8e67151dd1582f3f48867d.png width="75%' alt="img" />
>
> 末尾に /webhook をつけることを忘れずに。入力したら更新ボタンを押します
> <img src="https://i.gyazo.com/a94ba79604f839ca1ba0c2be0912d786.png width="75%' alt="img" />
>
> 検証を押して成功が出ればOKです
> <img src="https://i.gyazo.com/a360ed286576cfe8a8eebb8cf38b7b93.png width="75%' alt="img" />

ここまでできればおうむ返しLINE Botが動作します。試しに実施してみましょう

### 2. 動かしてみる

https://twitter.com/shuyin02/status/1825384311929123228

は、速すぎる・・・
１日1万リクエスト、感謝のおうむ返しは、ローディングアニメーションを置き去りにしてしまった

### 3. 実行環境

基本的にBot SDKは型のチェックにしか利用していないので、そこまでバージョン差異に困ることはないかと思いますが、念の為`Bun`のバージョンと`package.json`をおいておきます

```bash
❯ bun --version                                
1.1.24
```

```json: package.json
{
  "name": "line-server",
  "scripts": {
    "dev": "wrangler dev src/index.ts",
    "deploy": "wrangler deploy --minify src/index.ts"
  },
  "dependencies": {
    "@line/bot-sdk": "^9.2.2",
    "@types/crypto-js": "^4.2.2",
    "crypto-js": "^4.2.0",
    "hono": "^4.5.7"
  },
  "devDependencies": {
    "@cloudflare/workers-types": "^4.20240529.0",
    "wrangler": "^3.57.2"
  }
}
```

### 4. 最後に

あとは煮るなり焼くなりお好きにどうぞ。
特に、ノーコードツールの[make](https://www.make.com/en)を利用している場合など、直接LINEモジュールを使うとレスポンスが遅いですが、LINEの部分はcloudflareに任せて、makeにwebhookで繋いで処理を実行するようにすると、かなり軽快なLINE Botを作ることが可能です