不動の鳥の勉強記録

時間があるときに勉強したことをメモします。

JavaScriptがわからないなりにLINE botのサンプルを読み解く

ユーザとのインタフェースの一環としてLINEを利用した対話があります。
先日Node学園に参加した際は、残念ながらPC故障により参加できなかったので、
n0bisukeさんの記事を参考にLINE botを作りたいと思いたちました。

qiita.com

丁寧に記載していただいているので躓くことはなく、動くものができると思います。
ただ動いてオッケーではなくソースコードを理解しないと応用が利かないので、
server.js(下記)をJavaScript、Node.js初心者の筆者勉強したことをメモします。

'use strict';

const express = require('express');
const line = require('@line/bot-sdk');
const PORT = process.env.PORT || 3000;

const config = {
    channelAccessToken: '',
    channelSecret: ''
};

const app = express();

app.post('/webhook', line.middleware(config), (req, res) => {
    console.log(req.body.events);
    Promise
      .all(req.body.events.map(handleEvent))
      .then((result) => res.json(result));
});

const client = new line.Client(config);

function handleEvent(event) {
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }

  return client.replyMessage(event.replyToken, {
    type: 'text',
    text: event.message.text
  });
}

app.listen(PORT);
console.log(`Server running at ${PORT}`);

利用しているのは「express」と「@line/bot-sdk」の二つ。
line/bot-sdkについては下記が公式サイトのようです。

github.com

ソースコード内のconfigやline.middlewareやline.Client、line.replyMessageがLINE SDKを利用しているということがわかります。

■つまづいたポイント1- constって何?
Javascriptの変数宣言はすべてvarだと思っていました。constって宣言しているのは何だろう?ということで、
調べたらES6から採用された機能とのことです。値の再入を制御するということで、定義値などへ利用することで意図しない動きを制御でき便利そうです。
・参考:JavaScriptで書く「let,var,const」の違い・使い分け | TechAcademyマガジン

■つまづいたポイント2 - middlewareって何?
次にmiddlewareとはなんぞや?ということでした。
middleware関数はExpressが用意している関数とのこと。
req, resを呼び出すときに利用されるとのことで、リクエストの授受のときに共通的に利用したい処理がある場合は利用すると便利そうです。ちなみにExpress generatorのテンプレートを利用して同様の処理を作成したときは、「line.middleware」を利用しなくても動作はしました。

・参考:Writing middleware for use in Express apps


■つまづいたポイント3 - Promiseって何?
Promiseって何だろう?でした。いつのまにかこんなグローバルオブジェクトができていたのか!?という感じでした。
非同期処理のイベントを制御するにはよい機能と思います。
resolve. reject, then, catch, all そのあたりを抑えれたらとりあえずはいいのかと。
今回だと、

Promise.all(req.body.events.map(handleEvent)).then((result) => res.json(result));

とあり、req.body.events.map(handleEvent)でresolve関数が実行されたら、
resultをjson形式で返却するという処理になっていました。
reusultが何かというと、handleEvent関数のreturn値となります。

handleEvent関数は下記となっています。呼び出すときに「req.body.events.map」としているので、
req.body.eventsの各配列要素についてhandleEventを処理しているようです。

function handleEvent(event) {
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }

  return client.replyMessage(event.replyToken, {
    type: 'text',
    text: event.message.text
  });
}

LINEからテキストメッセージを送信した際に、
console.logでreq.body.eventsを表示すると下記配列が表示されます。

[ { type: 'message', 
    replyToken: 'トークンの値',
    source: { userID: 'ラインのユーザID', type: 'user'},
    timestamp: UNIXタイムスタンプ値,
    message: { type: 'text', id: 'メッセージのID?', text: '送られたメッセージ'}
}]

eventはオブジェクトなので、各要素について処理し、textのmessageではない場合はresolveを実行して終了。
メッセージの場合は、client.replyMessageでresolveされ?返信を行います。
という感じでいいのでしょうかね。

スタンプの場合は、下記が送られてきます。

[ { type: 'message', 
    replyToken: 'トークンの値',
    source: { userID: 'ラインのユーザID', type: 'user'},
    timestamp: UNIXタイムスタンプ値,
    message: { type: 'sticker', id: 'メッセージのID?', stickerId: 'スタンプのID', packageId: 'スタンプ集のID?'}
}]


・参考:Array.prototype.map() - JavaScript | MDN
・参考:Promise - JavaScript | MDN

以上。