讀書筆記 - LINE BOT 開發實戰
11 |
週末啃完 David 老師「LINE BOT 與人工智慧辨別開發實戰」一書(再一次慢半拍,哈),依慣例筆記備忘。
先前寫過 LINE 機器人,平日也依賴 LINE Notify 發系統通報,嚴格來說我不算 LINE 程式開發生手。但相較於程式用到什麼查什麼學什麼的遊擊式學習,書籍課程提供較完整的知識覆蓋面,讓你知道面對某個需求時有哪些可用選項,比起查到哪一篇範例就學哪一種寫法的福袋(盲盒)式開發,更能有效率地精巧解決問題。雖然 Google 、ChatGPT 跟 Copilot 很方便,想要功力大增,一本好書或一門課程總能讓你事半功倍。所以,網路時代還是要讀書呀~
- Line Message API 申請重點:建立公司和個人 Provider,取得 Channel Access Token 及 User ID,透過掃瞄 QR Code 將機器人加為好友
- 格式化呈現選項讓使用者操作 - Template Message,分為 Buttons Template, Confirm Template, Carosul Template (最多十個)
- Actions 為使用者可點擊的選項,有以下形式:Post Action (觸發Webhook), Message Action (代使用者送出一句話), Uri Action (開網頁)
- Template Message 在 PC 版會顯示公版不支援行動提示,可用 altText 自訂提示
- Carousel Template 直接用一張圖片當選項按鈕
- Quick Reply 文字輸入區上方一排小按鈕供使用者選取
- Flex Message 用 JSON 自訂訊息排版樣式。有 Flex Message Simulator 工具預覽 JSON 定義呈現效果
- LINE Bot 管理後台 https://developer.line.biz
- LIFF App (LINE Front-end Framework) 嵌入 HTML,呼叫
https://api.line.me/liff/v1/app
傳入 URL 得到 ID,寫成 line://app/11111111111-oxoxoxoxox 可在 LINE 開啟 - LIFF 網頁載入
https://d.line-scdn.net/liff/1.0/sdk.js
可用 JavaScript 取得使用者身分、送訊息等liff.init((data) => { data.context.userId; //使用者 ID liff.sendMessage([ { type:'text', text: 'message' } ]).then(() => { ... }); });
- .NET 開發 LINE 機器人可使用 NuGet 安裝 LineBotSDK 簡化開發 (以下簡稱 SDK)
- SDK 有提供方法建立及列舉 LIFF 連結,另外也可使用 LINE 管理後台操作
- WebHook 以網站形式接受 LINE 呼叫,做出回應
- Push API 要錢,可主動送訊息給 User Id, Reply API 免費,用 ReplyToken 回覆使用者發言,時效很短 (註:約 30 秒)
- WebHook 收到 ReceivedMessage events 一般只有一筆,但高負載情境下可能多筆。每個event可能來自不同使用者,想像成多個包裹一次送到
- event.source.userId 為使用者識別,SDK 有 GetUserInfo() 可取姓名、大頭照、狀態文字等
- event.text 訊息內容 event.packageId stickerId 貼圖資訊
- API 也可送貼圖,package ID/sticker ID 對照表可查 List of available stickers (書上的 PDF 網址已失效)
- 訊息也可能是GPS座標,由 address, latitude, longitude 取值
- 訊息型別包含 Text, Sticker, Image, Video, Audio, File, Location
- event 類別有
- Follow 加好友
- Unfollow 封鎖
- Join 被加入聊天室
- Leave 離開聊天室
- Postback 收到 Postback 訊息 (來自 Template Message,new isRock.LineBot.PostbackAction()
- Beacon 收到 Beacon 訊號
- 聊天室有兩種:隨意邀請人建立 - Room、透過選單建立群組 - Group
- 每個群組只能有一個Bot, Bot 不知被誰加,可識別目前說話者(不限好友), 可主動退出
event.source.type==room/group, event.source.roomId/.groupId <= 傳入 PushMessage 參數主動對群組發話 - 抓傳來的圖檔、聲音用 SDK GetUserUploadedContent(message.id,...) 取得內容
- 可繼承 SDK LineWebHookControllerBase 實作 ApiController。免自己解析 JSON,由 ReceivedMessage 取得訊息內容
- ngrok 將 localhost 特定 Port 轉接到 Internet 的 HTTPS,方便用 VS 逐行偵錯
註:也可用 SSH Tunneling 或 Cloudflare Tunnel - 防止有人假冒 LINE 服務呼叫你的 WebHook - 檢查 Channel Secret (X-Line-Signature HTTP Header) / isRock.LineBot.Utility.SignatureValidation(this);
- 免費通知服務 LINE Notify Service,申請後得到 Client ID、Client Secret
- 允許發 LINE Nontify 給使用者需經 OAuth 驗證:引導頁轉到 LINE 官方 OAuth,確認身分並同意授權後導回我們網取並附上 code (還有自訂的 state,一般用來識別使用者是誰),後端傳 code 給 API 取得 Token 儲存,以後用此 Token 傳訊給該使用者
- Rich Menu 是官方帳號常見下方兩排六格的快捷選單(有 3+3, 2x2, 1+3, 1+2, 1+1, 滿版等版型)
- LUIS - Language Understanding Intelligent Services, 理解輸入語句,歸納出意圖(Intent),並抓出關鍵 Entities (在 ChatGPT 等 LLM AI 出現後,LUIS 的學習價值待重新評估)
- Azure Cognitive Service:Computer Vision API、Translater API... 等章節,基於 AI 剛邁向新時代,這部分也先擱著。
最後,用一個簡單練習結束這回合。ASP.NET Core Minimal API 搭配 LineBotSDK,寫一個接收使用者傳送文字及傳回 TemplateMessage 的小範例:
程式碼如下,整支程式 50 行 C# 搞定:
using isRock.LineBot;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var chanAccToken = 取得被安全儲存的ChannelAccessToken();
var bot = new isRock.LineBot.Bot(chanAccToken);
app.MapGet("/", () => "Hello World!");
app.MapPost("/WebHook", async (HttpRequest request) =>
{
using var sr = new StreamReader(request.Body);
var reqBody = await sr.ReadToEndAsync();
var rcvMsg = isRock.LineBot.Utility.Parsing(reqBody);
foreach (var e in rcvMsg.events)
{
if (!string.IsNullOrEmpty(e.message?.text))
{
var replyToken = e.replyToken;
var userId = e.source.userId;
var userName = bot.GetUserInfo(userId).displayName;
var userMsg = e.message.text;
if (userMsg == "/buttons")
{
var btnTmpl = new isRock.LineBot.ButtonsTemplate()
{
altText = "請用手機檢視",
text = "請選擇",
title = "敲指令管 Azure,你選哪一種?",
thumbnailImageUrl = new Uri("... Fig2_638130550931190042.png"),
actions = new List<isRock.LineBot.TemplateActionBase>() {
new isRock.LineBot.MessageAction() {
label = "Azure CLI",
text = "Azure CLI"
},
new isRock.LineBot.MessageAction() {
label = "Azure PowerShell",
text = "Azure PowerShell"
},
new isRock.LineBot.UriAction() {
label = "看文章",
uri = new Uri("https://blog.darkthread.net/blog/azure-cli-or-azure-powershell/")
}
}
};
bot.ReplyMessage(replyToken, new TemplateMessage(btnTmpl));
}
else
bot.ReplyMessage(replyToken, $"[記錄]{userName}說:{userMsg}");
}
}
return Results.Ok();
});
app.Run();
Notes about LineBotSDK and a ASP.NET Core Minimal API example to response message from LINE.
Comments
# by Soon
雖然知道 method name 可以用中文但每次看到還是覺得挺酷炫 XD (還是那只是註解沒拿掉 :p line.6)
# by Jeffrey
to Soon,實務上我不愛用中文命名,呼叫時要切中文輸入搞死自己,唯一的例外是單元測試方法,永遠不會在自己程式碼用到,出現在測試項目清單一目了然。 第六行寫成註解性質假函式是提醒大家要留意API Key保密,嚴防外流。
# by Soon
了解~感謝黑大說明
# by Edward
黑大請問 使用 isRock.LineBot.Utility.PushMessage(Uid, Message, channelAccessToken); 一直出現 System.NullReferenceException: '並未將物件參考設定為物件的執行個體。' 能確定 都是有值的,細看錯誤訊息,是Message 未設定, string Message = "來了,測試"; 已有實例化,想請問這是元件沒安裝好?
# by Jeffrey
to Edward, 怪怪的,Message 有給值,與 Message 發生 NullReferenceException 錯誤二者矛盾。建議用 F5 Line By Line 偵錯到 PushMessage() 這行,確實檢查 Uid、Message、channelAccessToken 等變數值是否如預期。
# by Edward
黑大,你好,目前跑出來的錯誤是($exception).Message 並未將物件參考設定為物件的執行個體,但確定是有值,會是可能我用的是.net 4.5 版本開發api的關係嗎?
# by Jeffrey
to Edward,看不懂 ($exception).Message,能提供完整的原始錯誤訊息嗎?
# by Edward
黑大,午安 ,以下是我的原始碼就四行 string channelAccessToken = "xxxx(公司的token)"; string Uid = "Uf10a63bc55c4db53beb1cbdb3897da6b"; string Message = "來了,測試"; isRock.LineBot.Utility.PushMessage(Uid, Message, channelAccessToken); 出現的錯誤訊息如下: System.NullReferenceException: '並未將物件參考設定為物件的執行個體。' - $exception {"並未將物件參考設定為物件的執行個體。"} System.NullReferenceException + Data {System.Collections.ListDictionaryInternal} System.Collections.IDictionary {System.Collections.ListDictionaryInternal} HResult -2147467261 int HelpLink null string IPForWatsonBuckets 0x2a51b71a System.UIntPtr + InnerException null System.Exception IsTransient false bool Message "並未將物件參考設定為物件的執行個體。" string RemoteStackTrace null string Source "LineBot" string StackTrace " 於 isRock.LineBot.Utility.PushMessage(String ToUserID, String Message, String ChannelAccessToken)" string + TargetSite {System.String PushMessage(System.String, System.String, System.String)} System.Reflection.MethodBase {System.Reflection.RuntimeMethodInfo} WatsonBuckets null object _HResult -2147467261 int _className null string + _data {System.Collections.ListDictionaryInternal} System.Collections.IDictionary {System.Collections.ListDictionaryInternal} _dynamicMethods null object _exceptionMethod null System.Reflection.MethodBase _exceptionMethodString null string _helpURL null string + _innerException null System.Exception _ipForWatsonBuckets 0x2a51b71a System.UIntPtr _message "並未將物件參考設定為物件的執行個體。" string _remoteStackIndex 0 int _remoteStackTraceString null string + _safeSerializationManager {System.Runtime.Serialization.SafeSerializationManager} System.Runtime.Serialization.SafeSerializationManager _source "LineBot" string + _stackTrace {sbyte[24]} object {sbyte[]} _stackTraceString null string _watsonBuckets null object _xcode -1073741819 int _xptrs 0x2a38d98c System.IntPtr + 靜態成員
# by Jeffrey
to Edward, 破案了,請參考新文章 https://blog.darkthread.net/blog/tls12-issue-on-linebotsdk/ 。
# by Edward
黑大,太感謝了!
# by 阿榮
我也有相同問題,感謝Edward的提問,當然最感謝黑大的DEBUG對策指導