正在載入索引…

Day 14:LINE Bot Template Message 訊息回覆

6.4k 字,約 6 分鐘

2025 iThome 鐵人賽:Line Bot × NestJS:30 天開發日記。 原文發表於 iThome,也可以在下方留言。

文章目錄
  1. 前言
    1. Template Message
    2. messageEventHandlerMap 型別調整
    3. Button Template
      1. Step 1:新增模板訊息請求的型別
      2. Step 2:建立模板訊息的封裝函式
      3. Step 3:在接收到使用者發送訊息 button 時回傳模板訊息
    4. Confirm Template
      1. 型別定義
      2. 處理函式
    5. Carousel Template
      1. 型別定義
      2. 處理函式
    6. ImageCarousel Template
      1. 型別定義
      2. 處理函式
    7. 本日結語

前言

經過前幾天的學習,我們已經成功掌握了 LINE Bot 六大基礎訊息類型的發送技巧,為訊息互動奠定了穩固的基礎。今天,我們將踏入更具挑戰性的進階應用領域!

在實際的 LINE Bot 開發場景中,除了六大基礎訊息類型外,LINE 官方還提供了三種進階訊息格式:Template Message、Imagemap Message 及 Flex Message。

今天,我們將深入探索第一種進階訊息格式 Template Message,學習如何運用預設的模板佈局,快速打造出視覺統一且互動豐富的訊息回覆。

本日程式碼的範例連結

Template Message

Day 13 提到的訊息共通參數 Sender 及 Quick Reply 同樣適用於所有進階訊息格式(Template Message、ImageMapMessage、FlexMessage)。

Template Message 可以理解為 LINE 官方預先設計的訊息模板佈局系統。開發者只需將所需參數代入模板中,即可快速產生具有統一視覺風格的互動式訊息。這種設計大幅提升了開發效率,但相對地在客製化程度上會有一定的限制。

模板訊息總共有四種:

  1. Buttons template
  2. Confirm template
  3. Carousel template
  4. Image carousel template

四種模板訊息的共通屬性
所有 Template Message 都必須包含 altText 屬性,作為 LINE 通知顯示的替代文字。當使用者收到推播通知時,會顯示此文字內容。

messageEventHandlerMap 型別調整

Message 型別是 LINE SDK 提供的所有訊息回覆類型的聯合型別

在先前的實作中,我們採用「輸入什麼型別,就回覆什麼型別」的對應模式。舉例來說:使用者輸入文字訊息,LINE Bot 就回應文字訊息。這種設計主要是為了方便展示六大基礎訊息類型的對照效果。

然而在實際開發場景中,我們不應該被這種限制束縛。當收到文字訊息時,我們應該能夠靈活回覆各種訊息類型,只要符合 LINE Bot 支援的回覆格式即可。因此,我們將型別調整為更通用的 Message 類型,讓訊息回覆更加靈活。

line-webhook/line-webhook.types.ts

ts
export type MessageEventHandlerMap = {
  [K in EventMessage['type']]: (
    event: Extract<EventMessage, { type: K }>,
  ) => Message;  // 主要調整這個部分,讓回傳型別不被限制
};

Button Template

支援可選標題及縮圖配置,並提供 1-4 個按鈕的互動選項。

Step 1:新增模板訊息請求的型別

核心參數:

  • altText:Line 通知顯示的文字
  • text:顯示內文(如:「按鈕小精靈出來吧」)
  • action:按鈕陣列(支援 1-4 個按鈕)

可選參數:

  • title:模板標題
  • thumbnailImageUrl:模板縮圖(如圖中的狗狗圖片),預設使用rectangle(長寬比:1.51:1)顯示。

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

ts
import { Action } from '@line/bot-sdk';

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

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

Step 2:建立模板訊息的封裝函式

這裡透過 createButtonTemplateMessage 函式來封裝複雜的模板訊息結構,讓開發者只需專注在必要參數的配置上,簡化訊息建立流程。

line-message/line-message.service.ts

ts
  createButtonTemplateMessage(
    templateButtonMessageReq: TemplateButtonMessageReq,
  ): TemplateMessage {
    const { altText, text, actions, thumbnailImageUrl, title } =
      templateButtonMessageReq;

    const buttonTemplateMessage: TemplateMessage = {
      type: MessageType.Template,
      altText,
      template: {
        type: MessageType.TemplateButton,
        text,
        actions,
        title,
        ...(thumbnailImageUrl && { thumbnailImageUrl }), // 可選參數的處理
      },
    };

    return buttonTemplateMessage;
  }

Step 3:在接收到使用者發送訊息 button 時回傳模板訊息

Button Template Message Action 最多只能四個!!

在 Action 類型的選擇上,常用的有三種:

  • message(訊息回傳):點擊後會發送預設文字訊息
  • postback(資料回傳):點擊後會回傳資料給後端,不會在聊天室顯示訊息
  • uri(開啟連結):點擊後會開啟指定的網址

當使用者傳送 button 文字時,我們會回覆事先準備好的 Button Template 訊息。

line-webhook/line-webhook.service

ts
private async handleMessageEvent(event: MessageEvent): Promise<void> {
     const messageEventHandlerMap = {
      text: (message) {
        const { text } = message;
        // text 是使用者傳送的文字內容
        if (text === 'button') {
          return this.lineMessageService.createButtonTemplateMessage({
            altText: '按鈕小精靈通知',
            thumbnailImageUrl: 'https://res.cloudinary.com/dseg0uwc9/image/upload/v1753953684/2025%20IT%20%E9%90%B5%E4%BA%BA%E8%B3%BD/dog_icon_grxcsl.jpg',
            title: '標題',
            text: '按鈕小精靈出來吧',
            actions: [
              {
                type: 'message',
                label: '我是按鈕 1',
                text: '我是按鈕 1 號',
              },
              {
                type: 'message',
                label: '我是按鈕 2',
                text: '我是按鈕 2 號',
              },
              {
                type: 'message',
                label: '我是按鈕 3',
                text: '我是按鈕 3 號',
              },
              {
                type: 'message',
                label: '我是按鈕~',
                text: '我是按鈕 4 號',
              },
            ],
          });
        }
      },
    } satisfies Partial<MessageEventHandlerMap>;
}

Confirm Template

專為決策判斷場景設計的模板訊息,必須且只能擁有兩個按鈕

Confirm Template 是最簡潔的模板格式,適用於需要使用者做出「是/否」、「確認/取消」等二選一決策的情境。與 Button Template 不同,Confirm Template 沒有標題和縮圖功能

型別定義

透過 Tuple 型別明確定義需要兩個 Action,編譯時期避免參數數量錯誤的情況發生

line-message/types/template-confirm-message

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

export type TemplateConfirmMessageReq = {
  altText: string;
  text: string;
  actions: [Action, Action];
  sender?: Sender;
  quickReplyItems?: Omit<QuickReplyItem, 'type'>[];
};

處理函式

line-message/line-message.service.ts

ts
  createConfirmTemplateMessage(
    templateConfirmMessageReq: TemplateConfirmMessageReq,
  ): TemplateMessage {
    const { altText, text, actions } =
      templateConfirmMessageReq;

    const templateConfirmMessage: TemplateMessage = {
      type: MessageType.Template,
      altText,
      template: {
        type: MessageType.TemplateConfirm,
        text,
        actions,
      },
    };

    return templateConfirmMessage;
  }

常被使用的模板訊息 ⭐⭐⭐

支援最多 10 張卡片,每張卡片最多 3 個按鈕,且所有卡片的按鈕數量必須相同。

型別定義

建議可以限制一下 action 數量,避免 action 數量不一致的行為

這裡透過泛型約束的方式,讓原先的函數能夠在編譯時期驗證每張卡片的按鈕數量是否一致,確保所有卡片的 action 數量都符合 Carousel Template 的規範要求。

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

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

type FixedArray<T, N extends number> = T[] & { length: N };

type LimitedActionColumn<N extends number> = Omit<
  TemplateColumn,
  'defaultAction' | 'imageBackgroundColor' | 'actions'
> & {
  actions: FixedArray<TemplateColumn['actions'][number], N>;
};

export type TemplateCarouselMessageReq<N extends number = number> = {
  altText: string;
  cards: LimitedActionColumn<N>[];
  sender?: Sender;
  quickReplyItems?: Omit<QuickReplyItem, 'type'>[];
};

處理函式

Carousel Template 與 Button Template 的主要差異在於 cards 參數,每張卡片支援以下配置:

核心參數:

  • text:卡片內文
  • actions:卡片按鈕陣列(最多 3 個)

可選參數:

  • title:卡片標題
  • thumbnailImageUrl:卡片縮圖

卡片的參數配置與 Button Template 基本相同,但透過陣列形式支援多張卡片的輪播展示。

line-message.service/line-message.service.ts

ts
  createTemplateCarouselMessage<N extends number>(
    templateCarouselMessageReq: TemplateCarouselMessageReq<N>,
  ): TemplateMessage {
    const { altText, cards, sender, quickReplyItems } =
      templateCarouselMessageReq;

    const templateCarouselMessage: TemplateMessage = {
      type: MessageType.Template,
      altText,
      template: {
        type: MessageType.TemplateCarousel,
        columns: [...cards],
      },
    };

    return templateCarouselMessage;
  }

透過泛型參數指定每張卡片的按鈕數量,實現編譯時期的靜態檢查:

line-webhook.service/line-webhook.service.ts

ts
// 指定每張卡片只能有 1 個按鈕
this.lineMessageService.createTemplateCarouselMessage<1>({
    altText: '最新活動消息!!',
    cards: [
      {
        text: '新生代狗狗寵物展',
        thumbnailImageUrl:
          'https://res.cloudinary.com/dseg0uwc9/image/upload/v1753953684/2025%20IT%20%E9%90%B5%E4%BA%BA%E8%B3%BD/dog_icon_grxcsl.jpg',
        actions: [
          {
            type: 'message',
            label: '搶先第一手資訊',
            text: '可愛狗狗展',
          },
        ],
      },
      {
        text: '可愛貓貓展',
        thumbnailImageUrl:
          'https://res.cloudinary.com/dseg0uwc9/image/upload/v1753953684/2025%20IT%20%E9%90%B5%E4%BA%BA%E8%B3%BD/dog_icon_grxcsl.jpg',
        actions: [
          {
            type: 'message',
            label: '搶先第一手資訊',
            text: '可愛貓貓展',
          },
        ],
      },
    ],

ImageCarousel Template

實務開發中的熱門選擇 ⭐⭐⭐

以圖片為主軸的輪播卡片模板,版型固定,只能調整圖片及按鈕。

每張卡片均必須填寫以下配置:

  • imageUrl:卡片背景圖片
  • action:互動按鈕(限定一個)

型別定義

定義上會跟 Carousel Template 蠻類似的,但是這邊因為只能設定一個按鈕,所以會相對單純。

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

export type TemplateImageCarouselMessageReq = {
  altText: string;
  cards: TemplateImageColumn[]; // 這邊的型別跟 Carousel Template 不同喔!!!
  sender?: Sender;
  quickReplyItems?: Omit<QuickReplyItem, 'type'>[];
};

處理函式

使用上與 Carousel Template 相似,但 ImageCarousel Template 的每張卡片都必須設置圖片和按鈕。

ts
  createTemplateImageCarouselMessage(
    templateImageCarouselMessageReq: TemplateImageCarouselMessageReq,
  ): TemplateMessage {
    const { altText, cards, sender, quickReplyItems } =
      templateImageCarouselMessageReq;

    const templateImageCarouselMessage: TemplateMessage = {
      type: MessageType.Template,
      altText,
      template: {
        type: MessageType.TemplateImageCarousel,
        columns: [...cards],
      },
      ...this.buildCommonMessageProps(sender, quickReplyItems),
    };
    return templateImageCarouselMessage;
  }

本日結語

今天我們完成了第一種進階回覆訊息 Template Message 的實作。這類模板的使用方式相當直觀,只需準備好必要參數,就能快速產生對應的模板訊息。

然而,正如開頭提到的,Template Message 的客製化程度有限。例如想為商品模板加上「特價標籤」或其他個性化元素,Template Message 無法直接實現。這種標準化的限制,讓模板訊息比較難凸顯獨特的設計特色。

留言

留言使用 GitHub 帳號登入(Giscus),留言會存在 GitHub Discussions。