流程
-
概覽
User 透過 Messenger 傳訊息到 Facebook Server, Facebook Server 會打一個Post Request
,把event
傳給
Webhook endpoint
,在 Business Server 內邏輯判斷是什麼event
並形成request body
,
然後將打一個包括request body
的Post request
給Send API
通知 Facebook Server 回覆 User -
Component
- PSID(Page-scoped ID): 一個 User 連一個 Messenger Bot 時會被 Messenger Platform 指派一個 PSID,
這可以確保每個 User 和 Bot 的互動是一對一的關係。 - Send API:
Business Server
在收到Facebook Server
透過Webhooks
傳來的event
後,
可以透過Send API
來回傳東西去Facebook Server
,內容可以是text
、image
、file
等 - Webhook: 一個
end point
,通常是xxxsite/webhook
,用來接收Facebook Server
傳來的event
並根據其性質來看它是哪哪種webhook event
。至少會用到messages
和messaging_postbacks
這兩種 webhook events
- PSID(Page-scoped ID): 一個 User 連一個 Messenger Bot 時會被 Messenger Platform 指派一個 PSID,
-
Steps
-
Create FanPage
-
Create Facebook App
-
Add Messenger Platform to App
-
Coding
- 建立
Http server
- 加入 webhook verify token
- 自行設定
verify token
- 在接收到
GET Request
之後,驗證verify token
是否一致,若是,則subscribe
webhook 到
- 自行設定
- 加入
webhook endpoint
以接收POST Request
傳來的event
- 建立
-
Test
- 啟動: $node index.js
- 打
GET Request
以 subscribe webhook:$ curl -X GET "localhost:1337/webhook?hub.verify_token=<YOUR_VERIFY_TOKEN>&hub.challenge=CHALLENGE_ACCEPTED&hub.mode=subscribe"
- 若正確:
WEBHOOK_VERIFIED
、CHALLENGE_ACCEPTED
- 若正確:
- 打
POST Request
以測試 webhook:$ curl -H "Content-Type: application/json" -X POST "localhost:1337/webhook" -d '{"object": "page", "entry": [{"messaging": [{"message": "TEST_MESSAGE"}]}]}'
- 若正確:
TEST_MESSAGE
、EVENT RECEIVED
- 若正確:
-
Deploy
- 使用
ngrok
建立HTTPS連線
- 使用
-
Configure the webhook for app
- Callback URL: 貼上
ngrok
的URL/webhook - Verify Token: 貼上先前自訂的 Token
- 若以上兩個都正確,在點選
驗證
後,FB會發一個GET Request
確認並儲存下來,
此時應已將 app 給 subscribe 至粉專,在粉專 Messenger 已可 key 訊息
- Callback URL: 貼上
-
建立自動回覆 Bot 相關 coding
- 將環境變數設為連結粉專時取得的
Auth
,這將在打 POST Reqest 給
Send API時使用 - 建立
handleMessage
、callSendAPI
兩個 function 來建立
判別event type
及 callSend API
- 取得
PSID
- 在
handleMessage
內區別event type
是messages
還是messaging_postback
,
若是messages
則建立response
- 透過
callSendAPI
來建立request body
並利用POST request
打到
https://graph.facebook.com/v2.6/me/messages
來 triggerSend API
這邊就會須要使用粉專的Auth Token
- 將環境變數設為連結粉專時取得的
-
完成,此時FB 粉專 Bot 應可以正常 echo 訊息給 User。
-
Terminal Command
-
curl -X GET “localhost:1337/webhook?hub.verify_token=newMessenger&hub.challenge=CHALLENGE_ACCEPTED&hub.mode=subscribe”
-X
: 定義方法URI
的內容包括localhost
: domain:1337
: port 號/webhook
: path?
: 使用GET
時後面要帶出Query String
,這個問號就是用來帶Query String
用的hub.verify_token=newMessenger
: 前面hub.verify_token
是 key, 後面newMessenger
是 value&
: 分隔 Query String 用hub.challenge=CHALLENGE_ACCEPTED
: 同前述&
: 同前述hub.mode=subscribe"
: 同前述
-
curl -H “Content-Type: application/json” -X POST “localhost:1337/webhook” -d ‘{“object”: “page”, “entry”: [{“messaging”: [{“message”: “TEST_MESSAGE”}]}]}’
-H
: 定義 header- 這邊是把 content-type 定義為 json
-X
: 定義方法-d
: 定義 data-
可以透過 postman,點 body,選項選 raw
{ "object": "page", "entry": [ { "messaging": [ { "message": "TEST_MESSAGE" }] } ] }
-
可以看到最外圍是一個
object
,裡面有兩個東西,一個叫"object",一個叫"entry",裡面就一直包array和object
-
code
'use strict';
// Imports dependencies and set up http server
const
express = require('express'),
bodyParser = require('body-parser'),
app = express().use(bodyParser.json()), // creates express http server
port = 5000,
// 把原本是process.env.port (應該是要require process檔案 去指向裡面的.env,再指向以port為key的值) 直接改掉寫死 (node.js裡面的點點大概可以想成php的->)
page_access_token = 'EAAh84HrF238BAEsGvODMhMVvUbk8jT4czB99LTwM7lp2B6rt8bfaA4leyF2dU7e99VZCvSNS48eRAPzTnve8jN9Xhkz8IPJNX7IIvhJ44sUWiAHYL6CMVBa9QJ8yyKBDOvTak0Q0P4QMv9ssKD8rT72DiI8oPV16UZCJ4HZAwZDZD';
// 這邊原本是要設環境變數 後來是直接把它寫成常數放這
// Sets server port and logs message on success
app.listen(port || 1337, () => console.log('webhook is listening'));
// Creates the endpoint for our webhook
app.post('/webhook', (req, res) => {
// 這是個callback,可以想像成前面有function以及function name被省略了,而裡面有兩個參數,一個是req (request),一個是res (respond)
let body = req.body;
// 把req參數裡的body存到body變數裡
// Checks this is an event from a page subscription
if (body.object === 'page') {
// 可以想成body->object的值要等於'page'
// Iterates over each entry - there may be multiple if batched
body.entry.forEach(function(entry) {
// body->entry,並foreach出來,可以想成foreach (entry as entry) {},後面的花括號才是要做的事
// Gets the message. entry.messaging is an array, but
// will only ever contain one message, so we get index 0
let webhook_event = entry.messaging[0];
// entry.messaging裡面可能不只放一個東西(可能會放是誰傳的、何時傳的...目前的範例是只有放一個訊息),但我們只要一個,而這個是訊息,所以設參數0
console.log(webhook_event);
});
// Returns a '200 OK' response to all requests
res.status(200).send('EVENT_RECEIVED');
} else {
// Returns a '404 Not Found' if event is not from a page subscription
res.sendStatus(404);
}
});
// Adds support for GET requests to our webhook
app.get('/webhook', (req, res) => {
// 也是一樣有callback, 傳req和res進來,只是是用query string傳,傳進來後會變成array
// Your verify token. Should be a random string.
let verify_token = "newMessenger"
// Parse the query params
let mode = req.query['hub.mode'];
// 由於透過 query string 傳進來會是array,所以這邊用array的方式取值 (key是'hub.mode', value在此例是'subscribe')
let token = req.query['hub.verify_token'];
let challenge = req.query['hub.challenge'];
// Checks if a token and mode is in the query string of the request
if (mode && token) {
// Checks the mode and token sent is correct
if (mode === 'subscribe' && token === verify_token) {
// Responds with the challenge token from the request
console.log('WEBHOOK_VERIFIED');
res.status(200).send(challenge);
} else {
// Responds with '403 Forbidden' if verify tokens do not match
res.sendStatus(403);
}
}
});
- PS. 上面的
foreach
在粉專key “Test” 的話會把資料都印出來
```
{
sender: { id: '2205210249526222' },
recipient: { id: '1421661017849456' },
timestamp: 1560922272916,
message: {
mid: '6To75VlOa8MhW6BLs3xGHMiEYWEsnLoC3bTCQVJLbL8yaC7w3TRkv3e6sj86MI7gHyzOJF4uAkrxmmSczKEl-A',
seq: 0,
text: 'Test'
}
}
```