第12 屆iT邦幫忙鐵人賽系列文章 (Day10)

在上一篇我們用 Template Message 中的 ImageCarousel 實作輪播後,我們再來練習一篇 Template Message,實作到達婚禮現場的各種交通方式資訊,也是先來看看今天的成果

上一篇有介紹 Action 的 7 種類型,前面幾個操作都是用 MessageAction 來引島使用者,而當點選如何前往時,會直接跳出之前實作的"地址導覽" 的 intent,這就是我們今天會帶到的 postback event

CarouselTemplate.cs — 實作ITemplate來完成第二種 Template Message

上一篇輪播圖片,這一篇我們來輪播自訂的樣板,CarouselTemplate,一樣繼承 ITemplate 實作

    public class CarouselTemplate : ITemplate

    {

    public CarouselTemplate()

    {

    }

    public CarouselTemplate(List<ColumnMultipleAction> columns)

    {

    Columns = columns;

    }

    public string Type => "carousel";

    public List<ColumnMultipleAction> Columns { get; set; }

    }

ColumnMultipleAction — 每個樣本的內容

可以設定圖片/標題/副標題…等資訊,

  • ThumbnailImageUrl: 縮圖URL
  • ImageAspectRatio: 圖片的比例 rectangle(1.51:1) square(1:1)
  • ImageSize: cover(不適應地方會裁切掉) contain(包含全部圖片)
  • ImageBackgroundColor: 圖片區塊的背景色 (loading時候會看到)
  • Title: 標題
  • Text: 副標題
  • ImageUrl: 圖片網址
  • DefaultAction: 預設的Action (這樣就不用一個一個塞進 Actions 裡面)
  • Actions: 如我們前一篇文章所說的,7種Action
    public class ColumnMultipleAction

    {

    /// <summary>

    /// Max width: 1024px

    /// Max file size: 1 MB

    /// JPEG or PNG

    /// </summary>

    public string ThumbnailImageUrl { get; set; }

    public ImageAspectRatioType ImageAspectRatio { get; set; } = ImageAspectRatioType.rectangle;

    public ImageSizeType ImageSize { get; set; } = ImageSizeType.cover;

    public string ImageBackgroundColor { get; set; } = "#FFFFFF";

    public string Title { get; set; }

    public string Text { get; set; }

    public string ImageUrl { get; set; }

    public Defaultaction DefaultAction { get; set; }

    public List<IAction> Actions { get; set; }

    }

為了開發方便,陸續可以定義一些 Type

StringEnumConverter 主要是 JSON.NET 的功能,可以在我們從 Enum 轉JSON的時候,轉成字串,而不是該 Enum 的 Index Value

    [JsonConverter(typeof(StringEnumConverter))]

    public enum ImageAspectRatioType

    {

    /// <summary>

    /// 1.51:1

    /// </summary>

    rectangle,

    /// <summary>

    /// 1:1

    /// </summary>

    square

    }

    [JsonConverter(typeof(StringEnumConverter))]

    public enum ImageSizeType

    {

    cover,

    contain

    }

Directions.cs - 來實作如何前往 Intent

    public class Directions : IReplyIntent

    {

    private readonly LineReplyMessageUtility lineMessageUtility;

    private readonly LineProfileUtility lineProfileUtility;

    public Directions(LineReplyMessageUtility _lineMessageUtility, LineProfileUtility _lineProfileUtility)

    {

    lineMessageUtility = _lineMessageUtility;

    lineProfileUtility = _lineProfileUtility;

    }

    public async Task ReplyAsync(string replyToken)

    {

    var carouselTemplate = new CarouselTemplate();

    var columns = new List<ColumnMultipleAction>();

    // 輪播兩種 Template, 三種 Action

    columns.Add(new ColumnMultipleAction()

    {

    ThumbnailImageUrl = "https://imgur.com/2u6F3dV.jpg",

    Title = "搭乘大眾運輸",

    Text = "交通便利好方便~",

    Actions = new List<IAction>()

    {

    new MessageAction("從高鐵"),

    new MessageAction("從捷運"),

    new MessageAction("從機場")

    }

    });

    columns.Add(new ColumnMultipleAction()

    {

    ThumbnailImageUrl = "https://imgur.com/FGitN5M.jpg",

    Title = "自行開車前往",

    Text = "切記酒後不開車~",

    Actions = new List<IAction>()

    {

    new PostbackAction("如何前往", "婚宴地點"),

    new MessageAction("停車資訊"),

    new MessageAction("開車提醒")

    }

    });

    carouselTemplate.Columns = columns;

    await lineMessageUtility.ReplyTemplateMessageAsync(replyToken, carouselTemplate);

    }

    }

OnMessageAsync

DefaultIntent 增加一個選擇性參數,當有要傳訊息的時候回傳,這樣我們就不用定義到那麼多的 Intent

    public class DefaultIntent : IReplyIntent

    {

    private readonly LineReplyMessageUtility lineMessageUtility;

    private readonly LineProfileUtility lineProfileUtility;

    private readonly string replyText;

    public DefaultIntent(

    LineReplyMessageUtility _lineMessageUtility,

    LineProfileUtility _lineProfileUtility,

    string _replyText = "")

    {

    lineMessageUtility = _lineMessageUtility;

    lineProfileUtility = _lineProfileUtility;

    replyText = _replyText;

    }

    public async Task ReplyAsync(string replyToken)

    {

    if(!string.IsNullOrEmpty(replyText))

    {

    await lineMessageUtility.ReplyMessageAsync(replyToken, replyText);

    }

    else

    {

    await Task.CompletedTask;

    }

    }

    }

Postback Event

要來擴充 Event 了,OnMessage Event已經不夠我們使用,在如何前往那邊我們有塞一個 Action 叫做 PostbackAction,data 可以帶一些程式要溝通的資料,如果想傳的資料太多甚至你可以傳個JSON字串,在反序列化回來成物件,我這邊帶的是 “婚宴地點” 這個字串,用來判斷我要執行這個 Intent

    public class PostbackAction : IAction

    {

    public PostbackAction(string text, string data, string label = "")

    {

    Text = text;

    Data = data;

    Label = string.IsNullOrEmpty(label) ? text : label;

    }

    public string Data { get; set; }

    public string Text { get; set; }

    public ActionType Type => ActionType.postback;

    public string Label { get; set; }

    }

回到 LineBotApp.cs 的 RunAsync

Webhook 所接收的 Event 要多 postback 這個 class

實作 OnUnPostbackAsync

ev.postback.data 即可取得上面給的 data ,本範例是 “婚宴地點”

NOTE:看到這邊,我們開始嗅到第一個壞味道了,Intent開始定義兩邊,且開始多和複雜,沒關係,我們找篇文章來做重構的動作

JOKE:然後也會發現,透過引導的方式,感覺不用 AI 的技術,也能讓 chatbot 有流程對話,我懂你的感覺 (可以開始包裝我大AI時代了)

有啥限制嗎?

  • CarouselTemplate 在傳送的時候最多只能設定10個輪播 (其實太多也不好,很亂)
  • URL 最大 1000 字元
  • 一定要是 HTTPS 且加密規範 TLS 1.2 以上
  • 檔案大小上限 200 MB
  • 圖片需為 JPEG or PNG

懶人包,本次學到了什麼?