iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 3
2

初期在打造 Bottender 時,經過累積了非常多的實戰經驗並參考了許多不同領域的概念,漸漸地衍伸出許多具體的概念與模組。了解這些概念與設計模式後,就能充份的去利用這個框架所提供的彈性,並依照自己所需的方式去使用、繼承、修改或實作對應的介面。

而最重要的概念莫過於這些:

  • State、Event
  • Session、SessionStore
  • Context
  • Connector
  • CLI

都將於此篇之中介紹,而只要了解這些就基本上已經可以說是初步掌握 Bottender 這個框架了!

(注意:如果覺得這篇的內容太過艱澀或讀起來有障礙,請不要感到沮喪,直接跳到下一篇感受一下範例吧!)

State、Event

之所以會把這兩個概念放在第一個位置,是因為 Bot 所有的行為、理解力的根源都來自於此。你可以想像一個人的行為或對話模式也可以概略地用這兩個元素去描述,我在這邊舉個實際例子。

如果我處於一個剛吃飽的狀態,而且朋友問我要不要吃飯,那 State、Event 分別是這樣:

State - 我剛吃完飯
Event - 朋友問我要不要吃飯

如此一來,我可能會做出「不要,我剛吃完,你自己去吃」的回應:

State + Event => 不要,我剛吃完,你自己去吃

但如果我處於一個肚子很餓的狀態,而且朋友問我要不要吃飯,那 State、Event 分別是這樣:

State - 我肚子很餓
Event - 朋友問我要不要吃飯

這個情況下,我可能就做出「走啊,吃飯」的回應:

State + Event => 走啊,吃飯

所以說,定義下面這個 Function 幾乎等於是定義了這整個 Bot 的行為

(State, Event) => Action

這是不是跟前端工程師寫 React 的感覺很類似呢?

(State, Props) => HTML

沒錯!這樣去定義可以擁有 Declarative(宣告式)的一些優點。
了解了這些,未來要用 Functional Composition 或是 Finite State Machine (有限狀態機) 的方式來整理都不是問題。

而 event 可以在 context 上直接取用,用來判斷現在發生了怎樣的事件:

// 判斷是否是收到文字訊息
context.event.isText // true

// 取得使用者傳過來的文字
context.event.text // '要不要吃飯'

至於 state 則是使用跟 React 很類似的 API:

// 取得現在肚子餓的狀態
context.state.肚子餓 // true

// 設定現在肚子餓的狀態為 false
context.setState({
  肚子餓: false,
});

(注意一:state 是跟隨在 session 上的,每個 session 的 state 會是獨立的)
(注意二:類似於前端的 state,它可能會過期重來,所以要持久化的東西還是應該要另外保存在 Database)

Session、SessionStore

Session 則是一個 Bot 與 User 之間的一個互動狀態,可以設定過期的時間,這樣一來就可以做到一定時間內沒對話就當作是新的對話開始,上面講到的 State 就是保存在 Session 上。Bot 跟 Server 的 Session 雖然不是同一個東西,但它們的實作跟概念都非常類似。

而 SessionStore 則是儲存 Session 的地方,目前 Bottender 內建提供了四種不同的儲存方式:

  • memory
  • file
  • redis
  • mongo

可透過調整 bottender.config.js 設定檔切換 session.driver,來決定要把 Session 存在哪裡:

// bottender.config.js

module.exports = {
  session: {
    driver: 'file', // 調整這個,而這個當然也可以是利用 Condition 的變數
    stores: {
      memory: {
        maxSize: 500,
      },
      file: {
        dirname: '.session',
      },
      redis: {
        port: 6379,
        host: '127.0.0.1',
        password: 'auth',
        db: 0,
      },
    },
  },
};

(注意:也能在開發跟上線時使用不同的 SessionStore,使用 memory 可以讓開發非常方便)

Context

前面在講 Event、State 時,大家可能已經注意到我們是在 Context 上取用這兩個東西:

context.event
context.state

Context 代表一個 Event 發生時,我們當下要處理它的整個情境。除了收到的 Event 外,我們還可以用來觸發回覆訊息等等的功能,例如我們前一篇已經示範過的 context.sendText

await context.sendText('Welcome to Bottender'); 

而根據平台不同(例如,Messenger 或 LINE),你拿到的 Context 也會有很大的不同,各自可以使用不同的回覆功能,而這些功能的底層,則是透過我們專門串接 API 的套件 - messaging-apis 處理。

Connector

Connector 是用來定義 Bottender 要怎樣把平台發過來的 HTTP 請求,轉成可以處理的 Event 以及 Context,這其中包括不少複雜繁瑣的細節,例如要讓平台驗證 Webhook 的步驟、要利用 Signature 來防止偽造的攻擊等等。

而現在 Bottender 支援下面六種 Connector:

  • Console
  • Messenger
  • LINE
  • Slack
  • Telegram
  • Viber

未來肯定會繼續的支援更多的平台、WebChat,這都必須要靠 Connector。還有關於如何自定義 Connector 的部分也有可能會寫一篇來介紹。

CLI

除了 bottender startbottender dev 等等指令以外,CLI (Command Line Interface) 還有實作了一系列能幫助開發體驗的功能。只要是需要開發者開發或上線時額外執行的功能就會放在這個裡面,例如,如果想要幫 Messenger 設定 Webhook,可以使用這個之後在 Messenger 章節會講到的指令:

npx bottender messenger webhook set

(注意:加上 --help Options 可以顯示一些使用說明)

結語

概念介紹雖然有點類似名詞解釋,但說穿了 Bottender 的核心概念只有這些並不複雜,雖說各平台的功能各有其複雜之處,但學完了這篇的內容也大致能達到「Learn Once, Write Anywhere」的效果!


上一篇
Day 02:Bot 開發起手式
下一篇
Day 04:Console Mode 還能怎麼玩,開發與 Debug 技巧
系列文
使用 Modern Web 技術來打造 Chat App30

尚未有邦友留言

立即登入留言