const { SlashCommandBuilder, ModalBuilder, TextInputStyle, TextInputBuilder, ActionRowBuilder, ChannelType, PermissionsBitField, ButtonBuilder, ButtonStyle, ComponentType } = require('discord.js'); const { simpleEmbed } = require('../functions/embeds.js'); const { Users, Team } = require('../functions/models.js'); const { applyUsername } = require('../functions/utils.js'); const { client } = require('../index.js'); module.exports = { data: new SlashCommandBuilder() .setName('team') .setDescription('Team command') .addSubcommand(subcommand => subcommand .setName('create') .setDescription('Create a team')) .addSubcommand(subcommand => subcommand .setName('invite') .setDescription('Invite an user') .addUserOption(option => option .setName('user') .setDescription('The user to invite') .setRequired(true))) .addSubcommand(subcommand => subcommand .setName('leave') .setDescription('Leave your team')), async execute(interaction) { const user = await Users.findOne({ where: { id: interaction.user.id } }); if (!user) return await interaction.reply({ embeds: [simpleEmbed('Error while getting user information')], ephemeral: true }); if (interaction.options.getSubcommand() === 'create') { if (user.teamId) return await interaction.reply({ embeds: [simpleEmbed('You are already in a team')], ephemeral: true }); const modal = new ModalBuilder() .setCustomId('teamCreate') .setTitle('Create team'); const nameInput = new TextInputBuilder() .setCustomId('nameInput') .setLabel('Team name') .setMinLength(3) .setMaxLength(16) .setStyle(TextInputStyle.Short); const colorInput = new TextInputBuilder() .setCustomId('colorInput') .setLabel('Team color (hex)') .setStyle(TextInputStyle.Short) .setPlaceholder('#00ff00'); const firstActionRow = new ActionRowBuilder().addComponents(nameInput); const secondActionRow = new ActionRowBuilder().addComponents(colorInput); modal.addComponents(firstActionRow, secondActionRow); await interaction.showModal(modal); try { const filter = (filterInteraction) => filterInteraction.customId === 'teamCreate'; const modalInteraction = await interaction.awaitModalSubmit({ filter, time: 10 * 60 * 1000 }); const teamName = modalInteraction.fields.getTextInputValue('nameInput'); const teamColor = modalInteraction.fields.getTextInputValue('colorInput'); if (!/^[a-zA-Z0-9]+$/.test(teamName)) return await modalInteraction.reply({ embeds: [simpleEmbed('Team name can only include alphanumeric characters')], ephemeral: true }); if (!/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(teamColor)) return await modalInteraction.reply({ embeds: [simpleEmbed('Team color must be a valid hex code. [Color Picker](https://htmlcolorcodes.com/color-picker/)')], ephemeral: true }); const guild = await client.guilds.fetch(process.env.GUILD_ID); const category = await guild.channels.fetch(process.env.TEAM_CATEGORY_ID); const textChannel = await guild.channels.create({ name: teamName, type: ChannelType.GuildText, parent: category, permissionOverwrites: [ { id: guild.id, deny: [PermissionsBitField.Flags.ViewChannel], }, { id: interaction.member.id, allow: [PermissionsBitField.Flags.ViewChannel], }, ], }); const voiceChannel = await guild.channels.create({ name: teamName, type: ChannelType.GuildVoice, parent: category, permissionOverwrites: [ { id: guild.id, deny: [PermissionsBitField.Flags.ViewChannel], }, { id: interaction.member.id, allow: [PermissionsBitField.Flags.ViewChannel], }, ], }); const team = await Team.create({ name: teamName, color: teamColor, textChannelId: textChannel.id, voiceChannelId: voiceChannel.id, }); await team.addMembers([ user ]); await applyUsername(user, interaction.member); await modalInteraction.reply({ embeds: [simpleEmbed(`Successfully created team **${teamName}**`)], ephemeral: true }); } catch (error) { console.error(error); } } else if (interaction.options.getSubcommand() === 'leave') { if (!user.teamId) return await interaction.reply({ embeds: [simpleEmbed('You are not in a team')], ephemeral: true }); const team = await user.getTeam(); await team.removeMember(user); const guild = await client.guilds.fetch(process.env.GUILD_ID); const textChannel = await guild.channels.fetch(team.textChannelId); const voiceChannel = await guild.channels.fetch(team.voiceChannelId); if (await team.countMembers() <= 0) { await textChannel.delete(); await voiceChannel.delete(); await team.destroy(); } else { await textChannel.permissionOverwrites.delete(interaction.member); await voiceChannel.permissionOverwrites.delete(interaction.member); } await applyUsername(user, interaction.member); await interaction.reply({ embeds: [simpleEmbed('Successfully left team')], ephemeral: true }); } else if (interaction.options.getSubcommand() === 'invite') { if (!user.teamId) return await interaction.reply({ embeds: [simpleEmbed('You are not in a team')], ephemeral: true }); const invitedUser = interaction.options.getUser('user'); if (interaction.user.id === invitedUser.id) return await interaction.reply({ embeds: [simpleEmbed('You cannot invite yourself')], ephemeral: true }); const invitedUserInstance = await Users.findOne({ where: { id: invitedUser.id } }); if (invitedUserInstance.teamId) return await interaction.reply({ embeds: [simpleEmbed('The user you are trying already is in a team')], ephemeral: true }); const team = await user.getTeam(); const acceptButton = new ButtonBuilder() .setCustomId('accept') .setLabel('Accept') .setStyle(ButtonStyle.Success); const denyButton = new ButtonBuilder() .setCustomId('deny') .setLabel('Deny') .setStyle(ButtonStyle.Danger); const row = new ActionRowBuilder() .addComponents(acceptButton, denyButton); const inviteMessage = await invitedUser.send({ embeds: [simpleEmbed(`**${user.rawUsername}** invited you to join team **${team.name}**`)], components: [row], }); await interaction.reply({ embeds: [simpleEmbed(`Successfully invited ${invitedUser} to your team`)], ephemeral: true }); const collector = inviteMessage.createMessageComponentCollector({ componentType: ComponentType.Button, time: 60 * 60 * 1000 }); collector.on('collect', async i => { try { if (i.customId === 'accept') { await invitedUserInstance.reload(); if (invitedUserInstance.teamId) return await i.message.edit({ embeds: [simpleEmbed('You are already in a team')], components: [] }); await team.addMember(i.user.id); const guild = await client.guilds.fetch(process.env.GUILD_ID); const member = await guild.members.fetch(i.user.id); const textChannel = await guild.channels.fetch(team.textChannelId); const voiceChannel = await guild.channels.fetch(team.voiceChannelId); await textChannel.permissionOverwrites.edit(member, { ViewChannel: true }); await voiceChannel.permissionOverwrites.edit(member, { ViewChannel: true }); await applyUsername(invitedUserInstance, member); await i.message.edit({ embeds: [simpleEmbed(`Successfully joined team **${team.name}**`)], components: [] }); } else if (i.customId === 'deny') { await i.message.edit({ embeds: [simpleEmbed(`Successfully denied the request to join team **${team.name}**`)], components: [] }); } } catch (error) { console.error(error); } }); collector.on('end', async () => { inviteMessage.edit({ embeds: [simpleEmbed('Confirmation not received within 1 hour, cancelling')], components: [] }); }); } }, };