Untitled
const { Client, GatewayIntentBits, PermissionsBitField, ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, ModalBuilder, TextInputBuilder, TextInputStyle, SelectMenuBuilder } = require('discord.js'); const config = require('./config.json'); const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent, GatewayIntentBits.GuildMembers, ], }); // Map to store temporary channels and their creators const tempChannels = new Map(); // Map to store ticket channels and their creators const ticketChannels = new Map(); client.once('ready', async () => { console.log(`Logged in as ${client.user.tag}!`); // Find the "ticket" channel const ticketChannel = client.channels.cache.find(channel => channel.name === config.ticketChannel && channel.isTextBased()); if (!ticketChannel) { console.error(`Could not find the "${config.ticketChannel}" channel!`); } else { // Fetch the last 10 messages in the ticket channel const messages = await ticketChannel.messages.fetch({ limit: 10 }); // Check if the ticket creation embed already exists const existingEmbed = messages.find(msg => msg.embeds.length > 0 && msg.embeds[0].title === 'Support Ticket' && msg.components.length > 0 ); // If the embed doesn't exist, send it if (!existingEmbed) { // Create an embed with a select menu for ticket types const embed = new EmbedBuilder() .setTitle('Support Ticket') .setDescription('Select a ticket type from the menu below.') .setColor('#0099ff'); // Create a select menu for ticket types const selectMenu = new SelectMenuBuilder() .setCustomId('ticket_type') .setPlaceholder('Select a ticket type') .addOptions([ { label: 'Support', value: 'support', description: 'Get help with general issues.', emoji: '🛠️', }, { label: 'Donate', value: 'donate', description: 'Support us with a donation.', emoji: '💸', }, { label: 'Staff Report', value: 'staff_report', description: 'Report an issue with a staff member.', emoji: '🚨', }, { label: 'Other', value: 'other', description: 'For any other inquiries.', emoji: '❓', }, ]); const row = new ActionRowBuilder().addComponents(selectMenu); // Send the embed with the select menu await ticketChannel.send({ embeds: [embed], components: [row] }); } } // Find the "applications" channel const applicationsChannel = client.channels.cache.find(channel => channel.name === config.applicationsChannel && channel.isTextBased()); if (!applicationsChannel) { console.error(`Could not find the "${config.applicationsChannel}" channel!`); } else { // Fetch the last 10 messages in the applications channel const messages = await applicationsChannel.messages.fetch({ limit: 10 }); // Check if the applications embed already exists const existingEmbed = messages.find(msg => msg.embeds.length > 0 && msg.embeds[0].title === 'Applications' && msg.components.length > 0 ); // If the embed doesn't exist, send it if (!existingEmbed) { // Create an embed with buttons for each application const embed = new EmbedBuilder() .setTitle('Applications') .setDescription('Choose an application from the buttons below.') .setColor('#0099ff'); // Create buttons for Police Department, EMS, and Staff Application const buttons = config.applications.map(app => { if (app.formUrl) { // Link button for Police Department and EMS return new ButtonBuilder() .setLabel(app.name) .setStyle(ButtonStyle.Link) .setURL(app.formUrl); } else { // Regular button for Staff Application return new ButtonBuilder() .setCustomId(`apply_${app.name.toLowerCase().replace(/\s+/g, '_')}`) .setLabel(app.name) .setStyle(ButtonStyle.Primary); } }); const row = new ActionRowBuilder().addComponents(buttons); // Send the embed with the buttons await applicationsChannel.send({ embeds: [embed], components: [row] }); } } }); client.on('voiceStateUpdate', async (oldState, newState) => { const guild = newState.guild; const member = newState.member; // Check if the user joined the "Waiting For Support" channel if (newState.channelId === config.waitingForSupportChannelId) { // Find the role with "Manage Channels" permission const supportRole = guild.roles.cache.find(role => role.permissions.has(PermissionsBitField.Flags.ManageChannels)); if (!supportRole) { console.error('No role with "Manage Channels" permission found!'); return; } // Create a temporary voice channel const tempChannel = await guild.channels.create({ name: `support-${member.user.username}`, type: 2, // 2 = Voice Channel parent: newState.channel.parent, // Use the same category as the "Waiting For Support" channel permissionOverwrites: [ { id: guild.id, // Apply permissions to everyone deny: ['ViewChannel'], // Hide the channel by default }, { id: member.id, // Allow the user to view and connect allow: ['ViewChannel', 'Connect'], }, { id: supportRole.id, // Allow the support role to view and connect allow: ['ViewChannel', 'Connect'], }, ], }); // Store the temporary channel and its creator tempChannels.set(tempChannel.id, { creator: member.id, channel: tempChannel }); // Move the user to the temporary channel await member.voice.setChannel(tempChannel); console.log(`Created temporary voice channel: ${tempChannel.name}`); } // Check if the user left a temporary voice channel if (oldState.channelId && tempChannels.has(oldState.channelId)) { const tempChannel = guild.channels.cache.get(oldState.channelId); // Check if the channel is empty if (tempChannel.members.size === 0) { // Delete the temporary channel await tempChannel.delete(); tempChannels.delete(tempChannel.id); console.log(`Deleted temporary voice channel: ${tempChannel.name}`); } } }); client.on('interactionCreate', async (interaction) => { if (interaction.isSelectMenu()) { console.log(`Select menu clicked: ${interaction.customId}`); // Handle ticket type selection if (interaction.customId === 'ticket_type') { const ticketType = interaction.values[0]; // Get the selected value // Acknowledge the interaction immediately await interaction.deferReply({ ephemeral: true }); const guild = interaction.guild; const member = interaction.member; // Find the role with "Manage Channels" permission const supportRole = guild.roles.cache.find(role => role.permissions.has(PermissionsBitField.Flags.ManageChannels)); if (!supportRole) { await interaction.editReply({ content: 'No role with "Manage Channels" permission found!' }); return; } // Create a new ticket channel const ticketChannel = await guild.channels.create({ name: `ticket-${ticketType}-${member.user.username}`, type: 0, // 0 = Text Channel parent: interaction.channel.parent, // Use the same category as the "ticket" channel permissionOverwrites: [ { id: guild.id, // Apply permissions to everyone deny: ['ViewChannel'], // Hide the channel by default }, { id: member.id, // Allow the user to view and send messages allow: ['ViewChannel', 'SendMessages'], }, { id: supportRole.id, // Allow the support role to view and send messages allow: ['ViewChannel', 'SendMessages'], }, ], }); // Store the ticket channel and its creator ticketChannels.set(ticketChannel.id, { creator: member.id, channel: ticketChannel }); // Send a welcome message in the ticket channel with a "Close Ticket" button const welcomeEmbed = new EmbedBuilder() .setTitle(`${ticketType.charAt(0).toUpperCase() + ticketType.slice(1)} Ticket`) .setDescription(`Hello ${member.user.username}, how can we assist you today?`) .setColor('#0099ff'); const closeButton = new ButtonBuilder() .setCustomId('close_ticket') .setLabel('Close Ticket') .setStyle(ButtonStyle.Danger); const row = new ActionRowBuilder().addComponents(closeButton); await ticketChannel.send({ embeds: [welcomeEmbed], components: [row] }); // Reply to the interaction await interaction.editReply({ content: `Your ${ticketType} ticket has been created: ${ticketChannel}` }); console.log(`Created ${ticketType} ticket channel: ${ticketChannel.name}`); } } // Handle "Close Ticket" button if (interaction.isButton() && interaction.customId === 'close_ticket') { // Acknowledge the interaction immediately await interaction.deferReply({ ephemeral: true }); const ticketChannel = interaction.channel; // Check if the channel is a ticket channel if (ticketChannels.has(ticketChannel.id)) { // Delete the ticket channel await ticketChannel.delete(); ticketChannels.delete(ticketChannel.id); console.log(`Deleted ticket channel: ${ticketChannel.name}`); } else { await interaction.editReply({ content: 'This is not a valid ticket channel!' }); } } // Handle Staff Application button if (interaction.isButton() && interaction.customId === 'apply_staff_application_🏆') { const guild = interaction.guild; const member = interaction.member; // Find the role with "Manage Channels" permission const supportRole = guild.roles.cache.find(role => role.permissions.has(PermissionsBitField.Flags.ManageChannels)); if (!supportRole) { await interaction.reply({ content: 'No role with "Manage Channels" permission found!', ephemeral: true }); return; } // Find the application details from the config const application = config.applications.find(app => app.name.toLowerCase() === 'staff application 🏆'); if (!application) { await interaction.reply({ content: 'Invalid application!', ephemeral: true }); return; } // Create a modal for the application form const modal = new ModalBuilder() .setCustomId('application_form_staff_application_🏆') .setTitle(`Application: ${application.name}`); // Add text inputs to the modal based on the questions in the config const actionRows = application.questions.map(question => { const textInput = new TextInputBuilder() .setCustomId(question.id) .setLabel(question.label) .setStyle(question.style === 'Paragraph' ? TextInputStyle.Paragraph : TextInputStyle.Short) .setRequired(question.required); return new ActionRowBuilder().addComponents(textInput); }); modal.addComponents(...actionRows); // Show the modal to the user await interaction.showModal(modal); } // Handle Accept and Decline buttons if (interaction.customId.startsWith('accept_') || interaction.customId.startsWith('decline_')) { const guild = interaction.guild; const member = interaction.member; // Extract the user ID from the custom ID const userId = interaction.customId.split('_')[1]; // Find the user who submitted the application const user = await guild.members.fetch(userId); if (!user) { await interaction.reply({ content: 'User not found!', ephemeral: true }); return; } // Find the "staff-responses" channel const staffResponsesChannel = guild.channels.cache.find(channel => channel.name === 'staff-responses' && channel.isTextBased()); if (!staffResponsesChannel) { await interaction.reply({ content: 'Could not find the "staff-responses" channel!', ephemeral: true }); return; } // Handle Accept button if (interaction.customId.startsWith('accept_')) { // Send the acceptance message to the "staff-responses" channel await staffResponsesChannel.send(`Η αίτηση σου ${user} έγινε αποδεκτή! Επικοινώνησε με έναν @〢Staff Manager για να ορίσετε την ώρα του interview.`); // Update the embed to reflect the accepted status const embed = interaction.message.embeds[0]; const updatedEmbed = new EmbedBuilder(embed) .setColor('#00ff00') // Green color for accepted .setFooter({ text: `Application Accepted by ${interaction.user.tag}` }); // Disable the buttons const row = new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId('accept_disabled') .setLabel('Accepted') .setStyle(ButtonStyle.Success) .setEmoji('✅') .setDisabled(true), new ButtonBuilder() .setCustomId('decline_disabled') .setLabel('Declined') .setStyle(ButtonStyle.Danger) .setEmoji('❌') .setDisabled(true) ); // Update the original message with the updated embed and disabled buttons await interaction.update({ embeds: [updatedEmbed], components: [row] }); } // Handle Decline button if (interaction.customId.startsWith('decline_')) { // Send the decline message to the "staff-responses" channel await staffResponsesChannel.send(`Η αίτηση σου ${user} δεν έγινε αποδεκτή. Εάν έχεις απορίες, επικοινώνησε με έναν @〢Staff Manager.`); // Update the embed to reflect the declined status const embed = interaction.message.embeds[0]; const updatedEmbed = new EmbedBuilder(embed) .setColor('#ff0000') // Red color for declined .setFooter({ text: `Application Declined by ${interaction.user.tag}` }); // Disable the buttons const row = new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId('accept_disabled') .setLabel('Accepted') .setStyle(ButtonStyle.Success) .setEmoji('✅') .setDisabled(true), new ButtonBuilder() .setCustomId('decline_disabled') .setLabel('Declined') .setStyle(ButtonStyle.Danger) .setEmoji('❌') .setDisabled(true) ); // Update the original message with the updated embed and disabled buttons await interaction.update({ embeds: [updatedEmbed], components: [row] }); } } // Handle modal submissions if (interaction.isModalSubmit()) { console.log(`Modal submitted: ${interaction.customId}`); const guild = interaction.guild; const member = interaction.member; // Extract the application name from the custom ID const applicationName = interaction.customId.replace('application_form_', '').replace(/_/g, ' '); // Find the application details from the config const application = config.applications.find(app => app.name.toLowerCase() === applicationName.toLowerCase()); if (!application) { await interaction.reply({ content: 'Invalid application!', ephemeral: true }); return; } // Get the form responses const responses = {}; application.questions.forEach(question => { responses[question.id] = interaction.fields.getTextInputValue(question.id); }); // Find the "application-results" channel const resultsChannel = guild.channels.cache.find(channel => channel.name === 'application-results' && channel.isTextBased()); if (!resultsChannel) { await interaction.reply({ content: 'Could not find the "application-results" channel!', ephemeral: true }); return; } // Send the application form responses in the "application-results" channel const applicationEmbed = new EmbedBuilder() .setTitle(`Application: ${application.name}`) .setDescription( `**Applicant:** ${member.user}\n\n` + // Add the @user mention here application.questions.map(question => `**${question.label}:** ${responses[question.id]}`).join('\n') ) .setColor('#0099ff') .setFooter({ text: `Submitted by ${member.user.tag}` }); // Add Accept and Decline buttons const acceptButton = new ButtonBuilder() .setCustomId(`accept_${member.id}`) .setLabel('Accept') .setStyle(ButtonStyle.Success) .setEmoji('✅'); const declineButton = new ButtonBuilder() .setCustomId(`decline_${member.id}`) .setLabel('Decline') .setStyle(ButtonStyle.Danger) .setEmoji('❌'); const row = new ActionRowBuilder().addComponents(acceptButton, declineButton); await resultsChannel.send({ embeds: [applicationEmbed], components: [row] }); // Reply to the interaction await interaction.reply({ content: 'Your application has been submitted!', ephemeral: true }); console.log(`Application submitted by ${member.user.tag}`); } }); client.on('channelDelete', (channel) => { // Remove the ticket channel from the map if it's deleted if (ticketChannels.has(channel.id)) { ticketChannels.delete(channel.id); console.log(`Deleted ticket channel: ${channel.name}`); } // Remove the temporary voice channel from the map if it's deleted if (tempChannels.has(channel.id)) { tempChannels.delete(channel.id); console.log(`Deleted temporary voice channel: ${channel.name}`); } }); client.login(config.token);
Leave a Comment