最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Queuing audio to play via Discord Bot - Stack Overflow

programmeradmin1浏览0评论

I have been playing around in writing a bot for Discord at the moment.

I have managed to get it to query YouTube based given search terms and then play audio from the top result, however I have e across an issue when I am trying to to queue audio tracks in a list and play them once an audio stream has pleted.

const API_KEY = "<my API Key>";

var discord = require("discord.js");
var ytdl = require('ytdl-core');
var request = require('superagent');

var bot = new discord.Client();
var voiceChannel = null;
var ytAudioQueue = [];

bot.on('ready', function() {
    console.log('I am ready');
});

bot.on('message', function(message) {
    var messageParts = message.content.split(' ');

    var mand = messageParts[0].toLowerCase();
    var parameters = messageParts.splice(1, messageParts.length);

    console.log("mand: " + mand);
    console.log("parameters: " + parameters);

    switch (mand) {
        case "hi":
            message.reply("Hey there!");
            break;
        case "*help":
            HelpCommand(message);
            break;
        case "*join":
            message.reply("Attempting to join channel: " + parameters[0]);
            JoinCommand(parameters[0], message);
            break;
        case "*play":
            PlayCommand(parameters.join(" "), message);
            break;
    }
});

voiceChannel.on('speaking', (user, speaking) => {

    // the audio has finished playing, so remove it from the queue and start playing the next song
    if (!speaking && ytAudioQueue.length > 1) {
        ytAudioQueue.pop();

        if (voiceChannel == null) {
            JoinCommand(bot.channels.find(val => val.type === 'voice').name).then(function() {
                PlayStream(ytAudioQueue.first);
            });
        }
        else {
            PlayStream(ytAudioQueue.first);
        }
    }
});

/* COMMAND HANDLERS */

/// lists out all of the bot mands
function HelpCommand(originalMessage) {
    originalMessage.reply("*join <channel-to-join> - Connects to bot to a channel by channel name");
    originalMessage.reply("*play <YouTube search term> - Plays audio from YouTube based on the search term");
}

/// plays audio based on results from youtube search
function PlayCommand(searchTerm) {
    //bot.sendMessage("Searching Youtube for audio...");
    YoutubeSearch(searchTerm);
}

/// joins the bot to the specified voice channel
function JoinCommand(channelName) {

    if (voiceChannel) {
        voiceChannel.disconnet();
    }

    var voiceChannel = GetChannelByName(channelName);
    return voiceChannel.join();
}

/* END COMMAND HANDLERS */

/* HELPER METHODS */

/// returns the channel that matches the name provided
function GetChannelByName(name) {
    var channel = bot.channels.find(val => val.name === name);

    return channel;
}

function YoutubeSearch(searchKeywords) {
    var requestUrl = '' + `?part=snippet&q=${escape(searchKeywords)}&key=${API_KEY}`;

    request(requestUrl, (error, response) => {
        if (!error && response.statusCode == 200) {

            var body = response.body;
            if (body.items.length == 0) {
                console.log("Your search gave 0 results");
                return videoId;
            }

            for (var item of body.items) {
                if (item.id.kind === 'youtube#video') {
                    QueueYtAudioStream(item.id.videoId);
                }
            }
        }
        else {
            console.log("Unexpected error when searching YouTube");
            return null;
        }
    });

    return null;
}

/// Queues result of Youtube search into stream
function QueueYtAudioStream(videoId) {
    var streamUrl = `=${videoId}`;
    ytAudioQueue.push(streamUrl);
}

// plays a given stream
function PlayStream(streamUrl) {

    const streamOptions = {seek: 0, volume: 1};
    console.log("Streaming audio from " + streamUrl);

    if (streamUrl) {
        const stream = ytdl(streamUrl, {filter: 'audioonly'});
        const dispatcher = bot.voiceConnections.first().playStream(stream, streamOptions);
    }
}

/* END HELPER METHODS */

bot.login("<BOT LOGIN ID HERE>");

/

My idea was that I could mence the playing of the next audioStream once the voiceChannel has stopped speaking, however the event fails as this is not set on startup, whilst I could do this I do not think it will suit my needs as the voice channel can be changed with another mand.

I have been playing around in writing a bot for Discord at the moment.

I have managed to get it to query YouTube based given search terms and then play audio from the top result, however I have e across an issue when I am trying to to queue audio tracks in a list and play them once an audio stream has pleted.

const API_KEY = "<my API Key>";

var discord = require("discord.js");
var ytdl = require('ytdl-core');
var request = require('superagent');

var bot = new discord.Client();
var voiceChannel = null;
var ytAudioQueue = [];

bot.on('ready', function() {
    console.log('I am ready');
});

bot.on('message', function(message) {
    var messageParts = message.content.split(' ');

    var mand = messageParts[0].toLowerCase();
    var parameters = messageParts.splice(1, messageParts.length);

    console.log("mand: " + mand);
    console.log("parameters: " + parameters);

    switch (mand) {
        case "hi":
            message.reply("Hey there!");
            break;
        case "*help":
            HelpCommand(message);
            break;
        case "*join":
            message.reply("Attempting to join channel: " + parameters[0]);
            JoinCommand(parameters[0], message);
            break;
        case "*play":
            PlayCommand(parameters.join(" "), message);
            break;
    }
});

voiceChannel.on('speaking', (user, speaking) => {

    // the audio has finished playing, so remove it from the queue and start playing the next song
    if (!speaking && ytAudioQueue.length > 1) {
        ytAudioQueue.pop();

        if (voiceChannel == null) {
            JoinCommand(bot.channels.find(val => val.type === 'voice').name).then(function() {
                PlayStream(ytAudioQueue.first);
            });
        }
        else {
            PlayStream(ytAudioQueue.first);
        }
    }
});

/* COMMAND HANDLERS */

/// lists out all of the bot mands
function HelpCommand(originalMessage) {
    originalMessage.reply("*join <channel-to-join> - Connects to bot to a channel by channel name");
    originalMessage.reply("*play <YouTube search term> - Plays audio from YouTube based on the search term");
}

/// plays audio based on results from youtube search
function PlayCommand(searchTerm) {
    //bot.sendMessage("Searching Youtube for audio...");
    YoutubeSearch(searchTerm);
}

/// joins the bot to the specified voice channel
function JoinCommand(channelName) {

    if (voiceChannel) {
        voiceChannel.disconnet();
    }

    var voiceChannel = GetChannelByName(channelName);
    return voiceChannel.join();
}

/* END COMMAND HANDLERS */

/* HELPER METHODS */

/// returns the channel that matches the name provided
function GetChannelByName(name) {
    var channel = bot.channels.find(val => val.name === name);

    return channel;
}

function YoutubeSearch(searchKeywords) {
    var requestUrl = 'https://www.googleapis./youtube/v3/search' + `?part=snippet&q=${escape(searchKeywords)}&key=${API_KEY}`;

    request(requestUrl, (error, response) => {
        if (!error && response.statusCode == 200) {

            var body = response.body;
            if (body.items.length == 0) {
                console.log("Your search gave 0 results");
                return videoId;
            }

            for (var item of body.items) {
                if (item.id.kind === 'youtube#video') {
                    QueueYtAudioStream(item.id.videoId);
                }
            }
        }
        else {
            console.log("Unexpected error when searching YouTube");
            return null;
        }
    });

    return null;
}

/// Queues result of Youtube search into stream
function QueueYtAudioStream(videoId) {
    var streamUrl = `https://www.youtube./watch?v=${videoId}`;
    ytAudioQueue.push(streamUrl);
}

// plays a given stream
function PlayStream(streamUrl) {

    const streamOptions = {seek: 0, volume: 1};
    console.log("Streaming audio from " + streamUrl);

    if (streamUrl) {
        const stream = ytdl(streamUrl, {filter: 'audioonly'});
        const dispatcher = bot.voiceConnections.first().playStream(stream, streamOptions);
    }
}

/* END HELPER METHODS */

bot.login("<BOT LOGIN ID HERE>");

https://jsfiddle/o3Lvqt94/

My idea was that I could mence the playing of the next audioStream once the voiceChannel has stopped speaking, however the event fails as this is not set on startup, whilst I could do this I do not think it will suit my needs as the voice channel can be changed with another mand.

Share Improve this question edited Oct 23, 2016 at 17:08 ReyAnthonyRenacia 17.6k6 gold badges41 silver badges63 bronze badges asked Oct 23, 2016 at 8:09 Matthew PigramMatthew Pigram 1,4305 gold badges28 silver badges67 bronze badges 0
Add a ment  | 

3 Answers 3

Reset to default 1

I used your code as a base for my own Discord bot and solved the bug you mentioned. I simply changed

dispatcher.on('end', () => {                        
  PlayNextStreamInQueue();
});

to

 dispatcher.on('end', () => {                       
   dispatcher = null;                 
   PlayNextStreamInQueue();
 });

My solution.

index.js

var discord = require("discord.js");        // discord library
var ytdl = require('ytdl-core');            // youtube download library
var youtube = require('./youtube.js');      // performs youtube API requests

var bot = new discord.Client();
var ytAudioQueue = [];
var dispatcher = null;

bot.on('ready', function () {
    console.log('I am ready');
});

bot.on('message', function (message) {
    var messageParts = message.content.split(' ');

    var mand = messageParts[0].toLowerCase();
    var parameters = messageParts.splice(1, messageParts.length);

    console.log("mand: " + mand);
    console.log("parameters: " + parameters);

    switch (mand) {
        case "hi":
            message.reply("Hey there!");
            break;
        case "*help":
            HelpCommand(message);
            break;
        case "*join":
            message.reply("Attempting to join channel: " + parameters[0]);
            JoinCommand(parameters[0]);
            break;
        case "*play":
            PlayCommand(parameters.join(" "), message);
            break;
        case "*playqueue":
            PlayQueueCommand(message);
            break;
    }
});

/* COMMAND HANDLERS */

/// lists out all of the bot mands
function HelpCommand(originalMessage) {
    originalMessage.reply("*join <channel-to-join> - Connects to bot to a channel by channel name");
    originalMessage.reply("*play <YouTube search term> - Plays audio from YouTube based on the search term");
    originalMessage.reply("*playqueue - Lists the audio remaining in the play queue");
}

/// plays audio based on results from youtube search
function PlayCommand(searchTerm) {

    // if not connected to a voice channel then connect to first one
    if (bot.voiceConnections.array().length == 0) {
        var defaultVoiceChannel = bot.channels.find(val => val.type === 'voice').name;
        JoinCommand(defaultVoiceChannel);
    }

    // search youtube using the given search search term and perform callback action if video is found
    youtube.search(searchTerm, QueueYtAudioStream);
}

/// lists out all music queued to play
function PlayQueueCommand(message) {
    var queueString = "";

    for(var x = 0; x < ytAudioQueue.length; x++) {
        queueString += ytAudioQueue[x].videoName + ", ";
    }

    queueString = queueString.substring(0, queueString.length - 2);
    message.reply(queueString);
}

/// joins the bot to the specified voice channel
function JoinCommand(channelName) {
    var voiceChannel = GetChannelByName(channelName);

    if (voiceChannel) {
        voiceChannel.join();
        console.log("Joined " + voiceChannel.name);
    }

    return voiceChannel;
}

/* END COMMAND HANDLERS */
/*----------------------------------------------------------------------*/
/* HELPER METHODS */

/// returns the channel that matches the name provided
function GetChannelByName(name) {
    var channel = bot.channels.find(val => val.name === name);
    return channel;
}

/// Queues result of Youtube search into stream
function QueueYtAudioStream(videoId, videoName) {
    var streamUrl = `${youtube.watchVideoUrl}${videoId}`;

    if (!ytAudioQueue.length) {
        ytAudioQueue.push(
            {
                'streamUrl': streamUrl,
                'videoName': videoName
            }
        );

        console.log("Queued audio " + videoName);
        PlayStream(ytAudioQueue[0].streamUrl);
    }
    else {
        ytAudioQueue.push(
            {
                'streamUrl': streamUrl,
                'videoName': videoName
            }
        );

        console.log("Queued audio " + videoName);
    }

}

/// Plays a given stream
function PlayStream(streamUrl) {

    const streamOptions = {seek: 0, volume: 1};

    if (streamUrl) {
        const stream = ytdl(streamUrl, {filter: 'audioonly'});

        if (dispatcher == null) {

            var voiceConnection = bot.voiceConnections.first();
            //console.log(voiceConnection);

            if (voiceConnection) {

                console.log("Now Playing " + ytAudioQueue[0].videoName);
                dispatcher = bot.voiceConnections.first().playStream(stream, streamOptions);

                dispatcher.on('end', () => {
                    PlayNextStreamInQueue();
                });

                dispatcher.on('error', (err) => {
                    console.log(err);
                });
            }
        }
        else {
            dispatcher = bot.voiceConnections.first().playStream(stream, streamOptions);
        }
    }
}

/// Plays the next stream in the queue
function PlayNextStreamInQueue() {

    ytAudioQueue.splice(0, 1);

    // if there are streams remaining in the queue then try to play
    if (ytAudioQueue.length != 0) {
        console.log("Now Playing " + ytAudioQueue[0].videoName);
        PlayStream(ytAudioQueue[0].streamUrl);
    }
}
/* END HELPER METHODS */

bot.login("redacted");

youtube.js

var request = require('superagent');

const API_KEY = "redacted";
const WATCH_VIDEO_URL = "https://www.youtube./watch?v=";

exports.watchVideoUrl = WATCH_VIDEO_URL;

exports.search = function search(searchKeywords, callback) {
    var requestUrl = 'https://www.googleapis./youtube/v3/search' + `?part=snippet&q=${escape(searchKeywords)}&key=${API_KEY}`;

    request(requestUrl, (error, response) => {
        if (!error && response.statusCode == 200) {

            var body = response.body;
            if (body.items.length == 0) {
                console.log("Your search gave 0 results");
                return;
            }

            for (var item of body.items) {
                if (item.id.kind === 'youtube#video') {
                    callback(item.id.videoId, item.snippet.title);
                    return; // prevent adding entire list of youtube videos
                }
            }
        }
        else {
            console.log("Unexpected error when searching YouTube");
            return;
        }
    });

    return;
};

I'd say save the que in an array

var que = {
  0 = "LINK";
};

and you retreive it using

que[0]

and auto the que

function skip() {
  for (var i = 0; var length = que.length; i < length; i++) {
    if (i != length) {
      que[i] = que[(i+1)];
    } else {
      que[i] = null;
    }
  }
}

to show the que

function showque() {
  var queText = "";
  for (var i = 0; var length = que.length; i < length; i++) {
    queText = queText + "[" + i + "] " + que[i] + "\n";
  }
  return queText;
}

snipet of message to send

message("ANYTHING BEFORE THE QUE\n" + showque() + "ANYTHING AFTER");

that hopefully should work.

发布评论

评论列表(0)

  1. 暂无评论