注意!這篇文章是舊版的,新版文章請參考「以 Bottender v1 撰寫簡單的卡米狗」。
在台灣只要講到 LINE Chatbot,就不能不提到最有代表性的卡米狗。
剛好在前一陣子卡米狗的開發者寫了一個很完整的系列 — 「只要有心,人人都可以做卡米狗」,可以讓我們稍微了解卡米狗背後運作的模式。
Ruby 是很優雅的語言,Rails 也是功能完善且強大的框架,但在這篇文章中,我們會介紹另一種實作卡米狗功能的方式:使用我們公司所開發的 Chatbot 專用框架 — Bottender,來作為給 JavaScript 開發者的參考。
前置準備
準備開發帳號、開啟 Messaging API、加入群組功能等等細節就不在這細述,有需要請參閱卡米狗以及 Bottender 的 LINE 教學。
開啟一個 Bottender 的專案
如果你的電腦上的 npm 版本 >=5.2.0
,可以直接使用內建的 npx
來進行初始化專案:
npx bottender init
或是安裝到全域再來執行也可以:
npm install -g bottender
bottender init
需要注意的點:
- 在互動式的準備過程中,要選擇
line
作為 platform。 - 必須把之前準備好的
accessToken
以及channelSecret
填入bottender.config.js
。
建立 Database
為了不讓資料庫的概念跟種類模糊了這篇文章的焦點,我們直接使用 In-Memory 的方式來處理,宣告一個常數來儲存:
const db = {
map: {},
};
備註:因為沒有持久化,伺服器重啟之後資料被重置純屬正常現象。
實作功能
卡米狗學說話
首先我們要實做的就是第一重要的功能,教卡米狗說話,指令如下:
卡米狗學說話;關鍵字;要回的話
例如:
卡米狗學說話;紅茶拿鐵;拿的動嗎?
依照這個語法,我們準備了還算堪用的正規表達式 /^卡米狗學說話;([^;]+);([^;]+)$/
來判斷這個語句,並可以把擷取的鍵值存進 Database:
const { text } = context.event;if (/^卡米狗學說話;([^;]+);([^;]+)$/.test(text)) {
// 斷開後第一個部分是「卡米狗學說話」,可以直接忽略它
const [, key, val] = text.split(';');
// 如果沒有教過就初始化
if (!db.map[key]) db.map[key] = [];
// 記錄到列表中
db.map[key].push({
sessionId: context.session.id,
keyword: key,
message: val,
});
// 這整段程式碼會放到 async function 中,所以可以等待這個 promise
await context.replyText('好哦~好哦~*1');
}
備註:為了讓後面的功能好實作,這邊用物件的方式來存,並保留
sessionId
,用session.id
不使用session.room.id
、session.group.id
可方便同時支援 Room 跟 Group,又順便支援跟單一 User 對話。
卡米狗見人說人話,見鬼說鬼話
前面已經實作完教狗說話的學習部分了,接著就是要讓牠能真的把句子說出來。跟牠說任何一句非指令的話時都必須去檢查是否有教過,有教過就必須照著回應。另一個特色是,如果這個關鍵字沒在當下的 Room 或 Group 內教過,則會採其他地方所教過的回答:
const { partition, last } = require('lodash');const { text } = context.event;const mappings = db.map[text];// 如果曾經有任何關於這個關鍵字的紀錄
if (mappings && mappings.length > 0) {
// 以 sessionId 匹配與否切分成兩個陣列
const [localMappings, globalMappings] = partition(mappings, {
sessionId: context.session.id
});
// 先取 local 裡設定的最後一個,取不到才用 global 的
const answer = last(
localMappings.length > 0 ? localMappings: globalMappings
).message;
await context.replyText(answer);
}
最後的 context.replyText
會把我們藉由前面程式所找出的字串給送出去,這樣就完成了說話的部分。
卡米狗忘記
忘記的語法如下,下了這個指令後,前面教的對應回覆會被刪除:
卡米狗忘記;關鍵字
例如:
卡米狗忘記;紅茶拿鐵
接下來我們一樣用特製的正規表達式來判斷這個忘記的語法,再把對應的記錄過濾掉:
const { text } = context.event;if (/^卡米狗忘記;([^;]+)$/.test(text)) {
// 斷開後第一個部分是「卡米狗忘記」,可以直接忽略它
const [, key] = text.split(';');
// 只過濾掉這個 session 所定義的
db.map[key] = db.map[key].filter(
mapping => mapping.sessionId !== context.session.id
);
await context.replyText('好哦~好哦~*1');
}
使用 Handler 類別
Bottender 有內建可以用來組裝規則的 Handler
類別,蠻適合用在卡米狗這樣的對話情境。例如,我們可以用 LineHandler
進行這樣的改寫:
const { LineHandler } = require('bottender');const builder = new LineHandler()
.onText(/^卡米狗學說話;([^;]+);([^;]+)$/, async context => {
// 學說話指令的程式碼
})
.onText(/^卡米狗忘記;([^;]+)$/, async context => {
// 忘記指令的程式碼
})
.onText(async context => {
// 回話的程式碼
});
bot.onEvent(builder);
這樣就讓整個程式好管理一些。
有很多方式可以整理程式碼,Handler
類別使用的 Builder 設計模式是其中很簡單明瞭的一種。
這個範例的完整程式碼都可以在 bottender-kamigo-example 找到。
跟 Bottender 或是 Chatbot 開發相關的討論除了上 GitHub 開 Issue 以外,也可以到我們的 Discord Server 跟大家一起討論。
優拓資訊 (Yoctol Info Inc.)
At YOCTOL, We AI Your Business by Bot.
優拓為新銳 AI 團隊,利用自行研發的機器人框架、自然語意理解、網路爬蟲、推薦引擎,為企業提供全方位的商務機器人解決方案,不僅可以即時回應顧客的客服需求,也能主動推播個人化商品推薦,提昇企業經營效能。
若您有相關業務需求或是任何建議、疑問,都歡迎寄信至 service@yoctol.com,我們將盡速與您聯繫,期待您的來信!