Untitled

 avatar
unknown
plain_text
a month ago
20 kB
1
Indexable
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