You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Discord.js开发的音乐机器人中添加播放队列与切歌功能

实现方案

第一步:定义全局服务器队列

首先在你的机器人入口文件(通常是index.js)最顶部定义全局Map,用来存储每个服务器独立的播放队列,挂载到client上方便所有命令文件访问:

// 全局队列存储,key为服务器ID,value为队列信息对象
const serverQueues = new Map();
client.serverQueues = serverQueues;

每个服务器的队列对象包含3个核心字段:

  • voiceConnection:当前语音连接实例
  • songs:待播放的歌曲数组,每个元素存储歌曲的url、标题等信息
  • playing:当前是否正在播放的状态标记

第二步:修改播放命令逻辑

替换你原有的p.js播放命令代码,逻辑调整为:如果当前服务器没有正在播放的内容就直接播放请求的歌曲,已有内容在播放则把歌曲加入队列等待播放:

const ytdl = require('ytdl-core');
const ytSearch = require('yt-search');

module.exports = {
    name: 'p',
    description: '播放YouTube音乐,支持链接/关键词搜索',
    async execute(message, args) {
        const voiceChannel = message.member.voice.channel;
        if (!voiceChannel) return message.channel.send('你需要加入语音频道才能使用该命令!');
        const permissions = voiceChannel.permissionsFor(message.client.user);
        if (!permissions.has('CONNECT')) return message.channel.send('我没有加入该语音频道的权限');
        if (!permissions.has('SPEAK')) return message.channel.send('我没有在该语音频道说话的权限');
        if (!args.length) return message.channel.send('请输入要播放的歌曲链接或关键词');

        // 校验URL的工具函数
        const validURL = (str) =>{
            const regex = /(http|https):\/\/(\w+:{0,1}\w*)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%!\-\/]))?/;
            return regex.test(str);
        }

        // 获取当前服务器的队列,不存在就初始化
        const queue = message.client.serverQueues.get(message.guild.id) || {
            voiceConnection: null,
            songs: [],
            playing: false
        };

        // 解析要播放的歌曲
        let song;
        if(validURL(args[0])){
            const songInfo = await ytdl.getInfo(args[0]);
            song = {
                title: songInfo.videoDetails.title,
                url: songInfo.videoDetails.video_url
            }
        }else{
            const videoFinder = async (query) => {
                const videoResult = await ytSearch(query);
                return (videoResult.videos.length > 1) ? videoResult.videos[0] : null;
            }
            const video = await videoFinder(args.join(' '));
            if(!video) return message.channel.send('没有找到对应的歌曲');
            song = {
                title: video.title,
                url: video.url
            }
        }

        // 无播放内容时直接播放,否则加入队列
        if(!queue.playing){
            queue.songs.push(song);
            queue.playing = true;
            queue.voiceConnection = await voiceChannel.join();
            message.client.serverQueues.set(message.guild.id, queue);
            playSong(message.guild, queue.songs[0]);
            await message.reply(`:thumbsup: 正在播放 ***${song.title}***`)
        }else{
            queue.songs.push(song);
            message.client.serverQueues.set(message.guild.id, queue);
            await message.reply(`:musical_note: 已将 ***${song.title}*** 加入播放队列,当前队列位置 ${queue.songs.length}`)
        }

        // 封装播放函数,歌曲播放完成自动触发下一首
        async function playSong(guild, song){
            const serverQueue = message.client.serverQueues.get(guild.id);
            if(!song){
                serverQueue.voiceConnection.leave();
                message.client.serverQueues.delete(guild.id);
                return message.channel.send('队列所有歌曲播放完毕,已退出语音频道');
            }
            const stream = ytdl(song.url, {filter: 'audioonly'});
            serverQueue.voiceConnection.play(stream, {seek: 0, volume: 1})
            .on('finish', () =>{
                serverQueue.songs.shift();
                playSong(guild, serverQueue.songs[0]);
            });
        }
    }
}

第三步:实现skip切歌命令

新建skip.js命令文件,代码如下:

module.exports = {
    name: 'skip',
    description: '切歌,跳过当前正在播放的歌曲',
    async execute(message) {
        const voiceChannel = message.member.voice.channel;
        if (!voiceChannel) return message.channel.send('你需要加入语音频道才能使用切歌命令!');
        const serverQueue = message.client.serverQueues.get(message.guild.id);
        if(!serverQueue || !serverQueue.playing) return message.channel.send('当前没有正在播放的歌曲');
        // 结束当前播放,自动触发finish事件播放下一首
        serverQueue.voiceConnection.dispatcher.end();
        await message.reply(`:track_next: 已跳过当前歌曲`);
    }
}

内容的提问来源于stack exchange,提问作者AndiTheMajor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 03:54:00