[LINE Bot 開発入門] LINE Bot Serverを作ってみる その3

# はじめに

[LINE Bot Serverを作ってみる その2](https://qiita.com/bathtimefish/items/77453b367ce634f7d677)のつづきです。
前回開設の途中で終わった[dispatchMessageEvent](https://github.com/bathtimefish/line-bot-server-example/blob/e47f992274597843abe84ac55c815fc2984b0f77/src/lib/event.ts#L26)の部分からです。

サンプルの全ソースコードはこちらです
https://github.com/bathtimefish/line-bot-server-example

# Message Eventを判別する

```event.ts
export const dispatchMessageEvent = async (event: line.MessageEvent): Promise<(Promise<line.MessageAPIResponseBase> | undefined)[]> => {
  let result = undefined;
  const replyToken: string = event.replyToken; // message event has a reply token
  let textEventMessage: line.TextEventMessage;
  let imageEventMessage: line.ImageEventMessage;
  let imageUrl: string;
  switch (event.message.type) {
  case 'text':
    /* Text Event Message用の処理 */
    break;
  case 'image':
    /* Image Event Message用の処理 */
    break;
  }
```

LINE Platformから送信されるメッセージタイプは複数あります。イベントは`message.type`を持っているので上記の処理ではswitch文で評価しメッセージタイプを識別しています。このコードはサンプルのため`TextEventMessage`と`ImageEventMessage`のみを判別しています。その他のイベントを処理するにはこのswitch文を拡張することで対応できます

イベントメッセージタイプは現時点で他に`FileEventMessage`、`AudioEventMessage`、`VideoEventMessage`、`StickerEventMessage`、`LocationEventMessage`がありそれぞれにTypeScriptの型定義が提供されています

また、イベントは`replyToken`を持ちます。`replyToken`は返信用のメッセージトークンで受信したメッセージイベントに対してメッセージを返信するために使用します

# テキストメッセージを返信する

```event.ts
  switch (event.message.type) {
  case 'text':
    textEventMessage = {
      type: 'text',
      id: event.message.id,
      text: event.message.text,
    };
    result = client.replyMessage(
      replyToken,
      textEventMessage,
    );
    break;
```

TextEventMessageを受信した場合の処理を記述しています。このサンプルでは受信したメッセージのテキストをそのまま返信するようにしています。いわゆるオウム返しです。返信には上記のようにTextEventMessageを作成しreplyTokenと合わせて[replyMessage](https://line.github.io/line-bot-sdk-nodejs/api-reference/client.html#replymessage-replytoken-string-messages-message-message-notificationdisabled-boolean-false-promise-messageapiresponsebase)で返信します。

# 画像メッセージを処理する

```event.ts
  case 'image':
    imageEventMessage = {
      type: 'image',
      contentProvider: event.message.contentProvider,
      id: event.message.id,
    }
    imageUrl = await downloadImageContent(imageEventMessage);
    textEventMessage = {
      type: 'text',
      id: event.message.id,
      text: imageUrl,
    };
    result = client.replyMessage(
      replyToken,
      textEventMessage,
    );
    break;
```

ImageEventMessageの場合、送信されるメッセージデータに画像データそのものが載ってくるわけではありません。イベントメッセージの`id`に対応する画像データをダウンロードする処理を記述する必要があります。LINE Messaging API SDKにはデータをダウンロードするためのmethodが用意されています。処理を順に見ていきましょう

上記ではまず、受信したイベントを元に`ImageEventMessage`を作成し、[downloadImageContent](https://github.com/bathtimefish/line-bot-server-example/blob/e47f992274597843abe84ac55c815fc2984b0f77/src/lib/event.ts#L18)に渡しています

```event.ts
const downloadImageContent = async (imageEventMessage: line.ImageEventMessage): Promise<string> => {
  const exp = 'jpg';
  const downloadPath= path.join(__dirname, '../public', 'downloaded', `${imageEventMessage.id}.${exp}`);
  const messageId = await downloadContent(imageEventMessage.id, downloadPath);
  const imageUrl = `${config.baseUrl}/downloaded/${messageId}.${exp}`;
  return imageUrl;
};
```

ここではサーバー上に画像データを保存するパスを用意したあと[downloadContent](https://github.com/bathtimefish/line-bot-server-example/blob/e47f992274597843abe84ac55c815fc2984b0f77/src/lib/event.ts#L8)にidを渡して用意したパスに画像データをダウンロードして保存します。正常に保存されたら画像にアクセスできるURLを作成して返しています

```event.ts
const downloadContent = async (messageId: string, downloadPath: string): Promise<string> => {
  const stream = await client.getMessageContent(messageId);
  return new Promise((resolve, reject) => {
    const writable = fs.createWriteStream(downloadPath);
    stream.pipe(writable);
    stream.on('end', () => { resolve(messageId); });
    stream.on('error', () => { reject(); });
  });
};
```

`downloadContent`でLINE Platformから画像データをダウンロードして保存しています。[getMessageContent](https://line.github.io/line-bot-sdk-nodejs/api-reference/client.html#getmessagecontent-messageid-string-promise-readable)がそれです。getMessageContentは`Promise<internal.Readable>`を返します。このReadble Streamを[fs.createWriteStream](https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options)を使って指定のパスにファイルとして保存しています

```event.ts
    imageUrl = await downloadImageContent(imageEventMessage);
    textEventMessage = {
      type: 'text',
      id: event.message.id,
      text: imageUrl,
    };
    result = client.replyMessage(
      replyToken,
      textEventMessage,
    );
    break;
```

`dispatchMessageEvent`に戻って読むと`downloadImageContent`が返す画像のURLを`TextEventMessage`として返信しています。LINEアプリで画像をアップすると画像のURLが返ってくる仕組みになります。

![image_event_message.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61995/ac0d10fe-7107-1af7-3059-7646f6cebb53.png)

以上が`dispatchMessageEvent`でやっていることです

# おわりに

今回はサンプルBotサーバーの心臓部である部分のコードを解説しました。その他のコードはWebサーバーの基本機能部分ですので説明を割愛します

これで「[LINE Bot 開発入門] LINE Bot Serverを作ってみる」の連載は終わりです。この記事でLINE Bot サーバーのコードレベルでの基本的な仕組みを理解いただければ幸いです。おつかれさまでした！