blob: ca5147a426d354bde7a678471bfc6a4fb5194f90 [file] [log] [blame]
pineafan813bdf42022-07-24 10:39:10 +01001import Discord, { MessageActionRow, MessageButton } from "discord.js";
2import { tickets, toHexArray } from "../../utils/calculate.js";
3import client from "../../utils/client.js";
4import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
5import getEmojiByName from "../../utils/getEmojiByName.js";
6
7function capitalize(s: string) {
pineafan63fc5e22022-08-04 22:04:10 +01008 s = s.replace(/([A-Z])/g, " $1");
Skyler Grey75ea9172022-08-06 10:22:23 +01009 return s.length < 3
10 ? s.toUpperCase()
11 : s[0].toUpperCase() + s.slice(1).toLowerCase();
pineafan813bdf42022-07-24 10:39:10 +010012}
13
14export default async function (interaction) {
Skyler Grey75ea9172022-08-06 10:22:23 +010015 const {
16 log,
17 NucleusColors,
18 entry,
19 renderUser,
20 renderChannel,
21 renderDelta
22 } = client.logger;
pineafan813bdf42022-07-24 10:39:10 +010023
pineafan63fc5e22022-08-04 22:04:10 +010024 const config = await client.database.guilds.read(interaction.guild.id);
pineafan813bdf42022-07-24 10:39:10 +010025 if (!config.tickets.enabled || !config.tickets.category) {
Skyler Grey75ea9172022-08-06 10:22:23 +010026 return await interaction.reply({
27 embeds: [
28 new EmojiEmbed()
29 .setTitle("Tickets are disabled")
30 .setDescription(
31 "Please enable tickets in the configuration to use this command."
32 )
33 .setFooter({
34 text: interaction.member.permissions.has("MANAGE_GUILD")
35 ? "You can enable it by running /settings tickets"
36 : ""
37 })
38 .setStatus("Danger")
39 .setEmoji("CONTROL.BLOCKCROSS")
40 ],
41 ephemeral: true
42 });
pineafan813bdf42022-07-24 10:39:10 +010043 }
Skyler Grey75ea9172022-08-06 10:22:23 +010044 const category = interaction.guild.channels.cache.get(
45 config.tickets.category
46 ) as Discord.CategoryChannel;
pineafan813bdf42022-07-24 10:39:10 +010047 let count = 0;
Skyler Grey75ea9172022-08-06 10:22:23 +010048 category.children.forEach((element) => {
pineafane23c4ec2022-07-27 21:56:27 +010049 if (!(element.type === "GUILD_TEXT")) return;
Skyler Grey75ea9172022-08-06 10:22:23 +010050 if (
51 (element as Discord.TextChannel).topic.includes(
52 `${interaction.member.user.id}`
53 )
54 ) {
pineafan813bdf42022-07-24 10:39:10 +010055 if ((element as Discord.TextChannel).topic.endsWith("Active")) {
56 count++;
57 }
58 }
59 });
60 if (count >= config.tickets.maxTickets) {
Skyler Grey75ea9172022-08-06 10:22:23 +010061 return await interaction.reply({
62 embeds: [
63 new EmojiEmbed()
64 .setTitle("Create Ticket")
65 .setDescription(
66 `You have reached the maximum amount of tickets (${config.tickets.maxTickets}). Please close one of your active tickets before creating a new one.`
67 )
68 .setStatus("Danger")
69 .setEmoji("CONTROL.BLOCKCROSS")
70 ],
71 ephemeral: true
72 });
pineafan813bdf42022-07-24 10:39:10 +010073 }
74 let ticketTypes;
pineafan63fc5e22022-08-04 22:04:10 +010075 let custom = false;
Skyler Grey75ea9172022-08-06 10:22:23 +010076 if (config.tickets.customTypes && config.tickets.useCustom) {
77 ticketTypes = config.tickets.customTypes;
78 custom = true;
79 } else if (config.tickets.types)
80 ticketTypes = toHexArray(config.tickets.types, tickets);
pineafan813bdf42022-07-24 10:39:10 +010081 else ticketTypes = [];
82 let chosenType;
83 let splitFormattedTicketTypes = [];
84 if (ticketTypes.length > 0) {
85 let formattedTicketTypes = [];
Skyler Grey75ea9172022-08-06 10:22:23 +010086 formattedTicketTypes = ticketTypes.map((type) => {
pineafan813bdf42022-07-24 10:39:10 +010087 if (custom) {
88 return new MessageButton()
89 .setLabel(type)
90 .setStyle("PRIMARY")
pineafan63fc5e22022-08-04 22:04:10 +010091 .setCustomId(type);
pineafan813bdf42022-07-24 10:39:10 +010092 } else {
93 return new MessageButton()
94 .setLabel(capitalize(type))
95 .setStyle("PRIMARY")
96 .setCustomId(type)
Skyler Grey75ea9172022-08-06 10:22:23 +010097 .setEmoji(
98 getEmojiByName(
99 "TICKETS." + type.toString().toUpperCase(),
100 "id"
101 )
102 );
pineafan813bdf42022-07-24 10:39:10 +0100103 }
104 });
105 for (let i = 0; i < formattedTicketTypes.length; i += 5) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100106 splitFormattedTicketTypes.push(
107 new MessageActionRow().addComponents(
108 formattedTicketTypes.slice(i, i + 5)
109 )
110 );
pineafan813bdf42022-07-24 10:39:10 +0100111 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100112 const m = await interaction.reply({
113 embeds: [
114 new EmojiEmbed()
115 .setTitle("Create Ticket")
116 .setDescription("Select a ticket type")
117 .setStatus("Success")
118 .setEmoji("GUILD.TICKET.OPEN")
119 ],
120 ephemeral: true,
121 fetchReply: true,
122 components: splitFormattedTicketTypes
123 });
pineafan813bdf42022-07-24 10:39:10 +0100124 let component;
125 try {
Skyler Grey75ea9172022-08-06 10:22:23 +0100126 component = await m.awaitMessageComponent({ time: 300000 });
pineafan813bdf42022-07-24 10:39:10 +0100127 } catch (e) {
128 return;
129 }
130 chosenType = component.customId;
131 splitFormattedTicketTypes = [];
132 formattedTicketTypes = [];
Skyler Grey75ea9172022-08-06 10:22:23 +0100133 formattedTicketTypes = ticketTypes.map((type) => {
pineafan813bdf42022-07-24 10:39:10 +0100134 if (custom) {
135 return new MessageButton()
136 .setLabel(type)
pineafane23c4ec2022-07-27 21:56:27 +0100137 .setStyle(chosenType === type ? "SUCCESS" : "SECONDARY")
pineafan813bdf42022-07-24 10:39:10 +0100138 .setCustomId(type)
pineafan63fc5e22022-08-04 22:04:10 +0100139 .setDisabled(true);
140 } else {
141 return new MessageButton()
pineafan813bdf42022-07-24 10:39:10 +0100142 .setLabel(capitalize(type))
pineafane23c4ec2022-07-27 21:56:27 +0100143 .setStyle(chosenType === type ? "SUCCESS" : "SECONDARY")
pineafan813bdf42022-07-24 10:39:10 +0100144 .setCustomId(type)
Skyler Grey75ea9172022-08-06 10:22:23 +0100145 .setEmoji(
146 getEmojiByName(
147 "TICKETS." + type.toString().toUpperCase(),
148 "id"
149 )
150 )
pineafan63fc5e22022-08-04 22:04:10 +0100151 .setDisabled(true);
pineafan813bdf42022-07-24 10:39:10 +0100152 }
153 });
154 for (let i = 0; i < formattedTicketTypes.length; i += 5) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100155 splitFormattedTicketTypes.push(
156 new MessageActionRow().addComponents(
157 formattedTicketTypes.slice(i, i + 5)
158 )
159 );
pineafan813bdf42022-07-24 10:39:10 +0100160 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100161 component.update({
162 embeds: [
163 new EmojiEmbed()
164 .setTitle("Create Ticket")
165 .setDescription("Select a ticket type")
166 .setStatus("Success")
167 .setEmoji("GUILD.TICKET.OPEN")
168 ],
169 components: splitFormattedTicketTypes
170 });
pineafan813bdf42022-07-24 10:39:10 +0100171 } else {
pineafan63fc5e22022-08-04 22:04:10 +0100172 chosenType = null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100173 await interaction.reply({
174 embeds: [
175 new EmojiEmbed()
176 .setTitle("Create Ticket")
177 .setEmoji("GUILD.TICKET.OPEN")
178 ],
179 ephemeral: true,
180 components: splitFormattedTicketTypes
181 });
pineafan813bdf42022-07-24 10:39:10 +0100182 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100183 const overwrites = [
184 {
185 id: interaction.member,
186 allow: [
187 "VIEW_CHANNEL",
188 "SEND_MESSAGES",
189 "ATTACH_FILES",
190 "ADD_REACTIONS",
191 "READ_MESSAGE_HISTORY"
192 ],
193 type: "member"
194 }
195 ] as Discord.OverwriteResolvable[];
pineafan813bdf42022-07-24 10:39:10 +0100196 overwrites.push({
197 id: interaction.guild.roles.everyone,
198 deny: ["VIEW_CHANNEL"],
199 type: "role"
pineafan63fc5e22022-08-04 22:04:10 +0100200 });
pineafane23c4ec2022-07-27 21:56:27 +0100201 if (config.tickets.supportRole !== null) {
pineafan813bdf42022-07-24 10:39:10 +0100202 overwrites.push({
203 id: interaction.guild.roles.cache.get(config.tickets.supportRole),
Skyler Grey75ea9172022-08-06 10:22:23 +0100204 allow: [
205 "VIEW_CHANNEL",
206 "SEND_MESSAGES",
207 "ATTACH_FILES",
208 "ADD_REACTIONS",
209 "READ_MESSAGE_HISTORY"
210 ],
pineafan813bdf42022-07-24 10:39:10 +0100211 type: "role"
pineafan63fc5e22022-08-04 22:04:10 +0100212 });
pineafan813bdf42022-07-24 10:39:10 +0100213 }
214
215 let c;
216 try {
Skyler Grey75ea9172022-08-06 10:22:23 +0100217 c = await interaction.guild.channels.create(
218 interaction.member.user.username,
pineafan813bdf42022-07-24 10:39:10 +0100219 {
Skyler Grey75ea9172022-08-06 10:22:23 +0100220 type: "GUILD_TEXT",
221 topic: `${interaction.member.user.id} Active`,
222 parent: config.tickets.category,
223 nsfw: false,
224 permissionOverwrites:
225 overwrites as Discord.OverwriteResolvable[],
226 reason: "Creating ticket"
pineafan813bdf42022-07-24 10:39:10 +0100227 }
pineafan63fc5e22022-08-04 22:04:10 +0100228 );
Skyler Grey75ea9172022-08-06 10:22:23 +0100229 } catch (e) {
230 return await interaction.editReply({
231 embeds: [
232 new EmojiEmbed()
233 .setTitle("Create Ticket")
234 .setDescription("Failed to create ticket")
235 .setStatus("Danger")
236 .setEmoji("CONTROL.BLOCKCROSS")
237 ]
238 });
239 }
240 try {
241 await c.send({
242 content:
243 `<@${interaction.member.user.id}>` +
244 (config.tickets.supportRole !== null
245 ? ` • <@&${config.tickets.supportRole}>`
246 : ""),
247 allowedMentions: {
248 users: [(interaction.member as Discord.GuildMember).id],
249 roles:
250 config.tickets.supportRole !== null
251 ? [config.tickets.supportRole]
252 : []
253 }
254 });
255 let content = interaction.options
256 ? interaction.options.getString("message") || ""
257 : "";
pineafan813bdf42022-07-24 10:39:10 +0100258 if (content) content = `**Message:**\n> ${content}\n`;
Skyler Grey75ea9172022-08-06 10:22:23 +0100259 const emoji = custom
260 ? ""
261 : getEmojiByName("TICKETS." + chosenType.toUpperCase());
262 await c.send({
263 embeds: [
264 new EmojiEmbed()
265 .setTitle("New Ticket")
266 .setDescription(
267 `Ticket created by <@${interaction.member.user.id}>\n` +
268 `**Support type:** ${
269 chosenType !== null
270 ? emoji + " " + capitalize(chosenType)
271 : "General"
272 }\n` +
273 `**Ticket ID:** \`${c.id}\`\n${content}\n` +
274 "Type `/ticket close` to close this ticket."
275 )
276 .setStatus("Success")
277 .setEmoji("GUILD.TICKET.OPEN")
278 ],
279 components: [
280 new MessageActionRow().addComponents([
281 new MessageButton()
282 .setLabel("Close")
283 .setStyle("DANGER")
284 .setCustomId("closeticket")
285 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
286 ])
287 ]
288 });
pineafan63fc5e22022-08-04 22:04:10 +0100289 const data = {
Skyler Grey75ea9172022-08-06 10:22:23 +0100290 meta: {
pineafan63fc5e22022-08-04 22:04:10 +0100291 type: "ticketCreate",
292 displayName: "Ticket Created",
pineafan813bdf42022-07-24 10:39:10 +0100293 calculateType: "ticketUpdate",
294 color: NucleusColors.green,
pineafan63fc5e22022-08-04 22:04:10 +0100295 emoji: "GUILD.TICKET.OPEN",
pineafan813bdf42022-07-24 10:39:10 +0100296 timestamp: new Date().getTime()
297 },
298 list: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100299 ticketFor: entry(
300 interaction.member.user.id,
301 renderUser(interaction.member.user)
302 ),
303 created: entry(
304 new Date().getTime(),
305 renderDelta(new Date().getTime())
306 ),
pineafan63fc5e22022-08-04 22:04:10 +0100307 ticketChannel: entry(c.id, renderChannel(c))
pineafan813bdf42022-07-24 10:39:10 +0100308 },
309 hidden: {
310 guild: interaction.guild.id
311 }
pineafan63fc5e22022-08-04 22:04:10 +0100312 };
pineafan813bdf42022-07-24 10:39:10 +0100313 log(data);
Skyler Grey75ea9172022-08-06 10:22:23 +0100314 } catch (e) {
315 console.log(e);
316 }
317 await interaction.editReply({
318 embeds: [
319 new EmojiEmbed()
320 .setTitle("Create Ticket")
321 .setDescription(
322 `Ticket created. You can view it here: <#${c.id}>`
323 )
324 .setStatus("Success")
325 .setEmoji("GUILD.TICKET.OPEN")
326 ],
327 components: splitFormattedTicketTypes
328 });
329}