iT邦幫忙

1

《賴田捕手:番外篇》第 40 天:用 Netlify 整合前後端服務

《賴田捕手:番外篇》第 40 天:用 Netlify 整合前後端服務

故事還沒完呢,阿部。你也許會想問,那位英俊的年輕人是否騎著馬,經過了一座又一座的村莊?他會不會恰巧停下來向村民們要一杯水來喝?會不會恰巧拿出用 Flex Message 做的名片,分給熱心友善的村民們?

~節錄自《而 Netlify 的回音蕩漾》

  洋洋灑灑走過了 4 篇文章,相信大家對於如何在 Netlify 上佈署前端、後端服務都越來越不陌生了。還記得系列文一開始我們提到 (?),最終目標是要作出能夠為使用者產生客製化名片的 Line Bot。在第 39 天的文章當中,我們透過 Netlify 所提供的後端服務 Netlify Functions,成功架設了一個可以根據使用者輸入的內容,產生專屬於使用者的個人名片,並遞交給使用者,如圖一

https://ithelp.ithome.com.tw/upload/images/20210801/201201788XGjH8qIIj.png
圖一、Line Bot 將客製化名片送交給使用者

  真是可喜可賀!但總覺得哪裡怪怪的?名片是該自己保存下來的東西嗎?應該不是這樣吧。名片不就是該一張一張的遞交給其他人,像天女散花一樣分送到每一個角落才對嗎?把精美的客製化名片回傳給使用者是否搞錯了什麼?
  是的,在這個系列文的最後,我們就來試試看,如何結合後端和前端,由 Line Bot 與使用者互動產生名片,接著由 LINE Front-end Framework (LIFF) 將精美的名片分送給好友。而這一切,全都免費佈署在 Netlify 上。

LINE Front-end Framework: LIFF

  在說明什麼是 LIFF 之前,先來說說為什麼需要用 LIFF。以圖一為例,為了做出美輪美奐的名片,我們動用了 Line 所獨有的特殊訊息:Flex Message。就像做網頁時使用層疊樣式表 (Cascading Style Sheets, CSS ) 做出多樣貌的排版和迷人的美術效果一樣,Line 所提供的 Flex Message 具有十足的彈性和許多常見的排版功能,讓我們可以自由自在的設計出心目中的卡片。有興趣的人可以從 LINE Developers 找到 Flex Message Simulator➀,見識見識 Flex Message 是如何做出與眾不同又別緻有型的訊息 (與其說訊息,其實更接近卡片了)。
  如此完美的 Flex Message,卻有一個致命的缺點(?),那就是 Flex Message 的取得和分享非常困難。不知道大家有沒有從朋友那兒收過 Flex Message?應該是幾乎沒有。因為 Line 並沒有提供這樣的服務,使用者之間頂多傳送文字、貼圖、圖片、檔案等,傳送 Flex Message 幾乎只有 Line Bot 才做得到。除此之外,即使從 Line Bot 收到了 Flex Message,Line 也不提供將 Flex Message 轉傳給其他人的功能,頂多只能將 Flex Message 截圖下來。不過這樣就失去了 Flex Message 的美感。

  那我們用 Flex Message 作出來的名片,到底該怎麼發送給親朋好友呢?

  仔細的讀者可能注意到了,我剛才在描述 Flex Message 的時候,用的是「幾乎」沒有,或是「幾乎」不行。沒錯,可能性並不是零,那對我們來說,就是百分之百了呢!讓 Flex Message 不再是 Line Bot 的專利,讓使用者之間傳送 Flex Message 的可能性從零變成百分之百的,就是 LIFF!
  什麼是 LIFF?說的抽象一點,LIFF 是 Line 所提供的前端框架,可以跟 Line 本身的資源做結合,方便前端網頁取得 Line 使用者的資料 (當然,需要使用者同意),包括使用者 ID、暱稱、大頭照、狀態等等,除此之外,還能夠代表使用者發送訊息。若要說的簡單一點的話,就是 LIFF 為我們完成了從使用者到使用者發送 Flex Message 的夢想 (咦?)。既然如此,那就讓我們快點來建立一個 LIFF 服務,實現向親朋好友發送 Flex Message 的夢想吧。

申請 LIFF app

  就像在建立 Line Bot 服務一樣,首先登入 LINE developers,進入服務提供者 Provider 的頁面,然後選擇 Create a new channel。依據服務的內容,目前 Line 提供有 5 種不同種類的 channel。要創建 Line Bot,我們需要的 channel 是 Messaging API。而想要用 LIFF,我們需要的 channel 是 LINE Login,如圖二

https://ithelp.ithome.com.tw/upload/images/20210801/20120178WOLr8VoFXG.png
圖二、選擇 LINE Login

  選擇要建立一個 LINE Login channel 之後,就來到申請的流程。在這邊,我們需要填入一些 LINE Login channel 的相關資料,包括:

  • Channel type
  • Provider
  • Region
  • Channel icon
  • Channel name
  • Channel description
  • App type
  • Email address
  • Privacy police URL
  • Term of use URL

  其中,Channel type 跟 Provider 都已經帶入預設值,而 Privacy police URL 跟 Term of use URL 這兩欄可填可不填,剩下的隨各位高興填寫就好。完成了之後,Provider 底下應該就會多出一個新的 LINE Login channel。來到該 channel 的基本設定頁面。絕大多數的資料我們都用不到,只要切換到 LIFF 頁面就好。
  如果各位跟我一樣,都還沒建立任何 LIFF app,那應該會看到一個空空如也的頁面,如圖三,並且有一行說明寫著:「Add a LIFF app to get started」,那麼就照著做,點選【Add】按鈕來建立新的 LIFF app 吧。

https://ithelp.ithome.com.tw/upload/images/20210801/20120178qrHOJoljyT.png
圖三、切換到 LIFF 頁面並且點選【Add】建立新的 LIFF app

  相同的,建立 LIFF app 也有一些相關資料等著我們來填,包括:

  • LIFF app name
  • Size
  • Endpoint URL
  • Scopes
  • Bot link feature

  這邊的設定就比較重要了,讓我們一欄一欄來看➁。

  • LIFF app name:
      名字,隨各位喜好填就行。

  • Size:
      LIFF app 說穿了,其實就是帶有 Line 前端框架的網頁,或說,其實就是網頁。因 此我們可以用現代化的瀏覽器,如 Chrome、Firefox、Safari等來開啟,或者,當使用者在手機上用 Line 點選 LIFF app 的網址時,會用 LIFF browser 開啟。這個新開啟的 LIFF browser,會根據我們在這邊做的設定,來決定是要完全佔據螢幕畫面 (Full),佔據畫面的四分之三 (Tall),或是佔據畫面的二分之一 (Compact)。示意如圖四,參考自 LIFF app 的官方文件。

https://developers.line.biz/assets/img/viewTypes.075a87ea.png
圖四、參考自 LIFF app 官方文件:Size of the LIFF browser

  • Endpoint URL:
      LIFF app 其實就是一個用 Line 前端框架包裝起來的網頁,所以這個 Endpoint URL 就是前端網頁所在的網址。因為我們還沒開始撰寫需要的網頁內容,也還沒正式將其佈署到任何地方,所以這邊先隨便填一個就好。注意,為了資訊安全緣故,Line 只允許我們提供具有 HTTPS 傳出協定的網址。想不到要填什麼就先填個https://www.netlify.com/

  • Scopes:
      前面提過,我們可以用 LIFF app 來取得使用者的個人公開資料,如暱稱、大頭貼,隱私資料如 Email,甚至能夠代表使用者發送訊息。不過在那之前,我們必須要做好設定並且徵得使用者同意。Scope 就是這個設定。如果我們想要拿到使用者的公開資料,請勾選【profile】。如果想要拿到使用者的隱私資料,請勾選【openid】。如果想要代表使用者發送訊息,請勾選【chat_message.write】。勾選這些只是代表我們想要用這些功能而已,並不代表使用者一定會同意。當使用者開啟並登入我們的 LIFF app 時,LIFF app 就會根據這邊的設定,告知使用者我們想要用的功能。
    以接下來示範的名片分享 LIFF app 來說,我們只要勾選【profile】就行。

  • Bot link feature:
      這個設定可以決定 LIFF app 是否要跟 Line Bot 綁定在一起,白話來說,是否要藉由這個 LIFF app 推銷我們的 Line Bot。【On (Normal)】就是要推銷,但不強制,使用者會看到我們 Line Bot 的相關訊息,但使用者可自由選擇是否要加好友。【On (Aggressive)】就是要強制推銷,如果使用者想要用這個 LIFF app,就要加我們 Line Bot 的好友。【Off】就是不推銷。

【註】
若要推銷 Line Bot,記得到 LINE Login channel 的 Basic settings 頁面,Linked OA 這一欄,選擇要推銷的 Line Bot。一個 LINE Login channel 只能推銷一個 Line Bot。

  剩下非必需的選項沒那麼重要,等等也不會用到,就不多特別介紹了。
  完成之後,如圖五,會看到多了一個剛才建立好的 LIFF app,並且分配到了 LIFF ID 以及 LIFF URL。這邊比較重要的是,記得將【shareTargetPicker】這個選項勾起來。這正是等等我們要用來發送 Flex Message 的功能。到這邊,就算完成申請 LINE Login channel 以及 LIFF app 的前置作業了。

https://ithelp.ithome.com.tw/upload/images/20210801/20120178FgH8a8cKgx.png
圖五、記得勾選【shareTargetPicker】

撰寫 LIFF app 網頁內容

沒問題的話,就讓我們先來做個簡單的 LIFF app 試看看:

  • netlify-line-bot-demo/build/index.html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>LIFF Starter</title>
</head>

<body>
  <script charset="utf-8" src="https://static.line-scdn.net/liff/edge/2/sdk.js"></script>
  <script src="./index.js"></script>
</body>

</html>

  一個最單純的 HTML 網頁,這邊只有兩件事要注意:

  • 第八行:<script charset="utf-8" src="https://static.line-scdn.net/liff/edge/2/sdk.js"></script>
      既然說是 LIFF app,當然要引入需要用的資源庫,也就是 Line 前端框架,這一行程式碼的作用即是為此。

  • 第九行:<script src="./index.js"></script>
      引入我們自己需要用的程式碼index.js。當然還要寫一些自己的程式碼,不然引入 Line 前端框架不就沒有意義了。

  那麼,我們可以用 Line 前端框架來做些什麼呢?

  • netlify-line-bot-demo/build/index.js
async function liffInit() {
  await liff.init({ liffId: "你-LIFF app-的-LIFF ID" });
  if (!liff.isLoggedIn()) {
    liff.login({ redirectUri: window.location.href });
  }
}

async function liffProfile() {
  const profile = await liff.getProfile();
  const displayName = profile.displayName;
  const h1 = document.createElement("h1");
  const textNode = document.createTextNode(`Hello ${displayName}`);
  h1.appendChild(textNode);

  const body = document.querySelector('body');
  body.appendChild(h1);
}

async function main() {
  await liffInit()
  await liffProfile()
}

main()
  • 第一行:async function liffInit() {
      自己定義一個初始化 LIFF app 的函式,內容包括初始化 LIFF app 以及要求使用者登入。

  • 第二行:await liff.init({ liffId: "你-LIFF app-的-LIFF ID" });
      因為 LIFF app 能夠取得使用者資料等等相對隱私的資料以及操作,所以 Line 前端框架不是只要引入就可以用,必須要認明身分。這一段程式碼就是在做這件事。還記得我們建立了 LIFF app 之後,拿到一組 LIFF ID 以及 LIFF URL 嗎?就把拿到的 LIFF ID 填在這邊。

  • 第三行:if (!liff.isLoggedIn()) {
      要讓 LIFF app 發揮作用,通常會要求使用者要登入,不然我們哪來的使用者資料、代替使用者發送訊息這些東西可玩呢?這一行成式碼說的是,如果使用者還沒登入的話。

  • 第四行:liff.login({ redirectUri: window.location.href });
      那就要求使用者登入。登入之後,用window.location.href重新導向原本的網址。

  • 第七行:async function liffProfile() {
      這邊我們小試身手,用 LIFF app 提供的功能來取得使用者資料看看。

  • 第八行:const profile = await liff.getProfile();
      利用 Line 前端框架所提供的函式liff.getProfile()取得使用者的公開資料,傳回來的內容大致如下:

{
  "userId": 代表使用者身分的代碼,
  "displayName": 使用者暱稱,
  "pictureUrl": 使用者大頭貼,
  "statusMessage": 使用者狀態訊息
}

  其中,使用者大頭貼跟使用者狀態訊息是只有使用者有自行設定的情況才會傳回來的資料。

  • 第九行:const displayName = profile.displayName;
      從使用者資料profile當中拿出使用者暱稱displayName

  剩下的就是利用 JavaScript 對 DOM 的操作,就不詳細說明。具體來說,是將使用者暱稱以 h1 標籤的方式顯示在網頁上。因此我們做出的這個網頁,就會在使用者登入之後,像是在跟使用者打招呼一樣,顯示出大大的Hello ${displayName}

  沒問題的話,就將這個網頁佈署到 Netlify 上面,完成之後記得修改 LIFF app 當中的 Endpoint URL (還記得一開始我們先隨便給了一個網址嗎),如圖六。接著,要看到 LIFF app 的成果,我們必須要從 LIFF URL 進入我們的網頁才行 (而不是 Endpoint URL)。運作結果大致如圖七所示。由於我們寫的 LIFF app 會先檢查使用者是否登入(liff.isLoggedIn()),若否,則帶領使用者來到登入頁面(liff.login()),所以當我們透過 LIFF URL 進入 LIFF app,第一件事情就是被引導到登入頁面,如圖七左邊的情形。登入的同時,LIFF app 會跟著詢問使用者是否願意授權 LIFF app 即將採取的動作,如取得使用者資料、代表使用者傳送訊息等等 (實際情況會依據 LIFF app 的 Scopes 設定不同而有所差別)。都同意之後,LIFF app 才會真正運作起來,利用liff.getProfile()取得使用者資料,並顯示在網頁上,如圖七右邊。

https://ithelp.ithome.com.tw/upload/images/20210801/20120178v5rV6JCGp3.png
圖六、修改 Endpoint URL,並從 LIFF URL 進入我們的 LIFF app

https://ithelp.ithome.com.tw/upload/images/20210801/20120178EGDze5OoQ3.png
圖七、哈囉大家好

  沒錯,看到這裡,我們就成功的完成了一個簡單的 LIFF app!

用 shareTargetPicker 傳送 Flex Message

  回顧一下為什麼我們想要學著使用 LIFF 呢?原因很簡單,我們想要幫助使用者發送客製化的名片,也就是 Flex Message,給同樣是 Line 使用者的親朋好友們。用 Line Bot 可以藉著和使用者互動,讓使用者輸入名片內容所需要的資料。用 LIFF,可以代表使用者傳送 Flex Message。兩者結合,就可以將客製化的名片傳送給親朋好友了。因此,我們可以設計如下所示的運作流程:

  1. Line Bot 和使用者互動,取得名片內容資料
  2. 引導使用者來到 LIFF app 所在的網址,並將名片內容資料以查詢字串的方式一併帶到 LIFF app
  3. 在 LIFF app 當中,透過查詢字串重現客製化名片 (Flex Message)。
  4. 將客製化名片 (Flex Message) 傳送給親朋好友。

  我們已經知道怎麼設計 Line Bot 並且儲存需要的資料,同時也學會建立一個基本的 LIFF app 了。因此這一個章節,我們就來了解一下怎麼設計一個能夠幫助使用者傳送訊息的 LIFF app。

  LIFF app 是 Line 所提供的前端框架,因此能用前端網站實作出與 Line 結合的功能。若要在前端網站中擷取 Line 使用者的個人資料,可以先用liff.login()讓使用者登入 Line 帳號,接著用liff.getProfile()取得使用者個人資料。若想要代表使用者發送訊息,則可以用liff.shareTargetPicker(),如此一來,LIFF app 上就會跳出使用者的朋友清單,供使用者自由選擇要將訊息分享 (share) 給哪一位幸運兒 (target)。  
  liff.shareTargetPicker()的使用方式如下:

if (liff.isApiAvailable('shareTargetPicker')) {
  liff.shareTargetPicker([
    {
      'type': 'text',
      'text': 'Hello, World!'
    }
  ])
}
  • 第一行:if (liff.isApiAvailable('shareTargetPicker')) {
      先用liff.isApiAvailable('shareTargetPicker')確認一下當前的 LIFF app 是否可以使用 shareTargetPicker。還記得我們在 Developers Console 建立好一個 LIFF app 之後,有提醒大家要把【shareTargetPicker】這個選項勾選起來吧 (如圖五)。若有確實勾選的話,那這個 LIFF app 就是一個能用 shareTargetPicker 的 LIFF app。

  • 第二行:liff.shareTargetPicker([
      直接呼叫liff.shareTargetPicker()來開啟 shareTargetPicker。這個函式可以接受一個 JavaScript Array 當作函式的參數,Array 的內容則是 Line 的訊息物件 (Message Object)。shareTargetPicker 一次可以傳送五個訊息,也就是說這個 Array 內最多可以放進 5 個訊息物件。shareTargetPicker 可以處理的訊息種類包括文字訊息、圖片訊息、影片訊息、音訊訊息、位置訊息、樣板訊息 (template message)、以及我們最想要的 Flex Message。幾個 Line 常見的訊息物件格式如下:

  • 文字訊息:

{
    "type": "text",
    "text": "Hello, world"
}
  • 圖片訊息:
{
    "type": "image",
    "originalContentUrl": "https://example.com/original.jpg",
    "previewImageUrl": "https://example.com/preview.jpg"
}
  • Flex Message:
{
  "type": "flex",
  "altText": "this is a flex message",
  "contents": {/* Flex Message 的內容 */}
}

  要傳送 Flex Message,一定要填好這 3 個內容,包括type,也就是訊息的種類,當然是填入"flex"altText,也就是 Flex Message 的輔助說明文字,這個可以按照各位的想法隨意填入、以及 Flex Message 的真正內容contents
  其他的訊息就不一一介紹了,有興趣的可以自行到 Line Messaging API 官方文件上閱讀相關內容➂。
  了解liff.shareTargetPicker()需要的參數之後,回過頭來看上面那一段程式碼,其實就是透過liff.shareTargetPicker()的功能,將文字訊息'Hello, World!'分享給其他 Line 使用者。這就是liff.shareTargetPicker()的使用方式,是不是很簡單呢?
  那麼不囉嗦,我們就來實作一個可以接收查詢字串 (query string),加以解析,並根據其資訊做成 Flex Message 的 LIFF app 吧!

  • netlify-line-bot-demo/build/index.js
import flexCard from './liffFlex.js'

async function liffInit() {
  await liff.init({ liffId: "你-LIFF app-的-LIFF ID" });
  if (!liff.isLoggedIn()) {
    liff.login({ redirectUri: window.location.href });
  }
}

function createButton(profile, body) {
  const urlParams = new URLSearchParams(window.location.search);
  const userImage = profile.pictureUrl;
  const userReply = [urlParams.get('name'), urlParams.get('phone'), urlParams.get('email'), userImage];
  const flexContent = flexCard(userReply);
  const handleClick = async () => {
    if (liff.isApiAvailable("shareTargetPicker")) {
      try {
        const result = await liff.shareTargetPicker([
          {
            "type": "flex",
            "altText": `${urlParams.get('name')} present name card from Netlify`,
            "contents": flexContent
          }
        ])

        if (result) {
          alert('Flex Message success');
        }

      } catch (error) {
        alert("Flex Message got some error");
      }
    }
  }

  const button = document.createElement("button");
  button.innerHTML = "Share Your Name Card";
  button.onclick = handleClick;

  body.appendChild(button);
}

async function main() {
  await liffInit()
  const profile = await liff.getProfile();
  const body = document.querySelector('body');
  createButton(profile, body);
}

main()
  • 第一行:import flexCard from './liffFlex.js'
      避免程式碼變得太冗長複雜,我們將製作名片 (Flex Message) 的程式碼放到另一個檔案當中。因此在開頭的時候,我們用import引入這個檔案。

  • 第二行:async function liffInit() {
      同樣的,因為我們的 LIFF app 需要使用者的資料,同時也要能夠代表使用者發送訊息,因此在這個 LIFF app 的最開始,除了需要用liff.init()認證 LIFF app 之外,還需要用liff.login()讓使用者登入。因為操作方式跟前面介紹的一樣,這邊就不在贅述。

  • 第八行:function createButton(profile, body) {
      在這個 LIFF app 當中,我們把liff.shareTargetPicker()的功能包裝在一個按鈕當中,使用者點擊這個按鈕,便會讓liff.shareTargetPicker()運作起來。所有的運作邏輯,我們就放在createButton()這個函式當中。

  • 第九行:const urlParams = new URLSearchParams(window.location.search);
      利用window.location.search獲得當前網址的查詢字串 (從?開始的查詢字串),並作為參數來初始化一個URLSearchParams物件。URLSearchParams是 JavaScript 特別提供用來處理查詢字串的物件,可以將查詢字串轉變成鍵值配對 (key-value pair),根據鍵值配對做出各種操作,還能夠改變查詢字串。

  • 第十行:const userImage = profile.pictureUrl;
      利用liff.getProfile()拿到使用者的個人資料之後,呼叫pictureUrl這個屬性來取得使用者的大頭貼。

  • 第十一行:const userReply = [urlParams.get('name'), urlParams.get('phone'), urlParams.get('email'), userImage];
      做出一個 JavaScript 陣列 (Array),這個陣列將作為參數,進一步做出客製化的卡片 (Flex Message)。陣列內容將會從查詢字串當中取出欲呈現在卡片上的名字(name)、電話(phone)、電子郵件(email)的資料。舉例來說,如果我們的查詢字串是?name=華安&phone=9527&email=9527@華府.com,那麼利用urlParams.get('name')我們可以拿到鍵值配對當中鍵是name的值華安。同理,裡用phone拿到9527,利用email拿到9527@華府.com

  • 第十二行:const flexContent = flexCard(userReply);
      將userReply這個包含所需資訊的陣列作為參數,製作出客製化的卡片 (Flex Message)。

  • 第十三行:const handleClick = async () => {
      定義一個函數handleClick,這個函數將會跟我們的按鈕結合,使用者點擊按鈕,就會呼叫這個函式。由於liff.shareTargetPicker()會在使用者的好友選單 (Target Picker) 出現,並且傳送了訊息 (或取消動作) 之後,傳回 JavaScript Promise 物件,因此我們就將這個函式寫成非同步 (Async/Await) 函式。

  • 第十四行:if (liff.isApiAvailable("shareTargetPicker")) {
      用liff.isApiAvailable()先確認該 LIFF app 能不能夠使用 shareTargetPicker。

  • 第十六行:const result = await liff.shareTargetPicker([
      使用liff.shareTargetPicker()開啟好友選單,並且傳送客製化的卡片 (Flex Message) 給指定的好友。完成之後,將 Promise 物件傳回來的內容儲存到result

  • 第二十三行:if (result) {
      liff.shareTargetPicker()會在訊息成功傳送出去之後,傳回{status: "success"}這樣的內容。因此,這邊可以用一個簡單的判斷是來看是否順利傳出訊息。

  • 第二十四行:alert('Flex Message success');
      若有順利傳出訊息,就用alert()來告訴使用者訊息已經被順利傳送到指定的好友那邊了。

  • 第二十六行:} catch (error) {
      若在過程中發生錯誤,用catch接住。

  • 第二十七行:alert("Flex Message got some error");
      並用alert()告訴使用者出狀況了。

  剩下的程式碼,執行的內容大致上來說就是幫我們把需要的按鈕準備好。另外,根據參數userReply而做出客製化卡片 (Flex Message) 的程式碼詳細如下:

  • netlify-line-bot-demo/build/liffFlex.js
const colorDefault = "#666666"
const colorNetlify = "#00ad9f"

const flexCard = (userReply) => {
  console.log(userReply);
  const [name, phone, email, userImage] = userReply;

  return {
    "type": "bubble",
    "body": {
      "type": "box",
      "layout": "vertical",
      "contents": [flexNameContent(name, userImage), flexDetailContent(phone, email)],
      "paddingAll": "0px"
    }
  }
}

const flexNameContent = (name, userImage) => {
  return {
    "type": "box",
    "layout": "horizontal",
    "contents": [
      flexImage(userImage),
      {
        "type": "box",
        "layout": "vertical",
        "contents": [
          flexFiller(),
          flexText("迷途小書僮", colorNetlify, "xs", "bold"),
          flexText(name, colorDefault, "xl", "bold"),
          flexBar(colorNetlify)
        ]
      }
    ],
    "spacing": "xl",
    "paddingTop": "20px",
    "paddingStart": "20px",
    "paddingEnd": "20px"
  }
}

const flexDetailContent = (phone, email) => {
  return {
    "type": "box",
    "layout": "vertical",
    "contents": [
      {
        "type": "box",
        "layout": "horizontal",
        "contents": [
          flexText("Phone", colorNetlify, "md", "bold"),
          flexText(phone, colorDefault, "md", "regular", 2),
        ]
      },
      {
        "type": "box",
        "layout": "horizontal",
        "contents": [
          flexText("Email", colorNetlify, "md", "bold"),
          flexText(email, colorDefault, "md", "regular", 2)
        ]
      }
    ],
    "paddingBottom": "20px",
    "paddingStart": "20px",
    "paddingEnd": "20px"
  }
}

const flexImage = (userImage) => ({
  "type": "box",
  "layout": "vertical",
  "contents": [
    {
      "type": "image",
      "url": userImage,
      "aspectMode": "cover",
      "size": "full"
    }
  ],
  "cornerRadius": "100px",
  "width": "72px",
  "height": "72px"
})

const flexText = (text, color, size, weight, flex = 1) => ({
  "type": "text",
  "text": text,
  "color": color,
  "size": size,
  "weight": weight,
  "flex": flex
});

const flexFiller = () => ({ "type": "filler" });

const flexBar = (color) => ({
  "type": "box",
  "layout": "vertical",
  "contents": [],
  "height": "3px",
  "backgroundColor": color
})

export default flexCard;

  簡而言之,就是按照 Flex Message 的格式做出需要的內容,這邊就不特別說明。如此,就完成了我們能夠根據查詢字串產生相應名片內容的 LIFF app。試著佈署到 Netlify 上面,然後透過 LIFF URL 連到我們的 LIFF app 看看。記得要加上查詢字串:

https://liff.line.me/你-LIFF app-的-LIFF ID?name=華安&phone=9527&email=9527@華府.com

  登入 Line 帳好之後,是不是可以看到用來呼叫liff.shareTargetPicker() 的按鈕 Share Your Name Card 呢?點下去之後,試試看能不能像圖八一樣,看到好友選單,並將這一個 Flex Message 傳送給選定的朋友。

https://ithelp.ithome.com.tw/upload/images/20210801/20120178IQofCRr642.png
圖八、開始用 shareTargetPicker 大鬧一場吧

一些修飾工作

  好啦,今天我們從 LIFF app 開始介紹,學會 LIFF app 的基本設定,並實作出一個不僅能夠取得使用者個人資料,還能夠代表使用者發送訊息 (更別說是帥氣的 Flex Message) 的 LIFF app,精彩的差不多就到這邊,剩下就是一些必要但不困難的修飾工作:引導使用者來到 LIFF app。
  想想看,我們剛才透過手動輸入 LIFF URL 以及查詢字串,進入我們的 LIFF app 並傳送客製化名片。然而實際上,一般的使用者根本不會這麼做,沒人記得住我們的 LIFF URL,更別說該怎麼寫符合 LIFF app 需求的查詢字串了。因此,我們要在不知不覺中,幫助使用者做到這一件事。這也不困難,還記得 Line Bot 在我們回答完製作名片所需要的問題之後,向我們遞出客製化的名片了對嗎?只要在名片下方加上一個按鈕,利用這個按鈕連結到我們的 LIFF app 就行了!
  為此,我們要稍微修改一下 Line Bot 送出來的 Flex Message:

  • netlify-line-bot-demo/my_functions/lineBotWebhook/custom_module/qaisFlex.js
const flexCard = (userReply) => {
  console.log(userReply);
  const [_, name, phone, email] = userReply;

  return {
    "type": "bubble",
    "body": {
      "type": "box",
      "layout": "vertical",
      // 多加上 flexButton
      "contents": [flexNameContent(name), flexDetailContent(phone, email), flexButton(name, phone, email)],
      "paddingAll": "0px"
    }
  }
}

const flexButton = (name, phone, email) => {
  return {
    "type": "box",
    "layout": "vertical",
    "contents": [
      {
        "type": "button",
        "action": {
          "type": "uri",
          "label": "Share",
          // 將按鈕引導的 URI 設定成我們的 LIFF URI,並加上必要的查詢字串
          // 我們可以把 LIFF ID 藏在 Netlify 的環境變數當中
          "uri": `https://liff.line.me/${process.env.LIFF_ID}?name=${name}&phone=${phone}&email=${email}`
        },
        "style": "primary",
        "color": colorNetlify
      }
    ],
    "paddingAll": "20px"
  }
}

/* 其餘內容一切照舊 */

  關於 Flex Message 的製作方式以及格式已經做過蠻多說明了,有興趣的可以用 Line Developers Console 提供的 Flex Message Simulator 來設計自己想要的版面,這邊就不多做說明了。

  完成後的 Flex Message 如圖九,除了名片內容之外,最下面還多了一個寫著【Share】的按鈕,帶使用者進入我們的 LIFF app,並透過 LIFF app 來發送 Flex Message。這麼一來這個系列文就告一段落了,謝謝大家的閱讀,任何我寫不清楚的地方都歡迎留言討論。

https://ithelp.ithome.com.tw/upload/images/20210801/201201787WTVtz9WXc.png
圖九、我也學會傳送 Flex Message 了!

參考資料

➀ Flex Message Simulator 官方介紹
➁ LIFF app 設定 官方文件
➂ Line Message Object 官方文件


1 則留言

0
Hank Hsiao
iT邦新手 5 級 ‧ 2021-09-05 19:26:43

請問您有在手機 ios safari 試過呼叫 liff.shareTargetPicker() 嗎?我一呼叫畫面會閃一下然後就觸發 TargetPicker was closed! 不會跳出選擇朋友的畫面。但是在桌機的瀏覽器都沒有問題。想問您有沒有遇到一樣的狀況?謝謝。

我要留言

立即登入留言