blob: 91c876e745de8297ed20527d84915fc5c002274a [file] [log] [blame]
Skyler Grey75ea9172022-08-06 10:22:23 +01001import Discord, {
2 CommandInteraction,
3 GuildMember,
4 MessageActionRow,
5 MessageButton
6} from "discord.js";
pineafan4f164f32022-02-26 22:07:12 +00007import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
pineafan8b4b17f2022-02-27 20:42:52 +00008import confirmationMessage from "../../utils/confirmationMessage.js";
pineafan4edb7762022-06-26 19:21:04 +01009import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
pineafan8b4b17f2022-02-27 20:42:52 +000010import keyValueList from "../../utils/generateKeyValueList.js";
Skyler Grey75ea9172022-08-06 10:22:23 +010011import {
12 create,
13 areTicketsEnabled
14} from "../../actions/createModActionTicket.js";
pineafan63fc5e22022-08-04 22:04:10 +010015import client from "../../utils/client.js";
pineafan4f164f32022-02-26 22:07:12 +000016
17const command = (builder: SlashCommandSubcommandBuilder) =>
18 builder
pineafan63fc5e22022-08-04 22:04:10 +010019 .setName("warn")
20 .setDescription("Warns a user")
Skyler Grey75ea9172022-08-06 10:22:23 +010021 .addUserOption((option) =>
22 option
23 .setName("user")
24 .setDescription("The user to warn")
25 .setRequired(true)
26 );
pineafan4f164f32022-02-26 22:07:12 +000027
Skyler Grey75ea9172022-08-06 10:22:23 +010028const callback = async (
29 interaction: CommandInteraction
30): Promise<void | unknown> => {
pineafan63fc5e22022-08-04 22:04:10 +010031 const { log, NucleusColors, renderUser, entry } = client.logger;
pineafan8b4b17f2022-02-27 20:42:52 +000032 // TODO:[Modals] Replace this with a modal
pineafan73a7c4a2022-07-24 10:38:04 +010033 let reason = null;
pineafan02ba0232022-07-24 22:16:15 +010034 let notify = true;
35 let createAppealTicket = false;
pineafan73a7c4a2022-07-24 10:38:04 +010036 let confirmation;
37 while (true) {
38 confirmation = await new confirmationMessage(interaction)
39 .setEmoji("PUNISH.WARN.RED")
40 .setTitle("Warn")
Skyler Grey75ea9172022-08-06 10:22:23 +010041 .setDescription(
42 keyValueList({
43 user: renderUser(interaction.options.getUser("user")),
44 reason: reason
45 ? "\n> " + (reason ?? "").replaceAll("\n", "\n> ")
46 : "*No reason provided*"
47 }) +
48 `The user **will${notify ? "" : " not"}** be notified\n\n` +
49 `Are you sure you want to warn <@!${
50 (interaction.options.getMember("user") as GuildMember)
51 .id
52 }>?`
53 )
pineafan73a7c4a2022-07-24 10:38:04 +010054 .setColor("Danger")
55 .addCustomBoolean(
Skyler Grey75ea9172022-08-06 10:22:23 +010056 "appeal",
57 "Create appeal ticket",
58 !(await areTicketsEnabled(interaction.guild.id)),
59 async () =>
60 await create(
61 interaction.guild,
62 interaction.options.getUser("user"),
63 interaction.user,
64 reason
65 ),
66 "An appeal ticket will be created when Confirm is clicked",
67 "CONTROL.TICKET",
68 createAppealTicket
69 )
70 .addCustomBoolean(
71 "notify",
72 "Notify user",
73 false,
74 null,
75 null,
76 "ICONS.NOTIFY." + (notify ? "ON" : "OFF"),
77 notify
78 )
pineafan73a7c4a2022-07-24 10:38:04 +010079 .addReasonButton(reason ?? "")
pineafan63fc5e22022-08-04 22:04:10 +010080 .send(reason !== null);
81 reason = reason ?? "";
82 if (confirmation.cancelled) return;
83 if (confirmation.success) break;
84 if (confirmation.newReason) reason = confirmation.newReason;
pineafan02ba0232022-07-24 22:16:15 +010085 if (confirmation.components) {
pineafan63fc5e22022-08-04 22:04:10 +010086 notify = confirmation.components.notify.active;
87 createAppealTicket = confirmation.components.appeal.active;
pineafan02ba0232022-07-24 22:16:15 +010088 }
pineafan73a7c4a2022-07-24 10:38:04 +010089 }
pineafan377794f2022-04-18 19:01:01 +010090 if (confirmation.success) {
pineafan63fc5e22022-08-04 22:04:10 +010091 let dmd = false;
pineafan8b4b17f2022-02-27 20:42:52 +000092 try {
pineafan02ba0232022-07-24 22:16:15 +010093 if (notify) {
Skyler Grey75ea9172022-08-06 10:22:23 +010094 const config = await client.database.guilds.read(
95 interaction.guild.id
96 );
97 await (
98 interaction.options.getMember("user") as GuildMember
99 ).send({
100 embeds: [
101 new EmojiEmbed()
102 .setEmoji("PUNISH.WARN.RED")
103 .setTitle("Warned")
104 .setDescription(
105 `You have been warned in ${interaction.guild.name}` +
106 (reason ? ` for:\n> ${reason}` : ".") +
107 "\n\n" +
108 (confirmation.components.appeal.response
109 ? `You can appeal this here ticket: <#${confirmation.components.appeal.response}>`
110 : "")
111 )
112 .setStatus("Danger")
113 .setFooter({
114 text: config.moderation.warn.text
115 ? "The button below is set by the server admins. Do not enter any passwords or other account details on the linked site."
116 : "",
117 iconURL:
118 "https://cdn.discordapp.com/emojis/952295894370369587.webp?size=128&quality=lossless"
119 })
pineafan02ba0232022-07-24 22:16:15 +0100120 ],
Skyler Grey75ea9172022-08-06 10:22:23 +0100121 components: config.moderation.warn.text
122 ? [
123 new MessageActionRow().addComponents([
124 new MessageButton()
125 .setStyle("LINK")
126 .setLabel(config.moderation.warn.text)
127 .setURL(config.moderation.warn.link)
128 ])
129 ]
130 : []
pineafan63fc5e22022-08-04 22:04:10 +0100131 });
132 dmd = true;
pineafan8b4b17f2022-02-27 20:42:52 +0000133 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100134 } catch {
135 dmd = false;
136 }
pineafan63fc5e22022-08-04 22:04:10 +0100137 const data = {
Skyler Grey75ea9172022-08-06 10:22:23 +0100138 meta: {
pineafan63fc5e22022-08-04 22:04:10 +0100139 type: "memberWarn",
140 displayName: "Member warned",
141 calculateType: "guildMemberPunish",
pineafan1dc15722022-03-14 21:27:34 +0000142 color: NucleusColors.yellow,
pineafan63fc5e22022-08-04 22:04:10 +0100143 emoji: "PUNISH.WARN.YELLOW",
pineafan1dc15722022-03-14 21:27:34 +0000144 timestamp: new Date().getTime()
145 },
146 list: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100147 user: entry(
148 (interaction.options.getMember("user") as GuildMember).user
149 .id,
150 renderUser(
151 (interaction.options.getMember("user") as GuildMember)
152 .user
153 )
154 ),
155 warnedBy: entry(
156 interaction.member.user.id,
157 renderUser(interaction.member.user)
158 ),
pineafan73a7c4a2022-07-24 10:38:04 +0100159 reason: reason ? `\n> ${reason}` : "No reason provided"
pineafan1dc15722022-03-14 21:27:34 +0000160 },
161 hidden: {
162 guild: interaction.guild.id
163 }
pineafan63fc5e22022-08-04 22:04:10 +0100164 };
165 await client.database.history.create(
Skyler Grey75ea9172022-08-06 10:22:23 +0100166 "warn",
167 interaction.guild.id,
pineafan4edb7762022-06-26 19:21:04 +0100168 (interaction.options.getMember("user") as GuildMember).user,
Skyler Grey75ea9172022-08-06 10:22:23 +0100169 interaction.user,
170 reason
pineafan63fc5e22022-08-04 22:04:10 +0100171 );
pineafan4edb7762022-06-26 19:21:04 +0100172 log(data);
Skyler Grey75ea9172022-08-06 10:22:23 +0100173 const failed = !dmd && notify;
pineafan5d1908e2022-02-28 21:34:47 +0000174 if (!failed) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100175 await interaction.editReply({
176 embeds: [
177 new EmojiEmbed()
178 .setEmoji("PUNISH.WARN.GREEN")
179 .setTitle("Warn")
180 .setDescription(
181 "The user was warned" +
182 (confirmation.components.appeal.response
183 ? ` and an appeal ticket was opened in <#${confirmation.components.appeal.response}>`
184 : "")
185 )
186 .setStatus("Success")
187 ],
188 components: []
189 });
pineafan5d1908e2022-02-28 21:34:47 +0000190 } else {
Skyler Grey75ea9172022-08-06 10:22:23 +0100191 const canSeeChannel = (
192 interaction.options.getMember("user") as GuildMember
193 )
194 .permissionsIn(interaction.channel as Discord.TextChannel)
195 .has("VIEW_CHANNEL");
196 const m = (await interaction.editReply({
197 embeds: [
198 new EmojiEmbed()
199 .setEmoji("PUNISH.WARN.RED")
200 .setTitle("Warn")
201 .setDescription(
202 "The user's DMs are not open\n\nWhat would you like to do?"
203 )
204 .setStatus("Danger")
205 ],
206 components: [
pineafan5d1908e2022-02-28 21:34:47 +0000207 new MessageActionRow().addComponents([
208 new Discord.MessageButton()
209 .setCustomId("log")
210 .setLabel("Ignore and log")
211 .setStyle("SECONDARY"),
212 new Discord.MessageButton()
213 .setCustomId("here")
214 .setLabel("Warn here")
pineafanc1c18792022-08-03 21:41:36 +0100215 .setStyle(canSeeChannel ? "PRIMARY" : "SECONDARY")
216 .setDisabled(!canSeeChannel),
217 new Discord.MessageButton()
218 .setCustomId("ticket")
219 .setLabel("Create ticket")
220 .setStyle(canSeeChannel ? "SECONDARY" : "PRIMARY")
pineafan5d1908e2022-02-28 21:34:47 +0000221 ])
pineafan02ba0232022-07-24 22:16:15 +0100222 ]
Skyler Grey75ea9172022-08-06 10:22:23 +0100223 })) as Discord.Message;
pineafan5d1908e2022-02-28 21:34:47 +0000224 let component;
225 try {
Skyler Grey75ea9172022-08-06 10:22:23 +0100226 component = await m.awaitMessageComponent({
227 filter: (m) => m.user.id === interaction.user.id,
228 time: 300000
229 });
pineafan5d1908e2022-02-28 21:34:47 +0000230 } catch (e) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100231 return await interaction.editReply({
232 embeds: [
233 new EmojiEmbed()
234 .setEmoji("PUNISH.WARN.GREEN")
235 .setTitle("Warn")
236 .setDescription("No changes were made")
237 .setStatus("Success")
238 ],
239 components: []
240 });
241 }
242 if (component.customId === "here") {
243 await interaction.channel.send({
244 embeds: [
245 new EmojiEmbed()
246 .setEmoji("PUNISH.WARN.RED")
247 .setTitle("Warn")
248 .setDescription(
249 "You have been warned" +
250 (reason ? ` for:\n> ${reason}` : ".")
251 )
252 .setStatus("Danger")
253 ],
254 content: `<@!${
255 (interaction.options.getMember("user") as GuildMember)
256 .id
257 }>`,
258 allowedMentions: {
259 users: [
260 (
261 interaction.options.getMember(
262 "user"
263 ) as GuildMember
264 ).id
265 ]
266 }
267 });
268 return await interaction.editReply({
269 embeds: [
270 new EmojiEmbed()
271 .setEmoji("PUNISH.WARN.GREEN")
272 .setTitle("Warn")
273 .setDescription(
274 "The user was warned" +
275 (confirmation.response
276 ? ` and an appeal ticket was opened in <#${confirmation.response}>`
277 : "")
278 )
279 .setStatus("Success")
280 ],
281 components: []
282 });
283 } else if (component.customId === "log") {
284 await interaction.editReply({
285 embeds: [
286 new EmojiEmbed()
287 .setEmoji("PUNISH.WARN.GREEN")
288 .setTitle("Warn")
289 .setDescription("The warn was logged")
290 .setStatus("Success")
291 ],
292 components: []
293 });
294 } else if (component.customId === "ticket") {
295 const ticketChannel = await create(
296 interaction.guild,
297 interaction.options.getUser("user"),
298 interaction.user,
299 reason,
300 "Warn Notification"
301 );
302 if (ticketChannel === null) {
303 return await interaction.editReply({
304 embeds: [
305 new EmojiEmbed()
306 .setEmoji("PUNISH.WARN.RED")
307 .setTitle("Warn")
308 .setDescription("A ticket could not be created")
309 .setStatus("Danger")
310 ],
311 components: []
312 });
313 }
314 await interaction.editReply({
315 embeds: [
316 new EmojiEmbed()
317 .setEmoji("PUNISH.WARN.GREEN")
318 .setTitle("Warn")
319 .setDescription(
320 `A ticket was created in <#${ticketChannel}>`
321 )
322 .setStatus("Success")
323 ],
324 components: []
325 });
326 }
327 }
328 } else {
329 await interaction.editReply({
330 embeds: [
331 new EmojiEmbed()
pineafan63fc5e22022-08-04 22:04:10 +0100332 .setEmoji("PUNISH.WARN.GREEN")
333 .setTitle("Warn")
pineafan5d1908e2022-02-28 21:34:47 +0000334 .setDescription("No changes were made")
335 .setStatus("Success")
Skyler Grey75ea9172022-08-06 10:22:23 +0100336 ],
337 components: []
338 });
pineafan8b4b17f2022-02-27 20:42:52 +0000339 }
pineafan63fc5e22022-08-04 22:04:10 +0100340};
pineafan4f164f32022-02-26 22:07:12 +0000341
pineafanbd02b4a2022-08-05 22:01:38 +0100342const check = (interaction: CommandInteraction) => {
Skyler Grey75ea9172022-08-06 10:22:23 +0100343 const member = interaction.member as GuildMember;
344 const me = interaction.guild.me!;
345 const apply = interaction.options.getMember("user") as GuildMember;
346 if (member === null || me === null || apply === null)
347 throw "That member is not in the server";
pineafan63fc5e22022-08-04 22:04:10 +0100348 const memberPos = member.roles ? member.roles.highest.position : 0;
pineafan63fc5e22022-08-04 22:04:10 +0100349 const applyPos = apply.roles ? apply.roles.highest.position : 0;
pineafan8b4b17f2022-02-27 20:42:52 +0000350 // Do not allow warning bots
pineafan63fc5e22022-08-04 22:04:10 +0100351 if (member.user.bot) throw "I cannot warn bots";
pineafan8b4b17f2022-02-27 20:42:52 +0000352 // Allow the owner to warn anyone
pineafan63fc5e22022-08-04 22:04:10 +0100353 if (member.id === interaction.guild.ownerId) return true;
pineafan8b4b17f2022-02-27 20:42:52 +0000354 // Check if the user has moderate_members permission
Skyler Grey75ea9172022-08-06 10:22:23 +0100355 if (!member.permissions.has("MODERATE_MEMBERS"))
356 throw "You do not have the *Moderate Members* permission";
pineafan8b4b17f2022-02-27 20:42:52 +0000357 // Check if the user is below on the role list
Skyler Grey75ea9172022-08-06 10:22:23 +0100358 if (!(memberPos > applyPos))
359 throw "You do not have a role higher than that member";
pineafan8b4b17f2022-02-27 20:42:52 +0000360 // Allow warn
pineafan63fc5e22022-08-04 22:04:10 +0100361 return true;
362};
pineafan4f164f32022-02-26 22:07:12 +0000363
Skyler Grey75ea9172022-08-06 10:22:23 +0100364export { command, callback, check };