blob: 09b8914ca841c677d449f427570986e6d5428aa0 [file] [log] [blame]
Samuel Shuert27bf3cd2023-03-03 15:51:25 -05001import type Discord from "discord.js";
2import { ActionRowBuilder,
3 AnySelectMenuInteraction,
4 APIMessageComponentEmoji,
5 ButtonBuilder,
6 ButtonInteraction,
7 ButtonStyle,
8 ChannelSelectMenuBuilder,
9 ChannelSelectMenuInteraction,
10 CommandInteraction,
11 Message,
12 ModalBuilder,
13 RoleSelectMenuBuilder,
14 RoleSelectMenuInteraction,
15 StringSelectMenuBuilder,
16 StringSelectMenuInteraction,
17 StringSelectMenuOptionBuilder,
18 TextInputBuilder,
19 TextInputStyle,
20 UserSelectMenuBuilder,
21 UserSelectMenuInteraction
22} from "discord.js";
23import type { SlashCommandSubcommandBuilder } from "discord.js";
24import { LoadingEmbed } from "../../utils/defaults.js";
25import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
26import client from "../../utils/client.js";
27import getEmojiByName from "../../utils/getEmojiByName.js";
28import { modalInteractionCollector } from "../../utils/dualCollector.js";
29import listToAndMore from "../../utils/listToAndMore.js";
30
31
32const command = (builder: SlashCommandSubcommandBuilder) =>
33 builder.setName("automod").setDescription("Setting for automatic moderation features");
34
35
36const emojiFromBoolean = (bool: boolean, id?: string) => bool ? getEmojiByName("CONTROL.TICK", id) : getEmojiByName("CONTROL.CROSS", id);
37
38const toSelectMenu = async (interaction: StringSelectMenuInteraction, m: Message, ids: string[], type: "member" | "role" | "channel", title: string): Promise<string[]> => {
39
40 const back = new ActionRowBuilder<ButtonBuilder>().addComponents(new ButtonBuilder().setCustomId("back").setLabel("Back").setStyle(ButtonStyle.Secondary).setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji));
41 let closed;
42 do {
43 let render: string[] = []
44 let mapped: string[] = [];
45 let menu: UserSelectMenuBuilder | RoleSelectMenuBuilder | ChannelSelectMenuBuilder;
46 switch(type) {
47 case "member": {
48 menu = new UserSelectMenuBuilder().setCustomId("user").setPlaceholder("Select users").setMaxValues(25);
49 mapped = await Promise.all(ids.map(async (id) => { return (await client.users.fetch(id).then(user => user.tag)) || "Unknown User" }));
50 render = ids.map(id => client.logger.renderUser(id))
51 break;
52 }
53 case "role": {
54 menu = new RoleSelectMenuBuilder().setCustomId("role").setPlaceholder("Select roles").setMaxValues(25);
55 mapped = await Promise.all(ids.map(async (id) => { return (await interaction.guild!.roles.fetch(id).then(role => role?.name ?? "Unknown Role"))}));
56 render = ids.map(id => client.logger.renderRole(id, interaction.guild!))
57 break;
58 }
59 case "channel": {
60 menu = new ChannelSelectMenuBuilder().setCustomId("channel").setPlaceholder("Select channels").setMaxValues(25);
61 mapped = await Promise.all(ids.map(async (id) => { return (await interaction.guild!.channels.fetch(id).then(channel => channel?.name ?? "Unknown Role")) }));
62 render = ids.map(id => client.logger.renderChannel(id))
63 break;
64 }
65 }
66 const removeOptions = new ActionRowBuilder<StringSelectMenuBuilder>()
67 .addComponents(
68 new StringSelectMenuBuilder()
69 .setCustomId("remove")
70 .setPlaceholder("Remove")
71 .addOptions(mapped.map((name, i) => new StringSelectMenuOptionBuilder().setLabel(name).setValue(ids[i]!)))
72 .setDisabled(ids.length === 0)
73 );
74
75 const embed = new EmojiEmbed()
76 .setTitle(title)
77 .setEmoji(getEmojiByName("GUILD.SETTINGS.GREEN"))
78 .setDescription(`Select ${type}s:\n\nCurrent:\n` + (render.length > 0 ? render.join("\n") : "None"))
79 .setStatus("Success");
80 const components: ActionRowBuilder<
81 StringSelectMenuBuilder |
82 ButtonBuilder |
83 ChannelSelectMenuBuilder |
84 UserSelectMenuBuilder |
85 RoleSelectMenuBuilder
86 >[] = [new ActionRowBuilder<typeof menu>().addComponents(menu)]
87 if(ids.length > 0) components.push(removeOptions);
88 components.push(back);
89
90 await interaction.editReply({embeds: [embed], components: components})
91
92 let i: AnySelectMenuInteraction | ButtonInteraction;
93 try {
94 i = await m.awaitMessageComponent({filter: i => i.user.id === interaction.user.id, time: 300000});
95 } catch(e) {
96 closed = true;
97 continue;
98 }
99
100 if(i.isButton()) {
101 await i.deferUpdate();
102 if(i.customId === "back") {
103 closed = true;
104 break;
105 }
106 } else if(i.isStringSelectMenu()) {
107 await i.deferUpdate();
108 if(i.customId === "remove") {
109 ids = ids.filter(id => id !== (i as StringSelectMenuInteraction).values[0]);
110 if(ids.length === 0) {
111 menu.data.disabled = true;
112 }
113 }
114 } else {
115 await i.deferUpdate();
116 if(i.customId === "user") {
117 ids = ids.concat((i as UserSelectMenuInteraction).values);
118 } else if(i.customId === "role") {
119 ids = ids.concat((i as RoleSelectMenuInteraction).values);
120 } else if(i.customId === "channel") {
121 ids = ids.concat((i as ChannelSelectMenuInteraction).values);
122 }
123 }
124
125 } while(!closed)
126 return ids;
127}
128
129const imageMenu = async (interaction: StringSelectMenuInteraction, m: Message, current: {
130 NSFW: boolean,
131 size: boolean
132}): Promise<{NSFW: boolean, size: boolean}> => {
133 let closed = false;
134 do {
135 const options = new ActionRowBuilder<ButtonBuilder>()
136 .addComponents(
137 new ButtonBuilder()
138 .setCustomId("back")
139 .setLabel("Back")
140 .setStyle(ButtonStyle.Secondary)
141 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji),
142 new ButtonBuilder()
143 .setCustomId("nsfw")
144 .setLabel("NSFW")
145 .setStyle(current.NSFW ? ButtonStyle.Success : ButtonStyle.Danger)
146 .setEmoji(emojiFromBoolean(current.NSFW, "id") as APIMessageComponentEmoji),
147 new ButtonBuilder()
148 .setCustomId("size")
149 .setLabel("Size")
150 .setStyle(current.size ? ButtonStyle.Success : ButtonStyle.Danger)
151 .setEmoji(emojiFromBoolean(current.size, "id") as APIMessageComponentEmoji)
152 )
153
154 const embed = new EmojiEmbed()
155 .setTitle("Image Settings")
156 .setDescription(
157 `${emojiFromBoolean(current.NSFW)} **NSFW**\n` +
158 `${emojiFromBoolean(current.size)} **Size**\n`
159 )
160
161 await interaction.editReply({embeds: [embed], components: [options]});
162
163 let i: ButtonInteraction;
164 try {
165 i = await m.awaitMessageComponent({filter: (i) => interaction.user.id === i.user.id && i.message.id === m.id, time: 300000}) as ButtonInteraction;
166 } catch (e) {
167 return current;
168 }
169 await i.deferUpdate();
170 switch(i.customId) {
171 case "back": {
172 closed = true;
173 break;
174 }
175 case "nsfw": {
176 current.NSFW = !current.NSFW;
177 break;
178 }
179 case "size": {
180 current.size = !current.size;
181 break;
182 }
183 }
184 } while(!closed);
185 return current;
186}
187
188const wordMenu = async (interaction: StringSelectMenuInteraction, m: Message, current: {
189 enabled: boolean,
190 words: {strict: string[], loose: string[]},
191 allowed: {users: string[], roles: string[], channels: string[]}
192}): Promise<{
193 enabled: boolean,
194 words: {strict: string[], loose: string[]},
195 allowed: {users: string[], roles: string[], channels: string[]}
196}> => {
197 let closed = false;
198 do {
199 const buttons = new ActionRowBuilder<ButtonBuilder>()
200 .addComponents(
201 new ButtonBuilder()
202 .setCustomId("back")
203 .setLabel("Back")
204 .setStyle(ButtonStyle.Secondary)
205 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji),
206 new ButtonBuilder()
207 .setCustomId("enabled")
208 .setLabel("Enabled")
209 .setStyle(current.enabled ? ButtonStyle.Success : ButtonStyle.Danger)
210 .setEmoji(emojiFromBoolean(current.enabled, "id") as APIMessageComponentEmoji),
211 );
212
213 const selectMenu = new ActionRowBuilder<StringSelectMenuBuilder>()
214 .addComponents(
215 new StringSelectMenuBuilder()
216 .setCustomId("edit")
217 .setPlaceholder("Edit... ")
218 .addOptions(
219 new StringSelectMenuOptionBuilder()
220 .setLabel("Words")
221 .setDescription("Edit your list of words to filter")
222 .setValue("words"),
223 new StringSelectMenuOptionBuilder()
224 .setLabel("Allowed Users")
225 .setDescription("Users who will be unaffected by the word filter")
226 .setValue("allowedUsers"),
227 new StringSelectMenuOptionBuilder()
228 .setLabel("Allowed Roles")
229 .setDescription("Roles that will be unaffected by the word filter")
230 .setValue("allowedRoles"),
231 new StringSelectMenuOptionBuilder()
232 .setLabel("Allowed Channels")
233 .setDescription("Channels where the word filter will not apply")
234 .setValue("allowedChannels")
235 )
236 .setDisabled(!current.enabled)
237 );
238
239 const embed = new EmojiEmbed()
240 .setTitle("Word Filters")
241 .setDescription(
242 `${emojiFromBoolean(current.enabled)} **Enabled**\n` +
243 `**Strict Words:** ${listToAndMore(current.words.strict, 5)}\n` +
244 `**Loose Words:** ${listToAndMore(current.words.loose, 5)}\n\n` +
245 `**Users:** ` + listToAndMore(current.allowed.users.map(user => `<@${user}>`), 5) + `\n` +
246 `**Roles:** ` + listToAndMore(current.allowed.roles.map(role => `<@&${role}>`), 5) + `\n` +
247 `**Channels:** ` + listToAndMore(current.allowed.channels.map(channel => `<#${channel}>`), 5)
248 )
249 .setStatus("Success")
250 .setEmoji("GUILD.SETTINGS.GREEN")
251
252 await interaction.editReply({embeds: [embed], components: [selectMenu, buttons]});
253
254 let i: ButtonInteraction | StringSelectMenuInteraction;
255 try {
256 i = await m.awaitMessageComponent({filter: (i) => interaction.user.id === i.user.id && i.message.id === m.id, time: 300000}) as ButtonInteraction | StringSelectMenuInteraction;
257 } catch (e) {
258 closed = true;
259 break;
260 }
261
262 if(i.isButton()) {
263 await i.deferUpdate();
264 switch(i.customId) {
265 case "back": {
266 closed = true;
267 break;
268 }
269 case "enabled": {
270 current.enabled = !current.enabled;
271 break;
272 }
273 }
274 } else {
275 switch(i.values[0]) {
276 case "words": {
277 await interaction.editReply({embeds: [new EmojiEmbed()
278 .setTitle("Word Filter")
279 .setDescription("Modal opened. If you can't see it, click back and try again.")
280 .setStatus("Success")
281 .setEmoji("GUILD.SETTINGS.GREEN")
282 ], components: [new ActionRowBuilder<ButtonBuilder>().addComponents(new ButtonBuilder()
283 .setLabel("Back")
284 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
285 .setStyle(ButtonStyle.Primary)
286 .setCustomId("back")
287 )]})
288 const modal = new ModalBuilder()
289 .setTitle("Word Filter")
290 .setCustomId("wordFilter")
291 .addComponents(
292 new ActionRowBuilder<TextInputBuilder>()
293 .addComponents(
294 new TextInputBuilder()
295 .setCustomId("wordStrict")
296 .setLabel("Strict Words")
297 .setPlaceholder("Matches anywhere in the message, including surrounded by other characters")
298 .setValue(current.words.strict.join(", "))
299 .setStyle(TextInputStyle.Paragraph)
300 .setRequired(false)
301 ),
302 new ActionRowBuilder<TextInputBuilder>()
303 .addComponents(
304 new TextInputBuilder()
305 .setCustomId("wordLoose")
306 .setLabel("Loose Words")
307 .setPlaceholder("Matches only if the word is by itself, surrounded by spaces or punctuation")
308 .setValue(current.words.loose.join(", "))
309 .setStyle(TextInputStyle.Paragraph)
310 .setRequired(false)
311 )
312 )
313
314 await i.showModal(modal);
315 let out;
316 try {
317 out = await modalInteractionCollector(m, interaction.user);
318 } catch (e) {
319 break;
320 }
321 if (!out) break;
322 if(out.isButton()) break;
323 current.words.strict = out.fields.getTextInputValue("wordStrict")
324 .split(",").map(s => s.trim()).filter(s => s.length > 0);
325 current.words.loose = out.fields.getTextInputValue("wordLoose")
326 .split(",").map(s => s.trim()).filter(s => s.length > 0);
327 break;
328 }
329 case "allowedUsers": {
330 await i.deferUpdate();
331 current.allowed.users = await toSelectMenu(interaction, m, current.allowed.users, "member", "Word Filter");
332 break;
333 }
334 case "allowedRoles": {
335 await i.deferUpdate();
336 current.allowed.roles = await toSelectMenu(interaction, m, current.allowed.roles, "role", "Word Filter");
337 break;
338 }
339 case "allowedChannels": {
340 await i.deferUpdate();
341 current.allowed.channels = await toSelectMenu(interaction, m, current.allowed.channels, "channel", "Word Filter");
342 break;
343 }
344 }
345 }
346 } while(!closed);
347 return current;
348}
349
350const inviteMenu = async (interaction: StringSelectMenuInteraction, m: Message, current: {
351 enabled: boolean,
352 allowed: {users: string[], roles: string[], channels: string[]}
353}): Promise<{
354 enabled: boolean,
355 allowed: {users: string[], roles: string[], channels: string[]}
356}> => {
357
358 let closed = false;
359 do {
360 const buttons = new ActionRowBuilder<ButtonBuilder>()
361 .addComponents(
362 new ButtonBuilder()
363 .setCustomId("back")
364 .setLabel("Back")
365 .setStyle(ButtonStyle.Secondary)
366 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji),
367 new ButtonBuilder()
368 .setCustomId("enabled")
369 .setLabel(current.enabled ? "Enabled" : "Disabled")
370 .setStyle(current.enabled ? ButtonStyle.Success : ButtonStyle.Danger)
371 .setEmoji(emojiFromBoolean(current.enabled, "id") as APIMessageComponentEmoji)
372 );
373 const menu = new ActionRowBuilder<StringSelectMenuBuilder>()
374 .addComponents(
375 new StringSelectMenuBuilder()
376 .setCustomId("toEdit")
377 .setPlaceholder("Edit your allow list")
378 .addOptions(
379 new StringSelectMenuOptionBuilder()
380 .setLabel("Users")
381 .setDescription("Users that are allowed to send invites")
382 .setValue("users"),
383 new StringSelectMenuOptionBuilder()
384 .setLabel("Roles")
385 .setDescription("Roles that are allowed to send invites")
386 .setValue("roles"),
387 new StringSelectMenuOptionBuilder()
388 .setLabel("Channels")
389 .setDescription("Channels that anyone is allowed to send invites in")
390 .setValue("channels")
391 ).setDisabled(!current.enabled)
392 )
393
394 const embed = new EmojiEmbed()
395 .setTitle("Invite Settings")
396 .setDescription(
397 "Automatically deletes invites sent by users (outside of staff members and self promotion channels)" + `\n\n` +
398 `${emojiFromBoolean(current.enabled)} **${current.enabled ? "Enabled" : "Disabled"}**\n\n` +
399 `**Users:** ` + listToAndMore(current.allowed.users.map(user => `<@${user}>`), 5) + `\n` +
400 `**Roles:** ` + listToAndMore(current.allowed.roles.map(role => `<@&${role}>`), 5) + `\n` +
401 `**Channels:** ` + listToAndMore(current.allowed.channels.map(channel => `<#${channel}>`), 5)
402 )
403 .setStatus("Success")
404 .setEmoji("GUILD.SETTINGS.GREEN")
405
406
407 await interaction.editReply({embeds: [embed], components: [menu, buttons]});
408
409 let i: ButtonInteraction | StringSelectMenuInteraction;
410 try {
411 i = await m.awaitMessageComponent({filter: (i) => interaction.user.id === i.user.id && i.message.id === m.id, time: 300000}) as ButtonInteraction | StringSelectMenuInteraction;
412 } catch (e) {
413 return current;
414 }
415
416 if(i.isButton()) {
417 await i.deferUpdate();
418 switch(i.customId) {
419 case "back": {
420 closed = true;
421 break;
422 }
423 case "enabled": {
424 current.enabled = !current.enabled;
425 break;
426 }
427 }
428 } else {
429 await i.deferUpdate();
430 switch(i.values[0]) {
431 case "users": {
432 current.allowed.users = await toSelectMenu(interaction, m, current.allowed.users, "member", "Invite Settings");
433 break;
434 }
435 case "roles": {
436 current.allowed.roles = await toSelectMenu(interaction, m, current.allowed.roles, "role", "Invite Settings");
437 break;
438 }
439 case "channels": {
440 current.allowed.channels = await toSelectMenu(interaction, m, current.allowed.channels, "channel", "Invite Settings");
441 break;
442 }
443 }
444 }
445
446 } while(!closed);
447 return current;
448}
449
450const mentionMenu = async (interaction: StringSelectMenuInteraction, m: Message, current: {
451 mass: number,
452 everyone: boolean,
453 roles: boolean,
454 allowed: {
455 roles: string[],
456 rolesToMention: string[],
457 users: string[],
458 channels: string[]
459 }
460}): Promise<{
461 mass: number,
462 everyone: boolean,
463 roles: boolean,
464 allowed: {
465 roles: string[],
466 rolesToMention: string[],
467 users: string[],
468 channels: string[]
469 }
470}> => {
471 let closed = false;
472
473 do {
474
475 const buttons = new ActionRowBuilder<ButtonBuilder>()
476 .addComponents(
477 new ButtonBuilder()
478 .setCustomId("back")
479 .setLabel("Back")
480 .setStyle(ButtonStyle.Secondary)
481 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji),
482 new ButtonBuilder()
483 .setCustomId("everyone")
484 .setLabel(current.everyone ? "Everyone" : "No one")
485 .setStyle(current.everyone ? ButtonStyle.Success : ButtonStyle.Danger)
486 .setEmoji(emojiFromBoolean(current.everyone, "id") as APIMessageComponentEmoji),
487 new ButtonBuilder()
488 .setCustomId("roles")
489 .setLabel(current.roles ? "Roles" : "No roles")
490 .setStyle(current.roles ? ButtonStyle.Success : ButtonStyle.Danger)
491 .setEmoji(emojiFromBoolean(current.roles, "id") as APIMessageComponentEmoji)
492 );
493 const menu = new ActionRowBuilder<StringSelectMenuBuilder>()
494 .addComponents(
495 new StringSelectMenuBuilder()
496 .setCustomId("toEdit")
497 .setPlaceholder("Edit mention settings")
498 .addOptions(
499 new StringSelectMenuOptionBuilder()
500 .setLabel("Mass Mention Amount")
501 .setDescription("The amount of mentions before the bot will delete the message")
502 .setValue("mass"),
503 new StringSelectMenuOptionBuilder()
504 .setLabel("Roles")
505 .setDescription("Roles that are able to be mentioned")
506 .setValue("roles"),
507 )
508 )
509
510 const allowedMenu = new ActionRowBuilder<StringSelectMenuBuilder>()
511 .addComponents(
512 new StringSelectMenuBuilder()
513 .setCustomId("allowed")
514 .setPlaceholder("Edit exceptions")
515 .addOptions(
516 new StringSelectMenuOptionBuilder()
517 .setLabel("Users")
518 .setDescription("Users that are unaffected by the mention filter")
519 .setValue("users"),
520 new StringSelectMenuOptionBuilder()
521 .setLabel("Roles")
522 .setDescription("Roles that are unaffected by the mention filter")
523 .setValue("roles"),
524 new StringSelectMenuOptionBuilder()
525 .setLabel("Channels")
526 .setDescription("Channels where anyone is unaffected by the mention filter")
527 .setValue("channels")
528 )
529 )
530
531 const embed = new EmojiEmbed()
532 .setTitle("Mention Settings")
533 .setDescription(
534 `Log when members mention:\n` +
535 `${emojiFromBoolean(true)} **${current.mass}+ members** in one message\n` +
536 `${emojiFromBoolean(current.everyone)} **Everyone**\n` +
537 `${emojiFromBoolean(current.roles)} **Roles**\n` +
538 (current.allowed.rolesToMention.length > 0 ? `> *Except for ${listToAndMore(current.allowed.rolesToMention.map(r => `<@&${r}>`), 3)}*\n` : "") +
539 "\n" +
540 `Except if...\n` +
541 `> ${current.allowed.users.length > 0 ? `Member is: ${listToAndMore(current.allowed.users.map(u => `<@${u}>`), 3)}\n` : ""}` +
542 `> ${current.allowed.roles.length > 0 ? `Member has role: ${listToAndMore(current.allowed.roles.map(r => `<@&${r}>`), 3)}\n` : ""}` +
543 `> ${current.allowed.channels.length > 0 ? `In channel: ${listToAndMore(current.allowed.channels.map(c => `<#${c}>`), 3)}\n` : ""}`
544 )
545 .setStatus("Success")
546 .setEmoji("GUILD.SETTINGS.GREEN")
547
548 await interaction.editReply({embeds: [embed], components: [menu, allowedMenu, buttons]});
549
550 let i: ButtonInteraction | StringSelectMenuInteraction;
551 try {
552 i = await m.awaitMessageComponent({filter: (i) => interaction.user.id === i.user.id && i.message.id === m.id, time: 300000}) as ButtonInteraction | StringSelectMenuInteraction;
553 } catch (e) {
554 closed = true;
555 break;
556 }
557
558 if(i.isButton()) {
559 await i.deferUpdate();
560 switch (i.customId) {
561 case "back": {
562 closed = true;
563 break;
564 }
565 case "everyone": {
566 current.everyone = !current.everyone;
567 break;
568 }
569 case "roles": {
570 current.roles = !current.roles;
571 break;
572 }
573 }
574 } else {
575 switch (i.customId) {
576 case "toEdit": {
577 switch (i.values[0]) {
578 case "mass": {
579 await interaction.editReply({embeds: [new EmojiEmbed()
580 .setTitle("Word Filter")
581 .setDescription("Modal opened. If you can't see it, click back and try again.")
582 .setStatus("Success")
583 .setEmoji("GUILD.SETTINGS.GREEN")
584 ], components: [new ActionRowBuilder<ButtonBuilder>().addComponents(new ButtonBuilder()
585 .setLabel("Back")
586 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
587 .setStyle(ButtonStyle.Primary)
588 .setCustomId("back")
589 )]})
590 const modal = new ModalBuilder()
591 .setTitle("Mass Mention Amount")
592 .setCustomId("mass")
593 .addComponents(
594 new ActionRowBuilder<TextInputBuilder>()
595 .addComponents(
596 new TextInputBuilder()
597 .setCustomId("mass")
598 .setPlaceholder("Amount")
599 .setMinLength(1)
600 .setMaxLength(3)
601 .setStyle(TextInputStyle.Short)
602 )
603 )
604 await i.showModal(modal);
605 let out;
606 try {
607 out = await modalInteractionCollector(m, interaction.user);
608 } catch (e) {
609 break;
610 }
611 if (!out) break;
612 if(out.isButton()) break;
613 current.mass = parseInt(out.fields.getTextInputValue("mass"));
614 break;
615 }
616 case "roles": {
617 await i.deferUpdate();
618 current.allowed.rolesToMention = await toSelectMenu(interaction, m, current.allowed.rolesToMention, "role", "Mention Settings");
619 break;
620 }
621 }
622 break;
623 }
624 case "allowed": {
625 await i.deferUpdate();
626 switch (i.values[0]) {
627 case "users": {
628 current.allowed.users = await toSelectMenu(interaction, m, current.allowed.users, "member", "Mention Settings");
629 break;
630 }
631 case "roles": {
632 current.allowed.roles = await toSelectMenu(interaction, m, current.allowed.roles, "role", "Mention Settings");
633 break;
634 }
635 case "channels": {
636 current.allowed.channels = await toSelectMenu(interaction, m, current.allowed.channels, "channel", "Mention Settings");
637 break;
638 }
639 }
640 break;
641 }
642 }
643 }
644
645 } while(!closed);
646 return current
647}
648
649const cleanMenu = async (interaction: StringSelectMenuInteraction, m: Message, current?: {
650 channels?: string[],
651 allowed?: {
652 roles: string[],
653 users: string[]
654 }
655}): Promise<{
656 channels: string[],
657 allowed: {
658 roles: string[],
659 users: string[]
660 }
661}> => {
662 let closed = false;
663 if(!current) current = {channels: [], allowed: {roles: [], users: []}};
664 if(!current.channels) current.channels = [];
665 if(!current.allowed) current.allowed = {roles: [], users: []};
666
667 const channelMenu = new ActionRowBuilder<ChannelSelectMenuBuilder>()
668 .addComponents(
669 new ChannelSelectMenuBuilder()
670 .setCustomId("toAdd")
671 .setPlaceholder("Select a channel")
672 )
673
674 const allowedMenu = new ActionRowBuilder<StringSelectMenuBuilder>()
675 .addComponents(
676 new StringSelectMenuBuilder()
677 .setCustomId("allowed")
678 .setPlaceholder("Edit exceptions")
679 .addOptions(
680 new StringSelectMenuOptionBuilder()
681 .setLabel("Users")
682 .setDescription("Users that are unaffected by the mention filter")
683 .setValue("users"),
684 new StringSelectMenuOptionBuilder()
685 .setLabel("Roles")
686 .setDescription("Roles that are unaffected by the mention filter")
687 .setValue("roles")
688 )
689 )
690
691 do {
692
693 const buttons = new ActionRowBuilder<ButtonBuilder>()
694 .addComponents(
695 new ButtonBuilder()
696 .setCustomId("back")
697 .setLabel("Back")
698 .setStyle(ButtonStyle.Primary)
699 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
700 )
701
702 const embed = new EmojiEmbed()
703 .setTitle("Clean Settings")
704 .setEmoji("GUILD.SETTINGS.GREEN")
705 .setDescription(
706 `Current clean channels:\n\n` +
707 `${current.channels.length > 0 ? listToAndMore(current.channels.map(c => `<#${c}>`), 10) : "None"}\n\n`
708 )
709 .setStatus("Success")
710
711
712 await interaction.editReply({embeds: [embed], components: [channelMenu, allowedMenu, buttons]});
713
714 let i: ButtonInteraction | ChannelSelectMenuInteraction;
715 try {
716 i = await m.awaitMessageComponent({filter: (i) => interaction.user.id === i.user.id && i.message.id === m.id, time: 300000}) as ButtonInteraction | ChannelSelectMenuInteraction;
717 } catch (e) {
718 closed = true;
719 break;
720 }
721 await i.deferUpdate();
722 if(i.isButton()) {
723 switch (i.customId) {
724 case "back": {
725 closed = true;
726 break;
727 }
728 }
729 } else {
730 switch (i.customId) {
731 case "toAdd": {
732 const channelEmbed = new EmojiEmbed()
733 .setTitle("Clean Settings")
734 .setDescription(`Editing <#${i.values[0]}>`)
735 .setEmoji("GUILD.SETTINGS.GREEN")
736 .setStatus("Success")
737 const channelButtons = new ActionRowBuilder<ButtonBuilder>()
738 .addComponents(
739 new ButtonBuilder()
740 .setCustomId("back")
741 .setLabel("Back")
742 .setStyle(ButtonStyle.Primary)
743 .setEmoji(getEmojiByName("CONTROL.LEFT", "id")),
744 new ButtonBuilder()
745 .setCustomId("switch")
746 .setLabel(current.channels.includes(i.values[0]!) ? "Remove" : "Add")
747 .setStyle(current.channels.includes(i.values[0]!) ? ButtonStyle.Danger : ButtonStyle.Success)
748 )
749
750 await i.editReply({embeds: [channelEmbed], components: [channelButtons]});
751 let j: ButtonInteraction;
752 try {
753 j = await m.awaitMessageComponent({filter: (i) => interaction.user.id === i.user.id && i.message.id === m.id, time: 300000}) as ButtonInteraction;
754 } catch (e) {
755 closed = true;
756 break;
757 }
758 await j.deferUpdate();
759 switch (j.customId) {
760 case "back": {
761 break;
762 }
763 case "switch": {
764 if(current.channels.includes(i.values[0]!)) {
765 current.channels.splice(current.channels.indexOf(i.values[0]!), 1);
766 } else {
767 current.channels.push(i.values[0]!);
768 }
769 }
770 }
771 break;
772 }
773 case "allowed": {
774 switch (i.values[0]) {
775 case "users": {
776 current.allowed.users = await toSelectMenu(interaction, m, current.allowed.users, "member", "Mention Settings");
777 break;
778 }
779 case "roles": {
780 current.allowed.roles = await toSelectMenu(interaction, m, current.allowed.roles, "role", "Mention Settings");
781 break;
782 }
783 }
784 break;
785 }
786 }
787 }
788
789 } while(!closed);
790
791 return current as {
792 channels: string[],
793 allowed: {
794 roles: string[],
795 users: string[]
796 }
797 };
798
799}
800
801const callback = async (interaction: CommandInteraction): Promise<void> => {
802 if (!interaction.guild) return;
803 const m = await interaction.reply({embeds: LoadingEmbed, fetchReply: true, ephemeral: true});
804 const config = (await client.database.guilds.read(interaction.guild.id)).filters;
805
806 let closed = false;
807
808 const button = new ActionRowBuilder<ButtonBuilder>()
809 .addComponents(
810 new ButtonBuilder()
811 .setCustomId("save")
812 .setLabel("Save")
813 .setStyle(ButtonStyle.Success)
814 )
815
816 do {
817
818 const selectMenu = new ActionRowBuilder<StringSelectMenuBuilder>()
819 .addComponents(
820 new StringSelectMenuBuilder()
821 .setCustomId("filter")
822 .setPlaceholder("Select a filter to edit")
823 .addOptions(
824 new StringSelectMenuOptionBuilder()
825 .setLabel("Invites")
826 .setDescription("Automatically delete messages containing server invites")
827 .setValue("invites"),
828 new StringSelectMenuOptionBuilder()
829 .setLabel("Mentions")
830 .setDescription("Deletes messages with excessive mentions")
831 .setValue("mentions"),
832 new StringSelectMenuOptionBuilder()
833 .setLabel("Words")
834 .setDescription("Delete messages containing filtered words")
835 .setValue("words"),
836 new StringSelectMenuOptionBuilder()
837 .setLabel("Malware")
838 .setDescription("Automatically delete files and links containing malware")
839 .setValue("malware"),
840 new StringSelectMenuOptionBuilder()
841 .setLabel("Images")
842 .setDescription("Checks performed on images (NSFW, size checking, etc.)")
843 .setValue("images"),
844 new StringSelectMenuOptionBuilder()
845 .setLabel("Clean")
846 .setDescription("Automatically delete new messages in specific channels")
847 .setValue("clean")
848 )
849 );
850
851 const embed = new EmojiEmbed()
852 .setTitle("Automod Settings")
853 .setDescription(
854 `${emojiFromBoolean(config.invite.enabled)} **Invites**\n` +
855 `${emojiFromBoolean(config.pings.everyone || config.pings.mass > 0 || config.pings.roles)} **Mentions**\n` +
856 `${emojiFromBoolean(config.wordFilter.enabled)} **Words**\n` +
857 `${emojiFromBoolean(config.malware)} **Malware**\n` +
858 `${emojiFromBoolean(config.images.NSFW || config.images.size)} **Images**\n` +
859 `${emojiFromBoolean(config.clean.channels.length > 0)} **Clean**\n`
860 )
861 .setStatus("Success")
862 .setEmoji("GUILD.SETTINGS.GREEN")
863
864
865 await interaction.editReply({embeds: [embed], components: [selectMenu, button]});
866
867 let i: StringSelectMenuInteraction | ButtonInteraction;
868 try {
869 i = await m.awaitMessageComponent({filter: (i) => i.user.id === interaction.user.id && i.message.id === m.id, time: 300000}) as StringSelectMenuInteraction | ButtonInteraction;
870 } catch (e) {
871 closed = true;
872 continue;
873 }
874 await i.deferUpdate();
875 if(i.isButton()) {
876 await client.database.guilds.write(interaction.guild.id, {filters: config});
877 } else {
878 switch(i.values[0]) {
879 case "invites": {
880 config.invite = await inviteMenu(i, m, config.invite);
881 break;
882 }
883 case "mentions": {
884 config.pings = await mentionMenu(i, m, config.pings);
885 break;
886 }
887 case "words": {
888 config.wordFilter = await wordMenu(i, m, config.wordFilter);
889 break;
890 }
891 case "malware": {
892 config.malware = !config.malware;
893 break;
894 }
895 case "images": {
896 const next = await imageMenu(i, m, config.images);
897 config.images = next;
898 break;
899 }
900 case "clean": {
901 const next = await cleanMenu(i, m, config.clean);
902 config.clean = next;
903 break;
904 }
905 }
906 }
907
908 } while(!closed);
909 await interaction.deleteReply()
910
911};
912
913const check = (interaction: CommandInteraction, _partial: boolean = false) => {
914 const member = interaction.member as Discord.GuildMember;
915 if (!member.permissions.has("ManageMessages"))
916 return "You must have the *Manage Messages* permission to use this command";
917 return true;
918};
919
920export { command };
921export { callback };
922export { check };