接下來幾天會介紹怎麼把前幾天在 Console Mode 做的機器人搬上像是 Messenger、LINE 這些台灣人常在使用的管道。
而今天的目標會放在 Messenger 上,首先我們要先修改之前只支援使用 Console Mode 的設定檔:
bottender.config.js 設定檔在 bottender.config.js 的設定檔裡面需要新增 messenger 的設定在 channels 裡面:
// bottender.config.js
require('dotenv').config(); // 記得 npm install dotenv
module.exports = {
//... 其他省略
channels: {
messenger: {
enabled: true,
path: '/',
pageId: process.env.MESSENGER_PAGE_ID,
accessToken: process.env.MESSENGER_ACCESS_TOKEN,
verifyToken: process.env.MESSENGER_VERIFY_TOKEN,
appId: process.env.MESSENGER_APP_ID,
appSecret: process.env.MESSENGER_APP_SECRET,
},
},
};
這邊我們都是使用 process.env (環境變數) 來避免直接把敏感資訊的字串寫在程式碼裡面,這是一個好習慣,除了可以輕易地在不修改程式的情況下改設定,更可以避免輕易的把 Commit 到 Git 裡面去,或甚至因此上傳到網路、GitHub 上。
最上方的 require('dotenv').config(),是我們打算使用 dotenv 這個套件,來直接讀取 .env 這個檔案的設定注入到 process.env,所以這些敏感的資料我們需要另外建立 .env 檔案來放:
// .env
MESSENGER_PAGE_ID=
MESSENGER_ACCESS_TOKEN=
MESSENGER_VERIFY_TOKEN=
MESSENGER_APP_ID=
MESSENGER_APP_SECRET=
(注意:如果有在用 Git,這個 .env 必須列在 .gitignore 裡面)
從這邊可以看到,我們需要五個環境變數設定:
這些東西還不用馬上填,都將在下一個步驟 - 「建立 Facebook 應用程式」之後來填入。
如果你已經很熟悉怎麼建立 Facebook 應用程式,可以直接跳過這段教學,直接填好 .env 並從下一段「建立 Webhook 開始」。
為了建立 Facebook 應用程式,我們必須到 Facebook Developer 的網頁 - http://developers.facebook.com,然後點擊「我的應用程式」> 「建立應用程式」:

建立一個新的應用程式後,要來新增 Messenger 相關的設定,所以要點擊 Messenger 的「設定」按鈕:

跳轉頁面後接著頁面往下滑,可以看到「新增或移除粉絲專頁」,點他新增專頁:
(沒有粉絲專頁則要先建立新的粉絲專頁)

授權好了之後,會看到這樣的畫面會有「Page Id」,必須填到你的 .env 檔案裡面的 MESSENGER_PAGE_ID 欄位,接著要點「產生權杖」:

產生出來這個複製出來就是「Access Token」,必須填到你的 .env 檔案裡面的 MESSENGER_ACCESS_TOKEN 欄位:

Menu「設定」進去「基本資料」那邊則可以拿到「App Id」以及「App Secret」,分別填到你的 .env 檔案裡面的 MESSENGER_APP_ID 以及 MESSENGER_APP_SECRET 欄位:

// .env
MESSENGER_PAGE_ID=你的 Page Id
MESSENGER_ACCESS_TOKEN=你的 Access Token
MESSENGER_VERIFY_TOKEN=1Q40y4m1U0t05CL
MESSENGER_APP_ID=你的 App Id
MESSENGER_APP_SECRET=你的 App Secret
至於 MESSENGER_VERIFY_TOKEN 可以隨便填一段字或是學我在 Strong Password Generator 上面產生一段比較安全的。
這樣就收集完 Facebook 這邊需要的資料囉!
當我們按照前面的教學,把 bottender.config.js 加上 channels.messenger 的設定時,這時候執行 bottender dev 將會自動地開啟 Server,並使用 ngrok 產生一個對外的 HTTPS 網址。
npx bottender dev
(注意:這次就不要加 --console 了)

為什麼需要 ngrok 呢?
因為當我們開發時在自己電腦上開啟 Server 時,我們能透過 http://localhost:port 去連到,但這樣子 Facebook 是無法連到我們的 Server 的,所以需要開 Tunnel,而且他們還能提供 Secure 的 HTTPS 連線。
這樣子的服務、工具,最有名的就是以下幾個:
在打開 ngrok 的情況下,前往 http://localhost:4040 應該能看到它的 Inspector:

這可以用來檢查 Facebook 是否有送東西過來我們的 Server。
開好了 Server 跟 ngrok 後要另外用另一個 Terminal 分頁來執行以下指令:
npx bottender messenger webhook set

這邊會從你的 bottender.config.js 把一些資訊拉出來,並把網址從 ngrok 找到,送去給 Facebook 訂閱相關事件送到 Webhook。
最後在 Messenger 上測試一下 Bot:

是不是很熟悉?這就是我們前兩天做的笑話機器人跟比特幣機器人喔。
不管是接什麼平台,這開應用程式跟設定的步驟總是最煩瑣而且又容易出錯,不過馬上就要苦盡甘來了,最難的就是這個了。
請問一下,當我想開啟 ngrok 時遇到這個 error
PS. 目前步驟已填完 .env 那些KEY
目前還沒遇過這個狀況,這樣資訊還不太夠
不好意思有沒有可能提供一下更詳細的重現步驟或 Repo,讓我們有辦法 Debug 這個部分呢?
我直接下載 ngrok 測試,發現應該是我本機連不到 ngrok server 造成 timeout 所導致 ![]()
不好意思! 又來打擾
目前
但最後一步,從 Messager 傳訊息給 BOT
BOT 卻沒收到 request
請問還有哪些地方要設定呢 (?
附上過程圖
[ngrok 開啟成功]
[webhook 設定成功]

最常見的狀況可能是沒聽到對應的 event,可以檢查一下這裡

MESSENGER_PAGE_ID 有填到理論上應該要自動設定,這個部分有填嗎?(另外想問一下 bottender 的版號)
這邊有
bottender 版本
這樣看起來很正常,收不到蠻奇怪的
另外一個我懷疑的是 Primary Receiver 的設定有沒有異常,有時候如果這個粉絲頁曾經綁過其他 App 會出一點問題
可以參考這篇檢查一下這個設定:
https://ithelp.ithome.com.tw/articles/10220717#response-312610

感謝 !! 我晚上回去試試 ![]()
設定完後,可以在 ngrok 那邊看到 POST 200 成功
但訊息依舊直接進入 inbox ![]()
不好意思! 提個問題,想要看到 BOT 回應訊息
一定要用粉絲專頁的"管理員"
自己傳訊息到自己的紛絲專頁嗎 (?
我用兩個帳號測試
傳訊息 from 管理員的帳號 => 看的到 BOT 回應
傳訊息 from 一般的帳號 => 看不到 BOT 回應,訊息會直接進入 inbox
如果你的 App 尚未調成 Public 並送審權限,那就會只有管理員或是任何被你加到測試人員的人可以測試(應該可以在 App 設定某處加測試人員)
好的感謝 !! ![]()
請問一下
要把講笑話和btc放在一起是怎麼弄的
為什麼我的指顯示講話的內容
可以貼一下你的程式碼嗎?
const random = require('random-item');
const coinranking = require('../coinranking');
module.exports = async function App(context) {
if (context.event.text === '講笑話') {
await context.sendText(
random([
'加油站最怕什麼樣的員工?油腔滑調的員工',
'有一天,西瓜、榴槤、奇異果一起出去玩,結果榴槤不見了。因為榴槤忘返',
'海記憶體知己,天涯若比鄰',
])
);
} else {
await context.sendText(random(['你講什麼鬼話?', '乖,聊聊別的']));
}
};
const coins = [
{
id: 1,
symbol: 'BTC',
name: 'Bitcoin',
},
{
id: 2,
symbol: 'ETH',
name: 'Ethereum',
},
{
id: 3,
symbol: 'XRP',
name: 'XRP',
},
];
module.exports = async function App(context) {
const { text } = context.event;
for (let c of coins) {
if (new RegExp(`(${c.symbol})|(${c.name})`, 'i').test(text)) {
const {
data: { coin },
} = await coinranking.getCoin(c.id);
await context.sendText(`${c.symbol} 現在價格是 ${coin.price} TWD`);
return;
}
}
};
我是不是寫錯地方
我搞定了哈哈抱歉,就把方法一樣寫在裡面就好了!
不好意思剛剛使用這個框架有點不習慣QQ