0

I've been making my first discord bot recently and today I'm facing an issue for a little requirement. I need my bot to connect to all voice channels of the server and play a mp3 file. It's for an alert message.

I first made a basic test with this code to play the mp3 in the channel the user who launch the command is connected :

exports.run = async (client, message, args, level) => {
  if (message.member.voiceChannel) {
    message.member.voiceChannel.join()
    .then(connection => {
        const dispatcher = connection.playFile('/home/pi/.discordbots/TARVIS/1.mp3');
        dispatcher.on("end", end => {message.member.voiceChannel.leave()});
    })
    .catch(console.error);
  }
};

The code above works fine

So I tried to make it for all voice channels :

  let voiceChannels = message.guild.channels.filter(channel => channel.type == 'voice');

  voiceChannels.forEach(channel =>
    channel.join()
      .then(connection => {
        const dispatcher = connection.playFile('/home/pi/.discordbots/TARVIS/1.mp3');
        dispatcher.on("end", end => { channel.leave() });
      })
      .catch(console.error)
  );

The problem is that the bot connect to the first channel and then directly connect to the second without having time to play the file in the first channel.

I think I have to look at the client.createVoiceBroadcast(); method. I tried to use it but I wasn't able to find a good example. Here is what I tried but it's not working too :

exports.run = (client, message, args, level) => {
  let voiceChannels = message.guild.channels.filter(channel => channel.type == 'voice');
  const broadcast = client.createVoiceBroadcast();
  broadcast.playFile('/home/pi/.discordbots/TARVIS/1.mp3');

  voiceChannels.forEach(channel =>
    channel.join()
      .then(connection => {
        const dispatcher = connection.playBroadcast(broadcast);
        dispatcher.on("end", end => { channel.leave() });
      })
      .catch(console.error)
  );

Expected result is the bot connects in each voice channel, one by one and play the mp3 file.

Thank you in advance for your help

EDIT

I tried to make an async function and use the await on the connection.playFile() but I still having the same problem. Bot connect to all voice channel but do not wait for the file to be played.

Here is the code I tried:

exports.run = async (client, message, args, level) => {

  async function play(voiceChannel) {
    console.log(voiceChannel.name + ` Type:` + voiceChannel.type + ` (` + voiceChannel.id + `)`);
    voiceChannel.join().then(async function (connection) {
      dispatcher = await connection.playFile('/home/pi/.discordbots/TARVIS/sncf.mp3');
      dispatcher.on('end', function () {
        voiceChannel.leave()
      });
    });
  }
  let voiceChannels = message.guild.channels.filter(channel => channel.type == 'voice');

  voiceChannels.map(vc => play(vc));

};

I'm pretty sure the solution is near... but I'm stuck... Does someone can help me to find the correct syntax ?

EDIT 2

Here is what I tried with your solution:

exports.run = async (client, message, args, level) => {

  async function play(voiceChannels) {
    for (let channel of voiceChannels) {
      console.log('Joining channel ' + channel.name);

      await channel.join().then(async (connection) => {
        console.log('Joined channel');

        let dispatcher = connection.playFile('/home/pi/.discordbots/TARVIS/sncf.mp3');
        await dispatcher.on('end', function () {
          channel.leave();
        });
      });
    }
  }

  let channels = message.guild.channels.filter(channel => channel.type == 'voice');
  console.log(channels);
  play(channels);

};
BadSpencer
  • 106
  • 2
  • 6
  • Perhaps [this answer](https://stackoverflow.com/a/37576787/5793873) might be helpful to check out – T. Dirks Jan 24 '19 at 12:09
  • Thank you @T.Dirks I had a look and yes I think it's related to async/await but wasn't able to find the correct syntax. I'll try to adapt my code with this answer – BadSpencer Jan 24 '19 at 12:40
  • I'm still trying to make it work I saw that [p-iteration](https://github.com/toniov/p-iteration) may help me. I 'll try it but I'm curious to kjnow how to do this in native – BadSpencer Jan 25 '19 at 12:54

2 Answers2

0

I might have found (somewhat) of a solution. I tried to recreate this problem in a JSFiddle so I could test it more easily. Here is my solution.

The core code is in the function joinChannels which can also be seen below

async function joinChannels () {
  for (let channel of channels) {
    await new Promise((resolve) => {
      console.log('Joining channel ' + channel.name);

      resolve(new Promise((resolve) => {
        setTimeout(() => {
          console.log('Done with channel ' + channel.name);
          resolve();
        }, 1000);
      }));
    });
  }
}

 

Now to rewrite this into a solution based on your context, here is my attempt (which is untested and thus might need some tweeking)

async function play(voiceChannels) {
  for (let channel of voiceChannels) {
    console.log('Joining channel ' + channel.name);

    await channel.join().then(async (connection) => {
      console.log('Joined channel');

      let dispatcher = connection.playFile('/home/pi/.discordbots/TARVIS/sncf.mp3');
      await dispatcher.on('end', function () {
        voiceChannel.leave();
      });
    });
  }
}

Give it a go and let me know how it turns out. If you have questions, I'm happy to help

T. Dirks
  • 3,566
  • 1
  • 19
  • 34
  • I tried your code with a few adaptations. I updated my first post with the new code. I get now this error: `TypeError: channel.join(...).then is not a function` Before calling `play()`I loggued the `channels`and it is a collection of the guild's I also loggued `voiceChannels` before the `for()`and it also contains the collection of channels. It seems the problem now is with the loop – BadSpencer Jan 25 '19 at 15:53
  • I added `voiceChannels`in the function parameters... because I thought it comes from here, maybe it was a bad idea... I'm still trying to play wioth this code. I'm more confident now with your help – BadSpencer Jan 25 '19 at 15:55
  • Sorry but I didn't manage to make it `async`...but I found a workarround (my solution will be posted as an answer) I spent few hours trying to make it work but my main problem was that within the `for (let channel of voiceChannels) {` `channel` was `undefined`. I tried to extract the `for`from the function and call the function for each channel but everything I tried didn't work... within for always undefined... I found a workarround with `setTimeout` – BadSpencer Jan 26 '19 at 09:49
0

Here is my workarround. I used the setTimeoutto enqueue channel voice with a timer of 10s I know is a bit dirty but it works

exports.run = async (client, message, args, level) => {

  async function play(channel) {
    await channel.join().then(async (connection) => {
      let dispatcher = await connection.playFile('/home/pi/.discordbots/TARVIS/offline.m4a');
      await dispatcher.on('end', function () {
        channel.leave();
      });
    });
  }

  let timer = 1000;
  client.ShowSuccess('Démarrage des annonces vocales');
  message.guild.channels.forEach(async (channel) => {
    if (channel.type == 'voice' && channel.members.size > 0) {
      client.ShowSuccess('Annonce dans le salon ' + channel.name + ' pour ' + channel.members.size + ' membre(s)', message.channel);
      client.logger.log('Annonce dans le salon ' + channel.name + ' pour ' + channel.members.size + ' membre(s)');
      setTimeout(function () {
        play(channel);
      }, timer);
      timer = timer + 10000;
    }
  });
  setTimeout(function () {
    client.ShowSuccess('Annonces vocales terminées');
  }, timer);
};
BadSpencer
  • 106
  • 2
  • 6