ระบบ queue ของบอทเพลงดูเหมือนง่าย — เก็บ list ของเพลง เล่นทีละอัน จบก็ไปต่อ แต่ความจริงคือถ้าทำไม่ดี ผู้ใช้จะเจอเพลงหาย คิวสลับ หรือบอทข้ามเพลงไปเองโดยไม่มีสาเหตุ ผมเจอปัญหาพวกนี้มาหมดแล้วครับ
โครงสร้างที่ใช้
ผมเก็บ queue ในรูปแบบ Map<guildId, Queue> แต่ละ Queue มี:
current— track ที่กำลังเล่นอยู่tracks— array ของเพลงที่รออยู่loop— โหมด none / track / queuevolume— ระดับเสียงของห้องนั้น
interface Queue {
current: Track | null;
tracks: Track[];
loop: 'none' | 'track' | 'queue';
volume: number;
}
จุดที่คนมักทำผิด
ปัญหาใหญ่ที่สุดคือการ advance queue ในที่ที่ผิด หลายคนเขียน logic ไว้ใน skip command แต่นั่นคือหายนะ เพราะถ้าเพลงจบเองตามธรรมชาติ ก็จะมีการ advance สองครั้ง หรือถ้า skip กับ trackEnd เกิดขึ้นใกล้กัน คิวก็จะเพี้ยน
ให้ Lavalink event เป็นตัวขับเคลื่อน queue เสมอ — trackEnd คือจุดเดียวที่ควร advance
shoukaku.on('trackEnd', (player, track, reason) => {
if (reason === 'REPLACED') return; // skip command จัดการแล้ว
const queue = queues.get(player.guildId);
if (!queue) return;
queue.advance();
if (queue.current) player.playTrack({ track: queue.current });
else player.stopTrack();
});
ทำให้ผู้ใช้วางใจได้
สิ่งที่ทำให้คิวน่าเชื่อถือไม่ใช่แค่โค้ดถูกต้อง แต่คือการแสดงผลที่ชัดเจน ผมเพิ่ม embed ที่อัปเดตแบบ real-time บอกว่ากำลังเล่นอะไร ถัดไปคืออะไร และเหลืออีกกี่เพลง — ผู้ใช้ไม่ต้องเดาว่าคิวทำงานอยู่หรือเปล่า
- แสดง progress bar ของเพลงปัจจุบัน
- บอกชื่อเพลงถัดไปใน embed เสมอ
- แจ้งเตือนเมื่อ queue หมดแทนการหายเงียบ
Queue ที่ดีคือ Queue ที่ผู้ใช้ไม่ต้องคิดถึง มันแค่ทำงาน ถ้าใครมีคำถามหรืออยากแชร์ approach ของตัวเองมาคุยกันได้เลยครับที่ Discord