使用 LINE 聊天機器人回應圖片或檔案
0 |
前陣子說完用 CSS + Playwright 製作梗圖,這篇來試試我過去沒玩過的新嘗試 - 用 LINE Bot 聊天機器人回傳圖片或檔案。
之前寫的 LINE 聊天機器人,不管是投票人數統計、AI 聊天或是即時翻譯,都是接收使用者指令或談話內容,利用不計費的 Reply API 回傳文字提供服務,而 LINE 可回傳的訊息格式並不限於純文字,還可傳回應貼圖、圖片、影片、聲音檔、地理位置、可點擊圖片以,及確認對話框、選項按鈕、圖片輪播... 等各種彈性化互動介面 參考... 等。我平日開發 LINE 機器人主要靠 .NET LineBotSDK 套件,本身對這類訊息格式便有一定程度的支援。這篇就來試試在聊天室下指令叫 LINE 機器人動態生成梗圖,再分別用圖檔跟 PDF 格式傳給使用者。
實做效果會像這樣:
在 LINE 要回傳圖檔或 PDF,不是將檔案 byte[] 傳給 LINE API,而是要自己把檔案放上外界可存取的網站,再將下載網址提供給 LINE API。若不想費事,要串 LINK API 原本就有個對外的 Webhook 網站,把檔案放在上面給人下載是最簡單的解法。
若是圖檔內容,在聊天室會直接以圖片方式呈現,使用者可用 LINE 內建的儲存、另存新檔、分享、傳送至 Keep 筆記... 等選單操作,而點擊圖片時則會開啟 LINE 內建的圖片檢視器。
至於檔案下載,最無腦的簡單做法是用文字回傳 URL,LINE 會自動轉成超連結:
如果覺得這個做法太陽春,可考慮做成 Buttons Template,以圖片、標題、說明形式呈現,下方則可放上一到多個連結。
點選下載連結會開啟你指定的網址,呈現形式可完全客製打造。
回到如何用 LineBotSDK 實現以上需求。若要回傳圖片,可建立一個 ImageMessage 物件,提供圖片及預覽圖片網址,將用 ReplyMessage() 送出 ImageMessage:
var imgMsg = new ImageMessage(new Uri(imgUrl), new Uri(previewImgUrl));
lineBot.ReplyMessage(evt.replyToken, imgMsg);
至於 ButtonsTemplate,Bot.ReplyMesssage() 沒有直接支援此一格式,我的解決方法是改用 isRock.LineBot.Utility.ReplyTemplateMessage(),程式寫法會像這樣:
var tmplMsg = new ButtonsTemplate()
{
altText = "Download File",
thumbnailImageUrl = new Uri("https://www.svgrepo.com/show/33873/pdf.svg"),
title = "LINE Bot 回傳檔案展示",
text = "檔案僅保留5分鐘,請儘速下載",
actions = new List<TemplateActionBase>() {
new UriAction() { label = "下載檔案", uri = new Uri(fileUrl) }
}
};
isRock.LineBot.Utility.ReplyTemplateMessage(evt.replyToken, tmplMsg, apiKey);
借用之前大砲打小鳥之 ChatGPT 翻譯鳳梨酥 LINE Bot範例為基礎,略加修改後即可實現上面擷圖所展示的效果。
// 使用快取暫存檔案,在 Webhook Web API 新增 GET 路由提供下載
var fileCache = new MemoryCache(new MemoryCacheOptions());
app.MapGet("/Files/{id}/{fileName}", (string id) =>
{
if (fileCache.TryGetValue(id, out (string fileName, string contentType, byte[] content) file))
{
if (file.contentType.StartsWith("image") || // 圖片與 PDF 開放瀏覽器直接顯示
file.contentType.StartsWith("application/pdf"))
return Results.File(file.content, file.contentType);
else
// 其他檔案類型則提供檔名,下載後開啟
return Results.File(file.content, file.contentType, file.fileName);
}
return Results.NotFound();
});
app.MapPost("/MsgCallback", async (context) =>
{
var body = context.Request.Body;
using var reader = new StreamReader(body);
var json = await reader.ReadToEndAsync();
var msg = Utility.Parsing(json);
foreach (var evt in msg.events)
{
if (evt.type == "message")
{
if (evt.message.type == "text")
{
var text = evt.message.text;
// 檢查是否為 #MEMEPNG 或 #MEMEPDF 指令
var m = Regex.Match(text, @"(?ims)^#(?<tag>(MEMEPNG|MEMEPDF))\s*(?<top>.+?)\s*\|\s*(?<bottom>.+)");
if (m.Success)
{
var baseUri = $"https://{context.Request.Host}{context.Request.PathBase}";
var id = Guid.NewGuid().ToString();
var tag = m.Groups["tag"].Value;
var topWord = m.Groups["top"].Value;
var bottomWord = m.Groups["bottom"].Value;
if (tag == "MEMEPNG")
{
var imgBytes = await memeMaker.MakeMeme(memeFolder, topWord, bottomWord);
var fileName = $"meme-{DateTime.Now.Ticks.ToString("x")}.png";
var fileUrl = $"{baseUri}/Files/{id}/{Uri.EscapeDataString(fileName)}";
fileCache.Set(id, (fileName: fileName, contentType: "image/png", file: imgBytes),
absoluteExpiration: DateTimeOffset.Now.AddMinutes(5));
ImageMessage imgMsg = new ImageMessage(new Uri(fileUrl), new Uri(fileUrl));
lineBot.ReplyMessage(evt.replyToken, imgMsg);
}
else
{
var pdfBytes = await memeMaker.MakeMeme(memeFolder, topWord, bottomWord, true);
var fileName = $"meme-{DateTime.Now.Ticks.ToString("x")}.pdf";
var fileUrl = $"{baseUri}/Files/{id}/{Uri.EscapeDataString(fileName)}";
fileCache.Set(id, (fileName: fileName, contentType: "application/pdf", file: pdfBytes),
absoluteExpiration: DateTimeOffset.Now.AddMinutes(5));
var tmplMsg = new ButtonsTemplate()
{
altText = "Download File",
thumbnailImageUrl = new Uri("https://www.svgrepo.com/show/33873/pdf.svg"),
title = "LINE Bot 回傳檔案展示",
text = "檔案僅保留5分鐘,請儘速下載",
actions = new List<TemplateActionBase>() {
new UriAction() { label = "下載檔案", uri = new Uri(fileUrl) }
}
};
isRock.LineBot.Utility.ReplyTemplateMessage(evt.replyToken, tmplMsg, lineBotKey);
}
continue;
}
}
}
}
await context.Response.WriteAsync("OK");
}
不過,美中不足的是 - LineBotSDK 的 ButtonsTemplate 不支援將預覽圖設定正方形,另外原本 API 規格預覽圖也可點擊,會連到我們指定的超連結,這塊目前 SDK 也沒有實做。所幸,有個 ReplyMessageWithJSON() 函式允許自行組裝 Message JSON 原始內容,故程式可小改如下:(不得不說,C# 加入的 Raw String Literal (用 """ 取代 @" ) 超好用,文字可縮排、用 $$"""...""" 在其中改用 {{ varName }} 插值,原本的 不用跳脫,都比 @"..." 方便)
var templMsgJson= $$"""
[{
"type": "template",
"altText": "Download File",
"template": {
"type": "buttons",
"thumbnailImageUrl": "https://www.svgrepo.com/show/33873/pdf.svg",
"imageAspectRatio": "square",
"imageSize": "cover",
"imageBackgroundColor": "#FFFFFF",
"title": "LINE Bot 回傳檔案展示",
"text": "檔案僅保留5分鐘,請儘速下載",
"defaultAction": {
"type": "uri",
"label": "下載檔案",
"uri": "{{fileUrl}}"
},
"actions": [
{
"type": "uri",
"label": "下載檔案",
"uri": "{{fileUrl}}"
}
]
}
}]
""";
lineBot.ReplyMessageWithJSON(evt.replyToken, templMsgJson);
修改後的版本,可以使用正方形縮圖,滑鼠移到縮圖上會變手指,點下去可以下載檔案,感覺又更高級了。打完收工。
In this post, I explore how to make a LINE Bot return dynamically generated images or PDFs. The bot uses the .NET LineBotSDK to respond to user commands by creating memes and providing links to download the files. The article covers setting up the bot, generating the files, and sending them back to users using LINE’s messaging API.
Comments
Be the first to post a comment