iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 15
1
Modern Web

使用 Modern Web 技術來打造 Chat App系列 第 15

Day 15:機器人亂入下的「多人對話」、「群組聊天」

今天颱風天,來聊個輕鬆一點的話題 - 「多人對話」。前面十四天的內容,大多是在描述機器人怎麼跟單一使用者在私訊中完成對話,不過我們必須知道,除了「私訊」外,存在了二十年以上的「聊天室」、「論壇」、「留言板」,其實都算是多人對話的一種展現。當然裡面的內容可能最早只有文字,後來進化到了有圖片、貼圖、可以附加檔案上去。而 Email 也是,有可能你只發信給一個人,也有可能這個 Loop 中有很多不同的人有參與到。

接下來,來講講在平台運用上,每個平台有怎樣的不同。

Messenger 算是一直以來都沒有開放機器人加入群組、多人對話,應該也是有他們自己的考量、策略。

LINE 有開放讓機器人加入群組,而它的群組分成「Group」跟「Room」兩種,Group 就是我們一般所認知的群組,有個群組名稱,而 Room 就是由一人以上對話就會形成,而不需要有名字。不過在 Group 跟 Room 中最多都只能邀請一隻機器人進去,應該是要避免兩隻以上的機器人互相對話沒完沒了。目前在 LINE 上面,已經有不少在群組中更能發揮的機器人了,例如:「卡米狗」就是一個適合在群組中回話的機器人。

Slack 原本就是從工作頻道起家,因此許多能安裝的生產力工具 App,就是執行在多人的頻道,而他們也有分成公開的頻道「Channel」、私聊的頻道「Group」、多人私訊「MPIM」等多種群聊的類型。

接下來讓我們來看一個範例吧!

範例 - 揪團 Bot

我們公司從三年前到現在一直都有在 Slack 上用機器人訂飲料的習慣,因此之前公司的同事有寫了一篇 - 「輕鬆揪團不求人!在 LINE 上打造屬於你的 Chatbot 揪團小幫手」把一個簡化的版本搬上 LINE,而我這邊要來教一個用 Bottender v1.0 來寫的超簡化版本(因為不希望很多錯誤檢查什麼的模糊了焦點)。

https://ithelp.ithome.com.tw/upload/images/20190930/20103630HTAvddDPxF.png

基本設定

首先當然是繼續用「Create Bottender App」選擇 line 然後建立一個新專案,如果不了解的可以「用「Create Bottender App」來 Create Bottender App」看這篇文章。

接著要去 LINE 的 Channel 設定後台修改設定,允許機器人加入群組:

https://ithelp.ithome.com.tw/upload/images/20190930/20103630nolhfU4kef.png

https://ithelp.ithome.com.tw/upload/images/20190930/20103630HECRgiBGkc.png

必須把 「Allow bot to join group chats」設定成 Enabled,並「接受邀請加入群組或多人聊天室」。

然後要在 bottender.config.js 設定好我們這個範例要用的初始狀態:

// bottender.config.js
module.exports = {
  initialState: {
    ordering: false,
    host: '',
    orders: [],
  },
};
  • ordering 代表是否正在開團
  • host 代表開團的人
  • orders 代表已點的餐

開團和截止

首先我們先把這個規格釐清:

  • 未開團的情況
    • 可以下「開團」指令
  • 開團的情況
    • 可以利用「我要XXX」的句型來點餐
    • 可以下「截止」指令

接下來就先來寫這個主程式的部分:

// index.js

function 開團() { /* 先留空 */ }
function 截止() { /* 先留空 */ }
function 下訂單() { /* 先留空 */ }

// 沒開團的狀態下,輸入「開團」可以開團
function 處理未開團指令(context) {
  if (context.event.text === '開團') {
    return 開團;
  }
}

// 已開團的狀態下,有兩種指令可以用
function 處理開團中指令(context) {
  const { text } = context.event.text;
  if (/^我也?要點?(.*)/.test(text)) {
    return 下訂單;
  }
  if (text === '截止') {
    return 截止;
  }
}
  
// 按照 state 決定現在的狀態要用哪個子 function
module.exports = function App() {
  if (context.state.ordering) {
    return 處理開團中指令;
  } 
  return 處理未開團指令;
};

這邊有幾個重點:

  1. 接下來才要實作的「開團」、「下訂單」、「截止」function 先留空的
  2. 進來之後,先用 context.state.ordering 狀態來分流,分到「處理開團中指令」、「處理未開團指令」,這種類似狀態機的處理方式之後會再寫文章講
  3. 「處理未開團指令」只處理「開團」,其他指令都不回應
  4. 「處理開團中指令」處理「我要Xxxx」、「截止」,根據正規表達式的寫法這邊能有很多變化,而這種多個指令的分流之後會寫 Router 相關的文章來說

這樣就算是把整個 Bot 的結構處理好,其他就是小事了~

開團

開團我們要做的只是把狀態設定好,ordering 設定為 true 表示開團中,host 則是拿 LINE 所提供的 User,接著就可以回一句話到頻道通知大家來點餐:

function 開團(context) {
  const { user } = context.session;
 
  // 設定為開團 state
  context.setState({
    ordering: true,
    host: user,
  });
  
  await context.sendText(`${user.displayName} 開團囉! 大家快來點!`);
}

下訂單

下訂單就是這其中最關鍵的部分了,用正規表達式把點的東西抓出來,塞進 State 中,而一樣講一句話告訴使用者機器人有收到:

function 下訂單(context) {
  const { displayName } = context.session.user;
  const [, item] = context.event.text.match(/^我也?要點?(.*)/);

  // 把訂單塞進 state 的 orders 中
  context.setState({
    orders: [
      ...context.state.orders,
      {
        name: displayName,
        order: item.trim(),
      },
    ],
  });
    
  await context.sendText(`我知道 ${displayName} 你點的是 ${order}`);
}

截止

function 截止(context) { 
  await context.sendText('截止囉!');
  
  // 因為怕這個地方寫太多會搞錯重點
  // 這邊就請各位自己想想看要怎麼把 orders 用好看的格式印出來吧,
  await context.sendText(`訂單:${context.state.orders}....`);
  
  // 把 state 重設
  context.resetState();
};

最後請記得要呼叫 context.resetState 來清掉所有狀態,避免之後再使用的時候留下了什麼這次點的內容。

結語

執行在群組裡面的 Bot 雖然寫起來更加複雜,不過有一些多人一起常見的需求確實很適合用多人對話機器人來滿足,例如:點餐、投票、抽籤、分錢等等。


上一篇
Day 14:彈性的極致 - Imagemap 與 Flex
下一篇
Day 16:對話式 App 的必要之惡 - 「Webview」
系列文
使用 Modern Web 技術來打造 Chat App30

尚未有邦友留言

立即登入留言