Discord.js Voice Connection Timeout - javascript

I am currently struggling with a problem of my discord.js bot that plays a music stream via an url. Everything works fine, but after about 12h all players disconnect and I get the following error message: [VOICE_CONNECTION_TIMEOUT]: Connection not established within 15 seconds.
This is my code to join/leave a channel:
function join(voiceChannel, volume = 20) {
return new Promise((resolve, reject) => {
voiceChannel.join().then(vc => {
vc.voice.setSelfDeaf(true);
const dispatcher = vc.play(streamURL);
dispatcher.once('start', () => {
dispatcher.setBitrate(64);
dispatcher.setVolumeLogarithmic(volume / 100);
setTimeout(() => {
if (voiceChannel.members.filter(m => !m.user.bot).size === 0) dispatcher.pause(true);
}, 1500);
resolve();
});
// Tried to reconnect on error but does not seem to work
dispatcher.on('error', () => {
leave(voiceChannel.guild);
setTimeout(() => join(voiceChannel, volume), 5000);
});
}).catch(err => reject(err));
});
}
function leave(guild) {
guild.me?.voice?.connection?.disconnect();
}
I also have this event set up to pause the stream if nobody is listening:
client.on('voiceStateUpdate', (oldState, newState) => {
const state = oldState || newState;
if (state?.member.user.bot) return;
const guild = state?.guild;
if (!guild?.me?.voice?.connection) return;
if (newState.channel === oldState.channel) return;
Guild.findOne({guildId: guild.id}, (err, dbGuild) => {
if (err || !dbGuild) return;
const channel = guild.channels.cache.get(dbGuild.musicChannelId);
if (!channel || !guild.me.voice.connection.dispatcher) return;
const dispatcher = guild.me.voice.connection.dispatcher;
if (channel.members.filter(m => !m.user.bot).size > 0 && dispatcher.paused) {
dispatcher.resume();
return;
}
if (!dispatcher.paused && channel.members.filter(m => !m.user.bot).size === 0) {
dispatcher.pause(true);
}
});
});
Maybe somebody can help me with this. I don't know how to fix this error.

This error is familiar in the Discord.JS package. The problem is that Discord doesn't fully support voice connections for bots, so this has nothing to do with your code. Discord.JS recommends you tho to use their voice package for playing audio in a voice channel. Here are their documentations and here are some of their examples.

Related

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.

set time between actions in a command in discord.js

i have a question, i would like to know how i could do various actions at the same time, with timers, in this case this is my code so i can help you understand my case!
const discord = require ("discord.js")
const config = require('../config.json')
module.exports.run = async (bot, message, args) => {
const memberTarget = message.mentions.members.first();
const general = message.guild.channels.cache.find(channel => channel.name === "general");
const nazi = message.guild.channels.cache.find(channel => channel.name === "nazi");
const stripties = message.guild.channels.cache.find(channel => channel.name === "stripties");
const reportado = message.guild.channels.cache.find(channel => channel.name === "reportado");
const highquality = message.guild.channels.cache.find(channel => channel.name === "high-quality");
if (memberTarget.voice.channel)
memberTarget.voice.setChannel(nazi)
if (memberTarget.voice.channel === nazi)
memberTarget.voice.setChannel(stripties)
if (memberTarget.voice.channel === reportado)
memberTarget.voice.setChannel(reportado)
}
module.exports.help = {
name: 'tour'
}
i want to get that memberTarget i mention to move him through the channels, i want probably 2seconds or one second through each event, i mean he moves to first channel, then 1 second later, second channel, etc etc, ty a lot! i hope i did good at trying to explain!
You can setTimeOut in promise. I can give you a example like this.
function firstEvent(){
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("I'm the first event");
resolve()
}, 1000);
})
}
function secondEvent(){
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("I'm the second event");
resolve()
}, 1000);
})
}
function thirdEvent(){
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("I'm the third event");
resolve()
}, 1000);
})
}
function forthEvent(){
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("I'm the forth event");
resolve()
}, 1000);
})
}
async function setGapBetween() {
await firstEvent();
await secondEvent();
await thirdEvent();
await forthEvent();
}
setGapBetween();

Adding a global timer for tmi.js integrated discord.js

I'm trying to do a discord bot that listens to multiple twitch chat for commands, then run them on discord, using both tmi.js and discord.js. Currently it is working, but I can't seem to add a global cooldown on the commands itself to prevent spamming. At first I've tried adding a cd timer to each command, but I'm unable to get it working, hence the decision to try to make a global cd but still to no avail. Am I doing something wrongly?
twitch.on('message', (channel, tags, message, self) => {
if(!message.startsWith(prefix) || self) return;
const args = (message.slice(prefix.length).trim().split(/ +/));
const commandName = args.shift().toLowerCase();
if (!twitch.commands.has(commandName)) return;
const command = twitch.commands.get(commandName);
}
try {
command.execute(bot, botChannel, vcChannel, isReady);
} catch (error){
console.error(error);
}
});
just to update, I basically took a leaflet from async await functions here: https://stackoverflow.com/a/54772517/14637034
Then, I modified the code as such:
const setAsyncTimeout = (cb, timeout = 0) => new Promise(resolve => {
setTimeout(() => {
cb();
resolve();
}, timeout);
});
const doStuffAsync = async () => {
await setAsyncTimeout(() => {
isReady = true;
console.log(isReady);
}, 10000);};
twitch.on('message', (channel, tags, message, self) => {
if(!message.startsWith(prefix) || self) return;
const args = (message.slice(prefix.length).trim().split(/ +/));
const commandName = args.shift().toLowerCase();
if (!twitch.commands.has(commandName)) return;
if (isReady){
const command = twitch.commands.get(commandName);
isReady = false;
try {
command.execute(bot, botChannel, vcChannel);
} catch (error){
console.error(error);
}
doStuffAsync();
}
});
Seemed to work for now, as 10s is a long enough time for bot to leave discord properly without causing timeout. I'm still open to better suggestions for optimization though!

Discord.js add role by reacting to message

The following code wont work as I expected. I want a User to react with a checkmark and then log something in the console. Instead of that the bot activates the function by itself and then the function never gets called again.
client.once('ready', () => {
console.log('Ready!');
const exampleEmbed = <unimportant>;
client.channels.cache.get("771041812638728235").send(exampleEmbed).then((message) => {
message.react('✅');
message.createReactionCollector(r => r.emoji.name == '✅')
.on('collect', r => {
console.log("nice");
});
});
});
It might be because the reaction collector is created before the message.react() method finishes executing, causing it to activate on itself. You could either use async/await or a then() callback to make sure the reaction has finished, or simply add a line making sure the user is not a bot in your collector filter.
// method 1:
client.channels.cache
.get('771041812638728235')
.send(exampleEmbed)
.then(async (message) => {
// ^^^^^
// make sure the function is async
await message.react('✅');
message
.createReactionCollector((r) => r.emoji.name == '✅')
.on('collect', (r) => {
console.log('nice');
});
});
// method 2:
client.channels.cache
.get('771041812638728235')
.send(exampleEmbed)
.then(message) => {
// .then() callback
message.react('✅').then(() =>
message
.createReactionCollector((r) => r.emoji.name == '✅')
.on('collect', (r) => {
console.log('nice');
}););
});
// method 3:
client.channels.cache
.get('771041812638728235')
.send(exampleEmbed)
.then((message) => {
message.react('✅');
message
// make sure the user who reacted isn't a bot
.createReactionCollector((r, u) => r.emoji.name == '✅' && !user.bot)
.on('collect', (r) => {
console.log('nice');
});
});

Is there a way i can delay the role that my bot gives to a user? [duplicate]

This question already has answers here:
How do I delay a function call for 5 seconds? [duplicate]
(2 answers)
Closed 2 years ago.
I want to know if it's possible to put a time in Milliseconds in which my bot gives the role to a user once they have triggered the command for example once the user types "!Verify" the bot won't give the role to a user until a certain time, This is what i have set up for my bot to do.
bot.on('ready', () => console.log(`${bot.user.tag} has logged in fucker.`));
bot.on('message', async message => {
if (message.author.bot) return;
bot.on('guildMemberAdd', member => {
console.log(member.user.tag);
});
if (message.channel.id === '695566841291997244')
await message.delete();
if (message.content.toLowerCase() === '!verify' && message.channel.id === '695566841291997244')
{
await message.delete().catch(err => console.log(err));
const role = message.guild.roles.cache.get('695565873515069444');
if(role) {
try {
await message.member.roles.add(role);
console.log('Role added!');
}
catch(err) {
console.log(err);
}
}
}
});
You can use the expression setTimeout()
setTimeout(async () => {
await message.delete().catch(err => console.log(err));
const role = message.guild.roles.cache.get('695565873515069444');
if(role) {
try {
await message.member.roles.add(role);
console.log('Role added!');
}
catch(err) {
console.log(err);
}
};
}, 5000);

Categories