discordjs / discord.js

A powerful JavaScript library for interacting with the Discord API
https://discord.js.org
Apache License 2.0
25.36k stars 3.97k forks source link

Joinig channel times out when joining too fast after leaving #4058

Closed p0thi closed 3 years ago

p0thi commented 4 years ago

Please describe the problem you are having in as much detail as possible: I have a sound bot, that joins a channel, plays an audio file and leaves the channel again. If the user asks the bot to join this channel too soon after it disconnected, the bot joins the channel, but the channel.join() in the code rejects with a VOICE_CONNECTION_TIMEOUT Error after 15 sec . Then it is impossible to either join or leave the channel again. but technically the bot joined the channel and is still in there.

The sequence of triggering sound commands has to be really fast to reproduce this issue. Even some delays <300 ms are enough on my setup to let this work like expected.

Include a reproducible code sample here, if possible:

const guildOptions = new Map();

async play(sound, channel) {
        if (!channel.joinable || !channel.speakable) {
            return
        }

        let connection = await channel.join();

        if (!guildOptions.has(channel.guild.id)) {
            guildOptions.set(channel.guild.id, {});
        }
        let options = guildOptions.get(channel.guild.id);

        return await new Promise((resolve, reject) => {
            if (options.dispatcher) {
                options.dispatcher.off('finish', options.callback);
                options.resolve();
            }

            let readStream;

            try {
                readStream = dbManager.getFileStream(sound.file);
            }
            catch (e) {
                log.error(`Can't play`);
                connection.disconnect();
                reject();
                return;
            }

            let dispatcher = connection.play(readStream, { volume: .5, highWaterMark: 1 });

            options.callback = () => {
                log.info('file ended');
                options.dispatcher.off('finish', options.callback);
                resolve();
                setTimeout(() =>
                    connection.disconnect(),
                    100
                )
            }
            options.dispatcher = dispatcher;
            options.resolve = resolve;

            dispatcher.on('finish', options.callback);
        })

    }

I noticed, that issuing multiple channel.join() commands after the crashes in a catch block, make the bot leave the channel after sending the file stream. But in this case the audio in discord is not hearable. I just lets the bot disconnect from the channel after the finish event. After that everything is back to normal.

Here is another code sample of the same function of my workarounds that worked for me so far, but seem a bit hacky since it uses a static delay.

const guildOptions = new Map();

async play(sound, channel) {
        return await new Promise(async (resolve, reject) => {

            if (!channel.joinable || !channel.speakable) {
                return
            }

            if (!guildOptions.has(channel.guild.id)) {
                guildOptions.set(channel.guild.id, {});
            }
            let options = guildOptions.get(channel.guild.id);

            let connection;
            try {
                if (options.connection && options.connection.status === 4) {
                    if (options.disconnectTime) {
                        // ensures a delay of 300 ms between the last leaving event before joining again
                        const timeToWait = Math.max(0, 300 - Math.abs(moment().diff(options.disconnectTime)))

                        if (timeToWait > 0) {
                            log.debug(`waiting ${timeToWait} ms`)
                            await new Promise((resolve) => {
                                setTimeout(resolve, timeToWait)
                            })
                        }
                    }
                }
                connection = await channel.join()
            }
            catch (err) {

                try {
                    channel.join()
                    connection = await channel.join()
                }
                catch (e) {
                    channel.leave()
                    reject();
                    return;
                }
            }
            if (options.dispatcher) {
                options.dispatcher.off('finish', options.callback);
            }

            if (options.resolve) {
                options.resolve();
            }
            options.resolve = resolve

            options.connection = connection;

            let readStream;

            try {
                readStream = dbManager.getFileStream(sound.file);
            }
            catch (e) {
                connection.disconnect();
                reject();
                return;
            }

            let dispatcher = connection.play(readStream, { volume: .5, highWaterMark: 1 });

            options.callback = () => {
                setTimeout(() => {
                    options.dispatcher.off('finish', options.callback);
                    options.disconnectTime = moment();
                    connection.disconnect();
                    resolve();
                },
                    100
                )
            }
            dispatcher.on('finish', options.callback);

            options.dispatcher = dispatcher;

        })

    }

Further details:

amishshah commented 3 years ago

Hi there,

We're working on a new implementation of Discord's Voice API that has better playback quality and is more reliable than what we currently support in Discord.js v12 - check it out at https://github.com/discordjs/voice!

The new library solves many of the issues that users are facing, and as part of this, we're dropping built-in support for voice in our next major release. We have a PR (https://github.com/discordjs/discord.js/pull/5402) that adds native support for our new voice library - once this PR is merged, this issue will be closed.

You can still use our new voice library before that PR lands - just take a look at our music bot example to see how to get started upgrading your voice code. By using the boilerplate music player in the example, you can make it even easier to upgrade your code.

Note that the PR above only reduces some of the boilerplate code you'd otherwise have to write - you do not have to wait for the PR to be merged to start using the new voice library.


If you have any questions about this, feel free to: