Discord music bot saying I need to send a second argument even when i do - discord.js

Whats wrong with my code, I cant seem to figure it out. Followed a few tutorials before and made a few simple bots on my own, but cant seem to figure this out. Its not playing music nor connecting to the vc
Heres My Code:
const ytdl = require('ytdl-core');
const ytSearch = require('yt-search');
module.exports = {
name: 'play',
description: 'Joins and plays a video from youtube',
async execute(message, args) {
const voiceChannel = message.member.voice.channel;
if (!voiceChannel) return message.channel.send('You need to be in a channel to execute this command!');
const permissions = voiceChannel.permissionsFor(message.client.user);
if (!permissions.has('CONNECT')) return message.channel.send('You dont have the correct permissins');
if (!permissions.has('SPEAK')) return message.channel.send('You dont have the correct permissins');
if (!args.length) return message.channel.send('You need to send the second argument!');
const validURL = (str) =>{
var regex = /(http|https):\/\/(\w+:{0,1}\w*)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%!\-\/]))?/;
if(!regex.test(str)){
return false;
} else {
return true;
}
}
if(validURL(args[0])){
const connection = await voiceChannel.join();
const stream = ytdl(args[0], {filter: 'audioonly'});
connection.play(stream, {seek: 0, volume: 1})
.on('finish', () =>{
voiceChannel.leave();
message.channel.send('leaving channel');
});
await message.reply(`:thumbsup: Now Playing ***Your Link!***`)
return
}
const connection = await voiceChannel.join();
const videoFinder = async (query) => {
const videoResult = await ytSearch(query);
return (videoResult.videos.length > 1) ? videoResult.videos[0] : null;
}
const video = await videoFinder(args.join(' '));
if(video){
const stream = ytdl(video.url, {filter: 'audioonly'});
connection.play(stream, {seek: 0, volume: 1})
.on('finish', () =>{
voiceChannel.leave();
});
await message.reply(`:thumbsup: Now Playing ***${video.title}***`)
} else {
message.channel.send('No video results found');
}
}
}

The main principle we want to focus on here is Guard Statements which you make good use of in your code.
If you aren't already familiar, a guard statement is one that resides at the top of a block of code and prevents the execution of code below it if a specific condition is met. This is really useful for making discord bots because it will allow us to stop command execution if the permission to use it is missing for example (which you use in your code provided)
The caveat here is that in your specific guard statement where you check for the number of args used, you provide this line:
if (!args.length) return message.channel.send('You need to send the second argument!');
Now, there's nothing wrong with this line, except that we're only checking if the property length doesn't exist on args which isn't what we're really trying to check.
If we want to make sure that there are exactly two arguments provided, we would want to rewrite the condition like this:
if (args.length == 2) return message.channel.send('You need to send the second argument!');
From here, you can change the condition to meet your desired requirements and it should work great!

Related

How to create an embedded Queue list for discord bot the updates

I am making a discord bot that plays music. Currently, the embedded list works fine and every time I add a song to the queue, the list deletes itself and creates a new embedded queue list updated with the new song. However, I can't figure out how to make this queue list update itself when I use the skip button I have implemented (I'm new to programming).
Example of how it works now:
Pressing the button skips the song, but as stated before, how would I refresh the list every time I press the button?
const ytdl = require('ytdl-core');
const ytSearch = require('yt-search');
process.setMaxListeners(1000000000000);
const { MessageEmbed } = require('discord.js');
ffmpeg_options = {
'options': '-vn',
"before_options": "-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5"
}
//Global queue for your bot. Every server will have a key and value pair in this map. { guild.id, queue_constructor{} }
const queue = new Map();
module.exports = {
name: 'play',
aliases: ['skip', 'stop'], //We are using aliases to run the skip and stop command follow this tutorial if lost: https://www.youtube.com/watch?v=QBUJ3cdofqc
cooldown: 0,
description: 'Advanced music bot',
async execute(message, args, cmd, button, skip, client, Discord){
//Checking for the voicechannel and permissions (you can add more permissions if you like).
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 the correct permissions.');
if (!permissions.has('SPEAK')) return message.channel.send('You dont have the correct permissions.');
//This is our server queue. We are getting this server queue from the global queue.
const server_queue = queue.get(message.guild.id);
let checker = false;
if (cmd === 'stop' && !server_queue){
return message.channel.send("The bot is not in the channel, you dont have to stop him.");
}
//If the user has used the play command
if (cmd === 'stop') stop_song(message, server_queue);
else if(skip === true) {
skip_song(message, server_queue);
}
else if (cmd === message.content){
//if (!args.length) return message.channel.send('You need to send the second argument!');
let song = {};
//If the first argument is a link. Set the song object to have two keys. Title and URl.
if (ytdl.validateURL(args[0])) {
const song_info = await ytdl.getInfo(args[0]);
song = { title: song_info.videoDetails.title, url: song_info.videoDetails.video_url }
message.delete({ timeout:1000 })
} else {
//If there was no link, we use keywords to search for a video. Set the song object to have two keys. Title and URl.
const video_finder = async (query) =>{
const video_result = await ytSearch(query);
return (video_result.videos.length > 1) ? video_result.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 video.');
}
message.delete({ timeout:1000 })
}
if (skip === true && server_queue) {
}
//If the server queue does not exist (which doesn't for the first video queued) then create a constructor to be added to our global queue.
if (!server_queue){
const queue_constructor = {
voice_channel: voice_channel,
text_channel: message.channel,
connection: null,
songs: []
}
//Add our key and value pair into the global queue. We then use this to get our server queue.
queue.set(message.guild.id, queue_constructor);
queue_constructor.songs.push(song);
//Establish a connection and play the song with the vide_player function.
try {
const connection = await voice_channel.join();
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);
message.channel.bulkDelete(2)
.then(messages => console.log(`Bulk deleted ${messages.size} messages`))
.catch(console.error);
//where I actually make the embeded queue list
const queueList = server_queue.songs.map((song, i) => `[${++i}] - ${song.title}`);
const queueEmbed = new MessageEmbed()
.setDescription(queueList);
return message.channel.send(queueEmbed, button);
}
}
}
}
const video_player = async (guild, song) => {
const song_queue = queue.get(guild.id);
//If no song is left in the server queue. Leave the voice channel and delete the key and value pair from the global queue.
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(`🎶 Now playing **${song.title}**`)
}
const skip_song = (message, server_queue) => {
if (!message.member.voice.channel) return message.channel.send('You need to be in a channel to execute this command!');
if(!server_queue){
return message.channel.send(`There are no songs in queue 😔`,);
}
server_queue.connection.dispatcher.end();
//message.delete({ timeout:20000})
}
const stop_song = (message, server_queue) => {
if (!message.member.voice.channel) return message.channel.send('You need to be in a channel to execute this command!');
server_queue.songs = [];
server_queue.connection.dispatcher.end();
//message.delete({ timeout:20000})
}
return message.channel.send(queueEmbed, button);
That line above returns a Promise which resolves to a Message object. Change it to something like this:
const embeddedMessage = await message.channel.send(queueEmbed, button);
return embeddedMessage;
So when a user clicks the skip button you can simply call
embeddedMessage.edit(your_new_embed, button)

Discord.js cannot read property

I am trying to code a bot that will send a message when someone joins a voice channel. Code and error are below.
const Discord = require("discord.js");
const config = require("./config.json");
const bot = new Discord.Client();
bot.login(config.BOT_TOKEN);
bot.once('ready', () => {
console.log(`Bot ready, logged in as ${bot.user.tag}!`);
})
bot.on('voiceStateUpdate', (oldMember, newMember) => {
const newUserChannel = newMember.voice.channelID
const oldUserChannel = oldMember.voice.channelID
const textChannel = message.guild.channels.cache.get('766783720312537089')
if (newUserChannel === '764231813248843806') {
textChannel.send(`${newMember.user.username} (${newMember.id}) has joined the channel`)
} else if (oldUserChannel === '764231813248843806' && newUserChannel !== '764231813248843806') {
textChannel.send(`${newMember.user.username} (${newMember.id}) has left the channel`)
}
})
Error:
TypeError: Cannot read property 'channelID' of undefined
This is pretty easy to solve. The problem is that voiceStateUpdate does indeed take two variables, however they are not oldMember, newMember but oldState, newState.
As usual with functions it doesn't really matter what you call them but it makes more sense to use oldState, newState because they are a voiceState. As such they do not have a voice property.
So to fix this, all you have to do is use the correct voiceState properties.
const newUserChannel = newState.channelID;
const oldUserChannel = oldState.channelID;
Note: newState.user is also not a thing, however it does provide you with the member object so I suggest you use that instead.
EDIT: Your entire code should look a little something like this.
bot.on('voiceStateUpdate', (oldState, newState) => {
const newUserChannel = newState.channelID;
const oldUserChannel = oldState.channelID;
const textChannel = newState.guild.channels.cache.get('766783720312537089');
if (newUserChannel === '764231813248843806') {
textChannel.send(`${newState.member.user.username} (${newState.id}) has joined the channel`)
} else if (oldUserChannel === '764231813248843806' && newUserChannel !== '764231813248843806') {
textChannel.send(`${newState.member.user.username} (${newState.id}) has left the channel`)
}
});

Deleting a channel using reactions discord.js

I have a little question.
I'm currently in the progress of trying out creating a tickets bot, and had the idea of having so when a user types !close, it would present him with an embed, asking him if he really does want to close it using reactions (:wastebasket: for Yes, :x: for No).
If the user reacts for Yes, the channel will close. As for No, the embed message will be deleted.
I will be glad to help.
You can use something a little like this:
const prevMsg = message
message.channel.send(embed).then(message => {
const questionMessage = message;
questionMessage.react('🗑️')
.then(() => questionMessage.react('❌'));
const filter = (reaction, user) => {
return ['🗑️', '❌'].includes(reaction.emoji.name) && user.id === prevMsg.author.id;
};
questionMessage.awaitReactions(filter, { max: 1, time: 60000, errors: ['time'] })
.then(collected => {
const reaction = collected.first();
if (reaction.emoji.name === '🗑️') {
// closing function here
} else {
questionMessage.delete();
}
})
})
If it didn't work, please let me know!

Discord bot role command stopped working for unknown reasons

I have a command which worked, but at some point stopped, returning to the chat message that the role does not exist. An error "(node:12228) DeprecationWarning: Collection#find: pass a function instead" is sent to the console every time I use a command, but I always had it
const Discord = require("discord.js");
module.exports.run = async (bot, message, args) => {
if(!message.member.hasPermission("MANAGE_MEMBERS")) return message.reply();
let rMember = message.guild.member(message.mentions.users.first()) || message.guild.members.get(args[0]);
if(!rMember) return message.reply("nope.");
let role = args.join(" ").slice(22);
if(!role) return message.reply("nope!");
let gRole = message.guild.roles.find(`name`, role);
if(!gRole) return message.reply("role does not exist.");
const allowed = ['some id'];
if (!allowed.includes(gRole.id)) return;
if(rMember.roles.has(gRole.id)) return message.reply("nope.");
await(rMember.removeRoles(['some id']));
await(rMember.addRole(gRole.id));
if(gRole.id == 'id') rMember.addRole('id') && rMember.removeRoles(['some id']);;
try{
await rMember.send(`you got ${gRole.name}!`)
}catch(e){
}
}
module.exports.help = {
name: "role"
}
So I need the command to work.
In order of appearance, I see these mistakes in your code...
You're not catching any rejected promises.
MANAGE_MEMBERS is not a valid permission flag.
You should pass a function into Collection.find().
No idea what you're trying to do with the declaration of role.
Your use of the && logical operator in your if statement is incorrect. Use a block statement instead.
Your catch block in the try...catch statement has been left empty.
Combining the answers provided to your other questions about this code, this is a correct, much cleaner rewrite...
const getMention = require('discord-mentions'); // Don't forget to install.
module.exports.run = async (bot, message, args) => {
if (!message.guild) return;
try {
if (!message.member.hasPermission('MANAGE_ROLES')) return;
if (!args[1]) return await message.reply('Please provide a user.');
if (!args[2]) return await message.reply('Please provide a role.');
if (args[3]) return await message.reply('Too many arguments provided.');
let member;
if (getMention(args[1])) member = getMention(args[1], message.guild).member;
else member = message.guild.members.find(m => m.user.tag === args[1] || m.id === args[1]);
if (!member) return await message.reply('Invalid user provided.');
let role;
if (getMention(args[2])) role = getMention(args[2], message.guild).role;
else role = message.guild.roles.find(r => r.name === args[2] || r.id === args[2]);
if (!role) return await message.reply('Invalid role provided.');
const allowed = ['role1ID', 'role2ID', 'role3ID'];
if (!allowed.includes(role.id)) return await message.reply('That role is not allowed.');
if (member.roles.has(role.id)) return await message.reply('That user already has that role.');
await member.removeRoles(['someID', 'someOtherID']);
await member.addRole(role.id);
if (role.id === 'anAlternateRoleID') {
await member.removeRoles(['someID', 'someOtherID']);
await member.addRole('otherRoleID');
}
await member.send(`You got the ${role.name} role.`)
.catch(() => console.log(`Couldn't send message to ${member.user.tag}.`));
} catch(err) {
console.error(err);
}
};
module.exports.help = {
name: 'role'
};
Keep in mind, if you're removing just one role from a member, you should use the singular version of the method, GuildMember.removeRole().

How to make a bot that deletes a message and posts it in another channel based on reactions

I'm trying to make it so when my bot picks up a reaction in a specific channel, it'll see if it hit 10 reactions on a specific reaction. Then it'll delete the reacted message and post it into another specific channel with a message attached to it.
Here's the code
doopliss.on('message', message => {
if (message.channel.id === "587066921422290953") {
setTimeout(function(){
message.react(message.guild.emojis.get('587070377696690177'))
}, 10)
setTimeout(function(){
message.react(message.guild.emojis.get('587071853609353256'))
}, 50)
setTimeout(function(){
message.react(message.guild.emojis.get('587070377704816640'))
}, 100)
}
});
const message = require("discord.js");
const emoji = require("discord.js");
const reaction = require("discord.js");
doopliss.on('message', message => {
if (message.channel.id === "587066921422290953") {
let limit = 2; // number of thumbsdown reactions you need
if (message.reaction.emoji.name == message.guild.emojis.get('587070377696690177')
&& reaction.count >= limit) message.reaction.message.delete();
let tcontent = message.reaction.message.content
let accept = message.guild.channels.get('587097086256873483')
accept.send(`${tcontent} \`\`\`This server suggestion has been accepted by the community! Great job! Now a staff member just needs to forward it to username.\`\`\``)
}})
Can't figure out how to do this.
Expected Result: Bot sees if post has 10 reactions, then delete it and take the same message to a different channel
Actual Result: An error occurs Cannot read 'channel' property
First I want to say that some question here like this one have what you search.
Moreover, the discord documentation and the guide provide an awaiting reactions section.
There is some other question that refer to the subject or the function used in the guide and by searching a bit you can even find question like this one which is almost the same thing as you.
However, here is a complete example of what you want to do. You can add a timer to the promise instead of just waiting. I didn't use the reaction collector because promise are a bit faster, but you can also create a management system of multiple collector , or use the client.on('messageReactionAdd').
const Discord = require('discord.js');
const config = require('./config.json');
const channelSuggestion = '<channelID>';
const channelSend = '<channelID>';
const emojiReact = '<emojiID>';
const prefixSuggestion = '!';
const reactionMax = 11;
const client = new Discord.Client();
client.on('ready', () => {
console.log('Starting!');
client.user.setActivity(config.activity);
});
client.on('message', (msg) => {
if ((msg.content[0] === prefixSuggestion) && (msg.channel.type === 'dm')){
sendSuggestion(msg);
}
});
function filter(reaction) {
return reaction.emoji.id === emojiReact;
}
function moveSuggestion(msg) {
client.channels.get(channelSend).send(msg.content)
.then(() => msg.delete()).catch(err => console.log(error));
}
function sendSuggestion(msg){
client.channels.get(channelSuggestion).send(msg.content.substr(1))
.then((newMsg) => newMsg.react(emojiReact))
.then((newMsgReaction) =>
newMsgReaction.message
.awaitReactions(filter, {max: reactionMax})//, time: 15000, errors: ['time']})
.then(collected => {
moveSuggestion(newMsgReaction.message);
})
// .catch(collected => {
// console.log(`After a minute, only ${collected.size} out of 10 reacted.`);
// })
);
}
client.login(config.token)
.then(() => console.log("We're in!"))
.catch((err) => console.log(err));
The bot will listen to dm message (I don't know how you want your bot to send the suggestion message, so I made my own way) which start with a !.
Then the bot will send a message to a specific channel, wait for N person to add a reaction, and then will send the message to another channel.

Resources