← บทความทั้งหมด
บทช่วยสอน

ไฟล์แนบสื่อ Telegram ใน Unified Webhook: รูปภาพ เสียง เอกสาร รายชื่อติดต่อ และตำแหน่ง

Telegram Bot API อย่างเป็นทางการแสดงสื่อเป็น field เฉพาะของ Message เช่น photo, document, voice, contact และ location ถ้า receiver ของคุณใช้ unified webhook ของ UnifyPort คุณไม่จำเป็นต้องเอา field ของ Telegram ทุกตัวไปเป็น model หลักของ inbox ให้เก็บ envelope message.received ก่อน: ข้อความอยู่ที่ data.message.text, สื่ออยู่ที่ data.message.attachments[], รายชื่อติดต่ออยู่ที่ data.message.contact และพิกัดอยู่ที่ data.message.location

สรุปสำคัญ

  • ข้อความสื่อของ Telegram ไม่ใช่แค่ “ข้อความที่มีตัวอักษร”; Telegram Bot API ระบุ field optional สำหรับรูปภาพ เอกสาร ข้อความเสียง รายชื่อติดต่อ และตำแหน่งไว้แยกกัน
  • UnifyPort normalize inbound Telegram content เป็น event message.received รูปแบบเดียวกับ WhatsApp, LINE, TikTok, Zalo และ X เหมาะกับทีมไทยที่ต้องดูแล LINE ควบคู่กับช่องทางอื่น
  • ไฟล์สื่ออยู่ใน data.message.attachments[] โดยมี field เช่น type, url, mimetype และ metadata อย่างขนาดหรือระยะเวลาเมื่อมีข้อมูล
  • ต้องตรวจ X-Device-Signature จาก raw request body ก่อนอ่านเนื้อหา ดาวน์โหลดไฟล์ หรือส่งต่อให้ AI/CRM
  • ถ้ายังเลือกเส้นทางรับข้อความอยู่ ให้อ่าน Telegram Bot API Webhook เทียบกับ Unified Inbound Webhook ก่อน แล้วค่อยลงรายละเอียด media handling ในบทความนี้

Telegram ส่งอะไรมา และ inbox ควรเก็บอะไร

Telegram Bot API Message object เหมาะกับการสร้าง Telegram bot เพราะโค้ดสามารถ branch ตาม field ของ Telegram ได้โดยตรง แต่ shared inbox หรือระบบซัพพอร์ตหลายช่องทางต้องการ contract ที่ใช้ซ้ำได้มากกว่า ถ้าทีมของคุณรับ Telegram วันนี้ และต่อ LINE หรือ WhatsApp ในเดือนหน้า database ไม่ควรถูกผูกกับ Message object ของ Telegram เพียงแพลตฟอร์มเดียว

UnifyPort อธิบาย envelope มาตรฐานไว้ใน 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" }
  }
}

ถ้าเนื้อหาเป็นรูปภาพ voice note วิดีโอ หรือเอกสาร ให้ใช้ data.message.attachments[] ถ้าเป็นการแชร์ contact ให้ใช้ data.message.contact ถ้าเป็นการแชร์ location ให้ใช้ data.message.location ซึ่งมี longitude และ latitude

ตาราง map สำหรับสื่อ Telegram

เนื้อหาที่เข้ามาField ที่ควรเก็บหมายเหตุ
ข้อความหรือ captiondata.message.textอย่าบังคับให้ทุก media message ต้องมีข้อความ
รูปภาพ เสียง วิดีโอ เอกสาร ไฟล์data.message.attachments[]แต่ละ item มี type แบบ normalize แล้ว URL ของสื่ออาจเป็นชั่วคราว จึงควร copy หรือ process ตามนโยบาย retention ของคุณ
ระยะเวลา voice/audioattachments[].duration_ms เมื่อมีถือเป็น optional field
ชื่อเอกสารattachments[].title เมื่อมีใช้แสดงผลได้ แต่อย่าใช้เป็น unique identifier
รายชื่อติดต่อที่แชร์data.message.contactPayload อาจมีเบอร์โทร ชื่อ vCard และ user id
ตำแหน่งdata.message.locationเก็บ { longitude, latitude } แยกจาก free-text address
ID ข้อความdata.message.idใช้กับ action ระดับ message; ใช้ top-level id สำหรับ deduplication ของ webhook delivery

บทความนี้เป็นภาคปฏิบัติของ Webhook-first inbound integration checklist ลำดับที่ปลอดภัยคือสร้าง receiver ก่อน เก็บ event ที่ normalize แล้ว จากนั้นค่อยส่งต่อไป search index, CRM, AI triage หรือ worker ดาวน์โหลดไฟล์

ตรวจลายเซ็นก่อน แล้วค่อย parse

เอกสาร delivery ของ UnifyPort ระบุ header X-Device-Timestamp และ X-Device-Signature เมื่อเปิดใช้ signing_secret signature คือ hex HMAC-SHA256 ของข้อความนี้:

<X-Device-Timestamp>.<raw request body>

Node.js official docs อธิบาย crypto.createHmac() และ crypto.timingSafeEqual() ส่วน Express docs ระบุว่า express.raw() ใช้ parse payload เป็น Buffer ได้ ดังนั้น receiver ควรเก็บ raw body ไว้จนตรวจเสร็จ แล้วค่อย parse JSON

import crypto from 'node:crypto';
import express from 'express';

const app = express();
const signingSecret = process.env.WEBHOOK_SIGNING_SECRET;

if (!signingSecret) throw new Error('WEBHOOK_SIGNING_SECRET is required');

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);
});

ใน production ให้แทน mediaQueue, contactQueue และ geoQueue ด้วย database หรือ job queue ของคุณเอง จุดสำคัญคือเก็บ event ที่ตรวจแล้วอย่าง durable ก่อนเริ่มงานช้า เช่น media download, OCR หรือ AI enrichment รายละเอียดเรื่อง retries และ signature อยู่ใน Webhook delivery and signature verification และถ้าจะจัดการ reaction ต่อ ให้อ่าน วิธีประมวลผลรีแอ็กชันข้อความด้วย Webhook แบบรวม

UnifyPort อยู่ตรงไหน

UnifyPort เป็นอินเทอร์เฟซที่ไม่เป็นทางการสำหรับรับข้อความ Telegram จาก messaging account ที่เชื่อมต่อแล้ว และส่งต่อเป็น event มาตรฐาน คุณยังต้องกำหนดเองว่าจะเก็บสื่อนานแค่ไหน จะ copy URL ชั่วคราวหรือไม่ และ worker ใดมีสิทธิ์เข้าถึง attachment

ข้อดีคือ receiver เดียวกันสามารถรับ Telegram วันนี้ และรับ LINE photos, WhatsApp images, Zalo messages, TikTok DMs หรือ X messages ในอนาคต โดยไม่ทำให้ schema ของ Telegram กลายเป็นศูนย์กลางของ database

ข้อจำกัดและการเลือกใช้

ใช้ official Bot API เมื่อคุณสร้าง bot identity, bot commands, inline keyboards, BotFather configuration หรือพฤติกรรมเฉพาะของ Telegram bot ใช้ unified webhook เมื่อโจทย์คือ inbound intake สำหรับ messaging account ที่มีอยู่แล้ว หรือคิวซัพพอร์ตหลายช่องทาง

และอย่าสมมติว่าทุก provider มี media shape เหมือน Telegram ทั้งหมด เก็บข้อมูลใน storage layer ให้เป็นรูปแบบเดียวกันได้ แต่การเช็ก capability ควรอยู่ที่ขอบของแต่ละ platform

FAQ

รูป Telegram อยู่ใน data.message.text หรือไม่?

ไม่ ข้อความและ caption ใช้ data.message.text ส่วนไฟล์สื่อใช้ data.message.attachments[] พร้อม type ที่ normalize แล้ว

Webhook เดียวรับ contact และ location ได้ไหม?

ได้ message.received สามารถมี field โครงสร้างอย่าง data.message.contact หรือ data.message.location เมื่อข้อความที่เข้ามามีข้อมูลเหล่านั้น

ควรดาวน์โหลดสื่อให้เสร็จก่อนตอบ 2xx ไหม?

โดยทั่วไปไม่ควร เก็บ event ที่ตรวจแล้วก่อน ส่งงานสื่อเข้า queue แล้วตอบ 2xx งานที่ช้าควรทำหลัง acknowledgement

เหมือน Telegram Bot API webhook หรือไม่?

ไม่เหมือน Bot API webhook รับ Telegram Update objects สำหรับ bot token ส่วน UnifyPort webhook รับ event ที่ normalize แล้วจาก messaging account ที่เชื่อมต่อ และใช้ receiver เดียวกันกับ provider อื่นได้

ขั้นตอนถัดไป

เปิด Create webhook endpoint subscribe message.received, เปิด signing_secret แล้วทดสอบข้อความ ไฟล์แนบ รายชื่อติดต่อ และตำแหน่ง ก่อนต่อเข้ากับ workflow production

Sources checked on 2026-09-08

UnifyPort API

เปลี่ยนการเชื่อมต่อข้อความให้เป็น pipeline ผลิตภัณฑ์ที่เสถียร

เริ่มจากการส่งผ่าน API เดียว แล้วส่งข้อความขาเข้าทั้งหมดกลับสู่ระบบธุรกิจของคุณด้วย event มาตรฐาน