song_queue.connection.play is not a function - node.js

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.

Related

Discord Bot Update: How to play audio?

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.

discord-player stuck on search query

I'm having a problem with discord-player. When I execute !play URL, it joins my channel, it plays the song and it's perfectly fine, but when instead of passing a link I pass a search query ex. !play Despacito, it gets frozen and then cancels the play event. What is going on? On the video I have a program called NetLimiter4 that is monitoring how much bandwidth is used by any app. You can see that when I type in despacito it tries to search it and then gives up, but when I provide a link, it starts to play it. I checked everything, I even tried to put breakpoints in the Player class. From my observations, it tries to search the query, and it's successful but for some reason, it fails. No stack trace, no nothing
import * as Discord from "discord.js";
import {Player} from "discord-player";
const client = new Discord.Client();
const player = new Player(client);
client.on("ready", ()=>{
console.log("ready");
})
client.on("message", async (message)=>{
let args = message.content.split(" ");
if(args[0] == "!play" && args[1]){
console.log("playin0");
await player.play(message, args[1]);
console.log("playing");
}else if(args[0] == "!stop"){
if(!player.isPlaying(message)) return message.channel.send("Can't stop playing nothing");
player.stop(message);
}
})
player.on("trackStart", (message, track) =>{
message.channel.send(`Started playing ${track.title}!`);
})
client.login("token");
You might be using discord-player above v2 , and yeah discord-player has built in search feature so you need to add this code -
player.on('searchResults', (message, query, tracks) => {
const embed = new Discord.MessageEmbed()
.setAuthor(`Search Reults`)
.setDescription(tracks.map((k, x) => `${x}. ${k.title}`))
.setFooter('Send the number of the song you want to play!')
message.channel.send(embed);
})
where x is the number and k belongs to Track class, Now once the user enters a number like 1 it starts playing the song!

Discord.js - Send file to folder on PC

I am giving one of my bots a youtube download command because I both am tired of using the sketchy ones on the internet and also ran out of things to code. So far my code gets the video in question and returns the file in the chat, and it works great, except for the fact that 90% of the videos are too big to be sent. So what I want to do is to save the file to D:\bot_yt_vids\ or something but I cannot figure out how to do this (I have searched everywhere and nothing works for some reason)
My current code is this:
var splitMessage = message.content.split(' ');
if (splitMessage[0] === "!ytdl") {
try {
const vid = ytdl(splitMessage[1], { filter: format => format.container === 'mp4' })
message.channel.send({ files: [{ attachment: vid, name: "video .mp4" }] })
} catch (e) { message.channel.send("An error occured"); console.log(e)}
}
ytdl() returns a readable file stream, so I suggest looking creating a new writableStream in node.js and using the .pipe() method of a readable stream to pass it to the writable stream to write to the file. I'm not familiar with filestreams, but I think that should be all you need.
readableStream#pipe
Great article on filestreams

Discord.js Music bot "TypeError" when playing audio with dispatcher

I'm new to Discord.js and I'm trying to have the bot join a voice channel and play an audio file on my computer. I have been following this guide: https://discord.js.org/#/docs/main/stable/topics/voice . Here is the Index.js page:
Colesbot.on('message', message=>{
if (message.content === '/join') {
// Only try to join the sender's voice channel if they are in one themselves
if (message.member.voiceChannel) {
message.member.voiceChannel.join().then(connection => {
message.reply('I have successfully connected to the channel!');
// To play a file, we need to give an absolute path to it
const dispatcher = connection.playFile('C:\Users\bobal\Documents\GitHub\Spotify-Playlist-Discord-bot\Assets\Glory.mp3');
dispatcher.on('end', () => {
// The song has finished
console.log('Finished playing!');
});
dispatcher.on('error', e => {
// Catch any errors that may arise
console.log(e);
});
dispatcher.setVolume(0.5); // Set the volume to 50%
}).catch(console.log);
} else {
message.reply('You need to join a voice channel first!');
}
}
});
exports.run = (client, message, args) => {
let user = message.mentions.users.first || message.author;
}
FFMPEG is installed and I have set the environment path for it. When I type FFMPEG in the command line I get the proper response.
Some have said I need to install the ffmpeg binaries but when I run npm install ffmpeg-binaries I get an error message that is here
So then I tried installing an older version and I'm now using ffmpeg-binaries#3.2.2-3 but when I type /join I get the error
[ERR_INVALID_ARG_TYPE]: The "file" argument must be of type string. Received type object
Firstly you need to reset your token, you should never post it online as others can use it to access and control your bot
here is a good answer to your question
I think that you need to install FFMPEG and have the environment path set for it, however, can you provide more information, console logs, behaviour etc

Unresolved method in WebStorm (discord.js, code works)

I'm wrapping my head around this. Autocompletion does not work .then(r => { HERE }) either.
Kinda starting out with this and would be way easier if it just works (works outside of the promise).
Code runs without any problems as well. delete method is recognized as well but not at that part.
I have this problem in a bigger project as well and it gets me confused.
Trying to find something on the web for a few hours, but couldn't find anything that helps me out. Hope I wasn't blind at some point :P
Whole test source:
const Discord = require('discord.js');
const client = new Discord.Client();
client.on("message", message => {
if (message.content === 'test'){
message.channel.send('something').then(r => r.delete(5000));
}
});
Problem:
If you need to delete command triget message you can use
const Discord = require('discord.js');
const client = new Discord.Client();
client.on("message", message => {
if (message.content === 'test'){
message.channel.send('something')
message.delete(1000)
.catch(console.error)
}
});
If you use need to delete response message after some time you code must work, but you can try use reply method.
client.on("message", message => {
if (message.content === 'test'){
message.reply('somethink')
.then(msg => {
msg.delete(10000)
})
.catch(console.error);
}
});
Maybe problem in you discord.js version? In v.12 version you need use
msg.delete({ timeout: 10000 })
Just means webstorm can't discern what functions the object will have after the promise resolves. This is because the message create action in discord.js has multiple return types. So it's possible for the message object not to be passed into r, for instance in the event that the message failed to send, possibly by trying to send a message to a channel without the proper permissions.
If you add a check to confirm that r is the same type as message before trying to call .delete() I believe the warning will go away.
You can observe the potential error this highlight is warning you of by removing the bots permission to send messages in a channel, then by sending "test" to that same channel.
Having had the error recently and having found a solution, if for example you want a text channel, by doing a get you can have a text or a voice . so check if the channel instance TextChannel (for my example)
let channel = client.guilds.cache.get(process.env.GUILD_ID).channels.cache.get(config.status_channel);
if (channel instanceof TextChannel) {
await channel.messages.fetch()
}

Resources