ほりひログ

所属組織の製品 (Azure とか) に関連する内容が多めだけど、個人の見解であって、所属組織を代表する公式情報ではないです。

LINE Bot の Message をシンプルに扱いたい

はじめに

最近、LINE Bot で何か作れないか考えては、Echo ボットから先に進まない毎日です。

で、Echo ボットながらも、あーでもない、こーでもない、といろいろやっていると、

  • イベントタイプやステートの振り分けで、ネストが深くなりがちなる
  • Webhook に対するレスポンスは早めに返した方がいい
    -> ボットとしての処理は、別のイベントループで実施する

とか思った次第1

作った

それで作ったのがこれ。

github.com

インストール

いつもの npm install2

npm install node-linebot-message-handler --save

前準備

使い方ですが、まずは、new で作ったインスタンスに、on メソッドで各イベントタイプごとの処理を追加します。

const msgHandler = new LINEBotMessageHandler(config as Types.ClientConfig);

msgHandler
// `text` のメッセージを受け取ると、`text` イベントが飛ぶ。
// 'location' や 'sticker' (スタンプ) も同様。
.on('text', async (context:MessageContext) => {
  // MessageContext#getEvent() returns Message event object, so you can get message through MessageEvent#message
  // https://developers.line.biz/en/reference/messaging-api/#message-event

  const textEventMessage : TextEventMessage = context.getEvent().message as TextEventMessage;
  const eventSource : Types.EventSource = context.getEvent().source;

  // replyToken を気にせず、返事が送れる。
  // 例えば、エコーだけならこれ
  await context.replyMessage([{
    type: 'text',
    text: textEventMessage.text
  }]);

  // pushMessage も `to` を気にせず送れる
  await context.pushMessage([{
    type: 'text',
    text: textEventMessage.text
  }]);

  if (!eventSource.userId) return;

  // オリジナルの API にアクセスしたければ、getClient から。
  await context.getClient().pushMessage(eventSource.userId, [{
    type: 'text',
    text: textEventMessage.text
  }]);
})
// 'location' や 'sticker' (スタンプ) も同様。
.on('location', async (context:MessageContext) => {
  // : 
})
.on('sticker', async (context:MessageContext) => {
  // : 
})
// `image` のメッセージを受け取ると、`image` イベントが、データ情報とともに飛ぶ。
.on('image', async (context:MessageContext, data:RecievedData ) => {
  // contentType から拡張子を作って、WriteStream を生成
  const dest = fs.createWriteStream(`dest.${data.contentType ? data.contentType.replace(/[^/]+\//, '') : 'dat'}`);
  // ストリームをそのまま保存
  data.stream.pipe(dest);
})
// 'video', 'audio' や 'file' も同様。
.on('video', async (context:MessageContext, data:RecievedData ) => {
  // : 
})
.on('audio', async (context:MessageContext, data:RecievedData ) => {
  // : 
})
.on('file', async (context:MessageContext, data:RecievedData ) => {
  // : 
})
// 署名検証でこけたら `invalid` が飛ぶ
.on('invalid', async (data ) => {
  // invalid signature or request body.
  console.error('invalid');
});

利用時

Webhook 受信時には、リクエスト ボディをセットするだけで、前準備で設定したイベントに応じた処理が実行される、という単純なものです。

// Webhook で受信したリクエスト ボディを渡すと、上のイベントが発火する
msgHandler.setRecievedMessage(requestBody.toString(), signature /* リクエスト中の x-line-signature ヘッダーから */);

LINE Bot を作った経験がある人に試してもらって、使い勝手について感想をいただきたいところです。


  1. あとから調べてみたらいろいろ便利そうなモジュールがありましたw

  2. ちょっとしたしくじりがあり、npm に登録できなくなり焦ったけど、今なら問題なくインストールできるはず。