You're right! I apologize for the error in the previous code. I missed updating the client variable in
deploy-commands.js
with the correct intents.
Here's the corrected and complete code for the Discord queue bot:
index.js
(Main Bot File)
JavaScript
Copy
Insert
const { Client, GatewayIntentBits, Partials, SlashCommandBuilder, REST, Routes, EmbedBuilder, Collection } = require('discord.js');
require('dotenv').config();
// Constants
const prefix = process.env.DISCORD_PREFIX || '!';
const eloInitial = 50;
let matchNumber = 1;
// Data Structures
const players = new Map();
const queues = new Map();
const matches = new Map();
const matchLogChannel = null; // Will be set later
// Helper Functions
const createMatchChannel = async (guild, matchNumber) => {
return await guild.channels.create(`🏆 - Match - ${matchNumber}`, {
type: 'text',
permissionOverwrites: [
{ id: guild.id, deny: ['VIEW_CHANNEL'] },
{ id: client.user.id, allow: ['VIEW_CHANNEL', 'SEND_MESSAGES'] }
]
});
};
const createVoiceChannel = async (guild, matchNumber, teamNumber) => {
return await guild.channels.create(`C${matchNumber} Team ${teamNumber}`, {
type: 'voice',
userLimit: 6,
permissionOverwrites: []
});
};
const sortByElo = (a, b) => {
const eloA = players.get(a)?.elo || eloInitial;
const eloB = players.get(b)?.elo || eloInitial;
return eloB - eloA;
};
const getTopPlayers = () => {
const sortedPlayers = [...players.keys()].sort(sortByElo);
return sortedPlayers.slice(0, 25);
};
const updateQueueMessage = async (queueMessage, queue) => {
const playersInQueue = queue.members.length;
const embed = new EmbedBuilder()
.setTitle(`**${queue.name}**`)
.setDescription(`Click the ✅ to join the "${queue.name}" queue or ❌ to leave.\n\n**Players**\n${queue.members.map(id => `<@${id}>`).join('\n')}\n\n${playersInQueue} / ${queue.capacity}\n\nPowered by Community Super League`)
.setColor(0x00FF00);
await queueMessage.edit({ embeds: [embed] });
};
const startMatch = async (message, queue) => {
const guild = message.guild;
const matchChannel = await createMatchChannel(guild, matchNumber);
// Create voice channels (if needed)
if (queue.teamSortMode === 'ELO') {
queue.members.sort(sortByElo);
const team1 = queue.members.slice(0, queue.capacity / 2);
const team2 = queue.members.slice(queue.capacity / 2);
queue.voiceChannels.push(await createVoiceChannel(guild, matchNumber, 1));
queue.voiceChannels.push(await createVoiceChannel(guild, matchNumber, 2));
await matchChannel.send(`Welcome to Match ${matchNumber}!`);
await matchChannel.send(`Team 1: ${team1.map(id => `<@${id}>`).join(', ')}`);
await matchChannel.send(`Team 2: ${team2.map(id => `<@${id}>`).join(', ')}`);
} else if (queue.teamSortMode === 'CAPTAINS') {
const captain1 = queue.members[0];
const captain2 = queue.members[1];
queue.captainPicks.captain1 = captain1;
queue.captainPicks.captain2 = captain2;
await matchChannel.send(`Welcome to Match ${matchNumber}!`);
await matchChannel.send(`Captain 1: <@${captain1}>`);
await matchChannel.send(`Captain 2: <@${captain2}>`);
// Logic for captain picks
await createCaptainPickEmbed(matchChannel, queue);
}
// Store match details
matches.set(matchNumber, {
queue: queue.name,
teams: queue.teamSortMode === 'ELO' ? [queue.members.slice(0, queue.capacity / 2), queue.members.slice(queue.capacity / 2)] : null,
map: null,
winner: null,
matchChannel: matchChannel
});
// Update match number
matchNumber++;
// Clear the queue
queue.members = [];
updateQueueMessage(queue.queueMessage, queue);
};
const createCaptainPickEmbed = async (matchChannel, queue) => {
const embed = new EmbedBuilder()
.setTitle(`Match ${matchNumber} - Captain Picks`)
.setDescription(`Captain 1: <@${queue.captainPicks.captain1}>\nCaptain 2: <@${queue.captainPicks.captain2}>`)
.addFields(
{ name: 'Team 1', value: `${queue.captainPicks.picks.team1.map(id => `<@${id}>`).join('\n')}` },
{ name: 'Team 2', value: `${queue.captainPicks.picks.team2.map(id => `<@${id}>`).join('\n')}` }
)
.setColor(0x00FF00);
const pickMessage = await matchChannel.send({ embeds: [embed] });
// Setup pick reactions
const remainingPlayers = queue.members.filter(id => id !== queue.captainPicks.captain1 && id !== queue.captainPicks.captain2);
const pickEmojis = ['1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣', '8️⃣'];
for (let i = 0; i < remainingPlayers.length; i++) {
await pickMessage.react(pickEmojis[i]);
}
// Add reaction collectors for captain picks
const pickCollectors = {
captain1: pickMessage.createReactionCollector({ filter: (reaction, user) => remainingPlayers.includes(user.id) && pickEmojis.includes(reaction.emoji.name) && !queue.captainPicks.picks.team1.includes(user.id) && !queue.captainPicks.picks.team2.includes(user.id) }),
captain2: pickMessage.createReactionCollector({ filter: (reaction, user) => remainingPlayers.includes(user.id) && pickEmojis.includes(reaction.emoji.name) && !queue.captainPicks.picks.team1.includes(user.id) && !queue.captainPicks.picks.team2.includes(user.id) })
};
let currentPick = queue.captainPicks.captain1;
let picksLeft = 4; // 4 total picks (2 captain 1, 2 captain 2)
pickCollectors[currentPick].on('collect', async (reaction, user) => {
if (picksLeft === 0) {
return;
}
queue.captainPicks.picks[currentPick === queue.captainPicks.captain1 ? 'team1' : 'team2'].push(user.id);
picksLeft--;
// Update embed
const embed = new EmbedBuilder()
.setTitle(`Match ${matchNumber} - Captain Picks`)
.setDescription(`Captain 1: <@${queue.captainPicks.captain1}>\nCaptain 2: <@${queue.captainPicks.captain2}>`)
.addFields(
{ name: 'Team 1', value: `${queue.captainPicks.picks.team1.map(id => `<@${id}>`).join('\n')}` },
{ name: 'Team 2', value: `${queue.captainPicks.picks.team2.map(id => `<@${id}>`).join('\n')}` }
)
.setColor(0x00FF00);
await pickMessage.edit({ embeds: [embed] });
// Switch to the next picker
currentPick = currentPick === queue.captainPicks.captain1 ? queue.captainPicks.captain2 : queue.captainPicks.captain1;
pickCollectors[currentPick].on('collect', async (reaction, user) => {
// ... (Same logic as the previous on('collect') handler)
});
});
pickCollectors[currentPick].on('remove', async (reaction, user) => {
// Remove reaction without impacting picks
});
};
const startMapVoting = async (matchChannel, queue) => {
const embed = new EmbedBuilder()
.setTitle(`Match ${matchNumber} - Map Vote`)
.setDescription(`Vote for your preferred map:\n${queue.mapOptions.map((map, index) => `${index + 1}️⃣ - ${map}`).join('\n')}`)
.setColor(0x00FF00);
const mapVoteMessage = await matchChannel.send({ embeds: [embed] });
const mapVoteEmojis = ['1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣'];
for (const emoji of mapVoteEmojis) {
await mapVoteMessage.react(emoji);
}
const mapVoteCollector = mapVoteMessage.createReactionCollector({ filter: (reaction, user) => mapVoteEmojis.includes(reaction.emoji.name) && queue.members.includes(user.id) });
mapVoteCollector.on('collect', async (reaction, user) => {
const mapIndex = mapVoteEmojis.indexOf(reaction.emoji.name);
const map = queue.mapOptions[mapIndex];
if (!queue.mapVotes.has(map)) {
queue.mapVotes.set(map, 1);
} else {
queue.mapVotes.set(map, queue.mapVotes.get(map) + 1);
}
});
setTimeout(async () => {
const mostVotedMap = [...queue.mapVotes.entries()].reduce((a, b) => (a[1] > b[1] ? a : b))[0];
queue.mapVotes.clear();
// Update match object with chosen map
matches.get(matchNumber).map = mostVotedMap;
// Send the chosen map
await matchChannel.send(`The chosen map is: ${mostVotedMap}`);
}, 30000); // 30 seconds for voting
};
// Initialize Discord Client
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageReactions,
GatewayIntentBits.GuildMembers,
GatewayIntentBits.GuildVoiceStates,
],
partials: [Partials.Channel, Partials.Message, Partials.Reaction, Partials.GuildMember, Partials.User]
});
// Event Listeners
client.once('ready', async () => {
console.log('Bot is online!');
// Register Slash Commands
await registerSlashCommands(client);
});
client.on('messageReactionAdd', async (reaction, user) => {
if (user.id === client.user.id) return; // Ignore reactions from the bot
const queue = queues.get(reaction.message.guild.id);
if (!queue) return;
if (reaction.message.id === queue.queueMessage.id) {
if (reaction.emoji.name === '✅' && !queue.members.includes(user.id)) {
queue.members.push(user.id);
updateQueueMessage(queue.queueMessage, queue);
if (queue.members.length === queue.capacity) {
startMatch(reaction.message, queue);
}
}
}
});
client.on('messageReactionRemove', async (reaction, user) => {
if (user.id === client.user.id) return; // Ignore reactions from the bot
const queue = queues.get(reaction.message.guild.id);
if (!queue) return;
if (reaction.message.id === queue.queueMessage.id) {
if (reaction.emoji.name === '✅' && queue.members.includes(user.id)) {
queue.members.splice(queue.members.indexOf(user.id), 1);
updateQueueMessage(queue.queueMessage, queue);
}
}
});
// Slash Command Handling
client.on('interactionCreate', async interaction => {
if (!interaction.isChatInputCommand()) return;
if (interaction.commandName === 'setup-queue') {
// Create the queue object (with a fixed capacity of 10)
const newQueue = {
capacity: 10,
name: 'Main Queue',
teamSortMode: 'ELO',
members: [],
emojiJoin: '✅',
emojiLeave: '❌',
matchChannel: null,
voiceChannels: [],
mapOptions: [
'Blackwidow', 'Subbase', 'Port', 'Compound', 'Ankara', 'Mexico', 'Eagle Eye'
],
mapVotes: new Map(),
captainPicks: {
captain1: null,
captain2: null,
picks: {
team1: [],
team2: []
}
}
};
// Create the queue message in the channel
const queueMessage = await interaction.channel.send({ embeds: [
new EmbedBuilder()
.setTitle(`**${newQueue.name}**`)
.setDescription(`Click the ✅ to join the "${newQueue.name}" queue or ❌ to leave.\n\n**Players**\n\n0 / ${newQueue.capacity}\n\nPowered by Community Super League`)
.setColor(0x00FF00)
]});
await queueMessage.react('✅');
await queueMessage.react('❌');
// Store the queue
queues.set(interaction.guild.id, newQueue);
// Set the queue message for the queue
newQueue.queueMessage = queueMessage;
await interaction.reply(`The "${newQueue.name}" queue has been created!`);
} else if (interaction.commandName === 'clearqueue') {
const queue = queues.get(interaction.guild.id);
if (!queue) {
await interaction.reply('No active queue found.');
return;
}
// Clear the queue members
queue.members = [];
updateQueueMessage(queue.queueMessage, queue);
await interaction.reply(`The "${queue.name}" queue has been cleared.`);
} else if (interaction.commandName === 'report') {
const matchNumber = parseInt(interaction.channel.name.split(' - ')[2]);
const match = matches.get(matchNumber);
if (match) {
const result = interaction.options.getInteger('result');
if (result === 0) { // Cancel match
await interaction.reply('Match cancelled.');
match.winner = null;
reportMatch(match, match.winner);
} else if (result === 1) { // Team 1 wins
await interaction.reply('Team 1 wins!');
match.winner = 1;
reportMatch(match, match.winner);
} else if (result === 2) { // Team 2 wins
await interaction.reply('Team 2 wins!');
match.winner = 2;
reportMatch(match, match.winner);
} else {
await interaction.reply('Invalid report option.');
}
} else {
await interaction.reply(`Match ${matchNumber} not found.`);
}
} else if (interaction.commandName === 'flip') {
const matchNumberToFlip = interaction.options.getInteger('match_number');
const matchToFlip = matches.get(matchNumberToFlip);
if (matchToFlip) {
if (matchToFlip.winner === 1) {
matchToFlip.winner = 2;
await interaction.reply(`Match ${matchNumberToFlip} result flipped to Team 2 win.`);
} else if (matchToFlip.winner === 2) {
matchToFlip.winner = 1;
await interaction.reply(`Match ${matchNumberToFlip} result flipped to Team 1 win.`);
} else {
await interaction.reply(`Match ${matchNumberToFlip} has no result to flip.`);
}
} else {
await interaction.reply(`Match ${matchNumberToFlip} not found.`);
}
} else if (interaction.commandName === 'setlogchannel') {
const logChannel = interaction.options.getChannel('channel');
if (logChannel) {
matchLogChannel = logChannel;
await interaction.reply(`Match logs will now be posted to <#${logChannel.id}>.`);
} else {
await interaction.reply('Please specify a valid channel.');
}
} else if (interaction.commandName === 'resetelo') {
players.forEach((player) => {
player.elo = eloInitial;
});
await interaction.reply('ELO has been reset for all players!');
} else if (interaction.commandName === 'giveelo') {
const member = interaction.options.getMember('member');
const eloChange = interaction.options.getInteger('elo_change');
if (member && !isNaN(eloChange)) {
if (!players.has(member.id)) {
players.set(member.id, { elo: eloInitial });
}
players.get(member.id).elo += eloChange;
await interaction.reply(`<@${member.id}>'s ELO has been changed by ${eloChange} points.`);
} else {
await interaction.reply('Please provide a valid member and ELO change.');
}
}
});
// Helper function to report match results to the log channel
const reportMatch = async (match, winner) => {
if (matchLogChannel) {
let team1Players = [];
let team2Players = [];
if (match.teams) {
team1Players = match.teams[0].map(id => `<@${id}>`);
team2Players = match.teams[1].map(id => `<@${id}>`);
} else if (match.captainPicks) {
team1Players = match.captainPicks.picks.team1.map(id => `<@${id}>`);
team2Players = match.captainPicks.picks.team2.map(id => `<@${id}>`);
}
const winningTeam = winner === 1 ? 'Team 1' : winner === 2 ? 'Team 2' : 'None';
const logMessage = `**Match ${matchNumber - 1}:**\n**${winningTeam} Wins!**\n**Team 1:** ${team1Players.join(', ')}\n**Team 2:** ${team2Players.join(', ')}`;
await matchLogChannel.send(logMessage);
}
};
// Register Slash Commands
async function registerSlashCommands(client) {
const commands = [
new SlashCommandBuilder()
.setName('setup-queue')
.setDescription('Sets up the main queue'),
new SlashCommandBuilder()
.setName('clearqueue')
.setDescription('Clears the active queue'),
new SlashCommandBuilder()
.setName('report')
.setDescription('Reports the match result')
.addIntegerOption(option =>
option.setName('result')
.setDescription('Match result (0: Cancel, 1: Team 1, 2: Team 2)')
.setRequired(true)
),
new SlashCommandBuilder()
.setName('flip')
.setDescription('Flips the result of a match')
.addIntegerOption(option =>
option.setName('match_number')
.setDescription('The number of the match to flip')
.setRequired(true)
),
new SlashCommandBuilder()
.setName('setlogchannel')
.setDescription('Sets the channel for match logs')
.addChannelOption(option =>
option.setName('channel')
.setDescription('The channel to post logs to')
.setRequired(true)
),
new SlashCommandBuilder()
.setName('resetelo')
.setDescription('Resets ELO for all players'),
new SlashCommandBuilder()
.setName('giveelo')
.setDescription('Gives ELO to a player')
.addUserOption(option =>
option.setName('member')
.setDescription('The player to give ELO to')
.setRequired(true)
)
.addIntegerOption(option =>
option.setName('elo_change')
.setDescription('The amount of ELO to change')
.setRequired(true)
)
];
const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);
try {
console.log('Started refreshing application (/) commands.');
await rest.put(Routes.applicationCommands(client.application.id), { body: commands });
console.log('Successfully reloaded application (/) commands.');
} catch (error) {
console.error(error);
}
}
// Bot login
client.login(process.env.DISCORD_TOKEN);
deploy-commands.js
JavaScript
Copy
Insert
const { REST, Routes } = require('discord.js');
require('dotenv').config();
const commands = [
new SlashCommandBuilder()
.setName('setup-queue')
.setDescription('Sets up the main queue'),
new SlashCommandBuilder()
.setName('clearqueue')
.setDescription('Clears the active queue'),
new SlashCommandBuilder()
.setName('report')
.setDescription('Reports the match result')
.addIntegerOption(option =>
option.setName('result')
.setDescription('Match result (0: Cancel, 1: Team 1, 2: Team 2)')
.setRequired(true)
),
new SlashCommandBuilder()
.setName('flip')
.setDescription('Flips the result of a match')
.addIntegerOption(option =>
option.setName('match_number')
.setDescription('The number of the match to flip')
.setRequired(true)
),
new SlashCommandBuilder()
.setName('setlogchannel')
.setDescription('Sets the channel for match logs')
.addChannelOption(option =>
option.setName('channel')
.setDescription('The channel to post logs to')
.setRequired(true)
),
new SlashCommandBuilder()
.setName('resetelo')
.setDescription('Resets ELO for all players'),
new SlashCommandBuilder()
.setName('giveelo')
.setDescription('Gives ELO to a player')
.addUserOption(option =>
option.setName('member')
.setDescription('The player to give ELO to')
.setRequired(true)
)
.addIntegerOption(option =>
option.setName('elo_change')
.setDescription('The amount of ELO to change')
.setRequired(true)
)
];
const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);
(async () => {
try {
console.log('Started refreshing application (/) commands.');
await rest.put(Routes.applicationCommands(client.application.id), { body: commands });
console.log('Successfully reloaded application (/) commands.');
} catch (error) {
console.error(error);
}
})();
.env
File:
Copy
Insert
DISCORD_TOKEN=your_bot_token
DISCORD_PREFIX=!
Important Notes:
deploy-commands.js
: This file is used to register your slash commands globally. Make sure to run node deploy-commands.js after making changes to your slash commands in
index.js
to deploy the updated commands.
Intents: Make sure you have the correct intents enabled in your Discord Developer Portal.
Let me know if you have any other questions.
Preview:
downloadDownload PNG
downloadDownload JPEG
downloadDownload SVG
Tip: You can change the style, width & colours of the snippet with the inspect tool before clicking Download!
Click to optimize width for Twitter