简体   繁体   中英

How to make Text to speech queue Node.js

This might be a very simple thing to do for the pros like some of you, I hope you can help me, I will really appreciate your time, thanks.

I have this TTS discord bot, and it works! But I can't figure out how to queue extra incoming TTS requests.

When current TTS is playing and a new request is submitted, current TTS will stop and start executing next request without letting the current TTS finish.

What I want to do is queue all requests so every single one plays after each finishes.

Some one told me to use this package but I just can't figure it out.

I'm a noob with very limited knowledge, so can someone please add the extra lines that are needed for queues? Or provide a good guide?

I'm sorry for being too picky I know I shouldn't ask for too much, but I've been dealing with this issue for weeks now and I'm desperate.

Here is my code:

const { getAudioUrl } = require('google-tts-api');

module.exports = {
  name: 'say',
  aliases: ['s'],
  cooldown: 3,
  description: "tts",

  execute: async (message, args, cmd, client, Discord) => {
    console.log('Say command executed');

    if (!args[0]) 
      return message.channel.send('you gotta include a message!');
    
    const string = args.join(' ');

    if (string.length > 200) 
      return message.channel.send('the message cant be more than 200 letters!');
    
    const voiceChannel = message.member.voice.channel;

    if (!voiceChannel) 
      return message.reply('You have to be in a voice channel to send a message!');

    const audioURL = getAudioUrl(string, {
      lang: 'en',
      slow: false,
      host: 'https://translate.google.com',
      timeout: 10000,
    });

    try {
      message.channel.startTyping();

      setTimeout(function () {
        message.channel.send('Speaking your msg...');
        message.channel.stopTyping();
        console.log('Now starting to talk');
      }, 1000);

      voiceChannel.join().then(connection => {
        const dispatcher = connection.play(audioURL);
        dispatcher.on('finish', () => {
          console.log('Done talking');
        });
      });
    }
    catch (e) {
      message.channel.send('Bot error, please try again or try later');
      console.error(e);
    }

    setTimeout(function () {
      voiceChannel.leave();
    }, 240000);
  }
}

With queue package you "push" your execute logic wrapped in a function. The function will be then executed by the queue. The tricky part here is that you need to end the execution inside the dispatcher's finish event handler. You can introduce a new promise which will be then resolved from inside the event handler.

Here's a rough sketch of the solution:

const { getAudioUrl } = require('google-tts-api');
const queue = require('queue');

// Create the queue where we will push our jobs
const q = queue({ concurrency: 1, autostart: true });

module.exports = {
  name: 'say',
  aliases: ['s'],
  cooldown: 3,
  description: 'tts',

  execute: (message, args, cmd, client, Discord) => {
    // Enqueue task
    q.push(async () => {
      console.log('Say command executed');

      if (!args[0]) {
        return message.channel.send('you gotta include a message!');
      }

      const string = args.join(' ');

      if (string.length > 200) {
        return message.channel.send(
          'the message cant be more than 200 letters!'
        );
      }

      const voiceChannel = message.member.voice.channel;

      if (!voiceChannel) {
        return message.reply(
          'You have to be in a voice channel to send a message!'
        );
      }

      const audioURL = getAudioUrl(string, {
        lang: 'en',
        slow: false,
        host: 'https://translate.google.com',
        timeout: 10000,
      });

      try {
        message.channel.startTyping();

        setTimeout(function () {
          message.channel.send('Speaking your msg...');
          message.channel.stopTyping();
          console.log('Now starting to talk');
        }, 1000);

        const connection = await voiceChannel.join();
        const dispatcher = connection.play(audioURL);
        // Here we need to handle the promise resolution from the nested event handler
        return Promise.new((resolve, reject) => {
          dispatcher.on('finish', () => {
            console.log('Done talking');
            // voiceChannel.leave();
            resolve();
          });
          dispatcher.on('error', (err) => {
            console.log('Some error in dispatcher happenned!', err);
            reject(err);
          });
        });
      } catch (e) {
        message.channel.send('Bot error, please try again or try later');
        console.error(e);
      }

      // This code won't be called
      setTimeout(function () {
        voiceChannel.leave();
      }, 240000);
    });
  },
};

Take this solution with a grain of salt. I didn't figure out what version of Discord.js you are targeting. Also note that timeouts and possible other error conditions are not handled.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM