Telegram 媒體附件統一 Webhook:相片、語音、文件、聯絡人同位置
Telegram 官方 Bot API 會將媒體內容放入 Telegram 自己的 Message 欄位,例如 photo、document、voice、contact、location。如果你用 UnifyPort 統一 Webhook 做接收層,就毋須將每個 Telegram 專屬欄位直接變成資料庫核心模型。先存 message.received 標準事件:文字睇 data.message.text,媒體睇 data.message.attachments[],聯絡人睇 data.message.contact,位置睇 data.message.location。
重點
- Telegram 媒體訊息唔只係「有文字嘅訊息」;官方 Telegram Bot API 將相片、文件、語音、聯絡人、位置等內容列為不同可選欄位。
- UnifyPort 會將 Telegram 入站內容標準化成
message.received,同一個事件外殼亦可用於 WhatsApp、LINE、TikTok、Zalo 同 X。 - 媒體附件位於
data.message.attachments[],常見欄位包括type、url、mimetype,以及可選嘅大小或時長資料。 - 讀內容、下載附件或交俾 AI 分流之前,先用原始 request body 驗證
X-Device-Signature。 - 如果你仍然比較 Telegram Bot API webhook、
getUpdates同統一入站 Webhook,先睇接收路徑比較。
Telegram 原始形狀,同行內收件箱需要嘅形狀
Telegram Bot API 的 Message object 好適合 bot 專案:程式可以跟住 Telegram 欄位分支處理。但客服、CRM、跨境營運收件箱通常唔止 Telegram 一個渠道。資料庫應該圍繞一個可重用嘅入站事件合約,而唔係單一平台物件。
UnifyPort 的 Standard event types and payload 文件定義咗固定事件外殼:
{
"id": "evt_b1a7c3e5f8",
"type": "message.received",
"provider": "telegram",
"account_id": "acc_8c21d0",
"occurred_at": "2026-06-08T12:37:00Z",
"data": {
"conversation": { "id": "5005", "type": "user" },
"sender": { "id": "4004", "type": "user", "name": "Jordan Lee" },
"message": {
"id": "3003",
"direction": "inbound",
"sent_at": "2026-06-08T12:37:00Z",
"contact": {
"phone_number": "+8600000000000",
"first_name": "Demo",
"last_name": "User",
"vcard": "BEGIN:VCARD\nVERSION:3.0\nFN:Demo User\nEND:VCARD",
"user_id": 4004
}
},
"event": { "kind": "message_received" }
}
}
相片、語音、影片同文件進入 data.message.attachments[];共享聯絡人進入 data.message.contact;共享位置進入 data.message.location,入面有 longitude 同 latitude。
儲存映射表
| 入站內容 | 建議儲存欄位 | 說明 |
|---|---|---|
| 文字或 caption | data.message.text | 唔好假設每條媒體訊息都有文字。 |
| 圖片、音訊、影片、文件、檔案 | data.message.attachments[] | 每個附件有標準 type;媒體 URL 可能係臨時地址,按你嘅留存策略及早處理。 |
| 語音/音訊時長 | attachments[].duration_ms | 可能不存在,當 optional 欄位處理。 |
| 文件標題 | attachments[].title | 可用於展示,唔適合作唯一鍵。 |
| 聯絡人 | data.message.contact | Telegram 聯絡人可包含電話、姓名、vCard 同 user id。 |
| 位置 | data.message.location | 經緯度應獨立存,唔好只放文字地址。 |
| 訊息 ID | data.message.id | 訊息層級動作用佢;Webhook 投遞去重用頂層 id。 |
如果你未建立接收端,先跟 Webhook-first 入站整合清單:建立 Webhook、啟用簽名、儲存標準事件,再將附件交俾後續 worker。
先驗簽,再解析
啟用 signing_secret 後,UnifyPort 會發送 X-Device-Timestamp 同 X-Device-Signature。簽名內容係:
<X-Device-Timestamp>.<raw request body>
Node.js 官方文件講解 crypto.createHmac() 同 crypto.timingSafeEqual();Express 官方文件講明 express.raw() 會將 payload 解析成 Buffer。所以接收端要保留原始 bytes 到驗簽完成。
import crypto from 'node:crypto';
import express from 'express';
const app = express();
const signingSecret = process.env.WEBHOOK_SIGNING_SECRET;
app.post('/webhooks/unifyport', express.raw({ type: 'application/json' }), async (req, res) => {
const timestamp = req.get('X-Device-Timestamp') ?? '';
const signature = req.get('X-Device-Signature') ?? '';
const expected = crypto.createHmac('sha256', signingSecret).update(timestamp + '.').update(req.body).digest();
const supplied = /^[0-9a-f]{64}$/i.test(signature) ? Buffer.from(signature, 'hex') : Buffer.alloc(0);
if (supplied.length !== expected.length || !crypto.timingSafeEqual(supplied, expected)) return res.sendStatus(401);
const event = JSON.parse(req.body.toString('utf8'));
await storeEvent(event.id, event);
if (event.type !== 'message.received' || event.provider !== 'telegram') return res.sendStatus(202);
const message = event.data.message;
for (const attachment of message.attachments ?? []) {
await mediaQueue.enqueue({
eventId: event.id,
messageId: message.id,
conversationId: event.data.conversation.id,
type: attachment.type,
url: attachment.url,
mimetype: attachment.mimetype,
title: attachment.title,
durationMs: attachment.duration_ms
});
}
if (message.contact) await contactQueue.enqueue(message.contact);
if (message.location) await geoQueue.enqueue(message.location);
return res.sendStatus(202);
});
生產環境請將示例 queue 換成你自己嘅資料庫或工作佇列。重點係:慢下載、OCR、AI 摘要唔應該早過可信入庫。投遞、重試同排序邊界見 Webhook delivery and signature verification;如要處理 emoji reaction,可睇統一 Webhook 處理 message.reaction。
UnifyPort 應放喺邊一層
UnifyPort 提供非官方接口,將已連線訊息帳號上的 Telegram 入站訊息交付成標準事件。媒體留存幾耐、是否複製臨時檔案、邊啲內部服務可以讀附件,仍然由你嘅產品同合規設計決定。
價值在於接收層形狀統一:同一個 receiver 今日處理 Telegram 相片,之後亦可以處理 WhatsApp 圖片、LINE 相片、Zalo 訊息、TikTok DM 或 X 訊息。
限制同取捨
如果你要做 Telegram bot 身份、指令、inline keyboard、BotFather 設定或其他 bot 專屬能力,官方 Bot API 更直接。統一 Webhook 更適合既有訊息帳號入站同多渠道客服隊列。亦唔好假設每個平台都有 Telegram 所有媒體類型;儲存層可以統一,能力判斷仍應放喺平台邊界。
FAQ
Telegram 相片會放喺 data.message.text 嗎?
唔會。文字同 caption 用 data.message.text;媒體檔案用 data.message.attachments[]。
同一個 Webhook 可以收聯絡人同位置嗎?
可以。message.received 可以帶 data.message.contact 或 data.message.location 呢類結構化欄位。
要下載完媒體先回 2xx 嗎?
通常唔需要。先存已驗簽事件,將下載同處理排入 queue,再回 2xx。
呢個同 Telegram Bot API webhook 一樣嗎?
唔一樣。Bot API webhook 收 bot token 對應嘅 Telegram Update;UnifyPort webhook 收已連接訊息帳號嘅標準事件。
下一步
打開 Create webhook endpoint,訂閱 message.received,啟用 signing_secret,並測試文字、附件、聯絡人同位置訊息。
Sources checked on 2026-09-08
- Telegram Bot API: https://core.telegram.org/bots/api
- Node.js Crypto documentation: https://nodejs.org/api/crypto.html
- Express middleware documentation: https://expressjs.com/en/5x/guide/using-middleware
- UnifyPort Standard event types and payload: /zh-HK/docs/receiving-events/webhook-events/
- UnifyPort Webhook delivery and signature verification: /zh-HK/docs/receiving-events/webhook-delivery/
令訊息接入變成一條穩定嘅產品管線。
先用統一嘅 API 跑通發送,再用標準事件將所有入站訊息接返去業務系統。