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

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)

Related

Interaction has already been acknowledged when using a different button

I am getting the interaction has already been acknowledged error. Basically when the command is called the first time, the button and the modal work just fine. The second time, the button appears but then shows the modal and throws the "DiscordAPIError: Interaction has already been acknowledged." error. I am in a real slump.
} else if (order == "edit") {
let shelfNameSpaces = "";
for (let i = 1; i < args.length; i++) {
shelfNameSpaces += args[i] + " ";
}
let shelfName = shelfNameSpaces.trim();
let shelfObj = await Shelves.findOne({name: shelfName})
if (shelfObj) {
const embed = new MessageEmbed()
.setColor("#0099ff")
.setThumbnail(profileImage)
.setTitle(`${shelfObj["name"]}`)
.setDescription("Displaying shelf info")
.addField("Owner", `<#${shelfObj["owner"]}>`)
if (shelfObj["info"].length <= 0) {
embed.addField("Info", "No information added")
} else {
embed.addField("Info", shelfObj["info"])
}
let m = await message.channel.send({ embeds: [embed] })
const row = new MessageActionRow();
row.addComponents(
new MessageButton()
.setCustomId(`edit-shelf`)
.setLabel('Edit Shelf')
.setStyle("PRIMARY")
)
var buttons = await message.channel.send({ components: [row] })
setTimeout(() => {
if (buttons.deletable)
buttons.delete()
}, 60000);
// client.on("interactionCreate", async (interaction) => {
// module.exports = {
// name: 'interactionCreate',
// async execute (interaction) {
// }
// }
var editInfo, modal, editRow;
client.on("interactionCreate", async (interaction) => {
if (!interaction.isButton() || interaction.isModalSubmit()) return;
if (interaction.customId == "edit-shelf") {
// interaction.reply("Editing")
if (buttons.deletable)
buttons.delete()
modal = new Modal()
.setCustomId(`editModal`)
.setTitle('Edit Info')
editInfo = new TextInputComponent()
.setCustomId(`editInput`)
.setLabel("Input your edit")
.setValue(shelfObj["info"])
.setStyle("PARAGRAPH")
editRow = new MessageActionRow().addComponents(editInfo)
modal.addComponents(editRow)
await interaction.showModal(modal)
modalCheck++;
}
})
client.on("interactionCreate", async (inter) => {
if (!inter.isModalSubmit()) return;
inter.deferUpdate()
const editval = inter.fields.getTextInputValue('editInput')
// console.log(editval);
shelfObj["info"] = editval
const updatedShelf = new Shelves(shelfObj);
await updatedShelf.save()
const embed2 = new MessageEmbed()
.setColor("#0099ff")
.setThumbnail(profileImage)
.setTitle(`${shelfObj["name"]}`)
.setDescription("Displaying shelf info")
.addField("Owner", `<#${shelfObj["owner"]}>`)
if (shelfObj["info"].length <= 0) {
embed2.addField("Info", "No information added")
} else {
embed2.addField("Info", shelfObj["info"])
}
m.edit({ embeds: [embed2] })
// inter.reply("Shelf edited successfully")
// await inter.deleteReply()
// if (buttons.deletable)
// buttons.delete()
})
} else {
const embed = new MessageEmbed()
.setColor("#ff0000")
.setTitle("Shelves")
.setDescription("Unknown Shelf")
.addField("Error", "Shelf not found");
message.channel.send({ embeds: [embed] });
}
I've experienced this error multiple times, and I finally found out how to fix this error.
To start off, you need to install a package called nanoid (npm i nanoid in terminal).
At the top of your file, include const { nanoid } = require("nanoid");. This package is used for creating unique IDs for anything you'd like.
now, make a variable, and make it the following:
let id = `button-${nanoid()}`;
Make your .setCustomId() equal .setCustomId(id).
Lastly, make your if (interaction.customId == "edit-shelf") equal if (interaction.customId == id).
Note: If you have another error, try changing your nanoid package in your package.json to version ^3.3.4, and uninstall and install it again.

Setting a timeout to delete a message embed on discord.js

I have a basic ticketing system for a suggestions channel.
Ideally, when a user does .exesuggest <whatever suggestion they want> (.exe is the bot prefix), I want the bot to reply that the ticket has been sent to staff, i want the bot to delete the user's message, and to delete it's own message after 5 seconds. At the same time, the bot will send a message with the suggestion's author and the suggestion itself into a staff channel.
At the moment everything is working except for the bot deleting it's own message after 5 seconds.
Here is my code:
const Discord = require("discord.js")
const channelId = '873769980729106442'
const check = 'โœ…'
let registered = false
const registerEvent = client => {
if (registered) {
return
}
registered = true
client.on('messageReactionAdd', (reaction, user) => {
if (user.bot) {
return
}
const { message } = reaction
if (message.channel.id === channelId) {
message.delete()
}
})
}
module.exports = {
commands: ['ticket', 'suggest', 'suggestion'],
minArgs: 1,
expectedArgs: '<message>',
callback: (userMessage, arguments, text, client) => {
const { guild, member } = userMessage
registerEvent(client)
const channel = guild.channels.cache.get(channelId)
const newTicketEmbed = new Discord.MessageEmbed()
.setAuthor(userMessage.author.username)
.setTitle('Created a new ticket.')
.setDescription(`"${text}"`)
.setFooter(`Click the ${check} icon to delete this message.`)
channel.send(newTicketEmbed).then(ticketMessage => {
ticketMessage.react(check)
const replyEmbed = new Discord.MessageEmbed()
.setDescription(`<#${member.id}> Your ticket has been created! Expect a reply soon!`)
userMessage.channel.send(replyEmbed)
})
}
}
I have a working command base handler in another file that makes the command work.
I just need to know exactly how to make that bot's reply in replyEmbed to be deleted after 5 seconds.
You can use a setTimeout function to delay the <message>.delete() function from executing.
Example:
setTimeout(function() { // Setup a timer
userMessage.delete(); // Deletes the users message
ticketMessage.delete(); // Deletes the ticket message
}, 5000); // 5 seconds in milliseconds
Full example:
const Discord = require("discord.js")
const channelId = '873769980729106442'
const check = 'โœ…'
let registered = false
const registerEvent = client => {
if (registered) return;
registered = true
client.on('messageReactionAdd', (reaction, user) => {
if (user.bot) return;
const { message } = reaction
if (message.channel.id === channelId)
message.delete()
});
}
module.exports = {
commands: ['ticket', 'suggest', 'suggestion'],
minArgs: 1,
expectedArgs: '<message>',
callback: (userMessage, arguments, text, client) => {
const { guild, member } = userMessage
registerEvent(client)
const channel = guild.channels.cache.get(channelId)
const newTicketEmbed = new Discord.MessageEmbed()
.setAuthor(userMessage.author.username)
.setTitle('Created a new ticket.')
.setDescription(`"${text}"`)
.setFooter(`Click the ${check} icon to delete this message.`)
channel.send(newTicketEmbed).then(ticketMessage => {
ticketMessage.react(check)
const replyEmbed = new Discord.MessageEmbed()
.setDescription(`<#${member.id}> Your ticket has been created! Expect a reply soon!`)
userMessage.channel.send(replyEmbed);
setTimeout(function() { // Setup a timer
userMessage.delete(); // Deletes the users message
ticketMessage.delete(); // Deletes the ticket message
}, 5000); // 5 seconds in milliseconds
});
}
}
In Discord.js v13 you have to use setTimeout.
You can implement what you want like this:
userMessage.channel.send(replyEmbed).then(msg => {
setTimeout(() => msg.delete(), 5000);
});// It will delete after 5 seconds
It might work.
Message.delete has an options argument which is an object, and you can set the timeout there (v13 doesnโ€™t have this!):
userMessage.delete({timeout: 5000}) //deletes after 5000 ms
v13 must use setTimeout since the feature was removed
setTimeout(() => userMessage.delete(), 5000) //deletes after 5000 ms

Banned user cant be found in BansCollection using fetchBans()

ok so i made sure to check ids,
the user im trying to unban is banned.
the id is correct
i tried to console log what userBanned returns and it is undefined
like how?
const text = args.join(` `)
const bansCollection = await message.guild.fetchBans()
const userBanned = bansCollection.find(user => user.id == text)
if (!isNaN(text)) {
if (userBanned) {
await message.guild.members.unban(text)
message.channel.send(`<#${text}> has been unbanned`)
} else { message.channel.send(new Discord.MessageEmbed().setTitle(`**That user is not Banned!**`)) }
} else { message.channel.send(`You Need the user's ID!`) }
console.log(text , bansCollection, userBanned)}
I would fetch the audit logs nd filter them.
Example:
message.guild.fetchAuditLogs()
.then(logs => {
let ban = logs.entries.filter(e => e.action === 'MEMBER_BAN_ADD') //get all the bans
ban = ban.filter(e => e.target.id === text) //filter the bans from the user
if (!message.guild.members.cache.get(text)) return message.channel.send(`This user is not in this server.`);
if (userBanned) {
await message.guild.members.unban(text)
message.channel.send(`<#${text}> has been unbanned`);
} else {
let embed = new Discord.MessageEmbed();
embed.setTitle(`**That user is not Banned!**`);
message.channel.send(embed);
}
});

Voice Channel Detection Erorr

When I'm Trying To Make My Discord Music Bot I Get An Error In Discord Not The Command Line Though, It Cannot Tell If I'm In A Voice Channel Or Not
Here's My Code
if (message.author.bot) return;
if (!message.content.startsWith(prefix)) return;
const serverQueue = queue.get(message.guild.id);
if (message.content.startsWith(`${prefix}play`)) {
execute(message, serverQueue);
return;
} else if (message.content.startsWith(`${prefix}skip`)) {
skip(message, serverQueue);
return;
} else if (message.content.startsWith(`${prefix}stop`)) {
stop(message, serverQueue);
return;
} else {
message.channel.send('You need to enter a valid command!')
}
});
process.on('unhandledRejection', error => console.error('Uncaught Promise Rejection', error));
async function execute(message, serverQueue) {
const args = message.content.split(' ');
const voiceChannel = message.member.voiceChannel;
if (!voiceChannel) return message.channel.send('You need to be in a voice channel to play music!');
const permissions = voiceChannel.permissionsFor(message.bot.user);
if (!permissions.has('CONNECT') || !permissions.has('SPEAK')) {
return message.channel.send('I need the permissions to join and speak in your voice channel!');
}
const songInfo = await ytdl.getInfo(args[1]);
const song = {
title: songInfo.title,
url: songInfo.video_url,
};
if (!serverQueue) {
const queueContruct = {
textChannel: message.channel,
voiceChannel: voiceChannel,
connection: null,
songs: [],
volume: 5,
playing: true,
};
queue.set(message.guild.id, queueContruct);
queueContruct.songs.push(song);
try {
var connection = await voiceChannel.join();
queueContruct.connection = connection;
play(message.guild, queueContruct.songs[0]);
} catch (err) {
console.log(err);
queue.delete(message.guild.id);
return message.channel.send(err);
}
} else {
serverQueue.songs.push(song);
console.log(serverQueue.songs);
return message.channel.send(`${song.title} has been added to the queue!`);
}
}
function skip(message, serverQueue) {
if (!message.member.voiceChannel) return message.channel.send('You have to be in a voice channel to stop the music!');
if (!serverQueue) return message.channel.send('There is no song that I could skip!');
serverQueue.connection.dispatcher.end();
}
function stop(message, serverQueue) {
if (!message.member.voiceChannel) return message.channel.send('You have to be in a voice channel to stop the music!');
serverQueue.songs = [];
serverQueue.connection.dispatcher.end();
}
function play(guild, song) {
const serverQueue = queue.get(guild.id);
if (!song) {
serverQueue.voiceChannel.leave();
queue.delete(guild.id);
return;
}
const dispatcher = serverQueue.connection.playStream(ytdl(song.url))
.on('end', () => {
console.log('Music ended!');
serverQueue.songs.shift();
play(guild, serverQueue.songs[0]);
})
.on('error', error => {
console.error(error);
});
dispatcher.setVolumeLogarithmic(serverQueue.volume / 5);
}
I Found It On This Website https://medium.com/free-code-camp/how-to-create-a-music-bot-using-discord-js-4436f5f3f0f8
And When I Start The Code It Says I'm Not In One,
Any Type Of Feedback Would Be Greatly Appreciated!
Try this
if (!message.member.voice.channel)
return message.reply('you are not in a voice channel'),
The problem comes from the fact that you're using the GuildMember.voiceChannel property, which exists only in discord.js#v11. The v12 equivalent is GuildMember.voice.channel, you can use it like this:
// You can use this as before, since it's the same VoiceChannel element
const voiceChannel = message.member.voice.channel

Voice state update event

I am trying to move the bot from 1 channel to another when the user switches channels
const client = require("../index.js");
module.exports = client => {
client.on("voiceStateUpdate", (oldState, newState) => {
let oldChannel = oldState.voiceChannel, // the previous channel, if there was one
newChannel = newState.voiceChannel; // the current channel, if there is one
if (oldChannel != newChannel) {
// if the channel has changed
// do your stuff....
channel = msg.member.voice.channel
channel.join()
}
});
}
You just need to use the newState's voiceChannel
client.on("voiceStateUpdate", (oldState, newState) => {
let oldChannel = oldState.voiceChannel, // the previous channel, if there was one
let newChannel = newState.voiceChannel; // the current channel, if there is one
if (oldChannel != newChannel) {
newChannel.join();
}
});

Resources