《赖田捕手:番外篇》第 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 官方文件


<<:  [jest] Guides - Timer Mocks

>>:  JavaScript - 做个录音录影功能ㄅ

【Day01】楔子-关於永丰金融APIs

iT邦帮忙一直以来都是我查询技术问题的好夥伴;而铁人赛为IT界名闻遐迩的年度盛事。 在友人极力鼓吹报...

Day 15 — To Do List (1) 了解 HTML Service

昨天我们做完前置作业了,今天我们来看一下这个 HTML Service 是怎麽用! 简单来说,就是可...

# [Day17] 建立订单交易API_11

本节将接续上节,调整了一下request message中的ExpireDate 正确的respon...

Swift纯Code之旅 Day12. 「TableView(番外篇) - TableViewCell Accessory」

前言 现在我们已经很像IPhone的内建闹钟了,但是还是有一点不一样(下图红框圈起处) 因此今天就要...

Day5-自制网站卷轴(下)_我就特立独行

今天要介绍的是「如果我就想把卷轴放在不是最右边的位置怎麽办?」 这是自制网站卷轴的最後一篇啦~ 我知...