週末啃完 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

黑大,太感謝了!

Post a comment