blob: 4f58aa0b17d4633151e3abb6881976f74bf4c183 [file] [log] [blame]
pineafan1dc15722022-03-14 21:27:34 +00001import Discord, { CommandInteraction, MessageActionRow, MessageButton } from "discord.js";
pineafan4f164f32022-02-26 22:07:12 +00002import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
3import { WrappedCheck } from "jshaiku";
pineafan1dc15722022-03-14 21:27:34 +00004import { tickets, toHexArray, toHexInteger } from "../../utils/calculate.js";
5import readConfig from "../../utils/readConfig.js";
6import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
7import getEmojiByName from "../../utils/getEmojiByName.js";
8
9function capitalize(s: string) {
10 s = s.replace(/([A-Z])/g, ' $1');
11 return s.length < 3 ? s.toUpperCase() : s[0].toUpperCase() + s.slice(1).toLowerCase();
12}
pineafan4f164f32022-02-26 22:07:12 +000013
14const command = (builder: SlashCommandSubcommandBuilder) =>
15 builder
16 .setName("create")
17 .setDescription("Creates a new modmail ticket")
pineafan1dc15722022-03-14 21:27:34 +000018 .addStringOption(option => option.setName("message").setDescription("The content of the ticket").setRequired(false))
pineafan4f164f32022-02-26 22:07:12 +000019
pineafan1dc15722022-03-14 21:27:34 +000020const callback = async (interaction: CommandInteraction) => {
21 // @ts-ignore
22 const { log, NucleusColors, entry, renderUser, renderChannel, renderDelta } = interaction.client.logger
23
24 let config = await readConfig(interaction.guild.id);
25 if (!config.tickets.enabled || !config.tickets.category) {
26 return await interaction.reply({embeds: [new EmojiEmbed()
27 .setTitle("Tickets are disabled")
28 .setDescription("Please enable tickets in the configuration to use this command.")
29 .setStatus("Danger")
30 .setEmoji("CONTROL.BLOCKCROSS")
31 ], ephemeral: true});
32 }
33 let category = interaction.guild.channels.cache.get(config.tickets.category) as Discord.CategoryChannel;
34 let count = 0;
35 category.children.forEach(element => {
36 if (!(element.type == "GUILD_TEXT")) return;
37 if ((element as Discord.TextChannel).topic.includes(`${interaction.member.user.id}`)) {
38 if ((element as Discord.TextChannel).topic.endsWith("Active")) {
39 count++;
40 }
41 }
42 });
43 if (count >= config.tickets.maxTickets) {
44 return await interaction.reply({embeds: [new EmojiEmbed()
45 .setTitle("Create Ticket")
46 .setDescription(`You have reached the maximum amount of tickets (${config.tickets.maxTickets}). Please close one of your active tickets before creating a new one.`)
47 .setStatus("Danger")
48 .setEmoji("CONTROL.BLOCKCROSS")
49 ], ephemeral: true});
50 }
51 let ticketTypes
52 if (config.tickets.customTypes) ticketTypes = config.tickets.customTypes;
53 else if (config.tickets.types) ticketTypes = toHexArray(config.tickets.types, tickets);
54 else ticketTypes = [];
55 let chosenType;
56 if (ticketTypes.length > 0) {
57 let splitFormattedTicketTypes = [];
58 let formattedTicketTypes = [];
59 formattedTicketTypes = ticketTypes.map(type => {
60 return new MessageButton()
61 .setLabel(capitalize(type))
62 .setStyle("PRIMARY")
63 .setCustomId(type)
64 .setEmoji(getEmojiByName(("TICKETS." + type.toString().toUpperCase()), "id"));
65 });
66 for (let i = 0; i < formattedTicketTypes.length; i += 4) {
67 splitFormattedTicketTypes.push(new MessageActionRow().addComponents(formattedTicketTypes.slice(i, i + 4)));
68 }
69 let m = await interaction.reply({embeds: [new EmojiEmbed()
70 .setTitle("Create Ticket")
71 .setDescription("Please select a ticket type")
72 .setStatus("Success")
73 .setEmoji("GUILD.TICKET.OPEN")
74 ], ephemeral: true, fetchReply: true, components: splitFormattedTicketTypes});
75 let component;
76 try {
77 component = await (m as Discord.Message).awaitMessageComponent({time: 2.5 * 60 * 1000});
78 } catch (e) {
79 return;
80 }
81 component.deferUpdate();
82 chosenType = component.customId;
83 } else {
84 chosenType = null
85 await interaction.reply({embeds: [new EmojiEmbed()
86 .setTitle("Create Ticket")
87 .setEmoji("GUILD.TICKET.OPEN")
88 ], ephemeral: true})
89 }
90 let overwrites = [{
91 id: interaction.member,
92 allow: ["VIEW_CHANNEL", "SEND_MESSAGES", "ATTACH_FILES", "ADD_REACTIONS", "READ_MESSAGE_HISTORY"],
93 type: "member"
94 }] as Discord.OverwriteResolvable[];
95 if (config.tickets.supportRole != null) {
96 overwrites.push({
97 id: interaction.guild.roles.cache.get(config.tickets.supportRole),
98 allow: ["VIEW_CHANNEL", "SEND_MESSAGES", "ATTACH_FILES", "ADD_REACTIONS", "READ_MESSAGE_HISTORY"],
99 type: "role"
100 })
101 }
102
103 let c;
104 try {
105 c = await interaction.guild.channels.create(interaction.member.user.username, {
106 type: "GUILD_TEXT",
107 topic: `${interaction.member.user.id} Active`,
108 parent: config.tickets.category,
109 nsfw: false,
110 permissionOverwrites: (overwrites as Discord.OverwriteResolvable[]),
111 reason: "Creating ticket"
112 })
113 } catch (e) {
114 return await interaction.editReply({embeds: [new EmojiEmbed()
115 .setTitle("Create Ticket")
116 .setDescription("Failed to create ticket")
117 .setStatus("Danger")
118 .setEmoji("CONTROL.BLOCKCROSS")
119 ]});
120 }
121 try {
122 await c.send(
123 {
124 content: (`<@${interaction.member.user.id}>` + (config.tickets.supportRole != null ? ` • <@&${config.tickets.supportRole}>` : "")),
125 allowedMentions: {
126 users: [(interaction.member as Discord.GuildMember).id],
127 roles: (config.tickets.supportRole != null ? [config.tickets.supportRole] : [])
128 }
129 }
130 )
131 let content = interaction.options.getString("message") || "";
132 if (content) content = `**Message:**\n> ${content}\n`;
133 await c.send({ embeds: [new EmojiEmbed()
134 .setTitle("New Ticket")
135 .setDescription(
136 `Ticket created by <@${interaction.member.user.id}>\n` +
137 `**Support type:** ${chosenType != null ? (getEmojiByName("TICKETS." + chosenType.toUpperCase()) + " " + capitalize(chosenType)) : "General"}\n` +
138 `**Ticket ID:** \`${c.id}\`\n${content}\n` +
139 `Type \`/ticket close\` to archive this ticket.`,
140 )
141 .setStatus("Success")
142 .setEmoji("GUILD.TICKET.OPEN")
143 ]})
144 let data = {
145 meta:{
146 type: 'ticketCreate',
147 displayName: 'Ticket Created',
148 calculateType: true,
149 color: NucleusColors.green,
150 emoji: 'GUILD.TICKET.OPEN',
151 timestamp: new Date().getTime()
152 },
153 list: {
154 ticketFor: entry(interaction.member.user.id, renderUser(interaction.member.user)),
155 createdAt: entry(new Date().getTime(), renderDelta(new Date().getTime())),
156 ticketChannel: entry(c.id, renderChannel(c)),
157 },
158 hidden: {
159 guild: interaction.guild.id
160 }
161 }
162 log(data, interaction.client);
163 } catch (e) { console.log(e)}
164 await interaction.editReply({embeds: [new EmojiEmbed()
165 .setTitle("Create Ticket")
166 .setDescription(`Ticket created. You can view it here: <#${c.id}>`)
167 .setStatus("Success")
168 .setEmoji("GUILD.TICKET.OPEN")
169 ], components: []});
pineafan4f164f32022-02-26 22:07:12 +0000170}
171
172const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
173 return true;
174}
175
176export { command };
177export { callback };
178export { check };