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

แก้ข้อผิดพลาดรูปแบบข้อความ Telegram: ส่งข้อความแบบไดนามิกด้วย HTML

หาก Telegram ส่งข้อความคงที่ได้ แต่ส่งไม่สำเร็จเมื่อแทรกชื่อลูกค้าหรือคำตอบจาก AI ให้ตรวจสอบโหมดจัดรูปแบบก่อนลองส่งซ้ำ สำหรับข้อความธรรมดา ให้ละ parse_mode และใช้ข้อความต้นฉบับ สำหรับ HTML ให้ escape ค่าที่เปลี่ยนแปลงก่อนนำไปใส่ในเทมเพลตที่แอปควบคุม การเข้ารหัส JSON ถูกต้องไม่ได้หมายความว่าข้อความจะผ่านกฎจัดรูปแบบของ Telegram ด้วย

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

  • ข้อความธรรมดา, HTML และ MarkdownV2 ต้องใช้วิธีสร้างข้อความต่างกัน เปลี่ยนแค่ชื่อโหมดไม่พอ
  • เก็บต้นฉบับแยกจากผลลัพธ์ที่จัดรูปแบบแล้ว และ escape เพียงครั้งเดียวตอนสร้างผลลัพธ์
  • แยกการปฏิเสธเพราะรูปแบบออกจากการจำกัดอัตราส่งและผลลัพธ์ที่ยังไม่ทราบ
  • UnifyPort ใช้ provider_data.parse_mode ไม่ใช่ฟิลด์ระดับบนสุดแบบ Bot API

เริ่มจากแยกปัญหาที่ชั้นจัดรูปแบบ

เอกสาร Telegram Bot API ระบุว่า parse_mode ใช้สำหรับแปลเอนทิตีในข้อความ โหมด HTML รองรับเฉพาะแท็กที่กำหนด ไม่ใช่ตัวแสดง HTML แบบเบราว์เซอร์ เอกสารยังแยก Markdown รุ่นเดิมออกจาก MarkdownV2 ดังนั้นการเปลี่ยนโหมดโดยไม่เปลี่ยนตัวสร้างข้อความจึงไม่ใช่วิธีแก้

ทดสอบในแชตที่ควบคุมได้ด้วยข้อความสั้นและไม่มีข้อมูลอ่อนไหว:

  1. ตรวจสอบคำขอจริงหลัง SDK หรือเครื่องมืออัตโนมัติทำ serialization แล้ว ค่าเริ่มต้นของเครื่องมืออาจเพิ่มโหมดให้เอง
  2. ส่งข้อความต้นฉบับสั้น ๆ โดยไม่ใช้ตัวเลือกจัดรูปแบบ อย่าใช้สตริงที่ escape สำหรับ HTML หรือ Markdown ไปแล้ว
  3. เพิ่มเทมเพลต HTML ง่าย ๆ ที่มีแต่ข้อความคงที่
  4. escape ค่าไดนามิกสำหรับเนื้อหาข้อความ HTML ก่อนแทรก
  5. ตรวจสอบผลจาก API และสิ่งที่แสดงในแอปแยกกัน

ถ้าข้อความธรรมดาก็ส่งไม่ได้ ให้ตรวจสอบข้อผิดพลาดจริง ผู้รับ การยืนยันตัวตน และความยาว อย่าสรุปทันทีว่าเป็นปัญหาของตัวแปลรูปแบบ หากฝังคำสั่งส่งไว้ในคำตอบ webhook ให้อ่านการเปรียบเทียบกับคำขอ sendMessage แยกต่างหาก เพราะการฝังคำสั่งแบบนั้นไม่คืนผลการส่งให้ตรวจสอบ

escape ค่าข้อความ ไม่รับมาร์กอัปตามอำเภอใจ

ในโหมด HTML Telegram กำหนดให้แทน <, > และ & ที่ไม่ได้เป็นส่วนของแท็กหรือเอนทิตีด้วย HTML entity ที่ตรงกัน ตัวอย่าง JavaScript นี้จัดการเฉพาะ ข้อความที่แทรกระหว่างแท็ก ไม่ใช่ตัวกรอง HTML ทั่วไป ตัวแปลง Markdown หรือตัวตรวจสอบแอตทริบิวต์ URL

function escapeTelegramHtmlText(value) {
  if (typeof value !== 'string') {
    throw new TypeError('Expected text');
  }
  return value
    .replaceAll('&', '&amp;')
    .replaceAll('<', '&lt;')
    .replaceAll('>', '&gt;');
}

function renderSupportAnswer(customerName, answer) {
  return `<b>${escapeTelegramHtmlText(customerName)}</b>\n` +
    escapeTelegramHtmlText(answer);
}

const rendered = renderSupportAnswer(
  'R&D <Team>',
  'Use x < 5 & keep the original value.'
);

แทน & ก่อนเพื่อไม่ให้เอนทิตีที่สร้างจากขั้นตอนถัดไปถูก escape ซ้ำ ซอร์สที่คาดว่าจะได้คือ:

<b>R&amp;D &lt;Team&gt;</b>
Use x &lt; 5 &amp; keep the original value.

ผู้รับควรเห็นเครื่องหมายตามต้นฉบับ และชื่อเป็นตัวหนา ให้แอปเป็นเจ้าของโครงสร้างแท็ก ส่วนแท็กที่อยู่ในชื่อลูกค้าหรือคำตอบ AI ต้องยังเป็นเนื้อหา ไม่ใช่คำสั่งจัดรูปแบบใหม่

อย่า escape เทมเพลตทั้งชุดหลังประกอบเสร็จ เพราะแท็กที่ตั้งใจใช้จะกลายเป็นข้อความที่มองเห็น และอย่าข้ามการ escape เพียงเพราะจะเรียก JSON.stringify ภายหลัง การ serialize JSON กับการแปล HTML ของ Telegram เป็นคนละชั้นกัน

ถ้าต้องการลิงก์ ให้สร้างตัวประกอบลิงก์แยกที่ตรวจสอบปลายทางและจัดการแอตทริบิวต์อย่างปลอดภัย ตัวอย่างนี้ตั้งใจไม่รองรับแอตทริบิวต์แบบไดนามิก หากต้องเพิ่มรูปแบบ ให้ขยายและทดสอบตามแท็กที่เอกสารรองรับ แทนการรับ HTML อะไรก็ได้

วางพารามิเตอร์ให้ตรงกับ API

วิธีเชื่อมต่อฟิลด์ข้อความตัวเลือกจัดรูปแบบ
Telegram Bot API ทางการ sendMessagetextparse_mode
การส่งข้อความแบบรวมของ UnifyPortmessage.textprovider_data.parse_mode

อินเทอร์เฟซแบบไม่เป็นทางการของ UnifyPort ใช้บัญชีรับส่งข้อความที่เชื่อมต่อไว้ และมีโครงสร้างคำขอต่างจาก Bot API เอกสารส่งข้อความ กำหนด POST /v1/messages, message.text ที่ต้องไม่ว่าง และส่วนขยาย parse_mode ส่วนตารางความสามารถการส่งข้อความ ระบุส่วนขยายนี้สำหรับ Telegram ไม่ใช่คำรับรองว่า HTML ใช้ได้ทุกช่องทาง

ฟังก์ชันต่อไปนี้ใช้ตัวสร้างข้อความข้างต้น โดยรับ ID บัญชีจริง พร้อม ID และประเภทของบทสนทนาที่เลือก ฟังก์ชันสร้าง JSON เท่านั้น ไม่ส่ง ไม่ลองซ้ำ และไม่ใช่รายงานว่าทดสอบการเชื่อมต่อจริงแล้ว

function buildTelegramReply({ accountId, conversation, name, answer }) {
  return {
    account_id: accountId,
    to: { id: conversation.id, type: conversation.type },
    message: {
      type: 'text',
      text: renderSupportAnswer(name, answer)
    },
    provider_data: { parse_mode: 'HTML' }
  };
}

ส่ง body ผ่าน endpoint ตามเอกสาร โดยใช้ X-Api-Key ฝั่งเซิร์ฟเวอร์ สำหรับข้อความธรรมดา ให้ละ provider_data.parse_mode และใช้คำตอบต้นฉบับแทนสตริง HTML อย่าคัดลอก chat_id, entities หรือพารามิเตอร์อื่นของ Bot API ลงใน body แบบรวม หากไม่มีการระบุ mapping ไว้

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

ทดสอบขอบเขตเนื้อหาก่อนเพิ่มการลองซ้ำ

ใช้ข้อมูลทดสอบที่สร้างขึ้น ไม่ใช้บทสนทนาจริงของลูกค้า:

ข้อมูลทดสอบสิ่งที่ต้องตรวจสอบ
ชื่อที่มี & และวงเล็บมุมเห็นอักขระเดิม และมีเพียงแท็กที่แอปกำหนดเท่านั้นที่จัดรูปแบบ
ต้นฉบับมีข้อความ &amp;ยังคงเป็นข้อความเดิม ไม่ถูก decode โดยไม่ตั้งใจ
อีโมจิ หลายภาษา และขึ้นบรรทัดใหม่serialization และการแสดงผลไม่ทำลายเนื้อหา
ค่าว่างหรือคำตอบ AI ยาวมากตรวจสอบข้อมูลและใช้นโยบายความยาวก่อนส่ง
สร้างผลลัพธ์จากต้นฉบับเดิมสองครั้งเริ่มจากต้นฉบับทุกครั้ง ไม่ใช้ผลลัพธ์ครั้งก่อนมา escape ซ้ำ

สำหรับข้อความยาว ให้แบ่งต้นฉบับเป็นส่วนที่มีความหมายก่อน แล้วจัดรูปแบบแต่ละส่วนแยกกัน อย่าตัดสตริง HTML ที่สร้างเสร็จตามตำแหน่งใดก็ได้ เพราะอาจตัดกลางแท็กหรือเอนทิตี ตรวจสอบข้อกำหนดความยาวปัจจุบันของเมธอดที่ใช้ ตัวอย่างนี้ไม่ได้ตรวจสอบความยาวหรือรองรับ rich message

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

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

JSON ถูกต้องหมายความว่ารูปแบบข้อความถูกต้องหรือไม่?

ไม่เสมอไป JSON อาจแปลได้สำเร็จ แต่ข้อความภายในยังผิดกฎของโหมดที่เลือกได้

ควรเปลี่ยนทุกข้อผิดพลาดไปใช้ MarkdownV2 หรือไม่?

ไม่ควร MarkdownV2 มีกฎ escape ของตัวเอง เลือกโหมดให้ชัดและใช้ตัวสร้างข้อความที่ตรงกัน

ทำไมข้อความธรรมดาจึงแสดง HTML entity?

ตรวจสอบว่าหลังนำโหมดออกแล้ว ยังใช้สตริงที่ escape สำหรับ HTML อยู่หรือไม่ ข้อความธรรมดาควรเริ่มจากต้นฉบับ

ฟังก์ชันนี้ใช้กับเอกสาร HTML ทั้งฉบับได้หรือไม่?

ไม่ได้ ฟังก์ชันนี้ escape เฉพาะข้อความไดนามิกระหว่างแท็กที่เชื่อถือได้ ไม่ตรวจสอบแท็ก แอตทริบิวต์ ลิงก์ หรือความยาวทั้งหมด

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

แยกตัวสร้างข้อความธรรมดากับ HTML แล้วทดสอบในบทสนทนา Telegram ที่ควบคุมได้ หากใช้บัญชีที่เชื่อมต่อ ให้ยึดข้อกำหนดส่งข้อความของ UnifyPort

ตรวจสอบแหล่งข้อมูลเมื่อ 2026-10-21:

UnifyPort API

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

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