iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 19
0
Modern Web

從LINE BOT到資料視覺化:賴田捕手系列 第 19

第 19 天:LINE BOT SDK:FlexMessage

第 19 天:LINE BOT SDK:FlexMessage

他老是話說個不停,而且最糟的是,往往都是在說一些不中聽的話。但他卻能把一件事做得很好:FlexMessage。那個傢伙能寫出最漂亮的 FlexMessage,比我認識的每一個人都還厲害。

~節錄自《賴田捕手》第十七章

  昨天我們提到了利用 FlexMessage 來讓 LINE 聊天機器人創造出一個有點像是問答的環境,藉此從 Heroku Postgres 資料庫中選擇我們有興趣的其中幾筆資料。因為 FlexMessage 實在太有趣了,而昨天我又講得太匆忙,因此今天我們就來仔細的了解下 FlexMessage。我會從 FlexMessage 的基本架構開始,如何方便的寫出一個 FlexMessage 框架,如何將我們的參數送進 FlexMessage,到最後,如何讓 LINE 聊天機器人發送 FlexMessage,把我所知的 FlexMessage 講清楚說明白。
  首先來看一看今天的目標:我希望能作出一個根據使用者訊息而產生變化的 FlexMessage。圖一是實作的結果:

https://ithelp.ithome.com.tw/upload/images/20190927/20120178oyOcXpuAGr.png
圖一、陽光、沙灘、比基尼

  我利用關鍵字 flex 告訴 LINE 聊天機器人我們想要看 FlexMessage,請給我 FlexMessage。LINE 聊天機器人於是給了我一個 FlexMessage,上頭有我輸入的訊息(陽光)、該訊息的英文翻譯(sunlight)、與該訊息相關的圖片,以及圖片的出處:沒錯,我們把 FlexMessage 變成了一個單字小卡!難道是空中 FlexMessage 教室?

FlexMessage 的基本架構

  LINE 所建立的 FlexMessage 是一個超級巨大的字典物件,對寫網頁有點概念的人應該會發現,其實這個設計就跟網頁的架構有點像。FlexMessage 的基本架構分為"header""hero""body"、跟"footer",如圖二➀。

https://ithelp.ithome.com.tw/upload/images/20190927/20120178JJ8aJNL75k.png
圖二、FlexMessage 的基本架構

  要建立 FlexMessage,的一件事就是寫出包含這些基本架構的字典:

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

  第一個參數 "type": "bubble"可以想像成我們在創造一個 FlexMessage 的框架,接下來所有的基本架構都會放在這個框架裡。當然,也不是說這四樣基本架構通通都要放進去。如果你的 FlexMessage 需要一個"header",那就放進去"header"。如果你的 FlexMessage 不需要"footer",那就不用放進去。
  有了這四個基本架構之後,就可以根據你的設計,繼續在"header""hero""body""footer"中放入相對應的元素。這些元素也通通都是由字典物件所組成,常用的如'box''image''text''separator''button'等等➀。

box = {'type':'box',
            'layout':,
            'contents':}
image = {'type':'image',
            'url':,
            'size':,
            'aspect_ratio':,
            'aspect_mode':,
            'action':}
text = {'type':'text',
            'text':,
            'size':,
            'align':,
            'color':}
separator = {'type':'separator',
            'type':}
button = {'type':'button',
            'type':,
            'style':,
            'color':,
            'action':}

  其中'box'裡面的'contents'是一個由字典物件組成的清單,所以清單當中可以按照你的需求,再放入相對應的各種字典物件➁,如圖三

https://ithelp.ithome.com.tw/upload/images/20190927/20120178AAB70MiTRU.png
圖三'box'裡面的'contents'

  看仔細一點:

"body": {
    "type": "box",
    "layout": "vertical",
    "spacing": "md",
    "contents": [
      {"type": "separator"},
      {"type": "text", "text": "我是\"box\"中的第一個物件", "size": "lg", "weight": "bold"},
      {"type": "text", "text": "我是\"box\"中的第二個物件", "size": "lg", "weight": "bold", "color": "#888888"},
      {"type": "separator"}
      ]
}

  FlexMessage 當中的"box"字典物件,裡面有一個項目"contents""contents"是一個清單,而我們可以在其中放入需要的元素。由此層層疊疊,就可以組建出我們想要的 FlexMessage。

FlexMessage 模擬器 (Flex Message Simulator)

  當我們對 FlexMessage 提供的各種元素越來越熟悉之後,也許就會開始想要設計一個比較複雜的 FlexMessage。但漸漸地,隨著 FlexMessage 變得越來越複雜,我們也越來越難從一行行的程式碼想像出最後得到的結果。因此 LINE 官方很親切地提供了一個 FlexMessage 模擬器。就像寫 HTML 5 的檔案,我們可以從 codepen ➂即時看到結果,寫 FlexMessage,我們也可以藉由 FlexMessage 模擬器,馬上得知我們的程式碼還有哪裡需要修改。
  首先登入 LINE Developers,來到 Provider List 的頁面,看向左手邊,看到有一排 Providers、Tools、Support,點選 Tools,在點擊畫面中央的 Flex Message Simulator,如圖四,就會跳出一個分頁供我們編輯 FlexMessage 了。

https://ithelp.ithome.com.tw/upload/images/20190927/20120178n2dOWujU73.png
圖四、使用 FlexMessage 模擬器

  從零開始設計一個 FlexMessage 這種龐大的架構並不是辦不到,但真的挺累人的。還好還又另一種方法:進入 FlexMessage 模擬器之後,點選畫面右上角的加號( + ),就可以看到 LINE 提供的幾種 FlexMessage 模板,如圖五。我們可以選擇與我們設計需求相近的模板,再從模板的程式碼開始,慢慢的修修改改去得到我們的目標。修改的過程中,也可以隨時點擊 Preview 去觀看結果,如圖六

https://ithelp.ithome.com.tw/upload/images/20190927/201201786o22MVlWx2.png
圖五、LINE 所提供的 FlexMessage 模板

https://ithelp.ithome.com.tw/upload/images/20190927/20120178ltS0gfGHHN.png
圖六、Flex Message Simulator Preview

在 FlexMessage 當中放入變數

  藉由 FlexMessage 模擬器的幫忙,我們成功的做出了想要的 FlexMessage 外觀。接下來,我們希望這個 FlexMessage 當中的元素,能夠跟著我們發送的訊息而有所改變。以圖一為例,我們希望 FlexMessage 當中"header"的文字、"hero"的圖片、"footer"的連結,會因為我們發送的訊息而給出相應的回答。
  怎麼做呢?那還不簡單:

text = event.message.text
translate = get_translate(event.message.text)

"header": {
    "type": "box",
    "layout": "horizontal",
    "contents": [{"type": "text", "text": text, "size": "xl", "weight": "bold"},
                 {"type": "text", "text": translate, "size": "lg", "color": "#888888", "align": "end", "gravity": "bottom"}]
}

  以我的「空中 FlexMessage 教室」為例,可以看到我的"header"中有兩個變數texttranslate,直接把它們放在相對應的位置上就行了。當然,get_translate()這個函數是等等我們要來定義的。再看一例:

random_img_url = get_img_url(event.message.text)
"hero": {
    "type": "image",
    "url": random_img_url,
    "size": "full",
    "aspect_ratio": "20:13",
    "aspect_mode": "cover"
}

  一樣的,我在"hero"當中放入了圖片,而圖片的網址就是我們的變數,藉由我們之前寫過的get_img_url()這個函數來獲得。這邊要稍微注意一下:如果你用的是 Python,最後要利用 line-bot-sdk-python 這個套件來執行這些程式碼的話(像我一樣),那麼"aspectRatio""aspectMode"這種用駝峰式命名➃的項目在你的 Pyhont 檔案裡都要記得改成"aspect_ratio""aspect_mode"小寫加底線的命名方式,這樣 line-bot-sdk-python 才看得懂。
  知道怎麼在 FlexMessage 當中放入變數之後,接下來就回過頭來看看怎麼寫get_translate()這個函數。
  就像之前寫過的找圖函數一樣,我們請 google 大神幫我們翻譯。最直接快速的方式,就是在 google 搜尋列上輸入:「我有興趣的東西 英文」,google 就會很貼心地幫忙翻譯了,如圖七,而我們要找的,就是 google 翻譯出來的文字:

https://ithelp.ithome.com.tw/upload/images/20190927/20120178PpzdgaWyca.png
圖七、google 的貼心翻譯

def get_translate(text):
    
    translate = f'{text} 英文'
    
    url = f"https://www.google.com/search?{urllib.parse.urlencode(dict([['oq', translate], ['q', translate]]))}/"
    headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'}

    req = urllib.request.Request(url, headers = headers)
    conn = urllib.request.urlopen(req)

    data = conn.read()
    
    pattern = '<span tabindex="0">\S*</span>'
    match = re.search(pattern, str(data))
    
    return match.group()[19:-7]

  這樣子就算是翻譯好了!

發送 FlexMessage

  我們從了解 FlexMessage 的基本架構開始、用 FlexMessage 模擬器創立符合我們需求的 FlexMessage、並把變數送進 FlexMessage 當中,距離把 FlexMessage 裝備到 LINE 聊天機器人只剩一步之遙了,那就是:教 LINE 聊天機器人發送 FlexMessage。
  就像之前我們用TextSendMessage()ImageSendMessage()一樣,LINE 也有提供一個FlexSendMessage()函數來發送 FlexMessage。基本的方送方法如下:

line_bot_api.reply_message(
    event.reply_token,
    FlexSendMessage(alt_text, contents)
)

  FlexSendMessage()當中需要兩個參數,alt_textcontentsalt_text是 LINE 跳出通知時,上面會顯示的文字,而contents就是我們上面寫出來的 FlexMessage 那個複雜的字典物件。以我的 LINE 聊天機器人為例:

from linebot.models import FlexSendMessage

def img_search_flex(event):
    
    if re.match("flex", event.message.text.lower()):
        
        try:
            translate = get_translate(event.message.text[5:])
            random_img_url = get_img_url(img_source='pixabay', target=translate)

            contents = prepare_img_search_flex(event.message.text[5:], translate, random_img_url)

            line_bot_api.reply_message(
                event.reply_token,
                FlexSendMessage(
                    alt_text = f'flex {translate}',
                    contents = contents
                )
            )
  • 第一行:from linebot.models import FlexSendMessage
      要記得引入FlexSendMessage
  • 第三行: if re.match("flex", event.message.text.lower()):
      我這邊先下一個判斷,利用訊息裡面的"flex"作為啟動 FlexMessage 的條件。如果使用者輸入flex 想查的關鍵字,LINE 聊天機器人才會傳送 FlexMessage。
  • 第七行: contents = prepare_img_search_flex(event.message.text[5:], translate, random_img_url)
      我先寫了一個prepare_img_search_flex()的函數,用來創造 FlexMessage 當中的contents,並放入三個參數包括關鍵字、翻譯、圖片的網址,使得創造出來的contents會隨著關鍵字不同而改變。最後就是透過FlexSendMessage()將我們的 FlexMessage 發送到使用者手中啦!

  我們今天從 FlexMessage 的基本架構開始講起,講到了 FlexMessage 可用的許多元素,學到了利用 FlexMessage 模擬器快速做出符合需求的 FlexMessage,接著更進一步在我們的 FlexMessage 當中放入變數,使得 FlexMessage 可以隨著使用者輸入的訊息而改變,最後用 LINE 提供的 FlexSendMessage()這個函數將 FlexMessage 發送給使用者。經過這一番說明,大家是否有對 FlexMessage 更加了解了呢?試著寫寫看自己期望中的 FlexMessage 吧!今天相關的程式碼,我已經放到 Github 上了,有興趣的人歡迎過去我的 Github 參考看看。另外,如果覺得這篇文章中有哪部分說明不夠清楚,或是希望能夠再深入討論的,也歡迎大家在下面留言,謝謝大家!有關於 LINT 聊天機器人,以及 Heroku Postgres 資料庫的說明就到這裡。明天將要開始介紹用 Flask 架設網站,讓我們儲存在 Heroku Postgres 的資料能夠在網站上清楚呈現,邁向這個主題的最終目標:資料視覺化囉!

參考資料

➀ FlexMessage 基本架構
➁ FlexMessage 版面配置
codepen
➃ 駝峰式命名 wiki

註:對於此系列文有興趣的讀者,歡迎參考由此系列文擴編成書的 LINE Bot by Python,以及最新的系列文《賴田捕手:追加篇》
第 31 天 初始化 LINE BOT on Heroku
第 32 天 快速回覆 QuickReply 介紹
第 33 天 妥善運用 Heroku APP 暫存空間
第 34 天 妥善運用 LINE Notify 免費推播
第 35 天 製造 Deploy to Heroku 按鈕


上一篇
第 18 天:Heroku Postgres:連接 LINE 聊天機器人(二)
下一篇
第 20 天:Flask:基礎網頁製作
系列文
從LINE BOT到資料視覺化:賴田捕手30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言