開始進入快速入門教學導覽之前,請務必完成下列其中一項,備妥您需要的入門程式碼。入門程式碼提供基本 Webhook,這是 Messenger 體驗的基礎。
我們的 Webhook 設定指南會引導您建構在本快速入門中全程可以使用的第一個 Webhook。
建構 Webhook自 GitHub 下載我們的 Webhook 入門程式碼,再將程式碼部署於您所選的伺服器。
下載程式碼如果您沒有可部署 Webhook 的伺服器,可以在 Glitch 上混搭我們的入門 Webhook 專案,如此可為您的 Webhook 提供透過 HTTPS 所傳送的公開網址。
若要在 Glitch 上建立自己的 Webhook,請執行以下操作:
/webhook
:
https://
開始建構第一個 Messenger 體驗之前,請先設定應用程式的憑證。
如果您尚未準備好,請按照應用程式設定指南設定 Facebook 應用程式,以利在 Messenger 開放平台使用。
在您提交應用程式且獲准能在開放 Messenger 公開使用之前,粉絲專頁權杖只開放您的粉絲專頁與申請獲准的應用程式管理員、開發人員或測試人員的 Facebook 帳號互動。
若要將這些角色授予其他 Facebook 帳號,請前往應用程式設定的「角色」頁籤。
要驗證對 Messenger 開放平台 API 提出的要求,一律要在查詢字串的 access_token
參數中加入一個粉絲專頁層級的存取權杖。
若您在設定 Facebook 應用程式時未產生粉絲專頁存取權杖,請先完成下列步驟:
建議不要將粉絲專頁存取權杖之類的敏感資訊寫死在 Webhook 中,以利保障這類資訊的安全。
若要這麼做,請在環境變數中加入下列內容,其中 <PAGE_ACCESS_TOKEN>
是您剛剛產生的存取權杖,<VERIFY_TOKEN>
則是您設定用於驗證 Webhook 的隨機字串:
PAGE_ACCESS_TOKEN="
若您使用 Glitch,請在所提供的 .env
檔案中設定環境變數,避免其他 Glitch 用戶看見。
現在您只需要加入粉絲專頁存取權杖,然後在 app.js
檔案的最上方驗證權杖,即可在 Webhook 邏輯中使用這個權杖:
const PAGE_ACCESS_TOKEN = process.env.PAGE_ACCESS_TOKEN;
const VERIFY_TOKEN = process.env.VERIFY_TOKEN;
本教學導覽將建構一個會執行下列功能的 Messenger 體驗:
剖析源自傳入 Webhook 事件的訊息和傳送者的粉絲專頁範圍編號。
處理 messages
和 messaging_postbacks
Webhook 事件。
透過傳送 API 傳送訊息。
以文字訊息回覆文字訊息。
以通用範本回覆影像附件,這個範本會使用所收到的影像。
有條件回覆回傳承載。
首先我們要完成三個函式的虛設常式,這三個函式將處理我們想要支援的傳入 Webhook 事件類型,以及透過傳送 API 回覆。要完成這個步驟,請在 app.js
檔案後方加上下列內容:
// Handles messages events
function handleMessage(sender_psid, received_message) {
}
// Handles messaging_postbacks events
function handlePostback(sender_psid, received_postback) {
}
// Sends response messages via the Send API
function callSendAPI(sender_psid, response) {
}
要在 Messenger 回覆用戶,首先需要知道對方的身分。在 Messenger 中,要知道對方的身分就要從傳入的 Webhook 事件取得訊息傳送者的粉絲專頁範圍編號 (PSID)。
當用戶開始與 Facebook 粉絲專頁展開對話時,會為每個 Facebook 粉絲專頁分配一個不重複的粉絲專頁範圍編號 (PSID)。PSID 用於識別訊息傳送者的身分。
若您完成了上方入門專案部分的其中一個選項,現在應該會有一個基本的/webhook
端點,這個端點會接受 POST
要求並記錄所收到之 Webhook 事件的內文,如下所示:
app.post('/webhook', (req, res) => {
// Parse the request body from the POST
let body = req.body;
// Check the webhook event is from a Page subscription
if (body.object === 'page') {
// Iterate over each entry - there may be multiple if batched
body.entry.forEach(function(entry) {
// Get the webhook event. entry.messaging is an array, but
// will only ever contain one event, so we get index 0
let webhook_event = entry.messaging[0];
console.log(webhook_event);
});
// Return a '200 OK' response to all events
res.status(200).send('EVENT_RECEIVED');
} else {
// Return a '404 Not Found' if event is not from a page subscription
res.sendStatus(404);
}
});
要取得傳送者的 PSID,請更新 body.entry.forEach
區塊,換上下列程式碼,自事件的 sender.id
屬性擷取 PSID:
body.entry.forEach(function(entry) {
// Gets the body of the webhook event
let webhook_event = entry.messaging[0];
console.log(webhook_event);
// Get the sender PSID
let sender_psid = webhook_event.sender.id;
console.log('Sender PSID: ' + sender_psid);
});
Sender PSID: 1254938275682919
我們希望這個體驗能夠處理兩種類型的 Webhook 事件:messages
和 messaging_postback
。事件主體不會包含事件類型的名稱,但我們可以查看特定物件屬性,以判斷名稱。
Messenger 開放平台會傳送 Webhook 事件,讓您知道在 Messenger 中發生的操作。事件傳送格式以 JSON 為準,並且會以 POST
要求的方式傳送至 Webhook。如需詳細資訊,請參閱 Webhook 事件。
若要這麼做,請更新 Webhook 的 body.entry.forEach
區塊,加註會檢查收到的事件是否包含 message
或 postback
屬性的條件。我們也會新增呼叫稍早完成虛設常式的 handleMessage()
及 handlePostback()
函式:
body.entry.forEach(function(entry) {
// Gets the body of the webhook event
let webhook_event = entry.messaging[0];
console.log(webhook_event);
// Get the sender PSID
let sender_psid = webhook_event.sender.id;
console.log('Sender PSID: ' + sender_psid);
// Check if the event is a message or postback and
// pass the event to the appropriate handler function
if (webhook_event.message) {
handleMessage(sender_psid, webhook_event.message);
} else if (webhook_event.postback) {
handlePostback(sender_psid, webhook_event.postback);
}
});
將傳入訊息分送至適當的處理函式之後,就要更新 handleMessage()
,用於處理及回覆基本文字訊息。若要這麼做,請更新程式碼以定義回應的訊息承載,然後將該承載傳至 callSendAPI()
。我們希望回覆內容採用基本文字訊息,因此要使用 "text"
屬性定義 JSON 物件:
function handleMessage(sender_psid, received_message) {
let response;
// Check if the message contains text
if (received_message.text) {
// Create the payload for a basic text message
response = {
"text": `You sent the message: "${received_message.text}". Now send me an image!`
}
}
// Sends the response message
callSendAPI(sender_psid, response);
}
現在可以使用 Messenger 開放平台的傳送 API 傳送第一則訊息了!
我們要在 handleMessage()
中呼叫 callSendAPI()
,所以現在需要更新這個程式碼,建構好整個要求內文之後再將要求傳送至 Messenger 開放平台。傳送至傳送 API 的要求有兩個屬性:
recipient
:設定預定的訊息收件者。這個時候會依 PSID 分辨收件者的身分。message
:設定待傳送訊息的詳細資料。這裡我們會將它設定為自 handleMessage()
函式傳入的訊息物件。要建構要求內文,請更新 callSendAPI()
的虛設常式:
function callSendAPI(sender_psid, response) {
// Construct the message body
let request_body = {
"recipient": {
"id": sender_psid
},
"message": response
}
}
現在我們只要提交一則 POST
要求至 https://graph.facebook.com/v2.6/me/messages
的傳送 API 並傳送訊息即可。
請注意,您必須將 PAGE_ACCESS_TOKEN
附加在網址查詢字串的 access_token
參數之後。
在本快速入門中,我們使用 Node.js 要求模組將 HTTP 要求傳回 Messenger 開放平台,但您可以使用任何 HTTP 用戶端。
要安裝要求模組,請在命令列執行 npm install request --save
,然後將下列內容新增至 app.js
最上方,完成匯入:
const request = require('request');
function callSendAPI(sender_psid, response) {
// Construct the message body
let request_body = {
"recipient": {
"id": sender_psid
},
"message": response
}
// Send the HTTP request to the Messenger Platform
request({
"uri": "https://graph.facebook.com/v2.6/me/messages",
"qs": { "access_token": process.env.PAGE_ACCESS_TOKEN },
"method": "POST",
"json": request_body
}, (err, res, body) => {
if (!err) {
console.log('message sent!')
} else {
console.error("Unable to send message:" + err);
}
});
}
我們的回應會提示訊息收件者傳送影像,因此下一步就是要更新程式碼,以利處理附件。Messenger 開放平台會自動儲存已傳送的附件,並且透過 attachments
陣列中每一個索引的 payload.url
屬性公開,所以我們也會從事件擷取這一項。
要判斷訊息是否為附件,請更新 handleMessage()
函式中的條件,用於檢查 attachments
屬性的 received_message
,接著擷取其所需網址。在實際的 Bot(機器人程式)中,我們會反覆測試陣列並檢查多個附件,但在本快速入門中,我們只會取得第一個附件。
function handleMessage(sender_psid, received_message) {
let response;
// Checks if the message contains text
if (received_message.text) {
// Creates the payload for a basic text message, which
// will be added to the body of our request to the Send API
response = {
"text": `You sent the message: "${received_message.text}". Now send me an attachment!`
}
} else if (received_message.attachments) {
// Gets the URL of the message attachment
let attachment_url = received_message.attachments[0].payload.url;
}
// Sends the response message
callSendAPI(sender_psid, response);
}
接下來我們要以通用範本訊息回覆影像。通用範本是最常用且結構化訊息類型,支援您在同一則訊息中傳送影像、文字和按鈕。
訊息範本需在訊息的 attachment
屬性中定義,其中包含 type
和 payload
屬性。我們要在 payload
中的下列屬性設定通用範本的詳細資料:
template_type
:設定訊息所用範本的類型。我們要使用通用範本,所以這個值是「generic」。elements
:設定範本的自訂屬性。我們會為通用範本指定一個標題、副標題、影像,以及兩個回傳按鈕。結構化訊息要使用 attachment_url
,這是我們所收到的 image_url
,用於在範本中顯示,另外也會包含幾個回傳按鈕,方便訊息收件者回覆。要建構訊息承載並傳送通用範本,請將 handleMessage()
更新為下列內容:
function handleMessage(sender_psid, received_message) {
let response;
// Checks if the message contains text
if (received_message.text) {
// Create the payload for a basic text message, which
// will be added to the body of our request to the Send API
response = {
"text": `You sent the message: "${received_message.text}". Now send me an attachment!`
}
} else if (received_message.attachments) {
// Get the URL of the message attachment
let attachment_url = received_message.attachments[0].payload.url;
response = {
"attachment": {
"type": "template",
"payload": {
"template_type": "generic",
"elements": [{
"title": "Is this the right picture?",
"subtitle": "Tap a button to answer.",
"image_url": attachment_url,
"buttons": [
{
"type": "postback",
"title": "Yes!",
"payload": "yes",
},
{
"type": "postback",
"title": "No!",
"payload": "no",
}
],
}]
}
}
}
}
// Send the response message
callSendAPI(sender_psid, response);
}
最後一步是要處理訊息收件者點選通用範本的其中一個回傳按鈕時會傳送的 messaging_postbacks
Webhook 事件。
回傳按鈕會傳送一個 messaging_postbacks
Webhook 事件到 Webhook,其中包含一個自訂字串,其 payload
屬性包含的字元不超出 1,000 個。這項功能支援您輕鬆實作不同的回傳承載,以利剖析及回覆特定行為。
由於我們的通用範本提供兩個回傳按鈕供訊息收件者選擇,因此我們會根據回傳事件的 payload
屬性值回覆。若要這麼做,請將 handlePostback()
虛設常式更新為以下內容:
function handlePostback(sender_psid, received_postback) {
let response;
// Get the payload for the postback
let payload = received_postback.payload;
// Set the response based on the postback payload
if (payload === 'yes') {
response = { "text": "Thanks!" }
} else if (payload === 'no') {
response = { "text": "Oops, try sending another image." }
}
// Send the message to acknowledge the postback
callSendAPI(sender_psid, response);
}