ExpressじゃなくてNest.jsでLINEBotを書きませんか？

# はじめに
この記事は鈴鹿高専 Advent Calendar 2021 24日目の記事です。

https://qiita.com/advent-calendar/2021/snct

LINEBotを作りたい場面は数多く存在すると思います(たぶん）
LINEのドキュメントではExpressの実装例が掲載されていて、Expressで実装する方も多いのではないでしょうか。

https://developers.line.biz/ja/docs/messaging-api/nodejs-sample/#start-developing

そんなとき、Expressでは書きたく無い。そんな方はいませんか？
この記事ではオウム返しBotをNestjsを使ったハンズオン形式で実装し、その後にリッチメニューやFlex Message等も実装方法を紹介していきます。
※2021/12/24現在、基本的なおうむ返しBotしか実装できていません。時間のある時にリッチメニューやFlex Messageの実装方法を追記していきます。

リポジトリだけ見たい方はこちら↓

https://github.com/shoma3571/nest-linebot-template

# LINEBotハンズオン
## Nest.jsプロジェクトの作成
バージョン
node: 16.10.0
npm: 8.1.3

```zsh
npx @nestjs/cli new nest-linebot
```
パッケージマネージャーはyarnを選択しました。

```zsh
cd nest-linebot
yarn start:dev
```

ここで```http://localhost:3000```を見てもらうとアプリが起動していて、以下のような画面になっていると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537532/8a9a3341-4ac6-fe5d-454f-db4ff158a83b.png)

## LINEBot実装
### LINEBotの作成
LINE Developersにアクセスします。

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

ログインができたら、左側のメニューで**Providers**を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537532/c56d2822-2eb1-3355-a63a-7799a045c3ba.png)

新しいチャンネルを作成します。タイプは**Messaging API**を選択してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537532/3c7506de-5496-f99b-79b6-9b31fdd52ad9.png)

Channel name, Channel description等に適切なものを入力し、作成します。

作成できたら、```Basic settings```の```Channel secret```と```Messaging API```の```Channel access token```を書いた.envファイルを作成します。

```.env
CHANNEL_ACCESS_TOKEN=
CHANNEL_SECRET=
```

.envファイルを作成したら、プロジェクトのルートにおいてください。この例だと```nest-linebot/.env```となります。

### ファイルの作成
NestjsではCLIからそれぞれのファイルを生成することができます。ここでは```module```, ```service```, ```controller```をそれぞれ作成します。

```zsh
yarn nest g mo linebot
yarn nest g co linebot
yarn nest g s linebot
yarn nest g s linebot/config
```

参考

https://docs.nestjs.com/cli/usages

### 環境変数の設定
いよいよ実装に入っていきます。まずは環境変数周りを処理する```config.service.ts```を記述していきます。
その前に、```.env```ファイルの値を読み込むための```@nestjs/config```モジュールとLINEBotのSDK(```@line/bot-sdk```)をインストールします。

```zsh
yarn add @nestjs/config @line/bot-sdk
```

インストールできたら、```linebot/config.service.ts```を編集していきます。

```config.service.ts
import { Client } from '@line/bot-sdk';
import { Injectable } from '@nestjs/common';
import { ConfigService as NestConfigService } from '@nestjs/config';

@Injectable()
export class ConfigService {
  createLinebotClient() {
    const configService = new NestConfigService();
    const tokens = {
      channelAccessToken: configService.get<string>('CHANNEL_ACCESS_TOKEN'),
      channelSecret: configService.get<string>('CHANNEL_SECRET'),
    };
    return new Client(tokens);
  }
}
```


```.env```ファイルを読み込めるように ```app.module.ts```を編集します

```app.module.ts
import { ConfigModule } from '@nestjs/config'; // 追加

@Module({
  imports: [
    LinebotModule,
    ConfigModule.forRoot({       // 追加
      envFilePath: ['.env'],     // 追加
    }),                          // 追加
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
```

### コントローラーに処理を記述
コントローラーに処理を記述していきます。

```linebot.controller.ts
import { WebhookEvent, WebhookRequestBody } from '@line/bot-sdk';
import { Body, Controller, Post } from '@nestjs/common';
import { ConfigService as NestConfigService } from './config/config.service';

@Controller('linebot')
export class LinebotController {
  constructor(private configService: NestConfigService) {}

  @Post()
  async handler(@Body() req: WebhookRequestBody) {
    const events: WebhookEvent[] = req.events;
    events.map((event) => {
      if (event.type === 'message') {
        const returnMessage =
          event.message.type === 'text'
            ? event.message.text
            : 'テキストではありませんでした。';
        return this.configService
          .createLinebotClient()
          .replyMessage(event.replyToken, {
            type: 'text',
            text: returnMessage,
          });
      }
    });
  }
}
```

どういう分岐をさせているかは、LINE Messaging APIのリファレンスに記載されているJSON等を参考にするとわかりやすいと思います。
この段階ではコントローラーに全ての処理を書いていますが、サービスに任せたりしてもいいと思います。(また書きます...)

https://developers.line.biz/ja/reference/messaging-api/#common-properties

### 実際に動作させてみる
ここではngrokを導入してテストをしてみます。

https://ngrok.com/download

ngrokをインストールできたら、以下のコマンドを叩きます

```zsh
ngrok http 3000 #3000番ポートで実行しているので
```

コマンドを叩いたら生成されるURL(以下の例では```https://9444-61-206-245-202.ngrok.io```)をコピーしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537532/8edda6d2-f1e6-ff80-5eb5-ccf662f10dd3.png)

このURLをWebhookURLとして設定します。LINE DevelopersのMessaging APIのところで登録をします。
```/linebot```をつけるのを忘れないでください。```linebot.controller.ts```で定義しているので、```/linebot```が必要です

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537532/ebf90198-7e2c-3803-3064-81f7d106243e.png)

以下のように登録して、```Verify```ボタンをクリックして```Success```と返ってこれば成功しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537532/80295ff1-e221-91bd-8d0b-e7c1e415115c.png)


### 実機で確認
確認する前に、自動返信メッセージをOFFにしておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537532/95acdee9-e373-4cf7-24e1-665977d80f4b.png)

右側のEditより設定します。
設定できたらMessaging APIのところにあるQRコードを読み取り、友達追加をして、適当なメッセージを送ってみてください。
以下のようにおうむ返しBotが動くと思います。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537532/c8a0649f-7b13-9b46-6458-4d0fb07f9424.png" width="30%">

これでおうむ返しBotが実装できました！！！

## LINEBot実装Tips
### リッチメニュー
(そのうち書きます、ごめんなさい)
### Flex Message
(そのうち書きます、ごめんなさい)
### 友達追加時のイベント
(そのうち書きます、ごめんなさい)


# おわりに
Nest.jsでLINEBotを書いたという記事がまだまだ少ないなと思ったので今回は書きました！
参考になれば幸いです！
あ、あとこの機能も書いて欲しい！みたいなのがあればコメントまでお願いします！(例えばクイックリプライとか)

鈴鹿高専 Advent Calendar　も明日が最終日です！よかったらみてください！！！

https://qiita.com/advent-calendar/2021/snct