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

จัดการสถานะส่งถึงและอ่านแล้วแบบหลายข้อความด้วย UnifyPort

เหตุการณ์ยืนยันการส่งถึงหรือการอ่านของ UnifyPort อาจครอบคลุมหลายข้อความ หากมีข้อมูลแบบชุด ให้ประมวลผลทุก ID ใน data.message.ids; สำหรับข้อความเดียวให้ใช้ data.message.id เก็บเหตุการณ์ยืนยันแยกจากเนื้อหาข้อความเพื่อรองรับกรณีที่เหตุการณ์มาถึงก่อน การรับคำขอส่ง สถานะจากผู้รับ และสถานะอ่านแล้วในกล่องข้อความของทีม เป็นหลักฐานคนละประเภท ไม่ควรใช้ธง “สำเร็จ” ร่วมกัน

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

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

ตรวจขอบเขตของชุดก่อนเปลี่ยนสถานะบนหน้าจอ

ข้อกำหนดเหตุการณ์มาตรฐาน กำหนดให้ message.delivered หมายถึงส่งถึงอุปกรณ์ของผู้รับ และ message.read หมายถึงผู้รับอ่านแล้ว ฟิลด์สำหรับชุดข้อความคือ data.message.ids หากอัปเดตแค่ id จะตกหล่นข้อความที่เหลือ

หลักฐานยืนยันอะไรไม่ได้ยืนยันอะไร
ผลส่งที่มี status: acceptedรับคำขอแล้วส่งถึงหรืออ่านแล้ว
message.deliveredข้อความที่ระบุส่งถึงแล้วผู้รับอ่านแล้ว
message.readข้อความที่ระบุถูกอ่านแล้วปิดงานบริการแล้ว หรือทุกคนในกลุ่มอ่านแล้ว
สถานะอ่านของบทสนทนาฝั่งบัญชีสถานะรายการแชตของบัญชีที่เชื่อมต่อสถานะยืนยันจากผู้รับ

แถวสุดท้ายเป็นเรื่องของขั้นตอนอ่าน/ยังไม่อ่านในกล่องข้อความร่วม อย่าเรียกคำสั่งทำเครื่องหมายบทสนทนาว่าอ่านแล้วเพื่อแก้การขาดเหตุการณ์ยืนยันของข้อความขาออก

ตัวอย่างย่อนี้ใช้ฟิลด์จากเอกสาร message.read เพื่ออธิบายรูปแบบ ไม่ใช่เหตุการณ์จริงของลูกค้า:

{
  "id": "evt_2f9c1a4b7e",
  "type": "message.read",
  "provider": "whatsapp",
  "account_id": "acc_8c21d0",
  "occurred_at": "2026-06-08T12:35:10Z",
  "data": {
    "conversation": { "id": "8613912345678", "type": "user" },
    "sender": { "id": "8613912345678", "type": "user" },
    "message": {
      "id": "wamid.HBgM",
      "ids": ["wamid.HBgM", "wamid.HBgN"],
      "read_at": "2026-06-08T12:35:10Z"
    }
  }
}

เหตุการณ์นี้ครอบคลุมสองข้อความในรายการ ไม่ได้ยืนยันว่าข้อความก่อนหน้าทั้งหมดถูกอ่านแล้ว แม้ ID จะดูเหมือนตัวเลข ก็อย่าอนุมานเป็นช่วงต่อเนื่อง

เก็บหลักฐานก่อนสร้างป้ายสถานะ

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

  1. เพิ่มชื่อเหตุการณ์ยืนยันทั้งสองในรายการสมัครรับ โดยรักษาเหตุการณ์อื่นที่จำเป็นและการตั้งค่าลายเซ็นไว้
  2. ทำตามข้อกำหนดการส่ง webhook ใช้ signing_secret ตรวจ HMAC-SHA256 ของ timestamp ต่อด้วยจุดและ raw body พร้อมตรวจความสดใหม่ของ timestamp ที่ลงลายเซ็น
  3. เก็บเหตุการณ์ที่ตรวจแล้วอย่างถาวรก่อนตอบ 2xx บันทึกเหตุการณ์และงานถาวรแบบ atomic หรือบันทึกการอัปเดตมุมมองกับข้อมูลกันซ้ำใน transaction เดียวกัน
  4. แยกชุดเป็นบันทึกรายข้อความ เก็บบริบท workspace, provider, account_id, data.conversation.id, ID เหตุการณ์ และตัวตนผู้ส่งที่มีอยู่
  5. เก็บ occurred_at, read_at หรือ delivered_at ของผู้ให้บริการ และเวลารับของแอปแยกกัน เวลายืนยันยังมีสำเนาในฟิลด์ชื่อเดียวกันที่ระดับ data ถ้าไม่มีเวลาให้คงไว้ว่าไม่ทราบ อย่าใช้เวลารับแทนเวลาของผู้ให้บริการ

JavaScript ต่อไปนี้ดึงเฉพาะขอบเขตข้อความจากเหตุการณ์ที่ตรวจลายเซ็นแล้ว ไม่ใช่ตัวรับหรือระบบฐานข้อมูลที่สมบูรณ์:

function receiptMessageIds(event) {
  if (!['message.delivered', 'message.read'].includes(event.type)) return [];
  const message = event.data?.message;
  const ids = message?.ids === undefined ? [message?.id] : message.ids;
  if (!Array.isArray(ids) || ids.length === 0 ||
      ids.some(id => typeof id !== 'string' || id.length === 0)) {
    throw new Error('Receipt scope needs review');
  }
  return [...new Set(ids)];
}

หากข้อมูลชุดผิดรูปแบบ อย่าสลับไปใช้ ID เดี่ยวโดยเงียบ ๆ ให้เก็บเหตุการณ์ไว้ตรวจสอบแทนการเดาขอบเขต

รวมหลักฐานโดยไม่สร้างข้อสรุปขึ้นเอง

เก็บการสังเกตว่าส่งถึงและอ่านแล้วแยกกัน ถ้าเหตุการณ์อ่านแล้วมาก่อน สามารถแสดงว่า “พบหลักฐานการอ่าน” โดยไม่สร้าง delivered_at ขึ้นเอง เหตุการณ์ส่งถึงที่ตามมาคือหลักฐานเพิ่มเติม ไม่ควรลดสถานะหน้าจอกลับเป็น “ส่งถึงแล้ว” และเหตุการณ์ซ้ำต้องไม่สร้างการแจ้งเตือนซ้ำ

ถ้ายังไม่มีเนื้อหาข้อความ ให้เก็บเหตุการณ์ยืนยันตามตัวตนข้อความแบบมีขอบเขต แล้วเชื่อมเมื่อมีรายการที่ตรงกัน อย่าสร้างข้อความขาเข้าหรือเพิ่มจำนวนข้อความในกล่องจากเหตุการณ์ยืนยันเพียงอย่างเดียว ใช้ occurred_at กับ ID เหตุการณ์เพื่อกำหนดลำดับที่แน่นอนได้ แต่ไม่ควรให้สถานะที่มาถึงล่าสุดเขียนทับทุกอย่าง

เมื่อส่งข้อความ ให้เก็บ message_id และ provider_ref ที่ตอบกลับคู่กับรายการดำเนินการภายใน ก่อนจับคู่เหตุการณ์ ต้องยืนยันว่าตัวระบุใดตรงกับข้อความของผู้ให้บริการ ชื่อฟิลด์ที่ต่างกันไม่ได้รับประกันว่าค่าจะเท่ากันเสมอ เหตุการณ์ที่จับคู่ไม่ได้ควรเข้าคิวตรวจสอบ ไม่ใช่ผูกกับข้อความที่มีเวลาใกล้ที่สุด เก็บตัวตนผู้ส่งที่มีอยู่ และอย่าขยายหลักฐานหนึ่งรายการเป็น “ทุกคนอ่านแล้ว”

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

ขอบเขตการรองรับ LINE และกรณีทดสอบ

UnifyPort เป็นอินเทอร์เฟซที่ไม่เป็นทางการ ตารางเหตุการณ์รายผู้ให้บริการ อธิบายการรองรับของ parser ไม่ใช่การรับประกันว่าทุกบัญชีหรือทุกระบบจะส่งเหตุการณ์ แม้กล่องข้อความของทีมจะใช้ LINE ก็ควรตรวจผู้ให้บริการที่เลือกก่อนสัญญาว่าจะแสดงสถานะส่งถึงได้ การไม่มีเหตุการณ์ไม่ได้พิสูจน์ว่ายังไม่อ่าน ส่งไม่ถึง หรือถูกบล็อก และไม่มีการรับประกันว่าจะส่ง payload ที่พลาดไปให้ใหม่

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

คำถามที่พบบ่อย

ควรใช้ id หรือ ids?

ใช้ ids เมื่อมีชุดที่ถูกต้องและไม่ว่าง มิฉะนั้นใช้ id เดี่ยว ขอบเขตที่ไม่ถูกต้องต้องเข้าสู่การตรวจสอบ

ใช้เหตุการณ์อ่านแล้วเติม delivered_at ได้ไหม?

ไม่ได้ แสดงหลักฐานการอ่านได้ แต่ห้ามสร้างเวลาส่งถึงของผู้ให้บริการขึ้นเอง

ไม่มีเหตุการณ์ยืนยันแล้วตั้งเป็นส่งล้มเหลวได้ไหม?

ไม่ได้ด้วยเหตุผลนี้เพียงอย่างเดียว คงสถานะไม่ทราบ แล้วตรวจการรองรับ การสมัครรับ เส้นทางรับเหตุการณ์ และการจับคู่ ID

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

เริ่มจากเอกสารสถานะส่งถึง สร้างที่เก็บหลักฐานตามขอบเขตข้อความก่อนเพิ่มป้ายสถานะบนหน้าจอ

ตรวจเอกสารผลิตภัณฑ์เมื่อ 2026-11-06:

UnifyPort API

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

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