目前我們都只使用 LINE Message Event 中的純文字(text)格式進行回覆,但除了純文字之外,LINE Bot 後端伺服器其實可以接收多種不同類型的訊息。今天就讓我們一起來深入了解這些訊息類型吧!
本文將著重於實現以下核心功能:
本日程式碼的範例連結
後端伺服器能透過 Webhook
收到使用者的訊息類型在單聊
大致上分成六種:Text
、Image
、Video
、Audio
、Location
、Sticker
。
這邊埋個小彩蛋,後端伺服器其實也能透過 LINE 平台讀取檔案,比如:
PDF 檔案
。不過這項功能僅限在群聊環境下使用。
官方文件已詳細說明各訊息類型的長度限制與檔案大小規範,本表格主要聚焦於各類型間的差異特點。其中最特殊的是
File
類型,雖然在群聊環境下可以接收檔案,但後端伺服器卻無法透過LINE Messaging API
將檔案回傳給用戶。
前面我們介紹了後端伺服器可以接收七種訊息類型,接下來探討這七種類型是否也能透過後端伺服器回傳給用戶:
訊息類型 | 情境描述 | 支援回傳 | 回傳限制 |
---|---|---|---|
Text | 用戶發送文字訊息時觸發 | 是 | 無 |
Sticker | 用戶發送貼圖或表情貼時觸發 | 是 | 僅能使用平台提供的貼圖包 |
Image | 用戶發送圖片、照片、截圖等內容時觸發 | 是 | 僅接受 JPEG or PNG 格式 |
Video | 用戶發送影片檔案時觸發 | 是 | 僅接受 mp4 格式 |
Audio | 用戶發送語音訊息或音檔時觸發 | 是 | 僅接受 mp3 或 m4a 格式 |
File | 用戶發送文件檔案時觸發 | 否 | LINE Bot API 不支援檔案回傳 |
Location | 用戶分享地理位置時觸發 | 是 | 無 |
除了上述基本的訊息類型外,後端伺服器實際上還能回傳更多樣化的內容格式。我們將在接下來的章節中,完整介紹 LINE Messaging API 所支援的所有回傳訊息類型。
在開始今天的實作之前,我們需要將 NestJS 的結構做調整。
先前直接寫在app.controller.ts
的方式,主要是希望讓所有人都能用最低的成本,使用 NestJS 架設 LINE Bot Server。接著我們將會透過 Nest 官方的架構說明進行相對應的調整。
在 NestJS 中,Module
、Controller
及 Service
均以 Class
為設計核心,透過裝飾器(Decorator)
來標註其角色與功能。
等等等....一來感覺就壓力滿滿,試著使用餐廳情境來描述 NestJS 的結構 🍽️
負責將相關的功能(如 Controller、Provider 等)組織起來,形成獨立且可重用的程式碼單元。
我們可以將 Module 想像成「各個負責不同職能的部門」。每個部門都有明確的職責與分工,例如:
這些部門的存在,是為了將性質相近的任務歸類在一起,使每個部門都能專注處理特定類型的事務,確保責任分工明確。
主要功能:負責處理傳入的
請求(Request)
及回覆響應(Response)
的資料
Controller
的主要職責是理解並接收使用者的請求,然後將這些請求導向給對應的服務進行處理。
舉例來說,當顧客告訴接待員:「我想要一份白醬義大利麵,內用。」接待員會接收到這項指令,接著將這個需求轉達給廚房的廚師處理。
主要功能:
Provider
負責提供各種可重複使用的服務,包含多種類型:
- Value Provider:提供靜態常數或設定值
- Service Provider:處理複雜的業務邏輯
- Factory Provider:動態建立和配置服務實例
它們透過
依賴注入 (Dependency Injection, DI)
的機制,在不同的模組之間建立功能連結,
可以將它們想像成餐廳裡那些「擁有專業技能的幕後工作者」:他們雖然不會直接面對客人,卻是讓餐廳順利運作的核心。這就像廚師不必自己去研究如何製作所有醬料,只需從專門的醬料提供者那裡獲取現成的醬料服務即可。
以下透過五個步驟將先前撰寫的結構進行調整,使其更符合單一模組的特性。
介紹完概念就要把原先寫的程式碼找到他們的家
設置專門處理 Line webhook event
的模組。
輸入以下指令產生 module:
nest g module line-webhook
nest g controller line-webhook --no-spec
nest g service line-webhook --no-spec
在 line-webhook
資料夾底下,新增 line-webhook.types.ts
,負責統一管理 LINE Webhook 相關的型別定義。
service 負責將我們統整的業務邏輯進行統一的管理
當需要處理 Message
事件時,交由 handleMessageEvent
函式處理,每個函式都獨立處理一種 WebhookEvent
。
line-webhook.service.ts
// 略
@Injectable()
export class LineWebhookService {
// 略
async processWebhook(body: WebhookRequestBody): Promise<string> {
const { events } = body;
// 處理 follow、message、unfollow 三種事件
const webhookEventHandlerMap = {
message: (event) => this.handleMessageEvent(event),
follow: (event) => this.handleFollowEvent(event),
unfollow: (event) => this.handleUnfollowEvent(event),
} satisfies Partial<WebhookEventHandlerMap>;
for (const event of events) {
const handler = webhookEventHandlerMap[event.type];
if (handler) await handler(event);
}
return 'Webhook processed successfully';
}
/**
* 用戶發送訊息時觸發
* @param event 訊息事件
*/
private async handleMessageEvent(event: MessageEvent): Promise<void> {
// ... 略
}
}
所有 Line 平台傳遞的事件都透過 line-webhook.controller 進行請求的處理
line-webhook.controller
//...略
@Controller('webhook')
export class LineWebhookController {
constructor(private readonly lineWebhookService: LineWebhookService) {}
@Post()
async handleWebhook(@Body() body: WebhookRequestBody): Promise<string> {
return this.lineWebhookService.processWebhook(body);
}
}
建立 LINE 配置的集中化管理機制,為後續在各個 Service 中創建 LINE SDK 實例提供統一的配置來源
line.config.ts
import { ClientConfig } from '@line/bot-sdk';
import { ConfigService } from '@nestjs/config';
// 註冊名稱
export const LINE_CONFIG = 'LINE_CONFIG';
// 註冊的常數(這邊讀取的是.env 環境變數裡面的內容)
const lineConfig = (configService: ConfigService): ClientConfig => ({
channelAccessToken: configService.getOrThrow<string>(
'LINE_CHANNEL_ACCESS_TOKEN',
),
channelSecret: configService.getOrThrow<string>('LINE_CHANNEL_SECRET'),
});
// 匯出成 NestJS Provider 供依賴注入系統使用
export const LineConfigProvider = {
provide: LINE_CONFIG,
useFactory: (configService: ConfigService) => lineConfig(configService),
inject: [ConfigService],
};
將 LINE Config Provider 註冊到模組中,為後續在 LineWebhookService 中注入配置並創建 LINE SDK 實例做準備
line-webhook.module
//...略
import { LineConfigProvider } from 'config/line.config';
@Module({
controllers: [LineWebhookController],
providers: [LineWebhookService, LineConfigProvider],
})
export class LineWebhookModule {}
將
/webhook
端點的請求都透過LineWebhookModule
處理
app.module.ts
//...略
@Module({
imports: [ConfigModule.forRoot({ isGlobal: true }), LineWebhookModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
// 針對 webhook 路由使用 LINE 中介軟體驗證
consumer.apply(LineMiddleware).forRoutes('webhook');
}
}
採用與
LINE Webhook
事件處理相同的模式,針對Message Event
內的六種訊息類型建立對應的處理函式,根據訊息類型回傳不同的回應內容。其中圖片、音檔與影片訊息會根據內容來源(contentProvider)區分為 line 和 external 兩種類型進行不同處理。
line-webhook-service.ts
private async handleMessageEvent(event: MessageEvent): Promise<void> {
const messageEventHandlerMap = {
text: (message) => `📝 收到文字訊息:${message.text}`,
sticker: (message) =>
`🎭 收到貼圖訊息 => 貼圖包編號:${message.stickerId}-貼圖編號:${message.packageId}}`,
image: (message) =>
`🖼️ 收到圖片訊息 => 訊息編號:${message.id}-圖片來源:${message.contentProvider.type}`,
video: (message) =>
`🎬 收到影片訊息 => 訊息編號:${message.id}-影片來源:${message.contentProvider.type}`,
audio: (message) =>
`🎵 收到音檔訊息 => 訊息編號:${message.id}-時長:${message.duration} ms-音頻來源:${message.contentProvider.type}`,
location: (message) =>
`📍 收到位置訊息 => 地址:${message.address}-精度:${message.longitude}-緯度:${message.latitude}`,
} satisfies Partial<MessageEventHandlerMap>;
let replyMessage = '✨ 感謝你的訊息,我們已經收到了!';
const handler = messageEventHandlerMap[event.message.type];
if (handler) replyMessage = handler(event.message);
await this.lineClient.replyMessage({
replyToken: event.replyToken,
messages: [{ type: 'text', text: replyMessage }],
});
}
LINE 媒體訊息(圖片、音檔與影片訊息)的內容來源類型說明
Line 類型
- 定義:用戶透過 LINE 應用程式上傳的媒體資源
- 存取方式:需要透過 LINE Message API Getting content 進行存取
External 類型
- 定義:由
liff.sendMessages()
發送的靜態資源,這部分可參照官方說明- 存取方式:可以透過
originalContentUrl
屬性值存取。
至此,我們已經具備了處理 MessageEvent
事件六種基礎訊息的處理函數!
今天我們探討了 LINE Bot 可接收的七種訊息類型,並對 Day 3 建立的基礎架構進行了優化與調整。此外,我們深入理解了 NestJS
的三個核心概念——Module
、Controller
和 Provider
,並將這些概念融入原有架構中,進一步提升了整體設計的品質。
儘管對於 NestJS
還充滿著各種未知,但是我會嘗試在一個又一個的實作當中,慢慢在實踐中學習,希望能以更輕鬆的方式讓大家認識 LINE Bot 的開發世界。