https://d226lax1qjow5r.cloudfront.net/blog/blogposts/restaurant-is-now-delivering-a-facebook-bot-in-node-js/restaurant-closed_1200x600.png

Node.jsでFacebookボットを作る

最終更新日 October 13, 2021

所要時間:6 分

はじめに

コーディングをしていると、お腹が空くことがよくある。最高の開発者は怠け者だと誰もが知っている。 そして買い物や料理や掃除をする代わりに、私はたいていフードデリバリーアプリを使い、美味しい食事を注文する。問題は、お気に入りのレストランがオフラインであることが多いことだ。営業が終了していることもあれば、忙しすぎてオンライン注文を受け付けていないこともある。そのため、私はオンラインに戻ったかどうかをチェックするのを忘れず待ち、実際にアプリを開いてオンラインに戻ったかどうかを確認することを余儀なくされる。そして時には何度も何度も確認する。まさに重大な不公平だ😆。

もっといい、もっと革新的な方法があるはずだ!ありがたいことに、最近お気に入りのフードデリバリーアプリ、Wolt、 にはAPIがある。があることを発見した。そこで、Vonage Messages APIを使って、お気に入りのレストランがオンラインに戻ったら知らせてくれるFacebook Messengerボットを作ってみた!

(この例は、フードデリバリーのユースケースを中心に構築されているが、同じコードを再利用して、ブーリアンケースの変更をユーザーに警告するFacebookボットを作成することができる)

前提条件

このアプリには以下が必要です:

疑似コード:

コーディングを始める前に、私はロジックを考えるのが好きだ。このアプリを作るために必要なステップを分解してみよう:

  1. Expressサーバーのセットアップ

  2. Vonage Messages APIサンドボックスへの接続

  3. リクエストされたレストランのWolt APIを呼び出す

  4. 受け取ったレストランがオンラインかどうかを確認する

  5. レストランの状況に応じてユーザーにメッセージを送る

  6. レストランがオフラインの場合、オフラインレストランのリストに追加する。

  7. オフライン・レストランのリストにステータスの変化がないか継続的にチェックする。

  8. レストランがオンラインになった場合、ユーザーメッセージを送信し、オフラインレストランのリストから削除する。

プロジェクトのセットアップ

ノード・アプリケーションの作成

プロジェクトを作成することから始めよう:

mkdir isItDelivering

その後、プロジェクト・ディレクトリの中に移動する:

cd isItDelivering

ノードプロジェクトを初期化する:

npm init

必要なNodeパッケージをインストールします:

npm install -s @vonage/server-sdk@beta express dotenv got lokijs

そして最後に、コードを格納するファイルを作成する:

touch index.js .env

Vonageの ノード・サーバーSDKを使用して メッセージ API.Messages APIは現在ベータ版なので、SDKもベータ版が必要です。

サーバをセットアップするために、以下の情報が必要です。 Vonage Developer Dashboard.まず を作成します。.isItDeliveringのような素敵な名前を付けます。そして "Generate public and private key "をクリックする。

Generate Public/Private KeyGenerate Public/Private Key

これで認証用のキーが自動的に生成される。生成されたキーをローカル・プロジェクトのルートに移動する。

この時点で、プロジェクトにはインデックス・ファイル、ノード・モジュール、package.json、ENVファイルが含まれているはずです。コマンド lsを実行すると、プロジェクトはこのようになるはずだ:

Project Should Include index.js, node_modules, pack.json, private.keyProject Should Include index.js, node_modules, pack.json, private.key

ご覧のように、Vonageアプリケーションでは、さまざまなVonage APIを通じてさまざまな機能をオン/オフすることができます。ここではMessages機能をオンにします。ここで、Messages API が私たちのボット・アプリケーションとやりとりするために使用するウェブフックに対応する2つの URL を要求されます。

外の世界とつながる

セットアップ ngrok

ローカルの開発サーバーを外部からアクセスできるようにする方法はいくつかあるが、最も簡単な方法のひとつがngrokを使う方法だ。以下の記事を読んでほしい。 この記事を参照してください。

私たちの目的では、これを実行し、提供されるURLをコピーするだけでよい。

ngrokをマシンにインストールしたら、起動する必要がある。ngrokを起動するには、新しいターミナルウィンドウを開き、コマンドラインから以下を実行する:

$ ngrok http 3000

ターミナル・ウィンドウにngrokロギング・インターフェースが表示されます。インターフェイスの最上部近くには Forwardingで始まる行があり、2つのURLが含まれています。最初のURLは外部からアクセス可能なngrokのURLで、末尾に ngrok.ioが続きます。 http://localhost:3000これはあなたのローカル開発サーバーです。これで、あなたやVonageが ngrok.ioURLはあなたのローカルサーバーに転送されます。

Vonage Dashboardにngrok URLを追加し、適切なURLルートを追加します。URLがこのようになったら、"Generate new application "ボタンをクリックします。

Webook URLsWebhook URLs

Vonageでつながる

Vonageアカウントの接続

プロジェクトのENVファイルに、3つの環境変数を追加する必要がある; API_KEY, API_SECRETAPP_ID.

あなたの API_KEYAPI_SECRETをVonage Dashboardのホームページで見つけることができます:

Dashboard ENV VariablesDashboard ENV Variables

あなたのAPP_IDは、あなたが生成したアプリケーションの設定ページにあります。左側のナビゲーションバーの Your Applicationsの下で見つかります。あなたのAPP_IDは次のようになります:

APP_ID in DashboardAPP_ID in Dashboard

これらをプロジェクトにコピー・ペーストすると、ENVファイルは以下のようになるはずだ:

API_KEY="XXXXXXXXX"
API_SECRET="XXXXXXXXX"
APP_ID="XXXXXXXXX"

Messages API Sandboxを使い始める

サンドボックスにユーザーを追加する

私たちは Facebookサンドボックス.SandboxはVonage Dashboardの左側のMessages and Dispatchタブの下にあります。 をクリックしてください。.をクリックします。 Add to Sandboxをクリックすると、画面はこのようになります:

Set up your sandboxSet up your sandbox

Messages API Sandboxは、Business Accountの承認を待つことなく、アプリケーションの迅速なテストを可能にします。サンドボックスはテストユーザーを許可するためにホワイトリスト方式を採用しています。ホワイトリストに追加ユーザーを招待するには Send invite email buttonまたはハイパーリンク click this link.リンクはFacebook Messengerセッションを開きます。ユーザーは、ホワイトリストに追加するためのパスフレーズを送信する必要があります。詳細は こちら.

アプリケーションをサンドボックスに接続する

アプリケーションからのリクエストを聞き、Facebook Messengerに配信するようにSandboxに指示する必要があります。これはngrok URLを通して行います。先ほどと同じngrok URLを次のように追加する必要があります:

Messages API Sandbox ngrok URLSMessages API Sandbox ngrok URLS

ボタンを押したら Save webhooksボタンを押すと、セットアップは完了し、コードを書き始めることができる!

以降のコードはすべて index.jsファイルに書きます。

Expressサーバーのセットアップ

依存関係を使ってボイラープレート・サーバーを構築する

まず、定型のExpressサーバーを index.jsこのサーバーは必要なライブラリをインポートし、単純にポート3000で実行される:

// access our environment variables
require('dotenv').config();
// access the Vonage SDK so we can use the Voange object and API
const Vonage = require('@vonage/server-sdk');
// access Got library which allows us to make HTTP request to WOLT API
const got = require('got');

// boilerplate Express setup
const express = require('express');
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

app.listen(3000);

基本的なFacebookメッセージの送信

アプリケーションからFacebookメッセージを送信する

ENV変数を渡してVonageインスタンスを初期化し、HTTPリクエストを行うホストとしてVonage Sandboxを使うように指示する必要があります。以下のコードを

// initialize a new Vonage instance, with ENV variables/keys
const vonage = new Vonage(
  {
    apiKey: process.env.API_KEY,
    apiSecret: process.env.API_SECRET,
    applicationId: process.env.APP_ID,
    privateKey: './private.key'
  },
  {
    apiHost: 'https://messages-sandbox.nexmo.com/',
  }
 );

次に、このVonageオブジェクトを使って、POSTリクエストを /inboundルートに POST リクエストを送信します: typeそして text.

// Basic Sandbox Messaging
app.post('/inbound', (req, res) => {
  vonage.channel.send(
    req.body.from,
    req.body.to,
    {
      content: {
        type: 'text',
        text: 'You must be hungry! 🍕'
      },
    },
    (err, data) => {
      if(err){
        console.log(err);
      } else{
          console.log(data.message_uuid);
      }
    }
  );
  res.send('ok');
});

app.post('/status', (req, res) => {
  res.send('ok');
});

そこで、ngrokサーバーとは別の2つ目のターミナル・ウィンドウで、Expressサーバーを起動する必要がある:

$ node index.js

フェイスブックのボットとも交流できる!

Basic Facebook SandBox MessageBasic Facebook SandBox Message

Wolt APIからレストラン情報を受け取る

HTTPリクエストの作成

エンドポイントを使えば https://restaurant-api.wolt.com/v3/venues/slug/{restaurant}エンドポイントを使用すると、レストランに関するあらゆる情報を受け取ることができます。返されるJSONは以下のようになる:

Wolt Returned JSONWolt Returned JSON

インデックス・ゼロの内部には nameというプロパティがある。インデックス・ゼロの nameというブール値である。 onlineと呼ばれるブール値である。つまり、レストランの名前を受け取り、Woltからレストランオブジェクトを返す関数を作ることができる:

// call Wolt API for restaurant info
const getRestaurant = async (reqRestaurant) => {
  const response = await got.get(`https://restaurant-api.wolt.com/v3/venues/slug/${reqRestaurant}`)
      .json();
  return response.results[0];
}

受け取ったレストランがオンラインかどうかを確認する

プロパティ onlineプロパティを使って restaurantオブジェクト内のプロパティを使用して、ユーザーに送信するメッセージを決定するロジックを作成します。次のような関数を書きます:

const firstStatusCheck = (restaurant, recipient) => {
  if (restaurant.online) {
    sendFacebookMessage(`Hey, ${restaurant.name[0].value} is now accepting orders!!`, recipient);
  } else {
	  sendFacebookMessage(`Sorry, ${restaurant.name[0].value} is currently offline. I'll ping you when it's open again!`, recipient);
    }
}

この firstStatusCheck関数は、Facebookメッセージを送信するためのVonageコードを抽象化して sendFacebookMessage.この関数は、2つのパラメータ textrecipient.

定数変数 SENDERを使うことができます。まず、それを宣言します。

let SENDER;

そして reqエンドポイントから /inboundエンドポイントから

app.post('/inbound', async(req, res) => {
  SENDER = req.body.from;

これで sendFacebookMessageはこのようになるはずだ:

const sendFacebookMessage = async (text, recipient) => {
  vonage.channel.send(
    SENDER,
    recipient,
    {
      content: {
        type: 'text',
        text: text,
      },
    },
    (err, data) => {
      if (err) {
        console.log(err);
      } else {
        console.log(data.message_uuid);
      }
     }
   );
 }

レストランの状況に応じてユーザーにメッセージを送る

新しい機能を組み合わせることで、シンプルなサンドボックスメッセージングを更新し、リクエストされたレストランが現在オンラインかどうかをユーザーに伝えることができます。

// Enhanced Sandbox Messaging
app.post('/inbound', async(req, res) => {
	SENDER = req.body.from;
	const recipient = await req.body.to;
	const requestedRestaurant = await req.body.message.content.text.split('/').pop();
	const restaurant = await getRestaurant(requestedRestaurant);
	firstStatusCheck(restaurant, recipient);
	res.send('ok');
});

レストランがオフラインの場合のループ

レストランのステータスに基づいたロジックを作成したので、最終的にレストランがオンラインに戻るまでそのステータスをチェックし続けたい。そこで、擬似コードの最後の3つのステップを作成する必要がある:

  1. レストランがオフラインの場合、オフラインレストランのリストに追加する。

  2. オフライン・レストランのリストにステータスの変化がないか継続的にチェックする。

  3. レストランがオンラインになった場合、ユーザーにメッセージを送り、オフラインレストランのリストから削除する。

オフラインレストランのインメモリデータベースの作成

この時点で LokiJSライブラリを使用します。 LokiJSはインメモリデータベースで、リクエストされた各レストランをシンプルなランタイム方式で追跡することができる。MongoDBを使ったことがある人なら、LokiJSはとても見慣れたものに見えるだろう。

まず、Lokiを他の依存関係と一緒にインクルードする必要がある:

const loki = require('lokijs');

次に、データベースをインスタンス化する必要がある:

let db = new loki("restaurants.db");
let restaurants = db.addCollection("restaurants");

各レストランエントリーは4つのデータポイントを含む:名前、オンラインステータス、受信者、スラッグ。 Nameはレストランの名前です。 Online statusはレストランが現在オンラインかどうかのブール値です。 RecipientはMessages APIからのユーザー情報であり、誰が通知を受ける必要があるのかを追跡することができる。そして最後に slugはWolt APIがレストランを見つけるために使用するURLの末尾である。

データベースができたので、オフラインのレストランを追加していきましょう!オフラインリストにレストランを追加するには以下の関数を使います:

 const addRestaurantToDb = (restaurant, recipient) => {
  restaurants.insert({name: restaurant.name[0].value, online: restaurant.online, recipient: recipient, slug: restaurant.slug});
 }

オフラインリストにレストランを追加するために firstStatusCheckを更新する必要があります。

// Check initially whether restaurant is online or should it be added to list of offline restaurants to check
const firstStatusCheck = (restaurant, recipient) => {
  if (restaurant.online) {
    sendFacebookMessage(`Hey, ${restaurant.name[0].value} is now accepting orders!!`, recipient);
  } else {
      sendFacebookMessage(`Sorry, ${restaurant.name[0].value} is currently offline. I'll ping you when it's open again!`, recipient);
      addRestaurantToDb(restaurant, recipient);
    }
}

ステータスの変化を継続的にチェック

オフラインのレストランのリストができたので、オンラインに戻ったかどうかをチェックしたい。これを定期的かつ継続的に行いたいので、組み込みの setInterval関数を使います:

setInterval(function(){offlineRestaurantLookup(req)} , INTERVAL);

定数 INTERVAL定数は、setIntervalに関数を実行する頻度を指示する。 offlineRestaurantLookup関数を実行する頻度を指定する。ファイルの一番上の SENDER.デフォルトでは、60秒ごとにチェックする:

const INTERVAL = 60000;

を実行します。 offlineRestaurantLookupはオフラインデータベースにあるすべてのレストランを取得し、各レストランについて、そのレストランがまだオフラインであるかどうかをチェックします。

const offlineRestaurantLookup = async () => {
  let offlineRestaurants = restaurants.data;
  offlineRestaurants.forEach(await checkIsStill0ffline);
}

この checkIsStill0ffline関数はレストランがオンラインかどうかをチェックする。レストランがオンラインになった場合、正しいユーザーにメッセージを送り、オフラインレストランのリストからそのレストランを削除します。

const checkIsStill0ffline = async (restaurant) => {
  const checkedRestaurant = await getRestaurant(restaurant.slug);
  if (checkedRestaurant.online) {
    sendFacebookMessage(`Hey, ${restaurant.name} is now accepting orders!!`, restaurant.recipient);
    restaurants.chain().find({'name': restaurant.name}).remove();
  }
}

これで setInterval機能を追加することができます:

// Enhanced Sandbox Messaging
app.post('/inbound', async(req, res) => {
  SENDER = req.body.from;
  const recipient = await req.body.to;
  const requestedRestaurant = await req.body.message.content.text.split('/').pop();
  const restaurant = await getRestaurant(requestedRestaurant);
  firstStatusCheck(restaurant, recipient);
  res.send('ok');
});

app.post('/status', (req, res) => {
  res.send('ok');
});

setInterval(function(){offlineRestaurantLookup()} , INTERVAL);

app.listen(3000);

レストランがオフラインになるとそのことを知らせるメッセージが届き、レストランがオンラインになると新しいステータスに更新される。午前中にこのアプリを試して、レストランが突然ランチのためにオープンするのを見ることをお勧めする。Facebook Messengerからのプッシュ通知が携帯に届くのも面白い!

Enhanced Facebook SandBox MessageEnhanced Facebook SandBox Message

次の記事

  • このチュートリアルではMessages APIのFacebook Messenger機能を使ったが、このアプリケーションを拡張してWhatsAppやSMSを使ったオムニチャネル機能を提供することもできる。とても緊急なユースケース(私は土曜日の朝にある特定のベーグル屋さんを思い浮かべている)を想像して、ステータスの変化を即座に通知して欲しい。

  • このコードを拡張して、配達のスケジュールや、レストランに近いユーザーなどに基づいて、アラートをよりスマートにすることもできる。また、複数の仕事を抱えることもできる。

  • サンドボックスからアプリを取り出し、Facebookのビジネスアカウントに接続することができます。

チュートリアルの最終的なコードは GitHub.Vonage Messages API を使って作ったものをぜひ教えてください!私たちの コミュニティ・スラックで会話に参加し、あなたのストーリーを共有してください!

シェア:

https://a.storyblok.com/f/270183/384x384/e4e7d1452e/benjamin-aronov.png
Benjamin Aronovデベロッパー・アドボケイト

Benjamin AronovはVonageの開発者支援者です。彼はRuby on Railsのバックグラウンドを持つ実績のあるコミュニティ・ビルダーです。Benjaminは故郷であるテルアビブのビーチを楽しんでいる。テルアビブを拠点に、世界最高のスタートアップの創設者たちと出会い、学ぶことができる。技術以外では、完璧なパン・オ・ショコラを求めて世界中を旅するのが好き。