LINE ボットを簡単な構成で作ってみた（TypeScript + Bot SDK ver.8 + Lambda）

LINEのボットを簡単な構成で作る方法を書いてみます。 
LINE Messaging API SDK for nodejsの2023年10月にリリースされたver.8を使い、TypeScriptも使うことで型の恩恵を受けながらコードを書きます。
また、バックエンドはAWS Lambdaのみを用いるためサーバーの管理は不要で、コストも削減できます。

# 完成イメージ
LINEで名前を送信すると、「xxxさん、こんにちは！」と返してくれるボットを作ります。
![LINEテスト.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20639/39fe06ef-35b6-20c0-a501-cb83a571b552.png)

# 構成図
![2023-10-15_15h32_40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20639/bb5c61ee-f54c-f7cb-e7e2-d691012c2e86.png)

# 前提条件
 - LINE公式アカウントを作成していること（無料枠でテスト可能）
 - Node.js ver.18の環境を準備済
 - AWSアカウントがあること（無料枠でテスト可能）

# Node.jsプロジェクト作成
プロジェクトを作成して、必要なライブラリをインストールします。
```bash
# プロジェクト初期化
$ npm init -y

# ライブラリをインストール
$ npm install @vendia/serverless-express typescript tslint @types/node express @types/express cors @types/cors
```
 - Express：Webサーバー
 - @vendia/serverless-express：LambdaでExpressを利用するために必要

TypeScriptを使うための初期設定をします。
```bash
$ ./node_modules/.bin/tsc --init
$ ./node_modules/.bin/tslint --init
```

ビルドコマンドを設定しておきます。
```text
# package.json
{
    "scripts": {
        "build": "tsc"  # 追加
    },
}
```

## SDKインストール
LINE Messaging API SDK for nodejsをインストールします。
SDKを用いることでメッセージ送信などがAPIリクエストではなく、関数を呼び出すことで実行可能になるなど開発がしやすくなります。
```bash
$ npm install @line/bot-sdk
```

## コードを書く
LINEのWebhookに登録し、LINEサーバーから送信されたリクエストに応じてメッセージを返すコードを書きます。
プロジェクトディレクトリ直下に`index.ts`を作成します。
```TypeScript:index.ts
import serverlessExpress from "@vendia/serverless-express";
import express from "express";
import cors from "cors";
import { middleware, messagingApi, WebhookEvent } from "@line/bot-sdk";
const {MessagingApiClient} = messagingApi

// (1)
const config = {
  channelAccessToken: 'YOUR_CHANNEL_ACCESS_TOKEN',
  channelSecret: 'YOUR_CHANNEL_SECRET'
};

const app = express();
app.use(cors());
app.use("/webhook", middleware(config));  // (2)
app.use(express.json());

app.post("/webhook", async (req, res) => {
	const event: WebhookEvent = req.body.events[0];
	if (event.type !== "message") {
		res.sendStatus(200);
		return;
	}
	
	let message = "";
	if (event.message.type === "text"){
		message = event.message.text;
	}

    // (3)
	const client = new MessagingApiClient({
		channelAccessToken: config.channelAccessToken
	});
    // (4)
	await client.replyMessage({
		replyToken: event.replyToken,
		messages: [{type: "text", text: message + "さん、こんにちは！"}]
	});
	res.sendStatus(200);
});

if (process.env.NODE_ENV === `develop`) app.listen(3000);

export const handler = serverlessExpress({ app });
```

コードの説明は次のとおりです。

```TypeScript
// (1)
const config = {
  channelAccessToken: 'YOUR_CHANNEL_ACCESS_TOKEN',
  channelSecret: 'YOUR_CHANNEL_SECRET'
};
```
SDKを利用するための設定です。
 - channelAccessToken：[LINE 開発者サイト](https://developers.line.biz/ja/)のMessaging API設定から確認
 - channelSecret：[LINE 公式アカウント](https://manager.line.biz/)の設定->Messaging APIから確認

```TypeScript
app.use("/webhook", middleware(config));  // (2)
```
リクエストがLINEから送信されていることを[検証](https://developers.line.biz/ja/reference/messaging-api/#signature-validation)します。SDKが提供してくれるミドルウェアを使用します。

```TypeScript
// (3)
const client = new MessagingApiClient({
    channelAccessToken: config.channelAccessToken
});
```
メッセージを返信するために`Clinet`を作成します。SDK ver.8からLINE OpenAPIによって`Client`が生成されるようになったため、以前と使い方が異なります。

```TypeScript
// (4)
await client.replyMessage({
    replyToken: event.replyToken,
    messages: [{type: "text", text: message + "さん、こんにちは！"}]
});
```
ボットから応答メッセージを返信します。応答メッセージは課金対象ではないためテストでも利用できます。


## ビルド
ビルドコマンドを実行して、プロジェクトディレクトリ直下に`index.js`が作成されることを確認します。
```bash
# index.jsが作成される
$ npm run build
```

# Lambda作成
AWSの管理画面からNode.jsのLambdaを作成します。
![2023-10-15_14h33_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20639/d651c7af-9d2c-ebaa-179f-dd756e04770a.png)
![2023-10-15_14h35_32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20639/4244320e-dd11-3215-6a37-a3b745e6ac51.png)

コードをLambdaに登録するためZIP圧縮して、アップロードします。

:::note warn
注意
プロジェクト直下のファイルを全選択してZIP圧縮してください。（プロジェクトフォルダ自体をZIP圧縮するとLambdaがindex.jsを認識できずエラーになります）
:::

![2023-10-15_14h38_19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20639/b70799af-cfc4-5935-8059-15062ce1b202.png)

## 関数URLの設定
LINEサーバーにLambdaをWebhookとして登録するため、LambdaをURLで呼び出せるようにします。（Lambdaは直接HTTPから呼び出せるようになったため、API Gatewayは必要ありません）
「設定」タブ->左ペインの「関数URL」->「関数URLを作成」ボタンからURLを作成します。
![2023-10-15_14h43_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20639/e7abbacc-0226-7b02-7ab0-e0d2247d46d8.png)

「認証タイプ」は「NONE」にして、保存します。
![2023-10-15_14h44_31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20639/adb1f1a2-b811-d0a0-690d-83af7f224f08.png)


関数URLにLambdaを呼び出すURLが表示されることを確認します。
![2023-10-15_14h45_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20639/02b02483-8d2b-38e2-c4c3-d737df84f5fe.png)


# LINE側の設定
LINEのWebhook URLに先ほど作成したLambdaの関数URLを登録します。
LINE公式アカウントの「設定」->左ペインの「設定」の「Messaging API」をクリックして設定画面を開きます。
「Webhook URL」に作成した`関数URL + "/webhook"`を入力して、「保存」ボタンをクリックします。
![2023-10-15_14h49_35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20639/da231f68-74f2-d15c-3b67-1a5b8e274885.png)

# テスト
LINEから名前を送信すると、ボットが「xxxさん、こんにちは！」と返してくれることを確認します。
![LINEテスト.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20639/39fe06ef-35b6-20c0-a501-cb83a571b552.png)

# まとめ
SDKを用いることでボットを簡単に作れることを確認しました。また、バックエンドはLambdaのみで構成可能なためサーバーの管理も不要であり、コストも削減できそうです。

# 参考サイト
 - [LINE Messaging API SDK for nodejs GitHub](https://github.com/line/line-bot-sdk-nodejs)
 - [LINE Messaging API SDK for nodejs 公式ドキュメント](https://line.github.io/line-bot-sdk-nodejs/)
 - [LINE Messaging APIドキュメント](https://developers.line.biz/ja/docs/messaging-api/)
 - [@vendia/serverless-express(旧aws-serverless-express)でExpressアプリをAPIGateway+Lambdaにデプロイしてみた](https://dev.classmethod.jp/articles/vendia-serverless-express/)