TypeScriptでLINE BOTを操作してみる(Mac)

# line/bot-sdkをインストール
```
npm install @line/bot-sdk
```
`SDK`ドキュメントを参考に進めて参りますが、既にデフォルトで`TypeScript`の定義がされているので、難なくコピペで進行できるかと思います。`Webhook`サーバーをエコーするのに`Express`を使用します。
```
npm install express
```
タイトル通り`TypeScript`でってことなので下記をインストール
```
npm install typescript ts-node
```
```
npm install @types/node @types/express -D
```
## package.json

```package.json
{
 "dependencies": {
    "@line/bot-sdk": "^7.5.2",
    "dotenv": "^16.0.3",
    "express": "^4.18.2",
    "ts-node": "^10.9.1",
    "typescript": "^4.8.4"
  },
  "devDependencies": {
    "@types/express": "^4.17.14",
    "@types/node": "^18.11.4"
  },
  "private": true
}
```
## tsconfig.json

```tsconfig.json
{
    "ts-node": {
        "compilerOptions": {
            "module": "CommonJS"
        }
    },
}
```
下記から`.env`にいれるトークンを取得します。

https://manager.line.biz/

ホーム右上の設定から、応答設定と`Messaging API`を開き設定します。この時に、友達登録もしておきましょう。
![スクリーンショット 2022-10-24 17.38.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/520a279b-0850-42bc-57e0-bc8d66d53bb9.png)
![スクリーンショット 2022-10-24 17.39.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/b8fe3908-0cff-4142-3c25-c778282ae9d8.png)
**応答設定**
![スクリーンショット 2022-10-22 16.33.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/f5c0c2ab-ad67-bccf-008c-0bbb0a14527f.png)
`Webhook URL`に[ngrok](https://ngrok.com/)でトンネリングさせます。新規ターミナルから

```
ngrok http 3000
```
![スクリーンショット 2022-10-24 18.10.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/2bcb27e4-4c97-b746-ded3-d7d4729b6ba2.png)

して`ngrok`を立ち上げたら、`Forwarding`の`URL+/webhook`を下記、**Webhook URL**に保存します。
![スクリーンショット 2022-10-24 16.53.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/1154ba83-c021-9bfa-ee0c-e3eb1097c22c.png)
ngrokは常に起動した状態を保って下さい。次に[こちらのMessaging APIサイトから](https://developers.line.biz/ja/reference/messaging-api/#issue-shortlived-channel-access-token)下記の要領でチャンネルアクセストークンを取得して下さい。

![スクリーンショット 2022-10-22 12.35.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/e202b452-eadf-0a2e-b2c6-bc00db95c7b9.png)
![スクリーンショット 2022-10-22 12.35.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/52ab1893-8574-4f5f-c554-d5437c69ed30.png)
![スクリーンショット 2022-10-24 17.35.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/6e97461d-2cf9-bbb4-47f7-70f163cde00c.png)
以上を`.env`に入れます。
```.env
CHANNEL_ID='LINE BOTのクライアントID'
CHANNEL_SECRET='LINE BOTのクライアントシークレット'
CHANNEL_ACCESS_TOKEN='LINE BOTのチャンネルアクセストークン'
```
ルート直下に、`server.tsファイル`を作成して下記コードを入力。

```server.ts
// すべての依存関係をインポートし、見やすくするために構造化
import { ClientConfig, Client, middleware, MiddlewareConfig, WebhookEvent, TextMessage, MessageAPIResponseBase } from '@line/bot-sdk';
import { Application, Request, Response } from 'express';
import express = require('express');
require('dotenv').config();

// LINEクライアントとExpressの設定を行う
const clientConfig: ClientConfig = {
    channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN || '',
    channelSecret: process.env.CHANNEL_SECRET,
};

const middlewareConfig: MiddlewareConfig = {
    channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN,
    channelSecret: process.env.CHANNEL_SECRET || '',
};

const PORT = process.env.PORT || 3000;

// LINE SDKクライアントを新規に作成
const client = new Client(clientConfig);

// Expressアプリケーションを新規に作成
const app: Application = express();

// テキストを受け取る関数
const textEventHandler = async (event: WebhookEvent): Promise<MessageAPIResponseBase | undefined> => {
    // すべての変数を処理
    if (event.type !== 'message' || event.message.type !== 'text') {
        return;
    }

    // メッセージに関連する変数をこちらで処理
    const { replyToken } = event;
    const { text } = event.message;

    // 新規メッセージの作成
    const response: TextMessage = {
        type: 'text',
        text,
    };

    // ユーザーに返信
    await client.replyMessage(replyToken, response);
};

// LINEミドルウェアを登録
// ルートハンドラの中でミドルウェアを渡すことも可能↓
// app.use(middleware(middlewareConfig));

// Webhookイベントを受信する
// 接続テストを受信
app.get(
    '/',
    async (_: Request, res: Response): Promise<Response> => {
        return res.status(200).json({
            status: '成功',
            message: '正常に接続されました!',
        });
    }
);

// Webhookに使用されるルート
app.post(
    '/webhook',
    middleware(middlewareConfig),
    async (req: Request, res: Response): Promise<Response> => {
        const events: WebhookEvent[] = req.body.events;

        // 受信したすべてのイベントを非同期で処理
        const results = await Promise.all(
            events.map(async (event: WebhookEvent) => {
                try {
                    await textEventHandler(event);
                } catch (err: unknown) {
                    if (err instanceof Error) {
                        console.error(err);
                    }

                    // エラーメッセージを返す
                    return res.status(500).json({
                        status: 'エラー',
                    });
                }
            })
        );

        // 成功した場合のメッセージを返す
        return res.status(200).json({
            status: '成功',
            results,
        });
    }
);

// サーバーを作成し3000listenする
app.listen(PORT, () => {
    console.log(`http://localhost:${PORT}/。`);
});
```
立ち上げ時に`ts-node server.ts`だと`zsh: command not found: ts-nodeエラー`で怒られるので、`npx`に頼ります。
```
npx ts-node server.ts
```
`LINE`でメッセージを入力するとやまびこになって返ってきたら成功です。
![スクリーンショット 2022-10-24 18.40.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/9ff2b721-e2fb-8a47-93c5-ea7806e780d1.png)