Node.jsのマルチプラットフォームなボット開発フレームワークBottenderの紹介とLINE Botの実装例


# Bottenderとは

BottenderはNode.jsのボット開発フレームワークです。以下のような機能を提供してくれます。

- 以下のプラットフォームに対応
    - facebook messanger、whatsapp、line、slack、telegram、viber
- 宣言的なAPI
- テンプレートの生成
- Ngrokで開発用のローカルサーバーを外部に公開
- ホットリロード
- セッション管理
- コンソールでのテスト

ドキュメント: https://bottender.js.org/docs/en/getting-started

# 始め方

```sh
# npmの場合
$ npx create-bottender-app --use-npm {$APP_NAME}
# yarnの場合
$ yarn create bottender-app {$APP_NAME}
# TypeScriptを使いたい場合
$ yarn create bottender-app --typescript {$APP_NAME}
```

するとどのプラットフォームのBotを作成するか、どこにセッションを保持するかを聞かれるので選択します。

```
? What platform of bot do you want to create? (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◯ messenger
 ◯ whatsapp
 ◯ line
 ◯ slack
 ◯ telegram
 ◯ viber
? Where do you want to store session? (Use arrow keys)
❯ memory
  file
  redis
  mongo
```

今回はyarn、TypeScript、LINE Bot、オンメモリーで行きます。

すると以下のようなファイルができます。

```
.
├── .env
├── .env.example
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── README.md
├── bottender.config.js
├── index.js
├── jest.config.js
├── package.json
├── src
│   ├── index.test.ts
│   └── index.ts
├── tsconfig.json
└── yarn.lock
```

次に、`.env`ファイルを編集して、LINEのアクセストークンとチャンネルシークレットを登録します。`.env`ファイルは実行時にNodeのdotenvというライブラリによって`process.env`に反映されます。

```sh
LINE_ACCESS_TOKEN={$YOUR_ACCESS_TOKEN}
LINE_CHANNEL_SECRET={$YOUR_CHANNEL_SECRET}
```

そうしたら開発用のサーバーを起動してみましょう。ローカルのサーバーがNgrokというサービスによって外部に公開され、URLが発行されました。このURLをコピーし、LINE Developersの管理コンソールから、WebHook URLに先ほどのURLを登録します。

```
$ yarn run dev
yarn run v1.22.4
$ bottender dev
App has started
line webhook URL: https://c7937b1b.ngrok.io/webhooks/line
server is running on 5000 port...
```

これで開発環境は整いました。試しにLINEから適当なメッセージを送ってみてください。Welcome to Bottenderと返信されるはずです。
また以降プロジェクトファイルを変更すると自動でサーバーが再起動するため、面倒なURLの差し替えも必要ありません。

# 適当な実装例

とりあえず何が送られてもオウム返しをし、画像が送られたら送られたと返すだけのボットを作りました。

```typescript:src/index.ts
import { Action, LineContext, LineEvent } from 'bottender';
import { router, line } from 'bottender/router';
import { AnyContext } from 'bottender/dist/types';

const messageContext = line.message(async (context: LineContext) => {
  const event = context.event as LineEvent;

  switch (event.message?.type) {
    case 'text':
      await context.replyText(event.message.text);
      break;
    case 'image':
      const imageBuffer = await context.getMessageContent();
      await context.replyText('画像を受け取りました');
      break;
    default:
      await context.replyText('テキストを送ってください。');
      break;
  }
});

const FollowContext = line.follow(async (context: LineContext) => {
  await context.replyText('フォローありがとうございます。');
});

const AnyContext = line.any(async (context: LineContext) => {
  await context.replyText('おっけー');
});

export default async function App(): Promise<Action<AnyContext> | void> {
  return router([messageContext, FollowContext, AnyContext]);
}
```

`App()`でrouterを返すようにし、引数にコンテクストのリストを指定します。コンテクストは先頭から順に評価されます。
そして、メッセージが送られたとき、フォローされたとき、それ以外のすべてに対するコンテクストを用意すると、それぞれの状況に対して適切に対応できるようになります。また受け取ったメッセージから中身を取り出すには、`context.message`を参照します。

context.replyText以外のメソッドはAPIドキュメントを読んでください。

ドキュメント: https://bottender.js.org/docs/en/0.15.17/api-linecontext

# さいごに

Node.jsでBotを作ってみたいと考えている人は是非Bottenderを試してみてください。