setup.sh
Thu Jul 11 2024 12:19:37 GMT+0000 (Coordinated Universal Time)
Saved by @FRTZ
#!/bin/bash # Create directories mkdir -p your-bot/commands/admin mkdir -p your-bot/commands/general mkdir -p your-bot/commands/match mkdir -p your-bot/commands/warning # Create data.json file cat <<EOL > your-bot/data.json { "matches": {}, "matchCounter": 1, "playersELO": {}, "playerWarnings": {}, "karma": {}, "playerStats": {} } EOL # Create .env file cat <<EOL > your-bot/.env DISCORD_TOKEN=your-token-here DISCORD_PREFIX=! QUEUE_CHANNEL_ID=your-queue-channel-id MATCH_CATEGORY_ID=your-match-category-id MATCH_LOG_CHANNEL_ID=your-match-log-channel-id EOL # Create package.json file cat <<EOL > your-bot/package.json { "name": "your-bot", "version": "1.0.0", "main": "index.js", "scripts": { "start": "node index.js" }, "dependencies": { "discord.js": "^14.0.0", "dotenv": "^16.0.0" }, "type": "module" } EOL # Create index.js file cat <<EOL > your-bot/index.js import { Client, GatewayIntentBits, Partials, PermissionsBitField, ChannelType, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js'; import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; import dotenv from 'dotenv'; import { loadCommands } from './loadCommands.js'; import { registerCommands } from './registerCommands.js'; dotenv.config(); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const dataFilePath = path.join(__dirname, 'data.json'); console.log(\`Data file path: \${dataFilePath}\`); let data; let matches = {}; let matchCounter = 1; let playersELO = {}; let playerWarnings = {}; let playerStats = {}; let karma = {}; let queue = []; const maps = ['BW', 'SB', 'PORT', 'COMP', 'ANK', 'MEX', 'EE']; const saveMatchData = () => { const dataToSave = { matches, matchCounter, playersELO, playerWarnings, karma, playerStats }; fs.writeFileSync(dataFilePath, JSON.stringify(dataToSave, null, 2), 'utf-8'); }; const loadMatchData = () => { try { const fileContents = fs.readFileSync(dataFilePath, 'utf8'); const data = JSON.parse(fileContents); matches = data.matches || {}; matchCounter = data.matchCounter || 1; playersELO = data.playersELO || {}; playerWarnings = data.playerWarnings || {}; karma = data.karma || {}; playerStats = data.playerStats || {}; } catch (error) { console.error(\`Error reading file at path: \${dataFilePath}\`); console.error(error); } }; loadMatchData(); const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent, GatewayIntentBits.GuildMembers, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildPresences, GatewayIntentBits.GuildMessageReactions ], partials: [Partials.Channel, Partials.Message, Partials.Reaction] }); client.once('ready', async () => { console.log(\`Bot is ready! Logged in as \${client.user.tag}\`); await setupQueueMessage(); registerCommands(client); }); client.on('messageCreate', async (message) => { if (!message.content.startsWith(process.env.DISCORD_PREFIX) || message.author.bot) return; const args = message.content.slice(process.env.DISCORD_PREFIX.length).trim().split(/ +/); const command = args.shift().toLowerCase(); try { await loadCommands(command, message, args); } catch (error) { console.error(\`Error executing command \${command}:\`, error); message.reply('There was an error executing that command.'); } }); client.on('interactionCreate', async interaction => { try { if (interaction.isButton()) { await handleButtonInteraction(interaction); } else if (interaction.isCommand()) { const command = interaction.commandName; switch (command) { case 'setupreaction': await setupQueueMessage(interaction); break; case 'eloreset': await eloresetCommand(interaction); break; case 'warningreset': await warningresetCommand(interaction); break; case 'leaderboard': await leaderboardCommand(interaction); break; default: await interaction.reply({ content: 'Unknown command interaction.', ephemeral: true }); break; } } } catch (error) { console.error('Error handling interaction:', error); if (interaction.isCommand() || interaction.isButton()) { await interaction.reply({ content: 'There was an error handling this interaction.', ephemeral: true }); } } }); const setupQueueMessage = async () => { try { const channel = await client.channels.fetch(process.env.QUEUE_CHANNEL_ID); const embed = new EmbedBuilder() .setColor('#00ff26') .setTitle('🎮 CSL West Competitive Queue 🎮') .setDescription('Use the buttons below to join or leave the queue.\n\n**Current Queue:**\nNo one in the queue (0/10)') .setFooter({ text: 'Powered by Maverick', iconURL: 'https://i.imgur.com/wTF3RuJ.png' }); const row = new ActionRowBuilder() .addComponents( new ButtonBuilder() .setCustomId('join_queue') .setLabel('Join Queue') .setStyle(ButtonStyle.Success) .setEmoji('✅'), new ButtonBuilder() .setCustomId('leave_queue') .setLabel('Leave Queue') .setStyle(ButtonStyle.Danger) .setEmoji('❌') ); await channel.send({ embeds: [embed], components: [row] }); } catch (error) { console.error('Error setting up queue message:', error); } }; const joinQueue = async (interaction) => { try { await interaction.deferReply({ ephemeral: true }); const user = interaction.user; const member = await interaction.guild.members.fetch(user.id); if (!queue.includes(user.id) && member.roles.cache.some(role => ['Pug Approved', 'Admin', 'Score Reporter'].includes(role.name))) { queue.push(user.id); await updateQueueMessage(); if (queue.length === 10) { await createMatch(interaction.guild); } await interaction.editReply({ content: 'You have joined the queue!' }); } else { await interaction.editReply({ content: 'You are already in the queue or do not have permission to join the queue.' }); } } catch (error) { console.error('Error joining queue:', error); if (!interaction.replied) { await interaction.editReply({ content: 'There was an error joining the queue.' }); } } }; const leaveQueue = async (interaction) => { try { await interaction.deferReply({ ephemeral: true }); const user = interaction.user; const index = queue.indexOf(user.id); if (index > -1) { queue.splice(index, 1); await updateQueueMessage(); await interaction.editReply({ content: 'You have left the queue!' }); } else { await interaction.editReply({ content: 'You are not in the queue!' }); } } catch (error) { console.error('Error leaving queue:', error); if (!interaction.replied) { await interaction.editReply({ content: 'There was an error leaving the queue.' }); } } }; const updateQueueMessage = async () => { try { const channel = await client.channels.fetch(process.env.QUEUE_CHANNEL_ID); const messages = await channel.messages.fetch({ limit: 10 }); const queueMessage = messages.find(msg => msg.embeds[0]?.title === '🎮 CSL West Competitive Queue 🎮'); if (queueMessage) { const description = queue.length > 0 ? `${queue.map(id => \`<@${id}>\`).join('\n')}\n\n**(${queue.length}/10)**` : 'No one in the queue (0/10)'; const embed = new EmbedBuilder() .setColor('#00ff26') .setTitle('🎮 CSL West Competitive Queue 🎮') .setDescription(\`Use the buttons below to join or leave the queue.\n\n**Current Queue:**\n\${description}\`) .setFooter({ text: 'Powered by Maverick', iconURL: 'https://i.imgur.com/wTF3RuJ.png' }); const row = new ActionRowBuilder() .addComponents( new ButtonBuilder() .setCustomId('join_queue') .setLabel('Join Queue') .setStyle(ButtonStyle.Success) .setEmoji('✅'), new ButtonBuilder() .setCustomId('leave_queue') .setLabel('Leave Queue') .setStyle(ButtonStyle.Danger) .setEmoji('❌') ); await queueMessage.edit({ embeds: [embed], components: [row] }); } } catch (error) { console.error('Error updating queue message:', error); } }; const createMatch = async (guild) => { try { const matchCategory = guild.channels.cache.get(process.env.MATCH_CATEGORY_ID); const team1Voice = await guild.channels.create({ name: \`Match \${matchCounter} - Team 1\`, type: ChannelType.GuildVoice, parent: matchCategory, userLimit: 6, permissionOverwrites: [ { id: guild.roles.everyone.id, allow: [PermissionsBitField.Flags.Connect], }, ], }); const team2Voice = await guild.channels.create({ name: \`Match \${matchCounter} - Team 2\`, type: ChannelType.GuildVoice, parent: matchCategory, userLimit: 6, permissionOverwrites: [ { id: guild.roles.everyone.id, allow: [PermissionsBitField.Flags.Connect], }, ], }); const matchText = await guild.channels.create({ name: \`match-\${matchCounter}\`, type: ChannelType.GuildText, parent: matchCategory, permissionOverwrites: [ { id: guild.roles.everyone.id, allow: [PermissionsBitField.Flags.ViewChannel, PermissionsBitField.Flags.SendMessages], }, ], }); const team1Captain = queue.shift(); const team2Captain = queue.shift(); const remainingPlayers = queue.splice(0, 8); const randomMap = maps[Math.floor(Math.random() * maps.length)]; matches[matchCounter] = { team1: [team1Captain], team2: [team2Captain], remaining: remainingPlayers, pickingOrder: ['team1', 'team2', 'team2', 'team1', 'team1', 'team2', 'team1'], currentPick: 0, mapVotes: {}, chosenMap: randomMap, textChannelId: matchText.id }; saveMatchData(); console.log(\`Match \${matchCounter} created with map \${randomMap}\`); await updateQueueMessage(); await updateMatchMessage(matchCounter); setTimeout(async () => { if (!matches[matchCounter]) return; const mostVotedMap = Object.entries(matches[matchCounter].mapVotes) .reduce((max, entry) => entry[1] > max[1] ? entry : max, ['', 0])[0]; if (mostVotedMap) { matches[matchCounter].chosenMap = mostVotedMap; } await updateMatchMessage(matchCounter); }, 3 * 60 * 1000); matchCounter++; } catch (error) { console.error('Error creating match:', error); } }; const updateMatchMessage = async (matchNumber) => { try { const match = matches[matchNumber]; const matchText = client.channels.cache.get(match.textChannelId); if (!matchText) return; const team1 = match.team1.map(id => \`<@${id}>\`).join('\n') || 'No players yet'; const team2 = match.team2.map(id => \`<@${id}>\`).join('\n') || 'No players yet'; const embed = new EmbedBuilder() .setTitle(\`🎮 Match \${matchNumber} 🎮\`) .setColor('#ff4500') .setDescription('**Match Details**') .addFields( { name: '🏆 Team 1 Captain', value: \`<@${match.team1[0]}>\`, inline: true }, { name: '🏆 Team 2 Captain', value: \`<@${match.team2[0]}>\`, inline: true }, { name: '\u200B', value: '\u200B', inline: true }, { name: '👥 Team 1', value: team1, inline: true }, { name: '👥 Team 2', value: team2, inline: true }, { name: '🗺️ Map', value: \`**${match.chosenMap}**\`, inline: true } ) .setThumbnail('https://i.imgur.com/xfe96CL.png') .setImage('https://i.imgur.com/jyuglDa.png') .setFooter({ text: 'Powered by Maverick', iconURL: 'https://i.imgur.com/wTF3RuJ.png' }) .setTimestamp(); const playerPickRows = []; for (let i = 0; i < match.remaining.length; i += 5) { const row = new ActionRowBuilder() .addComponents( await Promise.all( match.remaining.slice(i, i + 5).map(async playerId => { const member = await client.guilds.cache.get(matchText.guild.id).members.fetch(playerId); const nickname = member.nickname || member.user.username; return new ButtonBuilder() .setCustomId(\`pick_\${matchNumber}_\${playerId}\`) .setLabel(\`Pick \${nickname}\`) .setStyle(ButtonStyle.Primary); }) ) ); playerPickRows.push(row); } const mapVoteRows = []; for (let i = 0; i < maps.length; i += 5) { const row = new ActionRowBuilder() .addComponents( maps.slice(i, i + 5).map(map => new ButtonBuilder() .setCustomId(\`map_vote_\${matchNumber}_\${map}\`) .setLabel(\`\${map} (\${match.mapVotes[map] || 0})\`) .setStyle(ButtonStyle.Secondary) ) ); mapVoteRows.push(row); } const messages = await matchText.messages.fetch({ limit: 10 }); const matchMessage = messages.find(msg => msg.embeds[0]?.title === \`🎮 Match \${matchNumber} 🎮\`); if (matchMessage) { try { await matchMessage.delete(); } catch (error) { console.error(\`Failed to delete message: \${error.message}\`); } } await matchText.send({ embeds: [embed], components: [...playerPickRows, ...mapVoteRows] }); } catch (error) { console.error('Error updating match message:', error); } }; const handleButtonInteraction = async (interaction) => { try { const customId = interaction.customId; if (customId.startsWith('map_vote_')) { await voteMap(interaction); } else if (customId.startsWith('join_queue')) { await joinQueue(interaction); } else if (customId.startsWith('leave_queue')) { await leaveQueue(interaction); } else if (customId.startsWith('pick_')) { await handlePick(interaction); } else { await interaction.reply({ content: 'Unknown button interaction.', ephemeral: true }); } } catch (error) { console.error('Error handling button interaction:', error); await interaction.reply({ content: 'There was an error handling this button interaction.', ephemeral: true }); } }; const handlePick = async (interaction) => { try { const [_, matchNumber, playerId] = interaction.customId.split('_'); const match = matches[matchNumber]; if (!match) { await interaction.reply({ content: \`Match \${matchNumber} not found.\`, ephemeral: true }); return; } const user = interaction.user; const currentPickTeam = match.pickingOrder[match.currentPick]; const isCaptain = (user.id === match.team1[0] && currentPickTeam === 'team1') || (user.id === match.team2[0] && currentPickTeam === 'team2'); if (!isCaptain) { await interaction.reply({ content: 'Only the respective captain can pick for their team on their turn.', ephemeral: true }); return; } if (!match.remaining.includes(playerId)) { await interaction.reply({ content: 'Invalid player ID for picking.', ephemeral: true }); return; } const team = match.pickingOrder[match.currentPick]; match[team].push(playerId); match.remaining = match.remaining.filter(id => id !== playerId); match.currentPick++; if (match.currentPick === match.pickingOrder.length && match.remaining.length === 1) { match.team2.push(match.remaining[0]); match.remaining = []; } await updateMatchMessage(matchNumber); await interaction.reply({ content: \`Player <@${playerId}> has been picked for \${team === 'team1' ? 'Team 1' : 'Team 2'}.\`, ephemeral: true }); } catch (error) { console.error('Error handling pick:', error); await interaction.reply({ content: 'There was an error handling the pick.', ephemeral: true }); } }; const voteMap = async (interaction) => { try { const [_, matchNumber, map] = interaction.customId.split('_'); const userId = interaction.user.id; if (!matchNumber || isNaN(matchNumber) || matchNumber < 1) { console.error(\`Invalid matchNumber: \${matchNumber}\`); await interaction.reply({ content: 'Invalid match number.', ephemeral: true }); return; } const match = matches[matchNumber]; if (!match) { console.error(\`Match \${matchNumber} not found.\`); await interaction.reply({ content: \`Match \${matchNumber} not found.\`, ephemeral: true }); return; } if (!match.mapVotes) match.mapVotes = {}; if (match.mapVotes[map] && match.mapVotes[map].includes(userId)) { await interaction.reply({ content: 'You have already voted for this map.', ephemeral: true }); return; } for (const m in match.mapVotes) { match.mapVotes[m] = match.mapVotes[m].filter(id => id !== userId); } if (!match.mapVotes[map]) match.mapVotes[map] = []; match.mapVotes[map].push(userId); const mostVotedMap = Object.entries(match.mapVotes).reduce((max, entry) => entry[1].length > max[1].length ? entry : max, ['', []])[0]; match.chosenMap = mostVotedMap; await interaction.reply({ content: \`Your vote for \${map} has been recorded!\`, ephemeral: true }); await updateMatchMessage(matchNumber); } catch (error) { console.error('Error voting map:', error); await interaction.reply({ content: 'There was an error voting for the map.', ephemeral: true }); } }; const findUserMatch = (userId) => { for (const [id, match] of Object.entries(matches)) { if (match.team1.includes(userId) || match.team2.includes(userId) || match.remaining.includes(userId)) { return id; } } return null; }; client.login(process.env.DISCORD_TOKEN); EOL # Create command files # Admin Commands cat <<EOL > your-bot/commands/admin/clearQueue.js export const clearQueueCommand = async (message) => { if (!message.member.permissions.has(PermissionsBitField.Flags.Administrator)) { return message.reply('You do not have permission to use this command.'); } try { queue = []; await updateQueueMessage(); message.reply('The queue has been cleared.'); } catch (error) { console.error('Error clearing queue:', error); message.reply('There was an error clearing the queue.'); } }; EOL cat <<EOL > your-bot/commands/admin/giveElo.js export const giveEloCommand = async (message, args) => { if (!message.member.permissions.has(PermissionsBitField.Flags.Administrator)) { return message.reply('You do not have permission to use this command.'); } try { const userId = args[0].replace('<@', '').replace('>', ''); const eloChange = parseInt(args[1], 10); if (!playersELO[userId]) playersELO[userId] = 0; playersELO[userId] += eloChange; saveData(); message.reply(\`ELO for <@${userId}> has been changed by \${eloChange}. New ELO: \${playersELO[userId]}\`); } catch (error) { console.error('Error giving ELO:', error); message.reply('There was an error changing the ELO.'); } }; EOL cat <<EOL > your-bot/commands/admin/resetElo.js export const resetEloCommand = async (message) => { if (!message.member.permissions.has(PermissionsBitField.Flags.Administrator)) { return message.reply('You do not have permission to use this command.'); } try { playersELO = {}; saveData(); message.reply('All ELO has been reset.'); } catch (error) { console.error('Error resetting ELO:', error); message.reply('There was an error resetting the ELO.'); } }; EOL cat <<EOL > your-bot/commands/admin/resetWarnings.js export const warningResetCommand = async (message) => { if (!message.member.permissions.has(PermissionsBitField.Flags.Administrator)) { return message.reply('You do not have permission to use this command.'); } try { playerWarnings = {}; saveData(); message.reply('All warnings have been reset.'); } catch (error) { console.error('Error resetting warnings:', error); message.reply('There was an error resetting the warnings.'); } }; EOL # General Commands cat <<EOL > your-bot/commands/general/profile.js export const profileCommand = async (message, args) => { const userId = args[0] ? args[0].replace('<@', '').replace('>', '') : message.author.id; const user = await message.guild.members.fetch(userId); const username = user.nickname || user.user.username; const elo = playersELO[userId] || 0; const userKarma = karma[userId] || 0; const stats = playerStats[userId] || {}; const defaultMapStats = { wins: 0, losses: 0 }; const maps = { BW: stats.BW || defaultMapStats, PORT: stats.PORT || defaultMapStats, ANK: stats.ANK || defaultMapStats, COMP: stats.COMP || defaultMapStats, SB: stats.SB || defaultMapStats, MEX: stats.MEX || defaultMapStats, EE: stats.EE || defaultMapStats }; const totalWins = Object.values(maps).reduce((acc, map) => acc + map.wins, 0); const totalLosses = Object.values(maps).reduce((acc, map) => acc + map.losses, 0); const embed = new EmbedBuilder() .setTitle(\`\${username}'s Profile\`) .setThumbnail(user.user.displayAvatarURL({ dynamic: true })) .addFields( { name: 'ELO', value: elo.toString(), inline: true }, { name: 'Karma', value: userKarma.toString(), inline: true }, { name: '\u200B', value: '\u200B', inline: true } ) .addFields( { name: 'Map Stats:', value: '\u200B' }, { name: 'Black Widow', value: \`\${maps.BW.wins} wins \${maps.BW.losses} losses\`, inline: true }, { name: 'Port', value: \`\${maps.PORT.wins} wins \${maps.PORT.losses} losses\`, inline: true }, { name: 'Ankara', value: \`\${maps.ANK.wins} wins \${maps.ANK.losses} losses\`, inline: true }, { name: 'Compound', value: \`\${maps.COMP.wins} wins \${maps.COMP.losses} losses\`, inline: true }, { name: 'Sub Base', value: \`\${maps.SB.wins} wins \${maps.SB.losses} losses\`, inline: true }, { name: 'Mexico', value: \`\${maps.MEX.wins} wins \${maps.MEX.losses} losses\`, inline: true }, { name: 'Eagle Eye', value: \`\${maps.EE.wins} wins \${maps.EE.losses} losses\`, inline: true }, { name: 'Total Wins', value: totalWins.toString(), inline: true }, { name: 'Total Losses', value: totalLosses.toString(), inline: true } ) .setColor('#ff4500') .setFooter({ text: 'FPS Game Stats', iconURL: 'https://i.imgur.com/wTF3RuJ.png' }) .setTimestamp(); message.reply({ embeds: [embed] }); }; EOL cat <<EOL > your-bot/commands/general/leaderboard.js export const leaderboardCommand = async (message) => { try { const sortedPlayers = Object.entries(playersELO).sort((a, b) => b[1] - a[1]).slice(0, 25); const leaderboard = await Promise.all( sortedPlayers.map(async ([userId, elo], index) => { const user = await message.guild.members.fetch(userId).catch(() => null); return \`#\${index + 1} | \${elo} | \${user ? \`<@${user.id}>\` : \`Unknown User\`}\`; }) ); if (leaderboard.length === 0) { leaderboard.push('No players available'); } const embed = new EmbedBuilder() .setColor('#00ff26') .setTitle('Top 25 Pug Players') .addFields({ name: 'Rank | Elo | Player', value: leaderboard.join('\n') }) .setFooter({ text: 'Use .lb or .leaderboard to see the leaderboard at any time', iconURL: 'https://i.imgur.com/wTF3RuJ.png' }) .setTimestamp(); message.reply({ embeds: [embed] }); } catch (error) { console.error('Error fetching leaderboard:', error); message.reply('There was an error fetching the leaderboard.'); } }; EOL cat <<EOL > your-bot/commands/general/elo.js export const eloCommand = async (message, args) => { try { const userId = args[0] ? args[0].replace('<@', '').replace('>', '') : message.author.id; const elo = playersELO[userId] || 0; message.reply(\`<@${userId}> has \${elo} ELO.\`); } catch (error) { console.error('Error fetching ELO:', error); message.reply('There was an error fetching the ELO.'); } }; EOL # Match Commands cat <<EOL > your-bot/commands/match/cancelMatch.js export const cancelMatchCommand = async (message, args) => { const roleIds = ['1075756565023445022', '1258877265870192673']; // Array of role IDs const hasAdminPermission = message.member.permissions.has(PermissionsBitField.Flags.Administrator); const hasRequiredRole = roleIds.some(roleId => message.member.roles.cache.has(roleId)); const hasPermission = hasAdminPermission || hasRequiredRole; // Check for permissions let matchId = Object.keys(matches).find(id => matches[id].textChannelId === message.channel.id); if (!matchId && hasPermission) { matchId = args[0]; } if (!matchId || !matches[matchId]) { try { await message.reply('Invalid match ID or no active match found.'); } catch (error) { console.error('Error sending reply:', error); } return; } if (!hasPermission) { // Check if user has the necessary permissions try { await message.reply('You do not have permission to cancel matches.'); } catch (error) { console.error('Error sending reply:', error); } return; } try { const match = matches[matchId]; delete matches[matchId]; saveMatchData(); const guild = message.guild; const matchCategory = await guild.channels.fetch(process.env.MATCH_CATEGORY_ID); // Fetch channels from cache or fetch from API if not cached const team1Voice = guild.channels.cache.get(match.team1VoiceChannelId) || await guild.channels.fetch(match.team1VoiceChannelId); const team2Voice = guild.channels.cache.get(match.team2VoiceChannelId) || await guild.channels.fetch(match.team2VoiceChannelId); const matchText = guild.channels.cache.get(match.textChannelId) || await guild.channels.fetch(match.textChannelId); if (team1Voice) await team1Voice.delete(); if (team2Voice) await team2Voice.delete(); if (matchText) await matchText.delete(); await message.reply(\`Match \${matchId} has been canceled.\`); } catch (error) { console.error('Error canceling match:', error); try { await message.reply('There was an error canceling the match.'); } catch (replyError) { console.error('Error sending error reply:', replyError); } } }; EOL cat <<EOL > your-bot/commands/match/needSub.js export const needSubCommand = async (message, args) => { const roles = ['1255072045574652035', '1075756565023445022', '1258877265870192673']; if (!message.member.permissions.has(PermissionsBitField.Flags.Administrator) && !roles.some(role => message.member.roles.cache.has(role))) { return message.reply('You do not have permission to use this command.'); } try { if (!args[0] || !args[0].match(/^<@!?(\d+)>$/)) { return message.reply('Please mention a valid user to sub.'); } const userId = message.author.id; const playerId = args[0].replace('<@', '').replace('>', ''); const matchId = findUserMatch(userId); if (!matchId) { return message.reply('You are not part of any active matches.'); } const match = matches[matchId]; const team = match.team1.includes(playerId) ? 'team1' : 'team2'; const playerIndex = match[team].indexOf(playerId); if (playerIndex === -1) { return message.reply('Player not found in any team.'); } const subMessage = await message.channel.send(\`<@${playerId}> has requested a substitution. React with ❌ within 2 minutes if you are still here.\`); await subMessage.react('❌'); const filter = (reaction, user) => reaction.emoji.name === '❌' && user.id === playerId; const collector = subMessage.createReactionCollector({ filter, time: 120000 }); collector.on('collect', () => { subMessage.edit(\`<@${playerId}> is still active.\`); collector.stop(); }); collector.on('end', async collected => { if (collected.size === 0) { const newPlayerId = queue.shift(); if (!newPlayerId) { return message.channel.send('No available substitutes in the queue.'); } match[team][playerIndex] = newPlayerId; await updateMatchMessage(matchId); message.channel.send(\`<@${playerId}> has been substituted by <@${newPlayerId}> in \${team === 'team1' ? 'Team 1' : 'Team 2'}.\`); } }); } catch (error) { console.error('Error handling substitution:', error); message.reply('There was an error handling the substitution.'); } }; EOL cat <<EOL > your-bot/commands/match/pick.js export const pickCommand = async (message, args) => { try { if (!args[0] || !args[0].match(/^<@!?(\d+)>$/)) { return message.reply('Please mention a valid user to pick.'); } const playerId = args[0].replace('<@', '').replace('>', ''); const userId = message.author.id; const matchId = findUserMatch(userId); if (!matchId) { return message.reply('You are not part of any active matches.'); } const match = matches[matchId]; const currentPickIndex = match.team1.length + match.team2.length - 2; if (currentPickIndex >= match.pickingOrder.length) { return message.reply('All picks have already been made.'); } const pickingTeam = match.pickingOrder[currentPickIndex] === 'team1' ? 'team1' : 'team2'; if (!match.remaining.includes(playerId)) { return message.reply('Invalid player ID for picking.'); } match[pickingTeam].push(playerId); match.remaining = match.remaining.filter(id => id !== playerId); await updateMatchMessage(matchId); message.reply(\`Player <@${playerId}> picked for \${pickingTeam === 'team1' ? 'Team 1' : 'Team 2'}.\`); } catch (error) { console.error('Error picking player:', error); message.reply('There was an error picking the player.'); } }; EOL cat <<EOL > your-bot/commands/match/pt.js export const ptCommand = async (message, args) => { const roles = [PermissionsBitField.Flags.Administrator, '1075756565023445022', '1258877265870192673']; if (!message.member.permissions.has(PermissionsBitField.Flags.Administrator) && !message.member.roles.cache.has(roles[1]) && !message.member.roles.cache.has(roles[2])) { return message.reply('You do not have permission to use this command.'); } try { if (args.length < 1) { return message.reply('Please specify the team number (1, 2, or 0) and an optional message.'); } const userId = message.author.id; const teamNumber = parseInt(args[0]); if (isNaN(teamNumber) || (teamNumber < 0 || teamNumber > 2)) { return message.reply('Invalid team number. Use 1 for Team 1, 2 for Team 2, or 0 for both teams.'); } const additionalMessage = args.slice(1).join(' '); let matchId = Object.keys(matches).find(id => matches[id].textChannelId === message.channel.id); if (!matchId) { return message.reply('You are not part of any active matches.'); } const match = matches[matchId]; let teamMentions = ''; if (teamNumber === 0) { const allTeams = [...match.team1, ...match.team2]; teamMentions = allTeams.map(id => \`<@${id}>\`).join(' '); } else if (teamNumber === 1) { teamMentions = match.team1.map(id => \`<@${id}>\`).join(' '); } else if (teamNumber === 2) { teamMentions = match.team2.map(id => \`<@${id}>\`).join(' '); } const finalMessage = \`\${teamMentions} \${additionalMessage}\`; try { const matchCategory = await message.client.channels.fetch(process.env.MATCH_CATEGORY_ID); const matchText = matchCategory.children.cache.find(c => c.name === \`match-\${matchId}\`); if (!matchText) { return message.reply('Match text channel not found.'); } await matchText.send(finalMessage); message.reply('Message sent.'); } catch (error) { console.error('Error sending message:', error); message.reply('There was an error sending the message. Please try again later.'); } } catch (error) { console.error('Error in PT command:', error); message.reply('There was an error executing the PT command.'); } }; EOL cat <<EOL > your-bot/commands/match/report.js export const reportCommand = async (message, args) => { const roleIds = ['1075756565023445022', '1258877265870192673']; // Array of role IDs const hasAdminPermission = message.member.permissions.has(PermissionsBitField.Flags.Administrator); const hasRequiredRole = roleIds.some(roleId => message.member.roles.cache.has(roleId)); const hasPermission = hasAdminPermission || hasRequiredRole; // Check for permissions let matchId = Object.keys(matches).find(id => matches[id].textChannelId === message.channel.id); if (!matchId && hasPermission) { matchId = args[0]; } if (!matchId || !matches[matchId]) { return message.reply('Invalid match ID or no active match found.'); } try { const winningTeamNumber = hasPermission ? args[1] : args[0]; const match = matches[matchId]; const team1 = match.team1; const team2 = match.team2; let winningTeam, losingTeam; if (winningTeamNumber === '1') { winningTeam = team1; losingTeam = team2; updateElo(team1, team2); updateStats(team1, team2, match.chosenMap, true); } else if (winningTeamNumber === '2') { winningTeam = team2; losingTeam = team1; updateElo(team2, team1); updateStats(team2, team1, match.chosenMap, true); } else { return message.reply('Invalid winning team.'); } await message.reply(\`Match \${matchId} has been reported and the channels will be deleted.\`); const guild = message.guild; const matchCategory = await guild.channels.fetch(process.env.MATCH_CATEGORY_ID); const team1Voice = matchCategory.children.cache.find(c => c.name === \`Match \${matchId} - Team 1\`); const team2Voice = matchCategory.children.cache.find(c => c.name === \`Match \${matchId} - Team 2\`); const matchText = matchCategory.children.cache.find(c => c.name === \`match-\${matchId}\`); if (team1Voice) await team1Voice.delete(); if (team2Voice) await team2Voice.delete(); if (matchText) await matchText.delete(); delete matches[matchId]; saveMatchData(); const reporter = message.author; logMatch(matchId, winningTeam, losingTeam, \`Team \${winningTeamNumber}\`, reporter); } catch (error) { console.error('Error reporting match:', error); message.reply('There was an error reporting the match.'); } }; EOL cat <<EOL > your-bot/commands/match/replace.js export const replaceCommand = async (message, args) => { const roleIds = ['1075756565023445022', '1258877265870192673']; // Array of role IDs const hasAdminPermission = message.member.permissions.has(PermissionsBitField.Flags.Administrator); const hasRequiredRole = roleIds.some(roleId => message.member.roles.cache.has(roleId)); const hasPermission = hasAdminPermission || hasRequiredRole; // Check for permissions let matchId = Object.keys(matches).find(id => matches[id].textChannelId === message.channel.id); if (!matchId && hasPermission) { matchId = args[2]; } if (!matchId || !matches[matchId]) { return message.reply('Invalid match ID or no active match found.'); } if (!hasPermission) { // Check if user has the necessary permissions return message.reply('You do not have permission to replace players.'); } try { if (args.length < 2 || !args[0].match(/^<@!?(\d+)>$/) || !args[1].match(/^<@!?(\d+)>$/)) { return message.reply('Please mention the old player and the new player in the correct format.'); } const oldPlayerId = args[0].replace('<@', '').replace('>', '').replace('!', ''); const newPlayerId = args[1].replace('<@', '').replace('>', '').replace('!', ''); const match = matches[matchId]; const team = match.team1.includes(oldPlayerId) ? 'team1' : match.team2.includes(oldPlayerId) ? 'team2' : null; if (!team) { return message.reply('Old player is not found in any team.'); } const playerIndex = match[team].indexOf(oldPlayerId); if (playerIndex === -1) { return message.reply('Old player not found in the team.'); } match[team][playerIndex] = newPlayerId; await updateMatchMessage(matchId); message.reply(\`Player <@${oldPlayerId}> has been replaced by <@${newPlayerId}> in \${team === 'team1' ? 'Team 1' : 'Team 2'}.\`); } catch (error) { console.error('Error replacing player:', error); message.reply('There was an error replacing the player.'); } }; EOL cat <<EOL > your-bot/commands/match/suspendUser.js export const suspendUserCommand = async (message, args) => { try { const userId = args[0].replace('<@', '').replace('>', ''); const duration = parseInt(args[1]); const reason = args.slice(2).join(' '); if (!karma[userId]) karma[userId] = {}; karma[userId].suspendedUntil = Date.now() + duration * 60000; karma[userId].reason = reason; saveData(); message.reply(\`<@${userId}> has been suspended for \${duration} minutes. Reason: \${reason}\`); } catch (error) { console.error('Error suspending user:', error); message.reply('There was an error suspending the user.'); } }; EOL cat <<EOL > your-bot/commands/match/unSuspendUser.js export const unSuspendUserCommand = async (message, args) => { const roles = [PermissionsBitField.Flags.Administrator, '1075756565023445022']; if (!message.member.permissions.has(PermissionsBitField.Flags.Administrator) && !message.member.roles.cache.has(roles[1])) { return message.reply('You do not have permission to use this command.'); } try { const userId = args[0].replace('<@', '').replace('>', ''); if (karma[userId] && karma[userId].suspendedUntil) { delete karma[userId].suspendedUntil; delete karma[userId].reason; saveData(); message.reply(\`<@${userId}>'s suspension has been lifted.\`); } else { message.reply(\`<@${userId}> is not currently suspended.\`); } } catch (error) { console.error('Error unsuspending user:', error); message.reply('There was an error lifting the suspension.'); } }; EOL # Warning Commands cat <<EOL > your-bot/commands/warning/warn.js export const warnCommand = async (message, args) => { const roles = [PermissionsBitField.Flags.Administrator, '1075756565023445022']; if (!message.member.permissions.has(PermissionsBitField.Flags.Administrator) && !message.member.roles.cache.has(roles[1])) { return message.reply('You do not have permission to use this command.'); } try { if (!args[0] || !args[0].match(/^<@!?(\d+)>$/)) { return message.reply('Please mention a valid user to warn.'); } const userId = args[0].replace('<@', '').replace('>', ''); const reason = args.slice(1).join(' ') || 'No reason provided'; if (!playerWarnings[userId]) { playerWarnings[userId] = { count: 0, reasons: [] }; } playerWarnings[userId].count += 1; playerWarnings[userId].reasons.push(reason); saveData(); const embed = new EmbedBuilder() .setColor('#ff4500') .setTitle('User Warned') .addFields( { name: 'User', value: \`<@${userId}>\`, inline: true }, { name: 'Warnings', value: playerWarnings[userId].count.toString(), inline: true }, { name: 'Reason', value: reason, inline: true } ) .setThumbnail('https://i.imgur.com/wTF3RuJ.png') .setFooter({ text: 'Stay in line, soldier!', iconURL: 'https://i.imgur.com/wTF3RuJ.png' }) .setTimestamp(); message.reply({ embeds: [embed] }); } catch (error) { console.error('Error warning user:', error); message.reply('There was an error warning the user.'); } }; EOL cat <<EOL > your-bot/commands/warning/warns.js export const warnsCommand = async (message, args) => { const roles = [PermissionsBitField.Flags.Administrator, '1075756565023445022']; if (!message.member.permissions.has(PermissionsBitField.Flags.Administrator) && !message.member.roles.cache.has(roles[1])) { return message.reply('You do not have permission to use this command.'); } try { const userId = args[0] ? args[0].replace('<@', '').replace('>', '') : message.author.id; const userWarnings = playerWarnings[userId] ? playerWarnings[userId].count : 0; const reasons = playerWarnings[userId] ? playerWarnings[userId].reasons : []; const username = message.mentions.users.first() ? message.mentions.users.first().username : message.author.username; const embed = new EmbedBuilder() .setColor('#ff4500') .setTitle(\`\${username}'s Warnings\`) .addFields( { name: 'Warnings', value: userWarnings.toString(), inline: true }, { name: 'Reasons', value: reasons.length > 0 ? reasons.join('\n') : 'No warnings', inline: true } ) .setThumbnail('https://i.imgur.com/wTF3RuJ.png') .setFooter({ text: 'Stay in line, soldier!', iconURL: 'https://i.imgur.com/wTF3RuJ.png' }) .setTimestamp(); message.reply({ embeds: [embed] }); } catch (error) { console.error('Error fetching warnings:', error); message.reply('There was an error fetching the warnings.'); } }; EOL # Load and register commands cat <<EOL > your-bot/loadCommands.js import { clearQueueCommand } from './commands/admin/clearQueue.js'; import { giveEloCommand } from './commands/admin/giveElo.js'; import { resetEloCommand } from './commands/admin/resetElo.js'; import { warningResetCommand } from './commands/admin/resetWarnings.js'; import { profileCommand } from './commands/general/profile.js'; import { leaderboardCommand } from './commands/general/leaderboard.js'; import { eloCommand } from './commands/general/elo.js'; import { cancelMatchCommand } from './commands/match/cancelMatch.js'; import { needSubCommand } from './commands/match/needSub.js'; import { pickCommand } from './commands/match/pick.js'; import { ptCommand } from './commands/match/pt.js'; import { reportCommand } from './commands/match/report.js'; import { replaceCommand } from './commands/match/replace.js'; import { suspendUserCommand } from './commands/match/suspendUser.js'; import { unSuspendUserCommand } from './commands/match/unSuspendUser.js'; import { warnCommand } from './commands/warning/warn.js'; import { warnsCommand } from './commands/warning/warns.js'; const commandMap = { 'clearqueue': clearQueueCommand, 'giveelo': giveEloCommand, 'resetelo': resetEloCommand, 'resetwarnings': warningResetCommand, 'profile': profileCommand, 'leaderboard': leaderboardCommand, 'elo': eloCommand, 'cancelmatch': cancelMatchCommand, 'needsub': needSubCommand, 'pick': pickCommand, 'pt': ptCommand, 'report': reportCommand, 'replace': replaceCommand, 'suspenduser': suspendUserCommand, 'unsuspenduser': unSuspendUserCommand, 'warn': warnCommand, 'warns': warnsCommand }; export const loadCommands = async (command, message, args) => { if (commandMap[command]) { await commandMap[command](message, args); } else { message.reply('Unknown command.'); } }; EOL cat <<EOL > your-bot/registerCommands.js export const registerCommands = (client) => { const commands = [ { name: 'setupreaction', description: 'Recreates the reaction message to join the queue', type: 1, }, { name: 'eloreset', description: 'Resets all ELO, must be superuser', type: 1, }, { name: 'warningreset', description: 'Resets all warnings, must be admin', type: 1, }, { name: 'leaderboard', description: 'Leaderboard in the stats channel', type: 1, } ]; const guild = client.guilds.cache.first(); guild.commands.set(commands).then(() => console.log('Commands registered')); }; EOL # Install dependencies npm install EOL echo "Setup completed successfully. You can now navigate to the 'your-bot' directory, add your .env values, and run 'npm start' to start your bot."
Comments