iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

4

今天要介紹的是 Flex Message,這是 LINE 推出的新訊息格式,可以讓開發者客製聊天機器人的回覆訊息,如下圖。

https://ithelp.ithome.com.tw/upload/images/20200102/20106865smaFoO1bdE.jpg

Flex Message 用途:

  • 製作漂亮的回覆訊息
  • 製作互動視窗
  • 製作橫向圖片滑動視窗
  • 製作表格

Flex Message 的優點:

相比傳統的 Template Message 訊息格式,Flex Message 有更大的設計空間,且支援 「電腦版」,不像 Template 到電腦版就無法運作,如果不想放棄電腦版的使用者,建議可以將原 Template 功能,改成 Flex Message 版本。

今天要做的事

接下來會和大家介紹 Flex Message 的組成結構和內部元素的用法,接著我會實際拉一個版型出來,美化上一篇結尾的文章排名功能,最後結合程式將資料套入。

這就開始吧 ~~~


Flex Message 結構

Flex Message 是由三層嵌套結構組成,內容參考 官方文件

  • Container 容器 > Block 區塊 > Component 組件

https://ithelp.ithome.com.tw/upload/images/20200102/20106865PU5gerYWpT.jpg


Container 容器

容器是 Flex Message 的最外層結構,有兩種類型可選。

  1. Bubble
  2. Carousel

Bubble 是包含一個訊息框的容器。

https://ithelp.ithome.com.tw/upload/images/20200102/201068651ehqitINln.jpg

JSON 結構

{
  "type": "bubble",
  "hero": {},
  "body": {},
  "footer": {}
}

Carousel 是包含多個 Bubble 的容器,可以橫向滑動。

https://ithelp.ithome.com.tw/upload/images/20200102/2010686569KupnjVWU.jpg

JSON 結構

{
  "type": "carousel",
  "contents": [
    {
      "type": "bubble",
      "hero": {},
      "body": {},
      "footer": {}
    },
    {
      "type": "bubble",
      "hero": {},
      "body": {},
      "footer": {}
    }
  ]
}

Block 區塊

區塊是組成 Bubble 的結構,分為四個部分。

  1. Header: 顯示標題的區塊。
  2. Hero: 顯示主要圖片的區塊。
  3. Body: 顯示主要內容的區塊。
  4. Footer: 顯示按鈕或補充訊息的區塊。

https://ithelp.ithome.com.tw/upload/images/20200102/20106865jhhgRT2WO3.jpg

JSON 結構

{
  "type": "bubble",
  "header": {
    "type": "box",
    "layout": "vertical"
  },
  "hero": {
    "type": "image",
    "url": "https://xxx/image.jpg"
  },
  "body": {
    "type": "box",
    "layout": "vertical"
  },
  "footer": {
    "type": "box",
    "layout": "vertical"
  }
}

Component 組件

組件是 Flex Message 的最基本結構,類型很多如下。

  • 1. Box: 定義介面的佈局,裡面可以包含其它組件
{
  "type": "bubble",
  "body": {
    "type": "box",
    "layout": "vertical",
    "contents": []
  }
}

參數說明: #box

  • 2. Button: 按鈕

https://ithelp.ithome.com.tw/upload/images/20200102/20106865Ovz7Vzqiq1.jpg

{
  "type": "bubble",
  "body": {
    "type": "box",
    "layout": "vertical",
    "contents": [
      {
        "type": "button",
        "action": {
          "type": "uri",
          "label": "action",
          "uri": "http://xxx/"
        },
        "style": "primary"
      }
    ]
  }
}

參數說明: #button

  • 3. Image: 圖片

https://ithelp.ithome.com.tw/upload/images/20200102/20106865dHxXdzTCKN.jpg

{
  "type": "bubble",
  "body": {
    "type": "box",
    "layout": "vertical",
    "contents": [
      {
        "type": "image",
        "url": "https://xxx/image.png"
      }
    ]
  }
}

參數說明: #f-image

  • 4. Icon: 文字前的圖標

https://ithelp.ithome.com.tw/upload/images/20200102/20106865bUmCQQ2qF9.jpg

{
  "type": "bubble",
  "body": {
    "type": "box",
    "layout": "baseline",
    "contents": [
      {
        "type": "icon",
        "url": "https://xxx/image.png"
      },
      {
        "type": "text",
        "text": "hello, world",
        "offsetStart": "5px"
      }
    ]
  }
}

參數說明: #icon

  • 5. Text: 文字

https://ithelp.ithome.com.tw/upload/images/20200102/20106865OKzDktHijC.jpg

{
  "type": "bubble",
  "body": {
    "type": "box",
    "layout": "vertical",
    "contents": [
      {
        "type": "text",
        "text": "HI~ HI~"
      }
    ]
  }
}

參數說明: #f-text

  • 6. Span: 單行文字,可以指定不同的顏色、大小、粗細

https://ithelp.ithome.com.tw/upload/images/20200102/201068656ow1csjJ8b.jpg

{
  "type": "bubble",
  "body": {
    "type": "box",
    "layout": "vertical",
    "contents": [
      {
        "type": "text",
        "text": "HI~ HI~",
        "contents": [
          {
            "type": "span",
            "text": "ABC",
            "color": "#ff0000",
            "weight": "bold",
            "size": "xxs"
          },
          {
            "type": "span",
            "text": "XYZ",
            "color": "#00ff00",
            "weight": "bold",
            "size": "lg"
          },
          {
            "type": "span",
            "text": "123",
            "color": "#0000ff",
            "weight": "bold",
            "size": "xxl"
          }
        ]
      }
    ]
  }
}

參數說明: #span

  • 7. Separator: 分隔線,可製作水平或垂直的分隔線

https://ithelp.ithome.com.tw/upload/images/20200102/20106865XBZJOjtolk.jpg

{
  "type": "bubble",
  "body": {
    "type": "box",
    "layout": "vertical",
    "contents": [
      {
        "type": "box",
        "layout": "horizontal",
        "contents": [
          {
            "type": "text",
            "text": "A",
            "offsetStart": "5px"
          },
          {
            "type": "separator",
            "color": "#000000"
          },
          {
            "type": "text",
            "text": "B",
            "offsetStart": "5px"
          }
        ]
      },
      {
        "type": "separator",
        "color": "#000000"
      },
      {
        "type": "box",
        "layout": "horizontal",
        "contents": [
          {
            "type": "text",
            "text": "C",
            "offsetStart": "5px"
          },
          {
            "type": "separator",
            "color": "#000000"
          },
          {
            "type": "text",
            "text": "D",
            "offsetStart": "5px"
          }
        ]
      }
    ]
  }
}

參數說明: #separator

  • 8. Filler: 可以在組件間插入空白區塊

https://ithelp.ithome.com.tw/upload/images/20200102/20106865KhpXBApaUU.jpg

{
  "type": "bubble",
  "body": {
    "type": "box",
    "layout": "horizontal",
    "contents": [
      {
        "type": "image",
        "url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_1_cafe.png"
      },
      {
        "type": "filler"
      },
      {
        "type": "image",
        "url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_1_cafe.png"
      }
    ]
  }
}

參數說明: #filler

這裡只簡單介紹結構,詳細用法建議直接看 官方文件


使用 Flex Message Simulator BETA 製作文章排名訊息框

官方有推出輔助工具,可以直接在網頁上製作 Flex Message。
連結: https://developers.line.biz/console/fx-beta/

如果想直接編輯 JSON 可以使用舊版的。
連結: https://developers.line.biz/console/fx/


我使用 BETA 版的編輯器製作。

結果如下:

https://ithelp.ithome.com.tw/upload/images/20200102/201068658BTkcZoahL.jpg

我盡力了~~~ (´・ω・`)

https://ithelp.ithome.com.tw/upload/images/20200102/20106865CLqEBYiQ65.jpg

{
  "type": "bubble",
  "body": {
    "type": "box",
    "layout": "vertical",
    "spacing": "md",
    "contents": [
      {
        "type": "text",
        "text": "fysh711426",
        "size": "xl",
        "weight": "bold"
      },
      {
        "type": "separator"
      },
      {
        "type": "box",
        "layout": "vertical",
        "spacing": "sm",
        "contents": [
          {
            "type": "box",
            "layout": "horizontal",
            "contents": [
              {
                "type": "box",
                "layout": "vertical",
                "contents": [
                  {
                    "type": "text",
                    "text": "1",
                    "flex": 0,
                    "align": "center",
                    "size": "xxs",
                    "color": "#ffffff",
                    "offsetTop": "1px"
                  }
                ],
                "backgroundColor": "#905C44",
                "cornerRadius": "3px",
                "margin": "xl",
                "width": "18px",
                "height": "18px",
                "offsetTop": "12px"
              },
              {
                "type": "box",
                "layout": "vertical",
                "contents": [
                  {
                    "type": "text",
                    "text": " [Day09] LINE Bot 爬蟲實作",
                    "weight": "bold",
                    "margin": "sm",
                    "flex": 0,
                    "wrap": true,
                    "maxLines": 2,
                    "size": "sm"
                  },
                  {
                    "type": "box",
                    "layout": "horizontal",
                    "contents": [
                      {
                        "type": "box",
                        "layout": "vertical",
                        "contents": [
                          {
                            "type": "text",
                            "text": "瀏覽 1049 (+145)",
                            "color": "#928D8B",
                            "size": "xs"
                          }
                        ],
                        "flex": 0,
                        "cornerRadius": "3px",
                        "margin": "sm"
                      }
                    ],
                    "paddingTop": "3px",
                    "paddingBottom": "3px"
                  }
                ],
                "paddingStart": "5px",
                "flex": 0
              }
            ]
          },
          {
            "type": "separator"
          }
        ],
        "offsetStart": "-2px"
      },
      {
        "type": "box",
        "layout": "vertical",
        "spacing": "sm",
        "contents": [
          {
            "type": "box",
            "layout": "horizontal",
            "contents": [
              {
                "type": "box",
                "layout": "vertical",
                "contents": [
                  {
                    "type": "text",
                    "text": "2",
                    "flex": 0,
                    "align": "center",
                    "size": "xxs",
                    "color": "#ffffff",
                    "offsetTop": "1px"
                  }
                ],
                "backgroundColor": "#905C44",
                "cornerRadius": "3px",
                "margin": "xl",
                "width": "18px",
                "height": "18px",
                "offsetTop": "12px"
              },
              {
                "type": "box",
                "layout": "vertical",
                "contents": [
                  {
                    "type": "text",
                    "text": "[VSCode] Visual Studio Code 執行 C++ (1) - 安裝 VSCode + MinGW",
                    "weight": "bold",
                    "margin": "sm",
                    "flex": 0,
                    "wrap": true,
                    "maxLines": 2,
                    "size": "sm"
                  },
                  {
                    "type": "box",
                    "layout": "horizontal",
                    "contents": [
                      {
                        "type": "box",
                        "layout": "vertical",
                        "contents": [
                          {
                            "type": "text",
                            "text": "瀏覽 45365 (+21)",
                            "color": "#928D8B",
                            "size": "xs"
                          }
                        ],
                        "flex": 0,
                        "cornerRadius": "3px",
                        "margin": "sm"
                      }
                    ],
                    "paddingTop": "3px",
                    "paddingBottom": "3px"
                  }
                ],
                "paddingStart": "5px",
                "flex": 0
              }
            ]
          },
          {
            "type": "separator"
          }
        ],
        "offsetStart": "-2px"
      }
    ]
  }
}

使用 LineMessagingApi 送出 Flex Message 訊息

接下來要將製作好的 JSON 套到程式內。

我使用的 Line Bot 套件是 LineMessagingApi。
連結: https://github.com/pierre3/LineMessagingApi

完整程式可以參考上一篇內容。
[Day09] LINE Bot 爬蟲實作 - 使用 HttpClient 和 Regex


首先將 JSON 結構轉成 C# 程式。

先把外層做出來。

https://ithelp.ithome.com.tw/upload/images/20200102/201068659YZJqj31h7.jpg

外層的程式

var container = new BubbleContainer
{
    Body = new BoxComponent
    {
        Layout = BoxLayout.Vertical,
        Spacing = Spacing.Md,
        Contents = new IFlexComponent[]
        {
            new TextComponent
            {
                Text = $"{iTHome.Name}",
                Weight = Weight.Bold,
                Size = ComponentSize.Xl
            },
            new SeparatorComponent()
        }
    }
};

接下跑迴圈產生中間的部分。

https://ithelp.ithome.com.tw/upload/images/20200102/20106865mzhbZQYa8O.jpg

不過發現有一些屬性不存在,應該是 Line 後來新增,但套件還沒更新。

https://ithelp.ithome.com.tw/upload/images/20200102/201068652yY3cGrIpV.jpg

使用 C# 孤兒的我們只好自立自強。 o(〒﹏〒)o

新增 FixFlex.cs 檔案,補上缺少的屬性,等套件更新後再改回去。

/*
MIT License

Copyright (c) 2017 pierre3

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

namespace FixFlex
{
    public class BoxComponent : Line.Messaging.BoxComponent
    {
        public string BackgroundColor { get; set; }
        public string CornerRadius { get; set; }
        public string Width { get; set; }
        public string Height { get; set; }
        public string OffsetTop { get; set; }
        public string offsetBottom { get; set; }
        public string OffsetStart { get; set; }
        public string OffsetEnd { get; set; }
        public string PaddingAll { get; set; }
        public string PaddingStart { get; set; }
        public string PaddingEnd { get; set; }
        public string PaddingTop { get; set; }
        public string PaddingBottom { get; set; }
        public string Position { get; set; }
    }

    public class TextComponent : Line.Messaging.TextComponent
    {
        public string OffsetTop { get; set; }
    }

    public class FillerComponent : Line.Messaging.FillerComponent
    {
        public int Flex { get; set; }
    }
    
    public class BubbleContainer : Line.Messaging.BubbleContainer
    {
        public string Size { get; set; }
    }
}

我沒有補完所有屬性,大家可以補自己缺少的部分。

中間的程式

for (var i = 0; i < topList.Count; i++)
{
    var item = topList[i];

    container.Body.Contents.Add(
        new FixFlex.BoxComponent
        {
            Layout = BoxLayout.Vertical,
            Spacing = Spacing.Sm,
            OffsetStart = "-2px",
            Contents = new IFlexComponent[]
            {
                new BoxComponent
                {
                    Layout = BoxLayout.Horizontal,
                    Contents = new IFlexComponent[]
                    {
                        new FixFlex.BoxComponent
                        {
                            Layout = BoxLayout.Vertical,
                            Margin = Spacing.Xl,
                            BackgroundColor = "#905C44",
                            CornerRadius = "3px",
                            Width = "18px",
                            Height = "18px",
                            OffsetTop = "12px",
                            Contents = new IFlexComponent[]
                            {
                                new FixFlex.TextComponent
                                {
                                    Text = $"{i+1}",
                                    Flex = 0,
                                    Align = Align.Center,
                                    Size = ComponentSize.Xxs,
                                    Color = "#ffffff",
                                    OffsetTop = "1px"
                                }
                            }
                        },
                        new FixFlex.BoxComponent
                        {
                            Layout = BoxLayout.Vertical,
                            Flex = 0,
                            PaddingStart = "5px",
                            Contents = new IFlexComponent[]
                            {
                                new TextComponent
                                {
                                    Text = $"{item.article.Title}",
                                    Flex = 0,
                                    Size = ComponentSize.Sm,
                                    Margin = Spacing.Sm,
                                    Wrap = true,
                                    Weight = Weight.Bold,
                                    MaxLines = 2
                                },
                                new FixFlex.BoxComponent
                                {
                                    Layout = BoxLayout.Horizontal,
                                    PaddingTop = "3px",
                                    PaddingBottom = "3px",
                                    Contents = new IFlexComponent[]
                                    {
                                        new FixFlex.BoxComponent
                                        {
                                            Layout = BoxLayout.Vertical,
                                            Flex = 0,
                                            Margin = Spacing.Sm,
                                            CornerRadius = "3px",
                                            Contents = new IFlexComponent[]
                                            {
                                                new TextComponent
                                                {
                                                    Text = $"瀏覽 {item.count} (+{item.inc})",
                                                    Color = "#928D8B",
                                                    Size = ComponentSize.Xs
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                new SeparatorComponent()
            }
        });
}

最後回覆訊息部分

文章排名文字會顯示在 Line 通知上。

//回傳訊息
result = new List<ISendMessage>
{
    new FlexMessage("文章排名")
    {
        Contents = container
    }
};

結果

  • 電腦版畫面

https://ithelp.ithome.com.tw/upload/images/20200107/20106865Y5ZiPoi1nm.jpg

怎麼覺得怪怪的。 (´・ω・`)


  • 手機畫面

https://ithelp.ithome.com.tw/upload/images/20200107/20106865Gz25s8LLOP.jpg

手機畫面感覺比較正常。

結語

下一篇要介紹的是 Template Message,我會使用 Flex Message 刻出四種 Template 版型,方便想轉到 Flex Message 卻不會刻版的人使用。

今天就到這裡,感謝大家觀看。


上一篇
[Day09] LINE Bot 爬蟲實作 - 使用 HttpClient 和 Regex
下一篇
[Day11] 使用 Flex Message 重製模板訊息 - Template Message
系列文
Line Bot 心得分享 LineMessagingApi + LUIS + BotFramework27

尚未有邦友留言

立即登入留言