Mars7383 / discord-interrupt-bot

A Discord bot that talks over your friends whenever they try to speak in a voice channel
8 stars 7 forks source link

bot times out after a few seconds when !troll (person) is ran #5

Open Xplodin opened 4 months ago

Xplodin commented 4 months ago

C:\Users\somet\Downloads\discord-interrupter-main\node_modules\discord.js\src\client\voice\VoiceConnection.js:303 this.emit('error', new Error(reason)); ^

Error [VOICE_CONNECTION_TIMEOUT]: Connection not established within 15 seconds. at VoiceConnection.authenticateFailed (C:\Users\somet\Downloads\discord-interrupter-main\node_modules\discord.js\src\client\voice\VoiceConnection.js:303:26) at C:\Users\somet\Downloads\discord-interrupter-main\node_modules\discord.js\src\client\voice\VoiceConnection.js:324:61 at Timeout. (C:\Users\somet\Downloads\discord-interrupter-main\node_modules\discord.js\src\client\BaseClient.js:83:7) at listOnTimeout (node:internal/timers:569:17) at process.processTimers (node:internal/timers:512:7) {

}

YockerFX commented 4 months ago

Explanation:

1.  Voice Connection Timeout Handling: Added a timeout handling mechanism to wait for the connection to become ready.
2.  Reconnection Logic: Implemented logic to handle reconnection attempts if the connection is disrupted.
3.  Updated to @discordjs/voice: Using the latest voice library to handle voice connections more reliably.
4.  Improved Intents: Specified intents to ensure the bot can receive the necessary events.

By implementing these changes, your bot should be able to handle voice connections more robustly and avoid common issues such as timeouts and disconnections.

Code:


const { Client, Intents } = require("discord.js");
const { joinVoiceChannel, createAudioPlayer, createAudioResource, AudioPlayerStatus, VoiceConnectionStatus, entersState } = require("@discordjs/voice");

const client = new Client({
    intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES, Intents.FLAGS.GUILD_VOICE_STATES]
});

/* Config */
const token = ""; // Bot token here
const ownerID = ""; // Optional, put your own ID here so that you're the only one who can run bot commands
/* Config */

let victim = ""; // This variable will be dynamically updated to the ID of the person you specify with !troll, best to leave this alone

client.once("ready", () => {
    console.log(`The bot is ready! Logged in as ${client.user.username}#${client.user.discriminator}`);
});

async function joinChannel(channel) {
    const connection = joinVoiceChannel({
        channelId: channel.id,
        guildId: channel.guild.id,
        adapterCreator: channel.guild.voiceAdapterCreator,
    });

    try {
        await entersState(connection, VoiceConnectionStatus.Ready, 30000); // Wait up to 30 seconds for the connection to become ready
        console.log(`Successfully joined 🔊 ${channel.name}!`);

        const player = createAudioPlayer();
        const resource = createAudioResource('audio.mp3');

        player.play(resource);
        connection.subscribe(player);

        player.on(AudioPlayerStatus.Idle, () => {
            player.play(resource); // Loop audio
        });

        connection.on('stateChange', (oldState, newState) => {
            if (oldState.status !== newState.status) {
                console.log(`Connection status changed from ${oldState.status} to ${newState.status}`);
            }
        });

        connection.on('error', error => {
            console.error('Voice connection error:', error);
        });

        connection.on(VoiceConnectionStatus.Disconnected, async (oldState, newState) => {
            try {
                await Promise.race([
                    entersState(connection, VoiceConnectionStatus.Signalling, 5000),
                    entersState(connection, VoiceConnectionStatus.Connecting, 5000),
                ]);
                console.log('Reconnected to the voice channel');
            } catch (error) {
                console.error('Failed to reconnect to the voice channel:', error);
                connection.destroy();
            }
        });
    } catch (error) {
        console.error('Failed to join the voice channel within 30 seconds:', error);
        connection.destroy();
    }
}

client.on("messageCreate", async (message) => {
    if (message.content.startsWith("!troll")) {
        if (ownerID && message.author.id !== ownerID) return; // If ownerID is specified, ignore everyone else besides the owner
        const args = message.content.split(" ");
        if (args[1] == null) {
            message.author.send("You need to put the ID of the person you're trying to troll after the command (example: !troll 1234567890)");
            return;
        }
        const victimMember = message.guild.members.cache.get(args[1]); // Get member object from ID
        if (victimMember != null) {
            victim = args[1];
            message.author.send(`I set the victim to <@${args[1]}>! If they're already in a VC, I'll auto-join. If not, I'll join the VC right after they do!`);
            console.log(`Now trolling: ${victimMember.user.username}#${victimMember.user.discriminator} (ID: ${victim})`);
            if (victimMember.voice.channel) {
                joinChannel(victimMember.voice.channel); // Join the victim's VC if they're already in one
            }
        } else {
            message.author.send("I couldn't find that user in your server, double-check the ID?");
        }
    }
});

client.on('voiceStateUpdate', (oldState, newState) => {
    if (newState.channel && newState.member.id === victim) {
        joinChannel(newState.channel); // Follow the victim into the voice channel
    } else if (!newState.channel && oldState.channel && oldState.member.id === victim) {
        const connection = getVoiceConnection(oldState.guild.id);
        if (connection) connection.destroy(); // Leave the voice channel if the victim leaves
    }
});

client.login(token);```
Xplodin commented 4 months ago

Explanation:

1.    Voice Connection Timeout Handling: Added a timeout handling mechanism to wait for the connection to become ready.
2.    Reconnection Logic: Implemented logic to handle reconnection attempts if the connection is disrupted.
3.    Updated to @discordjs/voice: Using the latest voice library to handle voice connections more reliably.
4.    Improved Intents: Specified intents to ensure the bot can receive the necessary events.

By implementing these changes, your bot should be able to handle voice connections more robustly and avoid common issues such as timeouts and disconnections.

Code:

const { Client, Intents } = require("discord.js");
const { joinVoiceChannel, createAudioPlayer, createAudioResource, AudioPlayerStatus, VoiceConnectionStatus, entersState } = require("@discordjs/voice");

const client = new Client({
    intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES, Intents.FLAGS.GUILD_VOICE_STATES]
});

/* Config */
const token = ""; // Bot token here
const ownerID = ""; // Optional, put your own ID here so that you're the only one who can run bot commands
/* Config */

let victim = ""; // This variable will be dynamically updated to the ID of the person you specify with !troll, best to leave this alone

client.once("ready", () => {
    console.log(`The bot is ready! Logged in as ${client.user.username}#${client.user.discriminator}`);
});

async function joinChannel(channel) {
    const connection = joinVoiceChannel({
        channelId: channel.id,
        guildId: channel.guild.id,
        adapterCreator: channel.guild.voiceAdapterCreator,
    });

    try {
        await entersState(connection, VoiceConnectionStatus.Ready, 30000); // Wait up to 30 seconds for the connection to become ready
        console.log(`Successfully joined 🔊 ${channel.name}!`);

        const player = createAudioPlayer();
        const resource = createAudioResource('audio.mp3');

        player.play(resource);
        connection.subscribe(player);

        player.on(AudioPlayerStatus.Idle, () => {
            player.play(resource); // Loop audio
        });

        connection.on('stateChange', (oldState, newState) => {
            if (oldState.status !== newState.status) {
                console.log(`Connection status changed from ${oldState.status} to ${newState.status}`);
            }
        });

        connection.on('error', error => {
            console.error('Voice connection error:', error);
        });

        connection.on(VoiceConnectionStatus.Disconnected, async (oldState, newState) => {
            try {
                await Promise.race([
                    entersState(connection, VoiceConnectionStatus.Signalling, 5000),
                    entersState(connection, VoiceConnectionStatus.Connecting, 5000),
                ]);
                console.log('Reconnected to the voice channel');
            } catch (error) {
                console.error('Failed to reconnect to the voice channel:', error);
                connection.destroy();
            }
        });
    } catch (error) {
        console.error('Failed to join the voice channel within 30 seconds:', error);
        connection.destroy();
    }
}

client.on("messageCreate", async (message) => {
    if (message.content.startsWith("!troll")) {
        if (ownerID && message.author.id !== ownerID) return; // If ownerID is specified, ignore everyone else besides the owner
        const args = message.content.split(" ");
        if (args[1] == null) {
            message.author.send("You need to put the ID of the person you're trying to troll after the command (example: !troll 1234567890)");
            return;
        }
        const victimMember = message.guild.members.cache.get(args[1]); // Get member object from ID
        if (victimMember != null) {
            victim = args[1];
            message.author.send(`I set the victim to <@${args[1]}>! If they're already in a VC, I'll auto-join. If not, I'll join the VC right after they do!`);
            console.log(`Now trolling: ${victimMember.user.username}#${victimMember.user.discriminator} (ID: ${victim})`);
            if (victimMember.voice.channel) {
                joinChannel(victimMember.voice.channel); // Join the victim's VC if they're already in one
            }
        } else {
            message.author.send("I couldn't find that user in your server, double-check the ID?");
        }
    }
});

client.on('voiceStateUpdate', (oldState, newState) => {
    if (newState.channel && newState.member.id === victim) {
        joinChannel(newState.channel); // Follow the victim into the voice channel
    } else if (!newState.channel && oldState.channel && oldState.member.id === victim) {
        const connection = getVoiceConnection(oldState.guild.id);
        if (connection) connection.destroy(); // Leave the voice channel if the victim leaves
    }
});

client.login(token);```

ty for the updated code, but it seems like it throws an error with throw new Error("Cannot play a resource that has already ended."); ^

Error: Cannot play a resource that has already ended.
    at AudioPlayer.play (C:\Users\somet\Downloads\discord-interrupter-main\node_modules\@discordjs\voice\dist\index.js:943:13)
    at AudioPlayer.<anonymous> (C:\Users\somet\Downloads\discord-interrupter-main\index.cjs:37:20)
    at AudioPlayer.emit (node:events:513:28)
    at set state [as state] (C:\Users\somet\Downloads\discord-interrupter-main\node_modules\@discordjs\voice\dist\index.js:935:12)
    at AudioPlayer.checkPlayable (C:\Users\somet\Downloads\discord-interrupter-main\node_modules\@discordjs\voice\dist\index.js:1040:18)
    at C:\Users\somet\Downloads\discord-interrupter-main\node_modules\@discordjs\voice\dist\index.js:212:60
    at Array.filter (<anonymous>)
    at audioCycleStep (C:\Users\somet\Downloads\discord-interrupter-main\node_modules\@discordjs\voice\dist\index.js:212:34)
    at Timeout.<anonymous> (C:\Users\somet\Downloads\discord-interrupter-main\node_modules\@discordjs\voice\dist\index.js:221:45)
    at listOnTimeout (node:internal/timers:569:17)