iT邦幫忙

2025 iThome 鐵人賽

DAY 15
0
Modern Web

Line Bot × NestJS:30 天開發日記系列 第 15

Day 15:共通屬性處理優化及 Imagemap 圖片製作

  • 分享至 

  • xImage
  •  

2025 鐵人賽背景圖

前言

針對 Day 14 的程式碼架構進行優化。由於 SenderQuick Reply 功能在所有回覆訊息類型中都能使用,我們應該將這些共用屬性抽取為獨立函數進行統一處理。

接下來將詳細說明 ImageMap Message 圖片的製作流程。ImageMap MessageRich Menu 在概念上十分相似,兩者皆需要將圖片劃分為不同的互動區塊,並為每個區塊設定相對應的觸發事件。

ImageMap 處理流程涉及多個工具協作,整體步驟如下:

  1. Canva 創建 ImageMap 背景圖片:使用 Canva 設計並匯出 ImageMap 所需的背景圖片。
  2. Bot Designer 產生 JSON 資料:透過 Bot Designer 根據 Canva 創建的背景圖片產生 ImageMap 的 JSON 資料。由於 ImageMap 的參數設定較為複雜,建議使用 Bot Designer 來自動產生基礎結構。
  3. 處理 baseUrl 的限制問題:Bot Designer 產生的 JSON 中包含 baseUrl 參數,但直接使用 Cloudinary 的網址並不符合 LINE ImageMap 的規範。
    • 不能包含副檔名
    • 必須支援動態路徑存取五種不同解析度(240、300、460、700、1040)
  4. CloudFlare Worker 作為代理層:為了解決上述限制,我們需要建立 CloudFlare Worker 作為代理層,處理副檔名移除和動態解析度路由的問題。
  5. 手動調整 JSON 的 baseUrl:將 Bot Designer 產生的 JSON 中的 baseUrl 替換成 CloudFlare Worker 的端點網址。
  6. 完成部署:修改後的 JSON 即可直接應用於後端伺服器,成功發送 LINE ImageMap 訊息。

今天主要會說明 1 ~ 3 項。

本日程式碼的範例連結

Sender 和 Quick Reply 優化

  • Sender:自定義頭像及名稱
  • Quick Reply:快速回覆按鈕

Step 1:共通型別定義

定義可選屬性,可應用於各種訊息型別

line-message/message-common.ts

import { QuickReplyItem, Sender } from '@line/bot-sdk';

export type MessageCommon = {
  sender?: Sender;
  quickReplyItems?: Omit<QuickReplyItem, 'type'>[];
};

Step 2:將回覆請求與共通型別結合為聯合型別

目前定義的所有訊息型別都可以加上共通型別

line-message/types/template-button-message.ts

import { Action } from '@line/bot-sdk';
import { MessageCommon } from './message-common';

type HttpsURL = `https://${string}`;

export type TemplateButtonMessageReq = MessageCommon & {
  altText: string;
  text: string;
  actions: Action[];
  title?: string;
  thumbnailImageUrl?: HttpsURL;
};

Step 3:定義私有方法處理共通屬性

建立私有處理方法,讓所有訊息請求都能共用此功能

line-message/line-message.service.ts

  private buildCommonMessageProps(
    sender: Sender,
    quickReplyItems: Omit<QuickReplyItem, 'type'>[],
  ): CommonMessageProps {
    return {
      ...(sender && { sender }),
      ...(quickReplyItems && {
        quickReply: {
          items: quickReplyItems.map((quickReplyItem) => ({
            type: 'action' as const,
            ...quickReplyItem,
          })),
        },
      }),
    };
  }

Step 4:在處理方法中使用私有方法處理共同屬性

以 Template Button Message 的 createTemplateButtonMessage 方法為例

line-message/line-message.service.ts

  createTemplateButtonMessage(
    templateButtonMessageReq: TemplateButtonMessageReq,
  ): TemplateMessage {
    const {
      altText,
      text,
      actions,
      thumbnailImageUrl,
      title,
      sender,
      quickReplyItems,
    } = templateButtonMessageReq;

    const templateButtonMessage: TemplateMessage = {
      type: MessageType.Template,
      altText,
      template: {
        type: MessageType.TemplateButton,
        text,
        actions,
        title,
        ...(thumbnailImageUrl && { thumbnailImageUrl }),
      },
      ...this.buildCommonMessageProps(sender, quickReplyItems), // 加入共同處理的部分
    };

    return templateButtonMessage;
  }

透過這種方式,後續新增其他回覆訊息的處理函數時,就能快速整合共同屬性功能,提升開發效率。

ImageMap Message

ImageMap Message 與一般圖片訊息有兩個主要差異。首先,顯示區域比一般 Image Message 更大,如同海報般的呈現效果;其次,可在圖片上設定互動區域,讓使用者點擊特定區塊觸發對應事件。

ImageMap 呈現差別

ImageMap 有兩個嚴格的規範必須遵守才能成功發送:

  1. 圖像連結限制:URL 不能包含任何副檔名(如 .jpeg、.png 等)
  2. 多尺寸需求:圖像必須提供五種不同寬度的版本,分別為 240px300px460px700px1040px。建議以 1040px 作為設計基底,再依比例縮放至其他尺寸。LINE 平台會根據使用者裝置解析度自動選擇最適合的圖片尺寸,並透過 baseUrl/{image width} 格式來動態載入不同解析度的圖片。

D+ AF 官方帳號 ImageMap 訊息
D+AF ImageMap 訊息

王品集團官方帳號 ImageMap 訊息
王品集團 ImageMap 訊息

使用 Canva 製作 ImageMap 圖片

製作 ImageMap 圖片時,建議搭配 Canva 的尺規功能,能更準確掌握圖片在各區塊內的呈現效果。設定自訂尺寸為 1040 x 1040 像素,以最大解析度作為設計基準。

Canva 尺規劃分效果

完成尺規設定後,使用形狀工具將畫布分割成四個矩形區塊,再將準備好的圖片素材匯入各區塊(範例使用 Unsplash 免費圖片)。
LINE 使用形狀放入背景照片

至此完成了 ImageMap 圖片的製作!接下來需要將圖片上傳至 Cloudinary 生成對應的 HTTPS URL

完成圖片部署後,即可使用 LINE Bot Designer 進行互動區域設定,快速產生所需的 JSON 配置。

透過 Bot Designer 快速生成 ImageMap JSON 配置

ImageMap 最多支援 20 個區塊限制

Canva 製作的圖片以 PNG 格式下載並匯入 Bot Designer。使用滑鼠拖曳框選功能圈選對應區域,被選取的區塊將會亮起以便確認位置。根據圖片設計內容決定切割區塊的數量,以目前的規劃為例共分為四個區塊,接著為每個區域設定相對應的觸發事件(Action)。

區塊可以觸發的事件有三種:

  1. uri:點擊後打開對應連結。
  2. message:點擊後發送對應訊息。
  3. clipboard:點擊後將對應訊息複製到用戶剪貼簿上。

這邊注意到一件事情,即使很多地方都可以設置 action,但是能設置的種類是不同的

LINE Bot Designer

這邊我們都先設置 message action,點擊到對應的圖片發送訊息!

Cloudinary 產生五種不同尺寸的照片

這裡我們善用 Cloudinary 的圖片轉換功能,能快速產生五種不同解析度的圖片版本。

Cloudinary URL 結構:

https://res.cloudinary.com/<cloud_name>/image/upload/<transformation>/<public_id>

因此我們只需調整網址格式,在 transformation 參數中加入寬度設定:

  • w_240,f_auto,q_auto:設定寬度為 240px,高度及品質自動最佳化

調整後的網址範例:

https://res.cloudinary.com/dseg0uwc9/image/upload/w_240,f_auto,q_auto/v1754466769/2025%20IT%20%E9%90%B5%E4%BA%BA%E8%B3%BD/ImageMap_02_escz9a.png

透過這種方式,我們可以快速產生五種不同解析度的圖片版本。

然而,雖然 Cloudinary 能產生五種尺寸的圖片,但仔細觀察 URL 會發現仍違反 ImageMap 規範。不僅網址存在 .png 副檔名,也無法直接透過路由參數方式替換不同解析度,例如:透過 https://example.com/bot/images/rm001/240 來讀取 240px 的圖片。

這部分我們將會在明天透過 CloudFlare Worker 的方式進行圖片代理,解決這個問題。

本日結語

今天我們主要聚焦於優化 SenderQuick Reply 的管理方式,讓 LINE Bot 處理訊息傳遞時能夠減少參數複雜度,讓開發者能以更直觀的方式使用這些服務。

另一方面則專注於 ImageMap 的圖片製作流程。由於 ImageMap 有較為嚴格的技術限制,製作過程需要搭配多種工具。這邊盡可能採用服務免費方案,讓大家在學習體驗的同時,也能實際製作出理想的成果。

雖然本篇展示的是四格 ImageMap 設計,但在實務應用中,將 ImageMap 設置為單一區塊來呈現海報般的視覺效果也很常見,這種應用方式我們會在後續內容中進行展示。


上一篇
Day 14:LINE Bot Template Message 訊息回覆
下一篇
Day 16:Cloudflare Workers 圖片代理實作
系列文
Line Bot × NestJS:30 天開發日記18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言