Update close.js
This commit is contained in:
1 parent
13590b08a8
commit
f34158caa6
1 file changed
+149
-66
+149
-66
@@ -17,7 +17,7 @@ module.exports = new Command({
|
||||
name: "reason",
|
||||
description: "Ticket Close Reason",
|
||||
type: Discord.ApplicationCommandOptionType.String,
|
||||
},
|
||||
}
|
||||
],
|
||||
permissions: cmdconfig.CloseTicket.Permission,
|
||||
|
||||
@@ -35,33 +35,33 @@ module.exports = new Command({
|
||||
const NoPerms = new Discord.EmbedBuilder()
|
||||
.setTitle("Invalid Permissions!")
|
||||
.setDescription(`${msgconfig.Error.IncorrectPerms}\n\nRole Required: \`${supportbot.Roles.StaffMember.Staff}\` or \`${supportbot.Roles.StaffMember.Admin}\``)
|
||||
.setColor(supportbot.Embed.Colours.Warn);
|
||||
.setColor(supportbot.Embed.Colours.Warn);
|
||||
|
||||
if (!interaction.member.roles.cache.has(SupportStaff.id) && !interaction.member.roles.cache.has(Admin.id)) {
|
||||
return interaction.reply({ embeds: [NoPerms] });
|
||||
}
|
||||
}
|
||||
|
||||
const isThread = interaction.channel.type === Discord.ChannelType.PrivateThread;
|
||||
if (
|
||||
(supportbot.Ticket.TicketType === "threads" && interaction.channel.type !== Discord.ChannelType.PrivateThread) ||
|
||||
(supportbot.Ticket.TicketType === "threads" && !isThread) ||
|
||||
(supportbot.Ticket.TicketType === "channels" && interaction.channel.type !== Discord.ChannelType.GuildText)
|
||||
) {
|
||||
const NotTicketChannel = new Discord.EmbedBuilder()
|
||||
.setTitle("Invalid Channel!")
|
||||
.setDescription(`This command can only be used in a ${supportbot.Ticket.TicketType === "threads" ? "ticket thread" : "ticket channel"}.`)
|
||||
.setColor(supportbot.Embed.Colours.Warn); // Ensure valid color
|
||||
.setColor(supportbot.Embed.Colours.Warn);
|
||||
|
||||
return interaction.reply({ embeds: [NotTicketChannel], ephemeral: true });
|
||||
}
|
||||
|
||||
await interaction.deferReply(); // Deferring to avoid double replies
|
||||
await interaction.deferReply();
|
||||
|
||||
// Reading TicketData.json once for efficiency
|
||||
let tickets;
|
||||
try {
|
||||
tickets = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
} catch (err) {
|
||||
console.error('Error reading ticket data file:', err);
|
||||
console.error("Error reading ticket data file:", err);
|
||||
return interaction.followUp({ content: "There was an error loading ticket data." });
|
||||
}
|
||||
|
||||
@@ -72,15 +72,16 @@ module.exports = new Command({
|
||||
const Exists = new Discord.EmbedBuilder()
|
||||
.setTitle("No Ticket Found!")
|
||||
.setDescription(msgconfig.Error.NoValidTicket)
|
||||
.setColor(supportbot.Embed.Colours.Warn); // Ensure valid color
|
||||
.setColor(supportbot.Embed.Colours.Warn);
|
||||
return interaction.followUp({ embeds: [Exists] });
|
||||
}
|
||||
|
||||
let reason = interaction.options?.getString("reason") || "No Reason Provided.";
|
||||
const ticketUserId = ticket.user;
|
||||
|
||||
if (supportbot.Ticket.ReviewSystem.Enabled) {
|
||||
const rating = await collectReviewRating(interaction);
|
||||
const comment = await collectReviewComment(interaction);
|
||||
const rating = await collectReviewRating(interaction, ticketUserId);
|
||||
const comment = await collectReviewComment(interaction, ticketUserId);
|
||||
|
||||
const reviewChannel = await getChannel(supportbot.Ticket.ReviewSystem.Channel, interaction.guild);
|
||||
const reviewer = supportbot.Ticket.ClaimTickets ? `<@${ticket.claimedBy}>` : null;
|
||||
@@ -103,11 +104,11 @@ module.exports = new Command({
|
||||
|
||||
if (supportbot.Ticket.ClaimTickets.Enabled) {
|
||||
reviewEmbed.setDescription(
|
||||
`**${msgconfig.ReviewSystem.ReviewEmbed.ReviewedStaffTitle}** ${reviewer}\n**${msgconfig.ReviewSystem.ReviewEmbed.ReviewedByTitle}** <@${interaction.user.id}>` || `N/A`
|
||||
`**${msgconfig.ReviewSystem.ReviewEmbed.ReviewedStaffTitle}** ${reviewer}\n**${msgconfig.ReviewSystem.ReviewEmbed.ReviewedByTitle}** <@${ticketUserId}>` || `N/A`
|
||||
);
|
||||
} else {
|
||||
reviewEmbed.setDescription(
|
||||
`**${msgconfig.ReviewSystem.ReviewEmbed.ReviewedByTitle}** <@${interaction.user.id}>`
|
||||
`**${msgconfig.ReviewSystem.ReviewEmbed.ReviewedByTitle}** <@${ticketUserId}>`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -126,11 +127,11 @@ module.exports = new Command({
|
||||
},
|
||||
});
|
||||
|
||||
async function collectReviewRating(interaction) {
|
||||
async function collectReviewRating(interaction, ticketUserId) {
|
||||
const reviewPrompt = new Discord.EmbedBuilder()
|
||||
.setTitle(msgconfig.ReviewSystem.Rate.Title)
|
||||
.setDescription(msgconfig.ReviewSystem.Rate.Description)
|
||||
.setColor(supportbot.Embed.Colours.General); // Ensure valid color
|
||||
.setDescription(`${msgconfig.ReviewSystem.Rate.Description}`)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
const stars = new Discord.ActionRowBuilder().addComponents(
|
||||
new Discord.StringSelectMenuBuilder()
|
||||
@@ -146,6 +147,7 @@ async function collectReviewRating(interaction) {
|
||||
);
|
||||
|
||||
await interaction.followUp({
|
||||
content: `<@${ticketUserId}>`,
|
||||
embeds: [reviewPrompt],
|
||||
components: [stars],
|
||||
ephemeral: true,
|
||||
@@ -153,27 +155,27 @@ async function collectReviewRating(interaction) {
|
||||
|
||||
const starRating = await interaction.channel.awaitMessageComponent({
|
||||
componentType: Discord.ComponentType.StringSelect,
|
||||
filter: (i) => i.customId === "starRating" && i.user.id === interaction.user.id,
|
||||
time: 30000,
|
||||
filter: (i) => i.customId === "starRating" && i.user.id === ticketUserId,
|
||||
});
|
||||
|
||||
await starRating.deferUpdate();
|
||||
return starRating.values[0];
|
||||
}
|
||||
|
||||
async function collectReviewComment(interaction) {
|
||||
async function collectReviewComment(interaction, ticketUserId) {
|
||||
const commentPrompt = new Discord.EmbedBuilder()
|
||||
.setTitle(msgconfig.ReviewSystem.Comment.Title)
|
||||
.setDescription(msgconfig.ReviewSystem.Comment.Description)
|
||||
.setColor(supportbot.Embed.Colours.General); // Ensure valid color
|
||||
.setDescription(`${msgconfig.ReviewSystem.Comment.Description}`)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
await interaction.followUp({
|
||||
content: `<@${ticketUserId}>`,
|
||||
embeds: [commentPrompt],
|
||||
ephemeral: true,
|
||||
});
|
||||
|
||||
const filter = (response) => response.author.id === interaction.user.id;
|
||||
const commentCollection = await interaction.channel.awaitMessages({ filter, max: 1, time: 30000 });
|
||||
const filter = (response) => response.author.id === ticketUserId;
|
||||
|
||||
const commentCollection = await interaction.channel.awaitMessages({ filter, max: 1 });
|
||||
const comment = commentCollection.first()?.content;
|
||||
return comment && comment.toLowerCase() !== "no" ? comment : "No Comment Provided";
|
||||
}
|
||||
@@ -181,12 +183,21 @@ async function collectReviewComment(interaction) {
|
||||
async function handleCloseTicket(interaction, reason, ticket, TicketData) {
|
||||
const { getChannel } = interaction.client;
|
||||
|
||||
let tickets = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
let tickets;
|
||||
try {
|
||||
tickets = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
} catch (err) {
|
||||
console.error("Error reading ticket data:", err);
|
||||
return interaction.followUp("Failed to load ticket data.");
|
||||
}
|
||||
|
||||
let tUser = interaction.client.users.cache.get(ticket.user);
|
||||
let transcriptChannel = await getChannel(supportbot.Ticket.Log.TicketDataLog, interaction.guild);
|
||||
|
||||
if (!transcriptChannel)
|
||||
return console.log("Ticket Log Missing");
|
||||
if (!transcriptChannel) {
|
||||
console.log("Transcript channel missing or inaccessible");
|
||||
return interaction.followUp("Error: Transcript log channel is missing or bot lacks permission.");
|
||||
}
|
||||
|
||||
try {
|
||||
tickets.tickets[TicketData].open = false;
|
||||
@@ -194,57 +205,129 @@ async function handleCloseTicket(interaction, reason, ticket, TicketData) {
|
||||
|
||||
const transcriptEmbed = new Discord.EmbedBuilder()
|
||||
.setTitle(msgconfig.TicketLog.Title)
|
||||
.setColor(msgconfig.TicketLog.Colour) // Ensure valid color
|
||||
.setColor(msgconfig.TicketLog.Colour)
|
||||
.setFooter({ text: supportbot.Embed.Footer, iconURL: interaction.user.displayAvatarURL() })
|
||||
.setDescription(`> **Ticket:** ${interaction.channel.name} (\`${interaction.channel.id}\`)\n> **User:** ${tUser?.username || "N/A"}#${tUser?.discriminator || "N/A"} (\`${tUser?.id || ticket.user}\`)\n> **Closed by:** <@${interaction.user.id}>`)
|
||||
.setDescription(
|
||||
`> **Ticket:** ${interaction.channel.name} (\`${interaction.channel.id}\`)\n` +
|
||||
`> **User:** ${tUser?.tag || "Unknown User"} (\`${tUser?.id || ticket.user}\`)\n` +
|
||||
`> **Closed by:** <@${interaction.user.id}>`
|
||||
)
|
||||
.addFields({ name: "Reason", value: `\`\`\`${reason}\`\`\``, inline: true });
|
||||
|
||||
let msgs = await interaction.channel.messages.fetch({ limit: 100 });
|
||||
let html = createTranscriptHTML(interaction, msgs);
|
||||
let html = createTranscriptHTML(ticket, msgs);
|
||||
|
||||
const fileName = `${interaction.channel.name}.html`;
|
||||
let file = new Discord.AttachmentBuilder(Buffer.from(html), { name: `SPOILER_${fileName}` });
|
||||
const fileName = `${interaction.channel.id}-transcript.html`;
|
||||
fs.writeFileSync(`./Data/Transcripts/${fileName}`, html);
|
||||
|
||||
transcriptChannel.send({
|
||||
await transcriptChannel.send({
|
||||
embeds: [transcriptEmbed],
|
||||
files: [file],
|
||||
files: [`./Data/Transcripts/${fileName}`],
|
||||
spoiler: true,
|
||||
});
|
||||
|
||||
if (interaction.channel.type === Discord.ChannelType.GuildText) {
|
||||
await interaction.channel.delete();
|
||||
} else if (interaction.channel.type === Discord.ChannelType.PrivateThread) {
|
||||
await interaction.channel.setLocked(true);
|
||||
await interaction.channel.setArchived(true);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Error closing ticket:", err);
|
||||
console.error("Error handling ticket close:", err);
|
||||
interaction.followUp("An error occurred while closing the ticket.");
|
||||
}
|
||||
|
||||
await interaction.channel.delete();
|
||||
}
|
||||
|
||||
function createTranscriptHTML(interaction, msgs) {
|
||||
let html = `
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||||
.message { padding: 10px; margin: 5px 0; border: 1px solid #ddd; }
|
||||
.username { font-weight: bold; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Ticket Transcript</h1>
|
||||
<h2>Ticket: ${interaction.channel.name}</h2>
|
||||
<h3>User: ${interaction.user.tag}</h3>
|
||||
<div id="messages">
|
||||
function createTranscriptHTML(ticket, reason) {
|
||||
const messages = ticket.messages || [];
|
||||
return `
|
||||
<html>
|
||||
<head>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
background-color: #1a1a1a;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
color: white;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.container {
|
||||
backdrop-filter: blur(10px);
|
||||
background-color: rgba(0, 128, 0, 0.3);
|
||||
border-radius: 15px;
|
||||
padding: 20px;
|
||||
margin: 20px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 1000px;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
.message {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
padding: 10px;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 10px;
|
||||
}
|
||||
.avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.content {
|
||||
flex: 1;
|
||||
}
|
||||
.username {
|
||||
font-weight: bold;
|
||||
}
|
||||
.timestamp {
|
||||
font-size: 0.8em;
|
||||
color: #bbb;
|
||||
}
|
||||
.download-button {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
margin-top: 20px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1 class="text-2xl font-bold mb-4">Ticket Transcript</h1>
|
||||
<p><strong>Server Name:</strong> Emerald Services</p>
|
||||
<p><strong>Ticket:</strong> ${ticket.id}</p>
|
||||
<p><strong>Messages:</strong> ${messages.length} Messages</p>
|
||||
<div class="mt-4">
|
||||
${messages.map(msg => `
|
||||
<div class="message">
|
||||
<img src="${msg.avatar}" alt="User Avatar" class="avatar" />
|
||||
<div class="content">
|
||||
<p class="username">${msg.username}</p>
|
||||
<p>${msg.content}</p>
|
||||
<p class="timestamp">${msg.timestamp}</p>
|
||||
</div>
|
||||
</div>`).join('')}
|
||||
</div>
|
||||
<a href="ticket-${ticket.id}.html" download class="download-button">Download Transcript</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
msgs.reverse().forEach((msg) => {
|
||||
html += `
|
||||
<div class="message">
|
||||
<span class="username">${msg.author.tag}</span>: ${msg.content}
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
html += `
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user