TokyoFacebookTwitterHatenaPocket2016年大ブームになったチャットボット。「そのうち作ってみよう」と思いながらも、なかなか手を動かせていなかった人に贈る、Heroku+Node.jsで始めるFacebookチャットボット開発のススメ。2016年のF8カンファレンスでFacebookはMessengerプラットフォームを発表しました。Messengerプラットフォームにより開発者は、MessengerもしくはFacebookページで訪問者と会話するボットを開発できるようになりました。ボットを使えば、アプリ開発者はユーザーが大勢でも個々人向けの双方向のやりとりを提供できるので、ユーザーには魅力的でしょう。サービス開始以降、産業界もアプリ開発者もこのチャットボットに大きな関心を寄せています。発表のわずか3カ月後にはMessengerプラットフォームに推定で約1万1000ものボットが作られました。チャットボットの恩恵を受けるのは産業界とアプリ開発者だけではありません。ユーザーも、以下のように多くのサービスが利用できるようになります。Intelligence、AI)の技術革新によりボットのコミュニケーションはさらに向上するでしょう。この記事では、Facebookのページの代わりにMessengerでユーザーと会話ができるチャットボットの作り方を説明します。ユーザーの見たい映画の選択に合わせて異なる会話を提供するボットを作ります。AIを分かっていないとボットは作れない?洗練されたボットを作るなら確かにAIに詳しいほうが有利ですが、必須ではありません。larning)に詳しくなくてもちゃんと作れます。ボットには2種類あります。1つはルールに基づいたボット、もう1つは機械学習を使うものです。前者はできることに限りがあり、特定のコマンドにしか対応できません。この記事で作るのはこの前者のルールに基づいたボットです。機械学習を使うボットなら、ユーザーとのコミュニケーションはさらに優れたものになります。コマンドを使うのではなく、人と人との会話のような自然なやり取りができます。ボットはユーザーとの会話を通じて学び、どんどん賢くなっていきます。機械学習を使うボットの作り方は将来の記事のテーマとして取っておきましょう。機械学習を使うボットでさえも、機械学習に関する知識は必要ありません。幸運なことにwit.aiやApi.aiのような、開発者が自分のアプリに機械学習(とりわけ自然言語処理)を組み込めるサービスがあります。さあ始めようこのデモの完全なコードはここからダウンロードできます。ボットがFacebookユーザーとコミュニケーションをとるためには、メッセージの送受信や処理をするサーバーの構築が必要です。APIがFacebookプラットホームでデータをやりとりする主な手段です。アプリのサーバーはFacebookのサーバーからアクセス可能なエンドポイントURLが必要なので、手元のPCにあるローカルなアプリでは動きません。アプリはオンラインである必要があります。APIのバージョン2.5以降、新規サブスクリプションには安全なHTTPSコールバックの使用が義務付けられました。Herokuの初期設定のappname.herokuapp.comドメインはすでにSSLが有効なので、この記事ではアプリをHerokuにデプロイします。Webアプリの作成にはNode.jsを使います。最初に、自分のマシンにNode.jsがインストールされていることを確認してください。-vコマンドで確認できます。インストールされていたらバージョン番号が表示されます。(CLI)をインストールします。あとでHerokuにアプリをプッシュするのに使います。--versionコマンドでCLIがインストールできたことを確認してください。プロジェクトフォルダーを作りpackage.jsonファイルを以下のコマンドで初期化します。init表示される指示にしたがって、プロジェクトを設定してください。package.jsonが初期化されたら、scriptsオブジェクトのところにstartプロパティを追加します。Herokuに対して、アプリを開始するために使うコマンドを通知しています。app.jsを使います。値などはプロジェクトの設定に応じて変更してください。"ISC"}以下のようにNodeパッケージをインストールします。--saveプロジェクトのルートディレクトリに.gitignoreファイルを作成したら、Gitにコミットされないようにするためにnode_modulesフォルダーを含めてください。(初期値のようにするのならindex.js)というファイルを作成し、以下のように変更します。}});最初のGETハンドラは、あとでアプリのデプロイに成功したかをテストするためのものです。2つ目のGETハンドラは、Facebookがアプリの認証に使うエンドポイントです。コードは認証リクエストに対してverify_tokenを見てchallengeを返します。使用するコードには自分のトークン(token)を使ってもかまいませんが、この手のデータは環境変数に保存するのが一番です。この記事でもHerokuにプロジェクト作成したあとで、そのようにします。HerokuにデプロイするFacebookプラットホームがバックエンドのWebアプリに接続できるように、アプリをオンラインにしておく必要があります。Gitレポジトリを作成し、以下のコマンドでプロジェクトファイルをコミットします。commit"ターミナルからHerokuにログインし(アカウントを持っていなければ登録してください)、アプリを作ります。openコマンドを実行すると、使用しているブラウザーからアプリへのリンクが開くはずです。すべてうまくいったら「Deployed!」と書かれたページが開きます。環境変数を作成する先へ進む前に、Herokuの環境変数を作ってアプリの認証トークンを格納します。Herokuダッシュボードを開き、デプロイしたアプリを選択します。Var(設定変数を開く)ボタンをクリックしてください。KeyにはVERIFICATION_TOKEN、Valueには自分のトークンを入力し、Addをクリックします。コードの中のトークン文字列「"this_is_my_token"」の部分をprocess.env.VERIFICATION_TOKENに変えてください。変更をコミットしたらHerokuにプッシュします。Facebookページとアプリを作るサーバーが稼働したので、Facebookアプリとページを作ります。新規に作ってもいいし、すでにあるものでもかまいません。Facebookページを作るにはFacebookにログインしてFacebookページを作成を開いてください。ページのタイプは、記事ではエンターテインメントを選択します。次にカテゴリーとページ名を選びます。スタートをクリックするとページが作成され、ページの詳細(説明、Webサイト、プロフィール写真、どのようなユーザー向けかなど)の設定になります。さしあたってこのステップを飛ばして次に進んでもかまいません。setupを選びます。必要な情報を入力してください。Pages(ページアプリ)を選びます。ID(アプリID)作成をクリックするとアプリのダッシュボードが開きます。Started(スタート)」を選びます。下のようなメッセンジャーの設定ページが開きます。Messengerのユーザーからのメッセージやその他のイベントをアプリが受け取るには、アプリの「Webhook統合を有効にする」必要があります。これは次項で説明します。Updatesという名称でした)を使えば狙った部分の変更を監視でき、APIを呼び出さなくてもリアルタイムで更新できます。Webhooksを選択します。URL(コールバックURL)」欄に、更新情報の送り先となるこのアプリのエンドポイントURL、<your-app-url>/webhookを入力します。Token(トークンを確認)」欄には認証用トークン、つまりバックエンドアプリで設定したprocess.env.VERIFICATION_TOKENの値を入力して、下にあるチェックボックスにはすべてチェックを入れます。これはアプリがフォロー(監視)する項目の指定で、それぞれがなにをするのかはあとで説明します。無事Webhookが有効になったら、Webhookの欄には「Complete(完了)」の表示とともに、フォローするイベントが表示されます。エラーがあれば、入力したエンドポイントのURLが正しいか(末尾が/webhookになっていること)、Nodeアプリと同じトークンを入力したかを確認してください。Generation(トークン生成)」の欄で、ドロップダウンメニューから自分のページを選択します。認証後にページアクセストークンが生成されます。Herokuでさらに別の環境変数を作り、そのキーをPAGE_ACCESS_TOKENに設定し、さきほど生成されたトークンをその値として入力します。注意してほしいのは、トークンはFacebookで表示されるページに保存されるわけではありません。ページを訪れたときは毎回ページアクセストークンは空の状態で、ドロップダウンメニューからFacebookページを選択するたびに新しいトークンが生成されます。以前に生成されたトークンは引き続き使えるので、Webページを閉じる前にトークンをコピーするようにします。Webhookが特定のページのイベントを受け取るには、Webアプリがそのページをフォローする設定になっていなければなりません。Webhookの設定で、フォローするページを選択します。ウェルカムページボットと新たに会話を始めるユーザーには、ページ名、ページの説明、プロフィール写真、カバー写真といった内容のウェルカムページが最初に表示されます。Text(あいさつメッセージ)」の設定で変更して、ユーザーに対してボットの紹介するのも良いでしょう。初期設定では会話を始めるにはユーザー側が最初のメッセージをボットに送信する必要があります。Started(スタート)ボタンを有効にすればボット側から最初のメッセージを送れます。ボタンが押されるとサーバーにイベントが送信されるので、応答処理ができるわけです。あいさつメッセージを設定するにはページを開いて、Settings(設定)を開きます。Greeting(あいさつメッセージ)」の表示をオンにします。好きなメッセージを設定します。スタートボタンを有効にするには、次のコードにあるPAGE_ACCESS_TOKENの部分を自分のトークンに置き換えて、ターミナルに以下のコマンドを追加してください。APIにリクエストを送信します。成功したら、新規で会話を開始する際のウェルカムページ上にスタートボタンが表示されます。ボタンがユーザーにクリックされるとポストバックコールバックによって、ボットがポストバック(同じページ宛にサーバーから処理を返す)に応答します。Message)」タイプのコンポーネントから実行されます。payloadのデータはどのような文字列でもセットできます。アプリ側は文字列を使って、スタートボタンのクリックによるポストバックだと特定します。ポストバックメッセージを受け取るには、アプリはWebhookでフォローしておく必要があります。先ほどWebhookの設定でmessaging_postbacksのところにチェックを入れたのはこのためです。スタートボタンの設定が正しくできたら、以下のようなレスポンスになります。CTAs"}ウェルカムページとスタートボタンは、新規に会話を始めるときにしか表示されないことに注意してください。コードを書いてテストをする際は、これまでの会話を削除して新規に会話を開始します。ポストバックメッセージを処理するには、Nodeアプリで以下のコードを使ってください。});}誰かがボットと会話したりページにメッセージ送信した際は、Webhook統合によって更新が送信されます。メッセージを受け取るにはWebhookでPOSTのコールを監視する必要があります。すべてのコールバックはWebhookに向けたものです。上のPOSTハンドラでは、アプリに送られたメッセージに対し順次処理をしています。ときには複数の束ねられたメッセージが同時に送信されるので、1つのエントリーに複数のオブジェクトが含まれていることもあります。そのため各メッセージエントリーは上のコードを通して型をチェックしています。以下のように、アプリに送られるコールバックメッセージはいろいろな種類があります。callback:ボットにメッセージが送られた際に送信される。callback:ポストバックのトリガーに設定したボタンのクリックで送信される。callback:ページから送信されたメッセージが配信されたときに実行される。callback:Send-to-Messenger(メッセンジャーに送信)プラグインがタップされたときに実行される。callback:ページから送信したメッセージがユーザーに読まれた際に実行される。callback:ページからメッセージを送信した際に実行される。テンプレート、フォールバックなど)を受け取れる。callback(ベータ版):可変量のトランザクションで、購入ボタンが押されると実行される。相手の配送先住所によって価格を変更したりできる。Webhookの設定でmessaging_checkout_updatesイベントをフォローしておく必要がある。callback:購入ボタンで表示される購入手続きのダイアログで支払いボタンを押したときに実行さる。Webhookの設定でmessaging_paymentsイベントをフォローしておく必要がある。現在米国以外の地域では使用できないもしイベントがPostbackならprocessPostback()関数をコールして中身をチェックします。スタートボタンでpayload(データの中身)にセットしたのはGreetingでした。そこで、最初にメッセージイベントはスタートボタンがクリックされたかどうかをチェックします。APIでユーザーの名前を取得し、各個人に合わせたメッセージをユーザーに返します。このAPIでユーザーの姓名、プロフィール写真、地域、その地域の時間、性別などが取得できます。メッセージはsendMessage()関数でメッセンジャープラットホームにポストされます。HTTP」とレスポンスされます。HTTP」は一刻も早く返すことが重要です。Facebookは次のメッセージ送信の前にこのレスポンスを待つからです。大量の情報を扱うボットで、このレスポンスが遅れるとFacebookからWebhookへのメッセージ配信が滞ってしまいかねません。Webhookからエラー(2xx番以外のステータス)が返ったり、タイムアウト(20秒以上返答がない場合)して、しかも15分以上継続した場合は、警告が返されます。Webhookが8時間にわたって処理に失敗するとFacebookからWebhookを無効にすることが通知され、アプリのイベントのフォローが解除されます。問題を修正したら再度Webhookを設定してイベントのフォローも有効にしなければなりません。変更を確定しコミットしたらHerokuにプッシュしてください。実際に会話をしてボットをテストします。facebook.comのページ、モバイルのFacebookアプリ、メッセンジャーの短縮URL:https://m.me/PAGE_USERNAME(ユーザー名の作成は後述)のどこからでもかまいません。Facebookとメッセンジャーでページ名を検索すると、自分のページが見えます。これまで説明してきたように、自分のページ名がほかのページと重複してしまうことがあります。ユーザーが誤ってほかのページを訪れてしまうかもしれません。それを防ぐには、ページに対して一意のユーザー名を設定しましょう。Info(ページ情報)」の編集を選択します。ここでユーザー名を設定してください。これでもし@page_usernameで検索すれば正しいページが表示されます。https://m.me/PAGE_USERNAMEからアクセスしても構いません。以下のように、ウェルカムページにあいさつメッセージとスタートボタンが表示されました。ボタンをタップするとサーバーからのメッセージが表示されます。この状態では、文を入力しても返事はありません。APIにより映画の詳細情報を入手します。このAPIリクエストは最初に一致した結果しか返しませんので、返ってきたタイトルはユーザーが意図したものではないかもしれません。そのため、最初にボットはタイトルが正しいかユーザーに確認し、正しければあらすじ、出演者、評価などの詳細を表示します。そこから別の映画タイトルを入力して情報を表示できます。このやりとりでは、ボットはユーザーが入力した映画タイトルを記憶しておく必要があります。FacebookはWebhookの一連のセッションを開いたままにはしませんので、前回セッションでオブジェクトが持っていたデータは次回リクエスト時には失われています。そこでデータはデータベース、具体的にはMongoDBに保存します。ここではmLabによるHerokuのアドオンを使います。mLabによりMongoDBは、Database-as-a-Service(サービスとして利用できるデータベース)として利用できます。Herokuでは無料のサンドボックスmlabプランを使えますが、認証のためにクレジットカード情報の登録が必要です。databaseを作成し、コードでそこへリンクしてください(後述)。Herokuでこのアドオンを使うには、アプリのダッシュボードからResourcesタブを選択します。Freeプランを選んだら、Provisionをクリックします。これで、選択したアドオンの確認が表示されます。Herokuの環境変数には、MongoDBのURIがすでに設定されています。deploymentを開いてください。Lineの欄でSandboxを選択します。deploymentボタンをクリックして完了です。続いて開くページで表示されたテーブルから、作成したデータベースを選択します。表示されるページにはデータベースへのアクセス方法が説明されています。userボタンをクリックします。ユーザー名とパスワードを入力してCreateをクリックしてください。以上で、アプリがデータベースにアクセスするための、新しい認証情報が登録できました。ページ上部のデータベースURIを見つけたらコピーしてください。URIは「mongodb://<dbuser>:<dbpassword>@dsxxxxxx.mlab.com:55087/spbot」のような感じです。作成したdbuser名とパスワードを入力します。Herokuで環境変数MONGODB_URIを作成し、値として先ほどのデータベースURIを入力します。モデルクラスの定義Nodeアプリに戻り、movie.jsという名前のファイルを作って、modelsフォルダーに保存してください。ファイルには以下のコードを書きます。MovieSchema);上のコードは映画のデータベースモデルを作成しています。APIから得た情報が入ります。ユーザーが最後に検索した映画だけを保存するので、1ユーザーにつき1レコードだけになります。user_idを作らずに、各レコードの_idにユーザーIDの使用もできます。FacebookページのユーザーIDが一意であるため、それも可能です。ユーザーIDのスコープがページ内だけである点に注意してください。ある1つのページ中ではユーザーIDは一意ですが、ほかのページにおいてユーザーは別のIDを持っている可能性があるのです。ボットが複数の異なるページに対応するなら(そうです、ボットは複数ページにまたがって対応できます)ユーザーIDには注意してください。ボットが複数ページに対応するならFacebookユーザーIDだけで訪問者を特定するのは適切ではなく、同様にデータベースの_idフィールドにユーザーIDを使うのもIDが一意でないため不適切です。複数ページ間にまたがってユーザーIDを1つに特定できないからです。すべてを仕上げるデータベースとモデルができたのでチャットボットを仕上げます。コードは分割して説明しましたが、すべてのコードを一度に使えるapp.jsファイルへのリンクはこちらです。データベースへの接続から始めます。Mongooseはほかのモジュールと一緒にインストールされているはずです。require("./models/movie");WebhookのPOSTハンドラを以下のように変更します。}});ここでイベントはprocessMessage()関数に渡され、messageの型を見てイベントの型のチェックをします。Callbackから送信されたかどうかをチェックします。このコールバックはページからメッセージが送信されると実行されます。最初にユーザーに送信するメッセージ(あいさつメッセージ)はWebhookに送り返されます。自分が送ったメッセージは一切処理しなくて良いのでチェックを加えました。次に、メッセージがテキストのみか添付ファイル(画像、動画、音声)付きかをチェックします。後者であればユーザーに対してエラーメッセージを表示します。テキストであれば、ユーザーが欲しい映画情報を示すキーワードと一致しているかチェックします。この時点でユーザーは映画に関するクエリ(問い合わせ)を送信しているはずで、データベースに保存されます。getMovieDetail()関数はデータベースへ問い合わせを送り、その結果を返します。APIをコールするfindMovie()関数へ渡します。});}該当する映画が見つかったら詳細がユーザーIDと一緒に保存されます。同じユーザーIDのレコードがあった場合はデータを更新します。次に、構造化メッセージを生成しユーザーに送ります。ファイル、構造化メッセージをユーザーへ送信できます。構造化メッセージはいろいろな状況に対応できるテンプレートです。ボタンテンプレートはテキストとボタン、一般テンプレートは画像、タイトル、サブタイトル、ボタンを送信できます。今回のアプリでは一般テンプレートを使用します。processPostback()関数を以下のように変更します。}}ここでは2つ、CorrectとIncorrectのメッセージのチェックを加えました。ユーザーがボットに対し、表示された映画タイトルが正しいかどうかを回答するボタンから送信されるメッセージです。コードをコミットしたらHerokuに送信し、ボットへメッセージを送ってみましょう。ボットにクエリを送り、もし該当する映画が見つかれば、ボットは表示した映画が正しいかどうか問い合わせる構造化メッセージを返します。WebサイトやiOSで見た場合、外観は少し異なります。構造化メッセージではいくつかのタイプのボタンを使用できます。URLボタン:アプリ内ブラウザーでURLを開くポストバックボタン:ボットになにかアクションを送信したいときに、Webhookにポストバックを送れるコールボタン:電話番号から電話をかけるシェアボタン:シェアダイアログを開き友人とメッセージをシェアできる購入ボタン:購入手続きを開くこのサンプルでは2つのポストバックボタンを使い、それぞれの値をCorrectとIncorrectにセットしました。processPostback()関数を見ると2つのpayload(中のデータ)をチェックしているのが分かります。Noボタンがタップされると以下のメッセージが表示されます。ユーザーは別の映画名で問い合わせをしてきます。求めている映画が確定したら、ボットからの案内で会話が進むわけです。キーワードをprocessMessage()関数でチェックしたことを思い出してください。ユーザーに送信する構造化メッセージは正しい形式でなければ表示されませんので注意してください。サンプルアプリを使ううち、APIから返された映画タイトルのなかには詳細情報の一部が欠けているものがあることに気がつきました。このような場合、情報のないフィールドの値はN/Aとなります。今回の構造化メッセージでは、APIから返ってきたオブジェクトの中から、Title(タイトル)とPoster(ポスター)という2つのフィールドを使用しました。映画が見つかったならタイトルがあるはずなので確認は不要ですが、映画のポスターが無く、代替表示URLも無い場合、構造化メッセージが表示されません。そのため常に構造化メッセージの属性には何らかの値が入っているようにしてください。今回は、ポスターが無かったときのために代替画像へリンクしました。ユーザーの問い合わせに合う映画があればボットが返します。Facebookチャットボットに命を吹き込むアプリをリリースする用意ができたら、承認手続きを踏まなければなりません。アプリが承認される前は、自分と招待したテスターだけがボットを利用できます。レビューチームへのアプリ提出の手続きは記事の範囲を超えますが、なにをすれば良いのかはガイドで解説されています。最後にこの記事では、ユーザーからのメッセージを受け取って返信するシンプルなFacebookチャットボットを作りました。このボットは完璧にはほど遠いですが......。ユーザーとの会話で応答できるコマンドはごく限られています。これでは人間同士の会話にはなりません。ボットはユーザーに対して、コマンドではなく人間同士のやりとりのような自然な応答ができるよう改良されていくでしょう。そのためにはボットに自然言語処理(NLP)を組み込むことです。アプリに自分で自然言語処理エンジンを実装するか、wit.aiのようにアプリに自然言語処理を組み込んでくれるサービスを利用します。wit.aiはFacebookに買収され、個人でも商用でも無料で使えます。Reyesが査読を担当しています。最高のコンテンツに仕上げるために尽力してくれたSitePointの査読担当者のみなさんに感謝します。EchessaWeb開発者で、ときどきモバイル開発も手がけていいます。Twitterの@joyceechessaに記事をアップしています。