Discord มีสองสิ่งที่คนเรียกว่า "webhook" และทั้งคู่ทำงานต่างกันสิ้นเชิง อย่างหนึ่งคือ URL แบบยิงแล้วลืมสำหรับโพสต์ข้อความ อีกอย่างคือ interaction endpoint ที่รับ POST ที่มีลายเซ็นทุกครั้งที่ผู้ใช้รันคำสั่ง slash อย่างแรกทำงานบน localhost อย่างที่สองไม่เคยทำได้ คู่มือนี้พูดถึงอย่างที่สอง เพราะมันคือสิ่งที่ต้องใช้ tunnel
webhook Discord สองแบบ
webhook ขาออก คือ URL ที่คุณ POST ไป คุณส่งข้อความ JSON แล้วมันโผล่ในแชนเนล ไม่ต้องใช้ tunnel เพราะทราฟฟิกไหลจากเครื่องของคุณออกไป
interaction endpoint ไปทิศตรงข้าม คุณลงทะเบียน URL กับ Discord; Discord จะ POST มาทุกครั้งที่ผู้ใช้รัน /yourcommand endpoint ต้องเป็น HTTPS ตอบภายในสามวินาที และตรวจสอบลายเซ็น Ed25519 ทุกคำขอ localhost เองทำสิ่งเหล่านี้ไม่ได้
ทำไม interaction endpoint จึงเข้มงวดกว่าส่วนใหญ่
Discord ไม่เพียงต้องการ HTTPS — มันบังคับให้คุณพิสูจน์ว่าคุณควบคุม endpoint นั้นก่อนจะยอมรับ URL การตรวจสอบเกิดตอนลงทะเบียน: Discord ส่ง interaction PING เซิร์ฟเวอร์ของคุณต้องตอบด้วย PONG ที่มีลายเซ็น ถ้า Ed25519 ล้มเหลวแม้ครั้งเดียว Discord ก็ปฏิเสธที่จะบันทึก URL
คุณจะไม่ได้ส่วน Ed25519 ถูกในครั้งแรก แทบไม่มีใครได้
การตรวจสอบ Ed25519 ส่วนที่กัด
Discord ใช้ Ed25519 (ไม่ใช่ HMAC SHA-256) และให้สอง header ในทุก interaction:
X-Signature-Ed25519— ลายเซ็นในรูป hexX-Signature-Timestamp— timestamp เป็นวินาที
คุณเซ็น timestamp + rawBody ด้วย public key ที่ Discord แสดงในพอร์ทัลนักพัฒนา ภาษาส่วนใหญ่มีไลบรารีสำหรับเรื่องนี้ ใน Node:
import nacl from 'tweetnacl';
const valid = nacl.sign.detached.verify(
Buffer.from(timestamp + rawBody),
Buffer.from(signature, 'hex'),
Buffer.from(PUBLIC_KEY, 'hex'),
);
สาเหตุอันดับหนึ่งของการตรวจสอบล้มเหลวคือ — อีกครั้ง — middleware ที่ parse body ก่อนโค้ดนี้จะรัน คุณต้องใช้สตริงดิบที่ Discord ส่งมา ไม่ใช่เวอร์ชันที่แปลงกลับเป็นสตริงของออบเจ็กต์ที่ parse แล้ว
ทีละขั้น: interaction endpoint บน localhost
- เริ่มเซิร์ฟเวอร์ HTTP ของบอตในเครื่อง (พอร์ตใดก็ได้)
- รัน
npx portpreview 3000เพื่อรับ URL HTTPS สาธารณะ - ในพอร์ทัลนักพัฒนา Discord เปิดแอปแล้ววาง URL tunnel พร้อม path ของ interactions ลงใน Interactions Endpoint URL
- กด Save Discord จะยิง
PINGทันที ถ้า Ed25519 ผ่าน URL จะถูกบันทึก ถ้าไม่ ฟิลด์จะแสดง error - เมื่อบันทึกแล้ว รันคำสั่ง slash ในเซิร์ฟเวอร์ใดก็ได้ที่บอตของคุณอยู่ คำขอจะมาถึง handler ของคุณ
ถ้าบันทึกล้มเหลวซ้ำ ๆ ให้ตรวจคำขอที่จับได้ — header, body และการตอบของเซิร์ฟเวอร์ เก้าในสิบครั้ง body ถูก middleware JSON แก้เงียบ ๆ ก่อนการตรวจสอบ
เส้นตายสามวินาที
Discord ต้องการการตอบภายในไม่ถึงสามวินาที ไม่งั้นนับว่า interaction ล้มเหลว สำหรับอะไรที่นานกว่านั้น ส่งการตอบเริ่มต้นแบบ type-5 (deferred) แล้วค่อย PATCH interaction เดิมด้วยคำตอบจริงทีหลัง การทดสอบในเครื่องทำให้เห็นชัดว่า handler ไหนช้าเกินไป — การจับคำขอแสดง latency ที่แท้จริง
webhook ขาออกยังมีประโยชน์สำหรับทดสอบ
ถ้าคุณแค่อยากส่งข้อความไปแชนเนลจากโค้ดในเครื่อง URL webhook ขาออกทำงานได้โดยไม่ต้องมี tunnel POST JSON แล้วข้อความก็โผล่ เราใช้สิ่งนี้ส่งแจ้งเตือนล็อกจากการรัน dev ในเครื่อง
เมื่อไรที่คุณโตเกินการตั้งค่านี้
สำหรับบอตเดี่ยวที่มีไม่กี่คำสั่ง การทดสอบในเครื่องด้วย tunnel ก็เหลือเฟือ ถ้าคุณดูแลบอตสาธารณะที่ให้บริการผู้ใช้หลายล้าน สุดท้ายคุณจะอยากได้การดีพลอย staging ที่มี URL คงที่ เพื่อให้ Discord ไม่ต้องตรวจสอบใหม่ทุกครั้งที่เซสชัน tunnel หมุนเวียน
timeout สามวินาทีของ Discord เป็นปัญหาตระกูลเดียวกับหน้าต่างสิบวินาทีของ GitHub อ่านมุมมองของเราเรื่อง การลองใหม่ของ webhook และ idempotency สำหรับฉบับยาว เข้าร่วมรายชื่อรอของ PortPreview เพื่อ tunnel + การจับใน CLI เดียว