LINE

BOT 應用正持續發展中,金融、線上購物、計程車叫車等很多領域都已提供商用服務,現在就來學習 LINE BOT 開發。

從 2016 起,Facebook、Microsoft 等大廠都提供了聊天機器人 bot 的相關方案,在台灣使用度極高的 LINE 也在 2016 上半年提供 BOT API。LINE 繼一開始的 LINE BOT API 後,又在 2016 年底推出了新版的 API 叫 Messaging API,而原本的 BOT API 則已標示棄用。

LINE Messaging API 簡介

LINE 將 BOT API 改為 Messaging API 是明確合理的。因為有了 bot 這個名稱後會讓人想到它有提供語意分析或其它 AI 功能,但它沒有。它單純提供訊息對談的串接,訊息接收後的處理及回應,都由開發的服務負責。

基本架構及資料流程

服務提供者可透過 Messaging API 將自己的服務內容串聯到 LINE@ 上。對這個 LINE@ 的好友來說,即可透過 LINE 聊天介面與該 LINE@ 帳號後的服務進行雙向互動,並操作各式服務提供者做好的線上服務,享用完整、一致的使用者體驗。

如下圖:

架構

Service 和 Messaging API 間以 HTTPS POST 交換 JSON 資料的方式做串接,簡單講就是字串來字串回,所以可利用各種 Web 技術實作 Messaging Service,Service 的網址就叫 WebHook。對 .NET 的開發者來說,最合適的技術就是 ASP.NET Web API

Messaging API 的三大功能

可加入群組聊天

串聯 Messaging API 的 LINE@ 帳號(聊天機器人) 可以被好友邀請至私人 LINE 聊天群組中,群組中的好友們即可與該機器人對話互動,在聊天室中直接完成例如景點推薦或餐廳查詢訂位等服務。

群組聊天

多樣的訊息格式

除了基本的文字及圖片影片對話外,Messaging API 還提供多樣的訊息格式:

  • 確認 - Confirm
  • 按鍵 - Button
  • 橫向捲軸 - Carousel
  • 影像地圖 - Imagemap

在好友與服務的互動過程中,透過這些訊息格式將可更精確地獲知好友的需求,讓訊息溝通更視覺化也更加順暢。

訊息格式

Push API 與 Reply API

Messaging API 提供 Push 與 Reply 兩種傳送訊息給好友的 API:

  • Push API:可以利用 UserId 在任何時間點對好友傳送訊息 - 主動通知
  • Reply API:針對 LINE 好友傳來的訊息進行回覆,不用先儲存 UserId - 被動回覆

Push API 與 Reply API

環境準備

申請帳號

Messaging API 或 Developer Trial

進到 LINE BUSINESS CENTER 的 Messaging API 專頁,最上面就是 Messaging API 或 Developer Trial 的申請入口,在這裡要2選1,決定申請的 API 方案類型:Messaging API 是正式服務的方案組合;Developer Trial 是開發者方案,申請的 LINE@ 帳號一旦綁定,就不能更換。
Messaging API 有多種費用方案 Developer Trial 則是免費但好友上限為50人,在同為免費的條件下,Messaging API 不能使用 Push API,所以建議,開發測試時請選用 Developer Trial 方案。

申請 Developer Trial 帳號

請參考官方部落格說明 - 【功能介紹】申請Developer Trial帳號

啟用 Webhook 傳訊

Webhook URL 即是自行開發的 Messaging Service 的網址,要啟用 Webhook 傳訊功能,好友的訊息才會串到我們的服務:

  • Bot設定 中,將 Webhooko傳訊 設為 允許
  • 開發者頁面第一個選項 Basic information 中設定 Webhook URL

注意,Webhook URL 必須是走 https 的對外開放網址,所以選擇適當主機來發佈服務很重要,對 .NET 開發者來說最簡單的方式應該是丟上 Azure,但支援 SSL 的方案是要收費的;不然就是自行架設主機並申請像是 Let’s Encrypt 的免費憑證來用。

取得 Channel Secret 及 Channel Token

從開發者頁面中記錄下 Channel Secret 及 Channel Token,之後要設定在程式中做為資料驗證及訊息發送之用。

SDK:專案代碼 line-bot-sdk-csharp

Messaging API 是以 JSON 格式做資料交互,官方現在有提供七種程式語言的 SDK 方便開發者使用。

但是沒有 .NET 版本!

.NET 開發者只能自立自強。

所幸 NuGet 上有數個程式庫提供 LINE bot SDK,這裡我選用了 line-bot-sdk-csharp 這個庫,原因是它 open source 並有提供 Visual Studio 的專案範本,可以直接用來建立專案或是當作文件參考,其它有些專案都沒有文件或範例,要自己花點時間了解。

準備專案

這裡會手動建立一個最基本的 Web API 專案來做示範。
也可利用 Web API 專案範本以加速專案的建立,但專案會比較肥一些。

建立空白 Web 專案

開啟 Visual Studio 2015,新增專案,選擇 ASP.NET Web Application (.NET Framework),選擇 空白,確定後建立專案。

加入 Web API 功能

在 套件管理器主控台 新增以下套件:

1
2
Install-Package Microsoft.Owin.Host.SystemWeb
Install-Package Microsoft.AspNet.WebApi.Owin

啟用 Web API

加入 OWIN 啟動類別,增加以下內容以啟用 Web API

1
2
3
4
5
6
7
8
9
10
11
public class Startup
{
public void Configuration(IAppBuilder app)
{
// Wire Web API
var httpConfiguration = new HttpConfiguration();
httpConfiguration.MapHttpAttributeRoutes();

app.UseWebApi(httpConfiguration);
}
}

建立 Controller 及 Action

新增 Controllers 目錄,在目錄下加入 控制器 選擇 Web API 2 控制器 - 空白,可取名
LineMessagesSampleController

在 class 上加上 [RoutePrefix("line")] 指定 route 路徑名稱為 “line”。再加上 Post action 來處理 LINE Server 的呼叫,如下:

1
2
3
4
5
6
7
8
9
[RoutePrefix("line")]
public class LineMessagesSampleController : ApiController
{
[Route]
public async Task<HttpResponseMessage> Post(HttpRequestMessage request)
{
return Request.CreateResponse(HttpStatusCode.OK, "Hello LINE BOT");
}
}

http://localhost:[port]/line 做 POST 測試以確認它可以正常執行。

安裝 line-bot-sdk-csharp 套件

在 套件管理器主控台 新增以下套件:

1
Install-Package LineMessagingAPI.CSharp

Messaging API 的基礎

官方文件:Messaging API Reference
文件中有各種訊息的 JSON 範例及官方 SDK 使用說明,很重要。

接收使用者活動訊息並做回應

當有人對 LINE@ 帳號做操作時,如加為好友或傳送訊息,就會收到一串 POST 過來的 JSON 內容。
LINE 可能會在一組訊息中送來多個 event 資訊,所以用陣列方式表達,後續就是取得裡面的 events 做處理。

透過 SDK 把 request 的內容轉為 Activity 物件

1
2
var content = await request.Content.ReadAsStringAsync();
Activity activity = JsonConvert.DeserializeObject<Activity>(content);

request 原始內容範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"events": [
{
"replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
"type": "message",
"timestamp": 1462629479859,
"source": {
"type": "user",
"userId": "U206d25c2ea6bd87c17655609a1c37cb8"
},
"message": {
"id": "325708",
"type": "text",
"text": "Hello, world"
}
}
]
}

驗證內容

在正式處理前,可以對 POST 過來的資料做驗證,確定真的是由 LINE 平台所丟來的。

LINE 會在 http header 寫入 X-LINE-ChannelSignature ,用收到的 request 內容用 Channel Secret 做雜湊,比較兩個值是否一樣,就可以驗證內容是否真的來自於 LINE 平台。

1
2
3
4
5
6
var hmac = new HMACSHA256(Encoding.UTF8.GetBytes([ChannelSecret]));
var computeHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(await request.Content.ReadAsStringAsync()));
var contentHash = Convert.ToBase64String(computeHash);
var headerHash = Request.Headers.GetValues("X-Line-Signature").First();

return contentHash == headerHash;

對送來的 Events 做處理

建立 LineClient

SDK 提供 LineClient 這個類別方便和 Line 平台做交互。發送時要配合送出 Channel Token 以取信於 LINE。

1
private LineClient lineClient = new LineClient([ChannelToken]);

取得好友資訊

從每個 event 中的 Source 屬性可以取得好友的 UserId。透過 SDK 可以用 UserId 跟 LINE 平台取得好友的基本資料。

1
Profile profile = await lineClient.GetProfile(lineEvent.Source.UserId);

當透過群組或聊天室傳訊過來,source 的類型可以是 room 或 group,可透過 lineEvent.Source.RoomIdlineEvent.Source.GroupId 取得 Id。

如果有發送 Push message 的需求,要把 UserId 儲存起來才能在要發送時拿出來用。

依 lineEvent.Type 對 event 做處理

event 有 7 種類型,Message event 是使用者輸入的內容,其它 6 種則是使用者行為,我稱為 Action event,收到 event 後可以做後續處理再回 Message 給好友。

1
2
3
4
5
6
7
8
9
10
11
foreach (Event lineEvent in activity.Events)
{
Profile profile = await lineClient.GetProfile(lineEvent.Source.UserId);

switch (lineEvent.Type)
{
case EventType.XXXXX:
// HandleXXXXXEvent
break;
}
}

Message event 的處理

好友可以傳送 7 種類型的 message:Text、Image、Video、Audio、File、Location、Sticker,就是我們在 LINE App 中可以傳的內容。
程式中取得 message 內容後透過 SDK 先轉為 Message 物件做類型判斷。

1
2
3
4
5
6
7
case EventType.Message:
Message message = JsonConvert.DeserializeObject<Message>(lineEvent.Message.ToString());

switch (message.Type)
{
...
}

Text - 一般文字內容

透過 SDK 將 message 內容轉為 TextMessage 物件後取 Text

1
2
3
4
5
6
case MessageType.Text:
var textMessage = JsonConvert.DeserializeObject<TextMessage>(lineEvent.Message.ToString());
var content = textMessage.Text;
...(產生 replyMessage)...
Reply(replyMessage);
break;

Audio,Image,Video - 多媒體內容

Message 的 message.Id,透過 lineClient 從 LINE server 取得內容。

1
2
3
4
5
6
7
8
case MessageType.Audio:
case MessageType.Image:
case MessageType.Video:
// Get media from Line server.
Media media = await lineClient.GetContent(message.Id);
...(產生 replyMessage)...
Reply(replyMessage);
break;

Sticker - 貼圖

透過 SDK 將 message 內容轉為 StickerMessage 物件,就可取得該圖所屬的圖庫和在圖庫裡的序號。

1
2
3
4
5
6
case MessageType.Sticker:
// Get media from Line server.
var stickerMessage = JsonConvert.DeserializeObject<StickerMessage>(lineEvent.Message.ToString());
...(產生 replyMessage)...
Reply(replyMessage);
break;

Location - 位置

透過 SDK 將 message 內容轉為 LocationMessage 物件,就可取得名稱、地址、經緯度。

1
2
3
4
5
6
7
case MessageType.Location:
var locationMessage = JsonConvert.DeserializeObject<LocationMessage>(lineEvent.Message.ToString());
var latitude = locationMessage.Latitude;
var longitude = locationMessage.Longitude;
...(產生 replyMessage)...
Reply(replyMessage);
break;

傳送 Message 給好友

BOT 能傳送給好友的訊息分為 3 種。

  • 第 1 種是單純的內容,就跟從好友收到的一樣,包含 Text、Image、Video、Audio、Location、Sticker。
  • 第 2 種是 Imagemap message,跟網頁上的 image map 一樣,可在一張圖片上定義不同區域被點擊後的行為。
  • 第 3 種是 Template messages,已經有預先定義好幾個不同格式及作用 UI 範本,可以做比較好的互動以提供商務應用,現在有 Buttons、Confirm、Carousel 3 種。

Text message

文字訊息

1
replyMessage = new TextMessage([Text]);

Media (Image、Video、Audio) message

多媒體訊息產生方式都一樣,建立 Message 物件時傳入素材的網址及預覽圖的網址。

1
replyMessage = new ImageMessage("https://github.com/apple-touch-icon.png", "https://github.com/apple-touch-icon.png");

Location message

位置訊息,傳入位置相關資訊

1
var replyMessage = new LocationMessage(Title, Address, Latitude, Longitude);

Sticker message

貼圖訊息,傳入圖庫和圖庫中圖的識別號。

目前 BOT 可以用的貼圖是有限的幾組,不是好友傳什麼我們就可以回什麼,要參考官方文件:sticker-list

1
var replyMessage = new StickerMessage("1", "2");

1 是圖庫識別號,2 是圖的識別號。

Imagemap message

Base URL

由於設備的螢幕有大有小,所以要有不同尺寸的圖片。在 ImageMapMessage,要把圖片的目錄指定到 baseUrl 參數中,當中必須要有寬度為 240px, 300px, 460px, 700px, 1040px 的圖片。

舉例來說,如果 base URL 是 https://example.com/images/cats,則 App 會試著用 https://example.com/images/cats/700 去下載 700px 的圖片。

請注意,很重要
圖片的檔名只用尺寸的數字,不要有附檔名,格式只能是 png 或 jpg。
例如說,尺寸為 700 的 png 檔,就命名為 700,不是 700.png,LINE 只會用 ~/700 做網址去下載 700px 的圖片。

message 的建立

設定完一至多個 ImageMapAction 的區域屬性,傳入 ImageMapMessage 後就可以完成設定。

1
2
3
4
5
6
// 用 action 建立 2 個 區域
List<ImageMapAction> actions = new List<ImageMapAction>();
actions.Add(new UriImageMapAction("http://github.com", new ImageMapArea(0, 0, 520, 1040)));
actions.Add(new MessageImageMapAction("I love LINE!", new ImageMapArea(520, 0, 520, 1040)));
// 建立 message
replyMessage = new ImageMapMessage(imageUrl, "GitHub", new BaseSize(1040, 1040), actions);

範例中設了 2 個 Action,圖的上半由 UriImageMapAction 定義,點了之後會開啟到 GitHub 的連結,下面由 MessageImageMapAction 定義的部份則是回覆 “I love LINE!” 的訊息。

Template message 的 SDK 使用方式

要建立 Template message,至少要透過三層物件,Carousel 還多了一層 Column:

  • Action:首先要定義 actions,設定呈現給好友的行為及內容。
  • Template:將 actions 加入特定類型的 Template 物件並設定一些屬性。
  • Message:統一為建立 TemplateMessage

Confirm Template message

Confirm Template 會產生一個 2 個選項 Action 的確認對話框,讓好友選擇 Yes/No 或 OK/Cancel 之類的回答。Action 中的顯示文字和回答內容是我們可以指定的。

1
2
3
4
5
6
7
8
// Actions
List<TemplateAction> actions = new List<TemplateAction>();
actions.Add(new MessageTemplateAction("OK", "ok"));
actions.Add(new MessageTemplateAction("Cancel", "cancel"));
// Template
ConfirmTemplate confirmTemplate = new ConfirmTemplate("Are you sure?", actions);
// Message
replyMessage = new TemplateMessage("this is a confirm template", confirmTemplate);

利用 MessageTemplateAction(label, text) 建立選項,當好友點選 MessageTemplateAction,LINE 系統就會幫好友在聊天室中輸入 text 參數的內容並送出,就像好友自己打的訊息一樣,然後 BOT 可以針對這個內容做後續處理後再回應。

confirm

Buttons Template message

Buttons Template 會呈現圖、文及 1-4 個選項的畫面,可以和好友做清楚溝通後讓好友做選擇。

選項 Action 有三種類型可用:

  • MessageTemplateAction:就像前述的,幫好友送出指定訊息,並做後續處理。
  • UriTemplateAction:直接在 LINE 裡開啟指定的網頁,參數 1 為顯示文字,參數 2 為網址。
  • PostbackTemplateAction:直接發送設定好的內容 (postback data) 給 BOT,但不呈現在聊天室的對話中。可以透過對 Postback event 的處理來取得內容。
1
2
3
4
5
6
7
8
9
// Actions
List<TemplateAction> actions = new List<TemplateAction>();
actions.Add(new MessageTemplateAction("Message Label", "sample data"));
actions.Add(new PostbackTemplateAction("Postback Label", "postback data", "postback text"));
actions.Add(new UriTemplateAction("Uri Label", "https://github.com/kenakamu"));
// Template
ButtonsTemplate buttonsTemplate = new ButtonsTemplate("https://github.com/apple-touch-icon.png", "Sample Title", "Sample Text", actions);
// Message
replyMessage = new TemplateMessage("Buttons", buttonsTemplate);

ButtonsTemplate 可以設定圖片、標題、說明及選項 4 個參數,不需要的參數輸入 null 即可。

buttons

Carousel Template 就是多個 Buttons Template message 橫著擺可以左右划,以呈現比較多的商品或主題選項。

1
2
3
4
5
6
7
8
9
10
11
12
13
List<TemplateColumn> columns = new List<TemplateColumn>();
List<TemplateAction> actions = new List<TemplateAction>();
// Action
actions.Add(new MessageTemplateAction("Message Label", "sample data"));
actions.Add(new PostbackTemplateAction("Postback Label", "postback data", "postback text"));
actions.Add(new UriTemplateAction("Uri Label", "https://github.com/kenakamu"));
// Column
columns.Add(new TemplateColumn() { Title = "Casousel 1 Title", Text = "Casousel 1 Text", ThumbnailImageUrl = "https://github.com/apple-touch-icon.png", Actions = actions });
columns.Add(new TemplateColumn() { Title = "Casousel 2 Title", Text = "Casousel 2 Text", ThumbnailImageUrl = "https://github.com/apple-touch-icon.png", Actions = actions });
// Template
CarouselTemplate carouselTemplate = new CarouselTemplate(columns);
// Message
replyMessage = new TemplateMessage("Carousel", carouselTemplate);

把原本的 ButtonsTemplate 改成 TemplateColumn 即是。

carousel

發送訊息

有 2 種發送訊息的模式,Reply message 及 Push message。

當收到好友主動發來的訊息後,可以利用 Reply message API 來做回覆。回覆時,要帶原本訊息中的 replyToken 回去做驗證,replyToken 是有時限且只能用一次,要儘快回覆。

BOT 可以透過 Push message API 主動發訊息給指定 UserId 的好友,但能不能使用這個方式發送要看選用的方案。

1
2
3
4
5
6
7
8
try
{
await lineClient.ReplyToActivityAsync(lineEvent.CreateReply(message: replyMessage));
}
catch
{
await lineClient.PushAsync(lineEvent.CreatePush(message: replyMessage));
}

Action event 的處理

由於 UserId 已經事先取得了,這些 event 的處理請依服務的內容去實作可能是做記錄或回覆訊息,就不提供範例。

Follow event

LINE@ 帳號被加為好友得到通知

1
2
3
case EventType.Follow:
// HandleFollowEvent
break;

Unfollow event

LINE@ 帳號被封鎖時得到通知

1
2
3
case EventType.Unfollow:
// HandleUnfollowEvent
break;

Join event

LINE@ 帳號被加入群組或聊天室時得到通知

1
2
3
case EventType.Join:
// HandleJoinEvent
break;

Leave event

LINE@ 帳號離開群組時得到通知

1
2
3
case EventType.Leave :
// HandleLeaveEvent
break;

Postback event

當好友從 template message 中 觸發 Postback Action,收到送來的參數

1
2
3
4
case EventType.Postback :
// HandlePostbackEvent
var postbackReplyMessage = new TextMessage(lineEvent.Postback.Data);
break;

Beacon event

當好友進入或離開 LINE Beacon 的區域時得到通知

什麼是 LINE Beacon 服務?

當進入或離開 LINE Beacon 的區域時,官方帳號將會透過 LINE 傳送商家設置的 Beacon 發訊機(藍牙發射器)所發出的優惠、特價資訊,或是特別情報給您。(參考4)

現在只在少數特定的地方有這個設置。

1
2
3
case EventType.Beacon :
// HandleBeaconEvent
break;

完整範例

最後,把上面的全部內容組織起來,提供一個簡單的範例。由於只是為了示範,就沒有多做設計,全部放到一個 Controller 裡做呈現。

如果是實際應用,事件處理、回覆的訊息單元及商務流程的實作是有很多可以設計的地方,以符合程式碼品質原則。

範例的情境很簡單:

  • 輸入特定的功能名稱會呈現該功能的內容:
    “buttons”, “confirm”, “carousel”, “imagemap”,
    懶得記也懶的打,就輸入 “demo”,會顯示 buttons 選單出來用選的。

  • 傳送多媒體檔案,會回一張 GitHub 的圖。

  • 傳送位置訊息,會回相同的位置訊息。

  • 輸入其它的內容,就會用原本的內容回覆。

結語

LINE 在台灣有很高的使用率,LINE BOT 開發也很容易入手,所以 LINE BOT 本地應用持續增加,開發人員值得一學。

但在國際上佔有率不高的情況下,像是 Microsoft Bot Frameworkrecime 等通用 BOT 平台大都尚未支援,少了後勤支援,開發上只能孤軍奮戰自己找資源做整合。

參考資料及圖片來源

  1. LINE Developer Day 2016 - Opening & Introduction
  2. LINE business center - Messaging API
  3. LINE Blog - 【功能介紹】Messaging API
  4. LINE Beacon是什麼樣的服務呢?