Untitled
unknown
plain_text
a year ago
20 kB
5
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);Editor is loading...
Leave a Comment