Discord Bot Update: How to play audio? - node.js

So recently the new version of the discord bot API came out for Node along with interactions and all that. And, they also changed some other stuff, don't know why. but they did.
I was trying to just try out the audio playing code to see how it works and maybe update some of my older bots, when I ran into the issue that it just doesn't work. I've been following the docs at https://discordjs.guide/voice/voice-connections.html#life-cycle and https://discordjs.guide/voice/audio-player.html#life-cycle but they're really just not working.
Just testing code looks like this:
const Discord = require('discord.js');
const {token} = require("./config.json");
const { join } = require("path");
const {joinVoiceChannel, createAudioPlayer, createAudioResource, AudioPlayerStatus, VoiceConnectionStatus, SubscriptionStatus, StreamType } = require("#discordjs/voice");
client.on("ready", async () => {
const connection = joinVoiceChannel({
channelId: channel.id,
guildId: channel.guild.id,
adapterCreator: channel.guild.voiceAdapterCreator,
});
const audioPlayer = createAudioPlayer();
const resource = createAudioResource(createReadStream(join(__dirname, "plswork.mp3")));
const subscription = connection.subscribe(audioPlayer);
audioPlayer.play(resource);
audioPlayer.on(AudioPlayerStatus.Playing, () => {
console.log("currently playing");
console.log("resource started:", resource.started);
});
audioPlayer.on('error', error => {
console.error(`Error: ${error.message} with resource ${error.resource.metadata.title}`);
});
audioPlayer.on(AudioPlayerStatus.AutoPaused, () => {
console.log("done?");
});
I create a connection, audioPlayer, and resource but after subscribing the connection to the audioPlayer and playing the resource no audio is played, no error is raised (in AudioPlayer.on("error"...)) and the AutoPaused status is immediately called.
By logging the resource I see that resource.playbackDuration is 0, but I don't know how to fix this as I can't find much on the internet about this topic.

From what I can tell by looking at your code you are not requiring the correct json from #discordjs/voice, you need to require at least AudioPlayerStatus and createAudioPlayer. Also, by your resource I think you're trying to create an AudioResource, so you'll need it as well. You'll need something like:
const { AudioPlayerStatus, createAudioPlayer, AudioResource, StreamType } = require('#discordjs/voice');
/* */
const audioPlayer = createAudioPlayer();
/* */
audioPlayer.play(resource);
audioPlayer.on(AudioPlayerStatus.Playing, () => {
// Do whatever you need to do when playing
});
/* */
In conclusion, I suggest you to look up the life cycle of an AudioPlayer and the creation of an AudioPlayer. Be sure, also, to create correctly your resource, here you'll find the AudioResource documentation if you'll ever need it.

Related

DiscordJS v13 send to specific channel

So I am trying to send an embed to a different channel than the command was used in as a log channel how ever I have tried a few different methods however where I am at now is the error i get is qChann.send is not a function it is yelling at the .send part.
This is the part referenced in index.js Yes I know it is bad practice to reference and export from main file but I am desperate for a solution
client.on('ready', async () => {
console.log('Online');
const qChann = client.channels.cache.get('960425106964885535');
console.log(qChann);
})
The second part is in a command file call deposit.js
It uses a 2 part collector ignore date and time stuff thats from express
I am also using mongoDB and it works completely fine how ever that last little statement is giving me a hard time qChann.send({embed: steelEmbed}); also qChann is included as const qChann = require('../index');
if (collected.content.toLowerCase() === 'steel') {
message.reply('Enter the amount youd like to deposit');
collector.stop('user entered steel');
const steelCollector = message.channel.createMessageCollector({
filter,
max: 1,
time: 1000 * 20,
});
steelCollector.on('collect', async (collected) => {
const steelAmount = collected.content;
await steelSchema.findOneAndUpdate({
$inc: {
steel: +steelAmount,
}
})
steelNewCount = await steelSchema.findOne({steelSchema}, {_id: 0});
const steelEmbed = new MessageEmbed()
.setColor('#4278f5')
.setTitle(`Ammo11 Stash Update`)
.setDescription(`Steel Count Updated`)
.addField(`Stash Updated`, `by <#${message.author.id}>`, true)
.addField(`Date Log`, `${date} / ${month} / ${year}`, true)
.addField(`Steel Deposited`, `${steelAmount}`, true)
.addField(`New Steel Count`, `${steelNewCount}`, true )
.setTimestamp()
.setFooter({text:'THIS MESSAGE IS NOT TO BE DELETED'});
qChann.send({embed: steelEmbed});
})
}
You need to define channel and then export it. You didn't export so you can't take it from another file as i know.
// outside of events
const qChann = client.channels.cache.get("960425106964885535")
module.exports = { qChann }
After this edit on your main file, you can access your channel with importing it with require in your command files.

DiscordJS Error, Embed Message Within Command Handler

I am trying to make my bot use an embed message however it will not work because i keep getting the Message not defined error
I have tried just using the author.bot but then it comes up with Author not defined, Something important to mention, I am using the command handler provided by the DiscordJS Guide and then i tried using the DiscordJS Guide for Embeds and it didnt work, thats basically how i ended up here
Code Below
const { SlashCommandBuilder } = require('#discordjs/builders');
const { MessageEmbed } = require('discord.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('embed')
.setDescription('A W.I.P Embed'),
async execute(interaction) {
const Embed1 = new MessageEmbed().setTitle('Some title');
if (message.author.bot) {
Embed1.setColor('#7289da');
}
}
};```
This is because you are using a message object when the exported item is: interaction.
Change:
if (message.author.bot) {
Embed1.setColor('#7289da');
}
to:
if (interaction.user.bot) {
Embed1.setColor('#7289da');
}
There is no interaction.author and only a interaction.user, therefore is the only way to check if it's a bot.

song_queue.connection.play is not a function

Here is the problem: when i'm running this code I get a error saying: song_queue.connection.play is not a function. The bot joins the voicechat correctly but the error comes when it tries to play a song. Sorry for the large amount of code but I really want to fix this so my bot can work. I got the code from a YouTube tutorial recorded in discord.js 12.4.1 (my version is the latest 13.1.0) and I think the error has to do with #discordjs/voice. I would really appreciate any help with getting this to work.
const ytdl = require('ytdl-core');
const ytSearch = require('yt-search');
const { joinVoiceChannel, createAudioPlayer, createAudioResource, } = require('#discordjs/voice');
const queue = new Map();
// queue (message.guild.id, queue_constructor object { voice channel, text channel, connection, song[]});
module.exports = {
name: 'play',
aliases: ['skip', 'stop'],
description: 'Advanced music bot',
async execute(message, args, cmd, client, discord){
const voice_channel = message.member.voice.channel;
if (!voice_channel) return message.channel.send('You need to be in a channel to execute this command');
const permissions = voice_channel.permissionsFor(message.client.user);
if (!permissions.has('CONNECT')) return message.channel.send('You dont have permission to do that');
if (!permissions.has('SPEAK')) return message.channel.send('You dont have permission to do that');
const server_queue = queue.get(message.guild.id);
if (cmd === 'play') {
if (!args.length) return message.channel.send('You need to send the second argument');
let song = {};
if (ytdl.validateURL(args[0])){
const song_info = await ytdl.getInfo(args[0]);
song = { title: song_title.videoDetails.title, url: song_info.videoDetails.video_url }
} else {
//If the video is not a URL then use keywords to find that video.
const video_finder = async (query) =>{
const videoResult = await ytSearch(query);
return (videoResult.videos.length > 1) ? videoResult.videos[0] : null;
}
const video = await video_finder(args.join(' '));
if (video){
song = { title: video.title, url: video.url }
} else {
message.channel.send('Error finding your video');
}
}
if (!server_queue){
const queue_constructor = {
voice_channel: voice_channel,
text_channel: message.channel,
connection: null,
songs: []
}
queue.set(message.guild.id, queue_constructor);
queue_constructor.songs.push(song);
try {
const connection = await joinVoiceChannel({
channelId: message.member.voice.channel.id,
guildId: message.guild.id,
adapterCreator: message.guild.voiceAdapterCreator
})
queue_constructor.connection = connection;
video_player(message.guild, queue_constructor.songs[0]);
} catch (err) {
queue.delete(message.guild.id);
message.channel.send('There was an error connecting');
throw err;
}
} else{
server_queue.songs.push(song);
return message.channel.send(`<:seelio:811951350660595772> **${song.title}** added to queue`);
}
}
}
}
const video_player = async (guild, song) => {
const song_queue = queue.get(guild.id);
if(!song) {
song_queue.voice_channel.leave();
queue.delete(guild.id);
return;
}
const stream = ytdl(song.url, { filter: 'audioonly' });
song_queue.connection.play(stream, { seek: 0, volume: 0.5 }).on('finish', () => {
song_queue.songs.shift();
video_player(guild, song_queue.songs[0]);
});
await song_queue.text_channel.send('(`<:seelio:811951350660595772> Now Playing **${song.title}**`)')
}
Discord.js V13 and #discordjs/voice
Since a relatively recent update to the Discord.js library a lot of things have changed in the way you play audio files or streams over your client in a Discord voice channel. There is a really useful guide by Discord to explain a lot of things to you on a base level right here, but I'm going to compress it down a bit and explain to you what is going wrong and how you can get it to work.
Some prerequisites
It is important to note that for anything to do with voice channels for your bot it is necessary to have the GUILD_VOICE_STATES intent in your client. Without it your bot will not actually be able to connect to a voice channel even though it seems like it is. If you don't know what intents are yet, here is the relevant page from the same guide.
Additionally you will need some extra libraries that will help you with processing and streaming audio files. These things will do a lot of stuff in the background that you do not need to worry about them, but without them playing any audio will not work. To find out what you need you can use the generateDependecyReport() function from #discordjs/voice. Here is the page explaining how to use it and what dependencies you will need. To use the function you will have to import it from the #discordjs/voice library.
Playing audio over a client
So once everything is set up you can get to how to play audio and music. You're already a great few steps on the way by using ytdl-core and getting a stream object from it, but audio is not played by using a .play() command on the connection. Instead you will need to utilize AudioPlayer and AudioResource objects.
AudioPlayer
The AudioPlayer is essentially your jukebox. You can make one by simply calling its function and storing that in a const like so:
const player = createAudioPlayer()
This is a function from the #discordjs/voice library and will have to be imported just like generateDependencyReport().
There are a few parameters you can give it to modify its behavior, but right now that is not important. You can read more about that on its page from the Discord guide right here.
AudioResource
To get your AudioPlayer to actually play anything you will have to create an AudioResource. This is basically a version of your file or stream modified to work with the player. This is very simply done with another function from the #discord.js/voice library called createAudioResource(...). This must once again be imported. As a parameter you can parse the location of an mp3 or webm file, but you can also use a stream object like you have already acquired. Just input stream as the parameter of that function.
To now play the resource there are two more steps. First you must subscribe your connection to the player. This basically tells the connection to broadcast whatever your AudioPlayer is playing. To do this simply call the .subscribe() function on your connection object with the player as a parameter like so:
connection.subscribe(player)
player.play(resource)
The second line of code you see above is how you get your player to play your AudioResource. Just parse the resource as a parameter and it will start playing. You can find more on the AudioResource side of things on its page in the Discord guide right here.
This way takes a few more steps than it did in V12, but once you get the hang of this system it really isn't that bad or difficult.
Leaving a voice channel
There is another thing that is going wrong in your code when you try to leave a voice channel. I can see that you did figure out how to join in V13 already, but .leave() unfortunately is no longer a valid function. Now, to leave a voice channel you must retrieve the connection object that you get from calling joinVoiceChannel(...) and call either .disconnect() or .destroy() on it. They are almost the same, but the latter also makes it so that you cannot use the connection again.

.push is not a function. (DISCORD.JS)

Trying to make a cooldown on message events when giving a user xp. Running into an issue where cooldowns.push() is not a fucntion I have done googling and what I have found is that I need to have an array but to my understanding a Discord.Collection is the same thing.
I need this to check the collection to see if the users ID is in there, but if it is not add it for 3 seconds then remove it.
const config = require(`../settings/config.js`)
const prefix = config.prefix
const { createConnection } = require(`mysql`)
const mysql = require("../settings/mysql.js")
const Discord = require(`discord.js`)
let cooldowns = new Discord.Collection()
module.exports = async (client, message, member) => {
if (!cooldowns.has(message.author.id)) {
cooldowns.push(message.author.id)
}
setTimeout(() => cooldowns.delete(message.author.id), 3000)
Thanks in advance!!
From https://discordjs.guide/additional-info/collections.html#array-like-methods:
Discord.js comes with this utility class known as Collection. It extends JavaScript's native Map class, so it has all the features of Map and more!
So no, it's not an array, it's a Map. In your case I think you'd want to use:
cooldowns.set(message.author.id, 1)

Discord.js | Is there a way to store variables into a different js file?

So I'm making a DS bot that will send a random link/images that I have provided the bot onto chat ever hour. I have a lot of links/images and it's a real big mess on the main index.js
Is there a way to take a variable from another js file to my main one?
Here is my code:
const app = express();
const port = 3000;
app.get('/', (req, res) => res.send('Hello Foxi!'));
app.listen(port, () => console.log(`The Dealer is listening at http://localhost:${port}`));
// ====================The Dealer's Heart====================
const Discord = require('discord.js');
const client = new Discord.Client();
// 804488904634925076
//var bbChannel = client.channels.cache.get('809808576406093847');
//console.log(`Logged in as ${client.user.tag}!`);
client.on('ready', () => {
console.log(`Logged in as ${client.user.tag}!`);
setInterval(() => {
var bbChannel = client.channels.cache.get('804488904634925076');
const image = images[Math.floor(Math.random() * images.length)];
const random = new Discord.MessageEmbed()
.setTitle('HOURLY PICTURES')
.setAuthor('Brought to you by: The Dealer')
.setDescription('SOME IMAGES MAY REPEAT')
.setImage(image)
bbChannel.send(random)
}, 3600000);
});
client.on('message', msg => {
if (msg.content === 'ping') {
msg.reply('pong!');
}
});
//Images
var images =["Image1", "Image2", "Image3", "ETC"];
client.login(process.env.DISCORD_TOKEN);```
OK so, what you want to do is create another file in the same location as your current file. Note: Only for simplification, you can of course create the file where you want but then you need to change the relative path to it
We'll call that file images.json for now. In that file you use a simple JSON structure and basically put the array that you have at the bottom of your file in here.
{
"images": [
"link1",
"link2",
"link3"
]
}
Now all you have to do is import it into your main file. You do that as you would with anything else. We use destructuring here to only extract the value we want. Note: You can also import the entire file if you have more then one entry that you want to access. After a certain amount, that is usually the way to go because it's not as much work. Programmers are a pretty lazy bunch 😉
const { images } = require('./images.json');
// if you want the entire thing you do this
// const images = require('./images.json');
// and then to access the array
// console.log(images.images);
If you use destructuring then you don't need to change your code at all because you already define the variable as you need it. If you don't then use the method described above.
Obviously you can now remove the array at the bottom of your code.
Another Note: If you use a halfway decent IDE then it should give you autocomplete when you write your code and also let you know if something is wrong

Resources