Corredor

ウェブ、プログラミングの勉強メモ。

LINE Messaging API を使ってオウム返しする Node.js 製チャットボットを作ってみる

突然だが、LINE Messaging API というモノを使って、簡単なチャットボットを作ってみる。

完成すると、LINE アプリでチャットボット用アカウントを友達登録して、トーク画面から会話できるようになる。メッセージの処理はしないので、ただユーザの発言をオウム返しする Chat Bot だが…。

LINE Developers に登録する

まずは、「LINE Developers」という開発者用プログラムに登録する。普段 LINE アプリで使っている LINE アカウントを紐付けて登録できるようになる。

登録は上の公式サイトより。登録が完了すると、LINE Developers Console と呼ばれる管理画面に遷移する。

プロバイダを作成する

LINE Developers Console が開けたら、「新規プロバイダー作成」ボタンから、プロバイダと呼ばれる「くくり」を作る。1つのプロバイダの中に、いくつかの「チャネル」を作れるようになっている。「チャネル」というのが、チャットボットアカウント1つに相当する感じ。

Messaging API チャネルを作成する

プロバイダを登録したら、「Messaging API」用の「チャネル作成する」ボタンがあると思うので、コレを押下する。

登録時、「プラン」より「Developer Trial」プランを選ぶ。友達登録数などに制限はあるものの、無料で Push API などを利用できる。企業向けの「Phone Number Push API」や「Switcher API」なんかは「Developer Trial」プランでも試せないのであしからず。

チャネルが作成できたら、「チャネル基本設定」画面に移動する。友達登録用の QR コードが見えているので、コレを利用して友達登録をしておこう。デフォルトで自動応答メッセージ機能などが組み込まれており、一応の応答は得られるので、まずは「自分で開発用アカウントを作った感」は味わえるかと。

チャネル情報を取得する

LINE Messaging API は、ユーザからの発言をまず「LINE 社のサーバ」が受け取り、それを「自前の Webhook サーバ」で受信する。受信したメッセージに応じて応答メッセージを構築し、「LINE Messaging API サーバ」に返信すると、ユーザに応答メッセージが返される、という仕組みになっている。

これから「自前の Webhook サーバ」を作るワケだが、その際、ちゃんと LINE から送られてきたリクエストなのか、自分が作ったチャネルでのやり取りなのか、といったことを検証するために、チャネルに関する以下の情報が必要になる。

  • Channel ID
  • Channel Secret
  • Channel Access Token

これらの文字列は、先程の「チャネル基本設定」画面で閲覧、もしくは発行できる。

「Channel ID」は最初から決まっているので控えておく。「Channel Secret」も最初から発行されているが、後から別のモノに再発行したりもできる。コレが漏洩すると不正な操作をされかねないので、厳重に管理しよう。

「Channel Access Token」は、「チャネル基本設定」画面の下部、「アクセストークン (ロングターム)」という画面から発行できる。初回であっても「再発行」ボタンだが、押下する。「失効までの時間」とやらを尋ねられるが、コレは、古いチャネルアクセストークンを失効させるまでの有効期限であり、これから発行するチャネルアクセストークンには関係ない。「0 時間」(失効しない) を選んでおけば OK。

この「アクセストークン (ロングターム)」欄から発行できるチャネルアクセストークンは、この後の「再発行」時に、時間を指定して失効させるまではずっと利用できる。開発中はコレで問題ないだろう。

新しく発行したロングタームのアクセストークンは再発行ボタンを再度押す(+指定した経過時間)まではずっと有効です。

コレとは別に、LINE API をコールしてもチャネルアクセストークンを取得できる。コチラは発行日から30日間で失効するモノで、直近に発行した30個のチャネルアクセストークンが有効となる。本番利用するモノはコチラがメインとなるだろうか。

Node.js Express サーバを実装する

さて、いよいよ Webhook サーバの実装だ。

ちなみに Webhook とは、POST メソッドによるリクエストを契機として何らかの処理を行うサーバの仕組みのことだ。別に特殊なツールやフレームワークを使う必要はなく、POST リクエストを受け付けて何かすれば、それはもう Webhook サーバなのである。

Node.js で Webhook サーバを実装する際は、LINE 公式が提供している @line/bot-sdk という npm パッケージを利用すると良いだろう。Express サーバ向けの専用ミドルウェアも提供されているので、Express + @line/bot-sdk な組み合わせで実装することにしよう。

# 作業用ディレクトリの準備
$ mkdir my-line-chat-bot && cd $_
$ npm init -y

# パッケージのインストール
$ npm install -S @line/bot-sdk express

# メイン処理を書くファイルを用意
$ touch index.js

オウム返し用の実装だが、実は @line/bot-sdk パッケージ内にサンプル実装が一式揃っているので、コレを利用してみよう。

ただし、この公式のサンプル実装は、後述する「Webhook 接続確認」でエラーを返してしまう作りになっているので、そこだけちょっと改善しておこう。

const line = require('@line/bot-sdk');
const express = require('express');

// 環境変数からチャネルアクセストークンとチャネルシークレットを取得する
const config = {
  channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN,
  channelSecret: process.env.CHANNEL_SECRET
};
// LINE クライアントを生成する : `channelSecret` が未定義だと例外が投げられる
const client = new line.Client(config);

// Express アプリを生成する
const app = express();

// LINE Bot SDK が提供するミドルウェアを挟み込み、リクエストヘッダの署名検証や JSON パースなどを任せてしまう
app.post('/callback', line.middleware(config), (req, res) => {
  // 1回のリクエストに複数のメッセージが含まれていたりすることもあるので
  // イベントの配列を1件ずつ取得して処理してやる
  const events = req.body.events;
  Promise.all(events.map((event) => {
    // イベント1件を処理する・エラー時も例外を伝播しないようにしておく
    return handleEvent(event).catch(() => { return null; });
  })
    .then((result) => {
      // 全てのイベントの処理が終わったら LINE API サーバには 200 を返す
      res.status(200).json({}).end();
    });
});

/**
 * イベント1件を処理する
 * 
 * @param {*} event イベント
 * @return {Promise} テキストメッセージイベントの場合は client.pushMessage() の結果、それ以外は null
 */
function handleEvent(event) {
  // メッセージイベントではない場合、テキスト以外のメッセージの場合は何も処理しない
  if(event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }
  
  // 返信用メッセージを組み立てる : ユーザからのメッセージにカギカッコを付けて返信してみる
  const echoMessage = {
    type: 'text',
    text: `「${event.message.text}」`
  };
  
  // Reply API を利用してリプライする
  return client.replyMessage(event.replyToken, echoMessage);
  // Push API を利用する場合は以下のようにする
  // return client.pushMessage(event.source.userId, echoMessage);
}

// サーバを起動する
const port = process.env.PORT || 8080;
app.listen(port, () => {
  console.log(`Listening on ${port}`);
});

このように実装すると、/callback パスに対する POST リクエストを契機に、LINE ユーザへの返信を行えるようになる。

「LINE から送られるイベント」とは何か、というのは、以下のページを見ると良いだろう。メッセージイベントの他に、フォローをしたとか、アカウント連携をしたとか、そういうユーザの操作も「イベント」として送信されてくるのだ。

単純に大人数が使うチャネルに成長して、複数のユーザが同時にメッセージを送ったような場合も、このイベントが複数格納される場合がある。1回のリクエストにつき、複数の返信を行わないといけない場合もあるので、Promise.all() でイベントを分割して1件ずつ処理している。

ユーザへの返信は client.replyMessage() 関数内で行われている。/callback パスへのレスポンスがどうであろうと、実はあまり関係ないのだ。そのため、Promise.all() 内で例外を発生させないようにし、/callback パスのレスポンスとしては必ず 200 を返すようにした。

Webhook サーバを Heroku にデプロイする

さて、Webhook サーバを実装したはいいものの、コレを実際に LINE と連携させるには、コレを HTTPS サーバに配置しないといけない。以前はオレオレ証明書を利用したローカルサーバでも LINE からの通信を受信できたようだが、現在はできなくなっているので、今回は無料で Node.js アプリをデプロイできる Heroku を利用する。

Heroku アカウントの用意や、デプロイするための設定事項は、以下の記事を参照されたし。

neos21.hatenablog.com

主なところだと、package.jsonscripts.start プロパティを設定してサーバが起動するように設定しておくのと、engines プロパティを記載しておくことくらいか。

あと、Heroku の管理画面より、以下の2つの環境変数を設定しておくこと。

  • CHANNEL_ACCESS_TOKEN : チャネルアクセストークン (管理画面「アクセストークン (ロングターム)」欄から発行したヤツ)
  • CHANNEL_SECRET : チャネルシークレット (管理画面「Channel Secret」欄の文字列)

コレで、$ git push heroku master と Push してやろう。https://my-line-chat-bot.herokuapp.com/callback といった URL が用意できれば OK だ。

Webhook 設定を行う

コレで、オウム返し処理を行う Webhook サーバが用意できたので、コイツを Messaging API チャネルに組み込んでやる。

「チャネル基本設定」画面に移動したら、以下のように設定を変更していく。

  • 「自動応答メッセージ」 : 「利用する」から「利用しない」に変更する
    • コレまで自動応答していたデフォルトメッセージを返さないようにする。Webhook サーバと併用してしまうと、「自動応答メッセージ」と「Webhook サーバからの返信」が両方届いてしまう変な Bot になってしまう
  • 「Webhook 送信」 : 「利用する」に変更する
    • 「Webhook URL」を設定してもコレを忘れると Webhook サーバが利用されない
  • 「Webhook URL」 : Heroku にアップした Webhook サーバの /callback パスまでの URL を入力する
    • 「この URL に POST リクエストを送ってください」という設定なので、https://my-line-chat-bot.herokuapp.com/callback といった URL を指定してやる
    • 設定してから少し待つと「接続確認」ボタンが表示されるので、コレを押して疎通確認してみる。POST リクエストに対して 200 を返してくるかどうかで、上手くいっているかを教えてくれる

コレで設定完了。設定の反映はほとんど即時で行われるが、気になるようなら数分待ってから動作検証してみよう。

LINE アプリで実際に試してみる

コレで LINE 側の設定が終わったので、実際に LINE アプリでチャットボットに向かってトークしてみよう。Webhook サーバがリクエストを受信し、メッセージイベントを検知して Reply API をコールしてくれたら、メッセージが返信されてくるはずだ。

以上

最も簡単なサンプルはこんな感じ。

画像をやり取りするとか、Flex Message と呼ばれるリッチな応答を返すとか、複雑なことをやろうとすると、もう少し作り込みが必要になってくるが、今回はこの辺で。

世界一やさしい LINE 最新版 (インプレスムック)

世界一やさしい LINE 最新版 (インプレスムック)