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

อัลบั้มสื่อจาก Webhook: รวมภาพโดยไม่ทำข้อความสูญหาย

เมื่อต้องประกอบอัลบั้มสื่อจาก webhook ให้เก็บข้อความแต่ละรายการแยกกันก่อน แล้วใช้รหัสอัลบั้มเพื่อสร้างมุมมองแบบกลุ่มเท่านั้น อย่าใช้ album ID เพื่อตัดข้อความซ้ำ เพราะจะทำให้ภาพคนละภาพในอัลบั้มเดียวกันถูกทิ้ง หาก payload ไม่ระบุจำนวนที่คาดไว้ การรอจนไม่มีรายการใหม่ช่วงหนึ่งอาจใช้เป็นเงื่อนไขเริ่มประมวลผลได้ แต่ไม่ได้ยืนยันว่าทุกรายการมาถึงแล้ว

ประเด็นสำคัญ

  • รหัสข้อความกับรหัสอัลบั้มมีหน้าที่ต่างกัน
  • ทั้งสองรหัสต้องมีขอบเขตบัญชีผู้รับและบทสนทนา
  • บันทึกข้อความอย่างถาวรก่อนตอบรับการส่ง แล้วค่อยรวมมุมมองภายหลัง
  • เก็บสถานะอัลบั้มไว้หลังตัวจับเวลาทำงาน เพื่อรองรับภาพที่มาช้า

รหัสอัลบั้มไม่ใช่รหัสข้อความ

เอกสาร Bot API ทางการของ Telegram นิยาม Message.media_group_id เป็นฟิลด์เสริมที่ระบุกลุ่มสื่อซึ่งข้อความนั้นอยู่ โดยรหัสมีความเป็นเอกลักษณ์ ภายในแชตนั้น ฟิลด์นี้แสดงความสัมพันธ์ระหว่างข้อความ ไม่ได้แทนรหัสของแต่ละข้อความ

UnifyPort ใช้สัญญาข้อมูลอีกแบบหนึ่ง เอกสาร webhook มาตรฐาน ระบุ data.message.album เป็นข้อมูลเสริมที่มี id, index และ total ซึ่งอาจไม่มีได้ ข้อความย่อยยังมี data.message.id ของตัวเอง ตัวอย่างอัลบั้มในเอกสารเป็น WhatsApp จึงไม่ควรสรุปว่าทุกแพลตฟอร์ม รวมถึง LINE จะส่งข้อมูลอัลบั้มเสมอ หรือฟิลด์ดิบของ Telegram จะปรากฏใน payload มาตรฐาน

หากยังอยู่ในขั้นตอนอ่านไฟล์แนบ ให้เริ่มจาก คู่มือการแมปไฟล์สื่อ การรวมอัลบั้มเป็นชั้นแสดงผลเพิ่มเติม ไม่ใช่กระบวนการดาวน์โหลดแบบใหม่

รหัสขอบเขตที่แนะนำหน้าที่
ID ของอีเวนต์ทั่วไปWorkspace และสตรีมอีเวนต์ตรวจการส่งอีเวนต์เดิมซ้ำ
ID ของข้อความย่อยWorkspace, provider, บัญชีข้อความ, บทสนทนาเก็บหรือแก้ไขข้อความหนึ่งรายการ
ID ของอัลบั้มWorkspace, provider, บัญชีข้อความ, บทสนทนาเชื่อมข้อความหลายรายการ
URL ไฟล์แนบระเบียนข้อความเจ้าของไฟล์ระบุตำแหน่งสื่อ ไม่ใช่คีย์จัดกลุ่ม

ใน ตัวอย่างสมมติที่ส่งสามภาพ หาก B มาถึงสองครั้งและ C มาถึงหนึ่งครั้ง คุณมีข้อความที่แตกต่างกันเพียงสองรายการ ไม่ใช่สาม จำนวน HTTP request จึงไม่ได้บอกความครบถ้วนของอัลบั้ม

เก็บข้อความย่อยก่อนสร้างมุมมองรวม

JavaScript ต่อไปนี้สร้าง คีย์สำหรับจัดเก็บในแอปพลิเคชัน จากอีเวนต์ที่ผ่านการตรวจสอบแล้ว ชื่อพร็อพเพอร์ตีที่คืนค่าเป็นการออกแบบภายใน ไม่ใช่ฟิลด์ API ใหม่ และโค้ดนี้ไม่ได้ตรวจลายเซ็นหรือบันทึกฐานข้อมูล

function albumKeys(workspaceKey, event) {
  if (event.type !== 'message.received') return null;
  const conversation = event.data?.conversation;
  const message = event.data?.message;
  if (!event.provider || !event.account_id ||
      !conversation?.id || !message?.id) {
    throw new Error('Missing message identity');
  }

  const scope = [workspaceKey, event.provider,
    event.account_id, conversation.id];
  return {
    childKey: JSON.stringify([...scope, message.id]),
    albumKey: message.album?.id
      ? JSON.stringify([...scope, message.album.id])
      : null
  };
}

ลำดับธุรกรรมที่แนะนำคือ:

  1. ตรวจคำขอดิบตาม สัญญาการส่ง webhook เมื่อเปิด signing_secret ให้ตรวจ HMAC-SHA256 ของ timestamp ตามด้วยจุดและ body ดิบ รวมทั้งตรวจความสดใหม่ของ timestamp
  2. บันทึกอีเวนต์และเพิ่มหรืออัปเดตข้อความย่อย โดยใช้ unique constraint กับรหัสข้อความที่รวมขอบเขตแล้ว เก็บคำบรรยาย ไฟล์แนบ sent_at และข้อมูลอัลบั้มที่มี
  3. เชื่อมข้อความกับอัลบั้มที่ถูกต้อง หากไม่มี album ID ให้เก็บเป็นข้อความเดี่ยว อย่าเดาจากคำบรรยายเหมือนกันหรือเวลามาถึงใกล้กัน
  4. Commit ระเบียนรับเข้าและงานที่จัดเก็บถาวรก่อนตอบ 2xx แล้วให้ worker อัปเดตมุมมองและดาวน์โหลดสื่อแยกกัน

ใช้ธุรกรรมเดียวกันหรือ durable outbox เพื่อไม่ให้โปรเซสหยุดหลังบันทึกข้อความแต่ก่อนสร้างงานรวมอัลบั้ม เก็บคำบรรยายของแต่ละข้อความไว้ ไม่ให้รายการล่าสุดเขียนทับคำบรรยายเดียวของทั้งอัลบั้ม

เลือกเวลาประมวลผลโดยไม่อ้างว่าข้อมูลครบแน่นอน

สถานะต่อไปนี้เป็น ข้อเสนอสำหรับแอปพลิเคชัน ไม่ใช่อีเวนต์หรือฟิลด์ของแพลตฟอร์ม

สิ่งที่พบการตัดสินใจของแอป
total ตรงกันและมีข้อความที่แตกต่างกันครบจำนวนนั้นระบุว่าถึงจำนวนที่คาดไว้ แต่ตรวจความพร้อมไฟล์แยกต่างหาก
ไม่มี totalประมวลผล snapshot ชั่วคราวหลังช่วงรอที่ทีมกำหนด
total ขัดแย้งกันหรือ index ซ้ำเก็บข้อความทั้งหมดและแจ้งให้ตรวจ metadata
มีข้อความใหม่หลังประมวลผลอัปเดตมุมมองและใช้นโยบายสำหรับรายการมาช้า
ข้อความเดิมถูกส่งซ้ำอัปเดตแบบ idempotent โดยไม่เพิ่มจำนวนรายการที่แตกต่างกัน

ทำให้การอัปเดตอัลบั้มเดียวกันเกิดตามลำดับ หรือใช้การตรวจเวอร์ชันในธุรกรรม เพื่อป้องกัน worker หลายตัวอ่านสถานะเก่าแล้วสร้างงานปลายทางซ้ำ บันทึกการตัดสินใจประมวลผลพร้อมรหัสข้อความที่ใช้ในครั้งนั้น หากมีภาพเพิ่มภายหลัง ให้เลือกอย่างชัดเจนว่าจะอัปเดตหน้าจอ วิเคราะห์เพิ่มเติม หรือส่งให้คนตรวจ อย่าส่งข้อความถึงลูกค้าซ้ำโดยอัตโนมัติ

เก็บ album.index ที่ได้รับไว้สำหรับแสดงผล แต่อย่าอนุมานจุดเริ่มต้นของดัชนีจากตัวอย่างเดียว หากดัชนีขาดหรือขัดแย้งกัน ก็ไม่ควรทิ้งข้อความ ตัวจับเวลาเป็นนโยบายด้านเวลารอของคุณ ไม่ใช่สัญญาณว่าแพลตฟอร์มส่งอัลบั้มครบแล้ว

แยกความพร้อมของสื่อออกจากจำนวนข้อความ

แม้ระเบียนข้อความจะครบตามจำนวนที่คาดไว้ ไฟล์บางรายการอาจยังใช้ไม่ได้ UnifyPort ระบุว่า URL ไฟล์แนบเป็น URL ชั่วคราว และไฟล์ที่ใหญ่เกินไปอาจไม่มี URL ควรจัดคิวดาวน์โหลดทันทีที่ทำได้ และแสดงสถานะสำเร็จหรือล้มเหลวแยกตามไฟล์

คู่มือกู้คืนลิงก์ดาวน์โหลดที่หมดอายุ อธิบายว่าเหตุใดจึงนำขั้นตอนต่ออายุไฟล์ของ Telegram Bot API มาใช้กับ URL ไฟล์แนบของ UnifyPort โดยตรงไม่ได้ อย่ารออัลบั้มครบอย่างไม่มีกำหนดจนพลาดการเก็บไฟล์ที่พร้อมแล้ว

UnifyPort เป็นอินเทอร์เฟซที่ไม่เป็นทางการ สตรีมมาตรฐานไม่ได้รับประกันว่า metadata จะเหมือนกันทุกช่องทาง ไม่มี REST API สำหรับอ่านประวัติข้อความ และไม่รับประกันการส่ง payload ที่พลาดไปซ้ำ หากต้องการสัญญาของบอตแบบเนทีฟ ให้ใช้ Bot API ทางการ และอย่าผสมสอง schema ใน parser เดียว

การทดสอบและคำถามที่พบบ่อย

ควรทดสอบข้อความซ้ำ ลำดับสลับ album ID เดียวกันในคนละบทสนทนา total ที่หายไป metadata ขัดแย้งกัน และภาพที่มาหลังประมวลผลแล้ว นี่คือข้อเสนอการทดสอบ ไม่ใช่ผลที่ได้ดำเนินการจริง

ใช้ album.id เพื่อตัดข้อความซ้ำได้ไหม?

ไม่ได้สำหรับข้อความย่อย เพราะหลายข้อความใช้ค่าเดียวกัน ให้ใช้ message ID ที่รวมขอบเขตแล้ว

ถึง total แล้วแปลว่าดาวน์โหลดครบหรือไม่?

ไม่ใช่ total ระบุขนาดกลุ่มที่คาดไว้เมื่อมีการส่งค่านี้มา การเก็บไฟล์ถาวรเป็นอีกขั้นตอนหนึ่ง

ควรทิ้งอัลบั้มที่ไม่มี total หรือไม่?

ไม่ควร ให้เก็บข้อความและประมวลผลมุมมองชั่วคราวตามนโยบายที่ชัดเจน พร้อมเปิดทางให้เพิ่มรายการภายหลัง

ขั้นตอนถัดไปและแหล่งอ้างอิง

สร้างระบบจัดเก็บระดับข้อความตาม เอกสารอีเวนต์มาตรฐาน ก่อนเปิดระบบอัตโนมัติระดับอัลบั้ม

ตรวจสอบเอกสารเมื่อ 2026-09-29:

UnifyPort API

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

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