blob: 50a27453cf8fdcf2dc4573c05cfe73368f546361 [file] [log] [blame]
TheCodedProf8b3da212023-02-02 15:09:55 -05001import type Discord from "discord.js";
Skyler Greyda16adf2023-03-05 10:22:12 +00002import {
3 ActionRowBuilder,
TheCodedProf5b53a8c2023-02-03 15:40:26 -05004 AnySelectMenuInteraction,
5 APIMessageComponentEmoji,
6 ButtonBuilder,
7 ButtonInteraction,
8 ButtonStyle,
9 ChannelSelectMenuBuilder,
10 ChannelSelectMenuInteraction,
11 CommandInteraction,
TheCodedProf5b53a8c2023-02-03 15:40:26 -050012 Message,
TheCodedProf5b53a8c2023-02-03 15:40:26 -050013 ModalBuilder,
TheCodedProf5b53a8c2023-02-03 15:40:26 -050014 RoleSelectMenuBuilder,
15 RoleSelectMenuInteraction,
16 StringSelectMenuBuilder,
17 StringSelectMenuInteraction,
18 StringSelectMenuOptionBuilder,
19 TextInputBuilder,
20 TextInputStyle,
21 UserSelectMenuBuilder,
22 UserSelectMenuInteraction
23} from "discord.js";
TheCodedProf8b3da212023-02-02 15:09:55 -050024import type { SlashCommandSubcommandBuilder } from "discord.js";
25import { LoadingEmbed } from "../../utils/defaults.js";
26import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
27import client from "../../utils/client.js";
28import getEmojiByName from "../../utils/getEmojiByName.js";
TheCodedProf5b53a8c2023-02-03 15:40:26 -050029import { modalInteractionCollector } from "../../utils/dualCollector.js";
TheCodedProf1f675042023-02-16 17:01:29 -050030import listToAndMore from "../../utils/listToAndMore.js";
TheCodedProf35e73712023-03-10 17:35:35 -050031import _ from "lodash";
TheCodedProf8b3da212023-02-02 15:09:55 -050032
33const command = (builder: SlashCommandSubcommandBuilder) =>
34 builder.setName("automod").setDescription("Setting for automatic moderation features");
35
Skyler Greyda16adf2023-03-05 10:22:12 +000036const emojiFromBoolean = (bool: boolean, id?: string) =>
37 bool ? getEmojiByName("CONTROL.TICK", id) : getEmojiByName("CONTROL.CROSS", id);
TheCodedProf8b3da212023-02-02 15:09:55 -050038
Skyler Greyda16adf2023-03-05 10:22:12 +000039const toSelectMenu = async (
40 interaction: StringSelectMenuInteraction,
41 m: Message,
42 ids: string[],
43 type: "member" | "role" | "channel",
44 title: string
45): Promise<string[]> => {
46 const back = new ActionRowBuilder<ButtonBuilder>().addComponents(
47 new ButtonBuilder()
48 .setCustomId("back")
49 .setLabel("Back")
50 .setStyle(ButtonStyle.Secondary)
51 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji)
52 );
TheCodedProf486bca32023-02-02 16:49:44 -050053 let closed;
54 do {
Skyler Greyda16adf2023-03-05 10:22:12 +000055 let render: string[] = [];
TheCodedProf486bca32023-02-02 16:49:44 -050056 let mapped: string[] = [];
57 let menu: UserSelectMenuBuilder | RoleSelectMenuBuilder | ChannelSelectMenuBuilder;
Skyler Greyda16adf2023-03-05 10:22:12 +000058 switch (type) {
PineaFanb0d0c242023-02-05 10:59:45 +000059 case "member": {
TheCodedProf486bca32023-02-02 16:49:44 -050060 menu = new UserSelectMenuBuilder().setCustomId("user").setPlaceholder("Select users").setMaxValues(25);
Skyler Greyda16adf2023-03-05 10:22:12 +000061 mapped = await Promise.all(
62 ids.map(async (id) => {
63 return (await client.users.fetch(id).then((user) => user.tag)) || "Unknown User";
64 })
65 );
66 render = ids.map((id) => client.logger.renderUser(id));
TheCodedProf486bca32023-02-02 16:49:44 -050067 break;
PineaFanb0d0c242023-02-05 10:59:45 +000068 }
69 case "role": {
TheCodedProf486bca32023-02-02 16:49:44 -050070 menu = new RoleSelectMenuBuilder().setCustomId("role").setPlaceholder("Select roles").setMaxValues(25);
Skyler Greyda16adf2023-03-05 10:22:12 +000071 mapped = await Promise.all(
72 ids.map(async (id) => {
73 return await interaction.guild!.roles.fetch(id).then((role) => role?.name ?? "Unknown Role");
74 })
75 );
76 render = ids.map((id) => client.logger.renderRole(id, interaction.guild!));
TheCodedProf486bca32023-02-02 16:49:44 -050077 break;
PineaFanb0d0c242023-02-05 10:59:45 +000078 }
79 case "channel": {
Skyler Greyda16adf2023-03-05 10:22:12 +000080 menu = new ChannelSelectMenuBuilder()
81 .setCustomId("channel")
82 .setPlaceholder("Select channels")
83 .setMaxValues(25);
84 mapped = await Promise.all(
85 ids.map(async (id) => {
86 return await interaction
87 .guild!.channels.fetch(id)
88 .then((channel) => channel?.name ?? "Unknown Role");
89 })
90 );
91 render = ids.map((id) => client.logger.renderChannel(id));
TheCodedProf486bca32023-02-02 16:49:44 -050092 break;
PineaFanb0d0c242023-02-05 10:59:45 +000093 }
TheCodedProf486bca32023-02-02 16:49:44 -050094 }
Skyler Greyda16adf2023-03-05 10:22:12 +000095 const removeOptions = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
96 new StringSelectMenuBuilder()
97 .setCustomId("remove")
98 .setPlaceholder("Remove")
99 .addOptions(
100 mapped.map((name, i) => new StringSelectMenuOptionBuilder().setLabel(name).setValue(ids[i]!))
101 )
102 .setDisabled(ids.length === 0)
103 );
TheCodedProf486bca32023-02-02 16:49:44 -0500104
105 const embed = new EmojiEmbed()
106 .setTitle(title)
107 .setEmoji(getEmojiByName("GUILD.SETTINGS.GREEN"))
108 .setDescription(`Select ${type}s:\n\nCurrent:\n` + (render.length > 0 ? render.join("\n") : "None"))
109 .setStatus("Success");
PineaFanb0d0c242023-02-05 10:59:45 +0000110 const components: ActionRowBuilder<
Skyler Greyda16adf2023-03-05 10:22:12 +0000111 | StringSelectMenuBuilder
112 | ButtonBuilder
113 | ChannelSelectMenuBuilder
114 | UserSelectMenuBuilder
115 | RoleSelectMenuBuilder
116 >[] = [new ActionRowBuilder<typeof menu>().addComponents(menu)];
117 if (ids.length > 0) components.push(removeOptions);
TheCodedProf486bca32023-02-02 16:49:44 -0500118 components.push(back);
119
Skyler Greyda16adf2023-03-05 10:22:12 +0000120 await interaction.editReply({ embeds: [embed], components: components });
TheCodedProf486bca32023-02-02 16:49:44 -0500121
122 let i: AnySelectMenuInteraction | ButtonInteraction;
123 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000124 i = await m.awaitMessageComponent({ filter: (i) => i.user.id === interaction.user.id, time: 300000 });
125 } catch (e) {
TheCodedProf486bca32023-02-02 16:49:44 -0500126 closed = true;
PineaFanb0d0c242023-02-05 10:59:45 +0000127 continue;
TheCodedProf486bca32023-02-02 16:49:44 -0500128 }
129
Skyler Greyda16adf2023-03-05 10:22:12 +0000130 if (i.isButton()) {
TheCodedProf486bca32023-02-02 16:49:44 -0500131 await i.deferUpdate();
Skyler Greyda16adf2023-03-05 10:22:12 +0000132 if (i.customId === "back") {
TheCodedProf486bca32023-02-02 16:49:44 -0500133 closed = true;
134 break;
135 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000136 } else if (i.isStringSelectMenu()) {
TheCodedProf486bca32023-02-02 16:49:44 -0500137 await i.deferUpdate();
Skyler Greyda16adf2023-03-05 10:22:12 +0000138 if (i.customId === "remove") {
139 ids = ids.filter((id) => id !== (i as StringSelectMenuInteraction).values[0]);
140 if (ids.length === 0) {
TheCodedProf486bca32023-02-02 16:49:44 -0500141 menu.data.disabled = true;
142 }
143 }
144 } else {
145 await i.deferUpdate();
Skyler Greyda16adf2023-03-05 10:22:12 +0000146 if (i.customId === "user") {
TheCodedProf486bca32023-02-02 16:49:44 -0500147 ids = ids.concat((i as UserSelectMenuInteraction).values);
Skyler Greyda16adf2023-03-05 10:22:12 +0000148 } else if (i.customId === "role") {
TheCodedProf486bca32023-02-02 16:49:44 -0500149 ids = ids.concat((i as RoleSelectMenuInteraction).values);
Skyler Greyda16adf2023-03-05 10:22:12 +0000150 } else if (i.customId === "channel") {
TheCodedProf486bca32023-02-02 16:49:44 -0500151 ids = ids.concat((i as ChannelSelectMenuInteraction).values);
152 }
153 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000154 } while (!closed);
TheCodedProf486bca32023-02-02 16:49:44 -0500155 return ids;
Skyler Greyda16adf2023-03-05 10:22:12 +0000156};
TheCodedProf8b3da212023-02-02 15:09:55 -0500157
Skyler Greyda16adf2023-03-05 10:22:12 +0000158const imageMenu = async (
159 interaction: StringSelectMenuInteraction,
160 m: Message,
TheCodedProf35e73712023-03-10 17:35:35 -0500161 unsavedChanges: boolean,
Skyler Greyda16adf2023-03-05 10:22:12 +0000162 current: {
163 NSFW: boolean;
164 size: boolean;
165 }
166): Promise<{ NSFW: boolean; size: boolean }> => {
TheCodedProf8b3da212023-02-02 15:09:55 -0500167 let closed = false;
168 do {
Skyler Greyda16adf2023-03-05 10:22:12 +0000169 const options = new ActionRowBuilder<ButtonBuilder>().addComponents(
170 new ButtonBuilder()
171 .setCustomId("back")
172 .setLabel("Back")
173 .setStyle(ButtonStyle.Secondary)
174 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji),
175 new ButtonBuilder()
176 .setCustomId("nsfw")
177 .setLabel("NSFW")
178 .setStyle(current.NSFW ? ButtonStyle.Success : ButtonStyle.Danger)
179 .setEmoji(emojiFromBoolean(current.NSFW, "id") as APIMessageComponentEmoji),
180 new ButtonBuilder()
181 .setCustomId("size")
182 .setLabel("Size")
183 .setStyle(current.size ? ButtonStyle.Success : ButtonStyle.Danger)
184 .setEmoji(emojiFromBoolean(current.size, "id") as APIMessageComponentEmoji)
185 );
TheCodedProf8b3da212023-02-02 15:09:55 -0500186
187 const embed = new EmojiEmbed()
188 .setTitle("Image Settings")
pineafan10ac5542023-03-19 10:06:19 +0000189 .setStatus("Success")
190 // .setEmoji("") // TODO
TheCodedProf8b3da212023-02-02 15:09:55 -0500191 .setDescription(
Skyler Greyda16adf2023-03-05 10:22:12 +0000192 `${emojiFromBoolean(current.NSFW)} **NSFW**\n` + `${emojiFromBoolean(current.size)} **Size**\n`
TheCodedProf35e73712023-03-10 17:35:35 -0500193 )
194 .setFooter({
195 text: unsavedChanges ? "No changes made" : "Changes not saved"
196 });
TheCodedProf8b3da212023-02-02 15:09:55 -0500197
Skyler Greyda16adf2023-03-05 10:22:12 +0000198 await interaction.editReply({ embeds: [embed], components: [options] });
TheCodedProf8b3da212023-02-02 15:09:55 -0500199
200 let i: ButtonInteraction;
201 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000202 i = (await m.awaitMessageComponent({
203 filter: (i) => interaction.user.id === i.user.id && i.message.id === m.id,
204 time: 300000
205 })) as ButtonInteraction;
TheCodedProf8b3da212023-02-02 15:09:55 -0500206 } catch (e) {
207 return current;
208 }
209 await i.deferUpdate();
Skyler Greyda16adf2023-03-05 10:22:12 +0000210 switch (i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000211 case "back": {
TheCodedProf8b3da212023-02-02 15:09:55 -0500212 closed = true;
213 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000214 }
215 case "nsfw": {
TheCodedProf8b3da212023-02-02 15:09:55 -0500216 current.NSFW = !current.NSFW;
TheCodedProf35e73712023-03-10 17:35:35 -0500217 unsavedChanges = true;
TheCodedProf8b3da212023-02-02 15:09:55 -0500218 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000219 }
220 case "size": {
TheCodedProf8b3da212023-02-02 15:09:55 -0500221 current.size = !current.size;
TheCodedProf35e73712023-03-10 17:35:35 -0500222 unsavedChanges = true;
TheCodedProf8b3da212023-02-02 15:09:55 -0500223 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000224 }
TheCodedProf8b3da212023-02-02 15:09:55 -0500225 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000226 } while (!closed);
TheCodedProf8b3da212023-02-02 15:09:55 -0500227 return current;
Skyler Greyda16adf2023-03-05 10:22:12 +0000228};
TheCodedProf8b3da212023-02-02 15:09:55 -0500229
Skyler Greyda16adf2023-03-05 10:22:12 +0000230const wordMenu = async (
231 interaction: StringSelectMenuInteraction,
232 m: Message,
TheCodedProf35e73712023-03-10 17:35:35 -0500233 unsavedChanges: boolean,
Skyler Greyda16adf2023-03-05 10:22:12 +0000234 current: {
235 enabled: boolean;
236 words: { strict: string[]; loose: string[] };
237 allowed: { users: string[]; roles: string[]; channels: string[] };
238 }
239): Promise<{
240 enabled: boolean;
241 words: { strict: string[]; loose: string[] };
242 allowed: { users: string[]; roles: string[]; channels: string[] };
TheCodedProf8b3da212023-02-02 15:09:55 -0500243}> => {
TheCodedProf486bca32023-02-02 16:49:44 -0500244 let closed = false;
245 do {
Skyler Greyda16adf2023-03-05 10:22:12 +0000246 const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
247 new ButtonBuilder()
248 .setCustomId("back")
249 .setLabel("Back")
250 .setStyle(ButtonStyle.Secondary)
251 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji),
252 new ButtonBuilder()
253 .setCustomId("enabled")
254 .setLabel("Enabled")
255 .setStyle(current.enabled ? ButtonStyle.Success : ButtonStyle.Danger)
256 .setEmoji(emojiFromBoolean(current.enabled, "id") as APIMessageComponentEmoji)
257 );
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500258
Skyler Greyda16adf2023-03-05 10:22:12 +0000259 const selectMenu = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
260 new StringSelectMenuBuilder()
261 .setCustomId("edit")
262 .setPlaceholder("Edit... ")
263 .addOptions(
264 new StringSelectMenuOptionBuilder()
265 .setLabel("Words")
266 .setDescription("Edit your list of words to filter")
267 .setValue("words"),
268 new StringSelectMenuOptionBuilder()
269 .setLabel("Allowed Users")
270 .setDescription("Users who will be unaffected by the word filter")
271 .setValue("allowedUsers"),
272 new StringSelectMenuOptionBuilder()
273 .setLabel("Allowed Roles")
274 .setDescription("Roles that will be unaffected by the word filter")
275 .setValue("allowedRoles"),
276 new StringSelectMenuOptionBuilder()
277 .setLabel("Allowed Channels")
278 .setDescription("Channels where the word filter will not apply")
279 .setValue("allowedChannels")
280 )
281 .setDisabled(!current.enabled)
282 );
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500283
284 const embed = new EmojiEmbed()
285 .setTitle("Word Filters")
286 .setDescription(
287 `${emojiFromBoolean(current.enabled)} **Enabled**\n` +
Skyler Greyda16adf2023-03-05 10:22:12 +0000288 `**Strict Words:** ${listToAndMore(current.words.strict, 5)}\n` +
289 `**Loose Words:** ${listToAndMore(current.words.loose, 5)}\n\n` +
290 `**Users:** ` +
291 listToAndMore(
292 current.allowed.users.map((user) => `<@${user}>`),
293 5
294 ) +
295 `\n` +
296 `**Roles:** ` +
297 listToAndMore(
298 current.allowed.roles.map((role) => `<@&${role}>`),
299 5
300 ) +
301 `\n` +
302 `**Channels:** ` +
303 listToAndMore(
304 current.allowed.channels.map((channel) => `<#${channel}>`),
305 5
306 )
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500307 )
308 .setStatus("Success")
TheCodedProf35e73712023-03-10 17:35:35 -0500309 .setEmoji("GUILD.SETTINGS.GREEN")
310 .setFooter({
311 text: unsavedChanges ? "No changes made" : "Changes not saved"
312 });
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500313
Skyler Greyda16adf2023-03-05 10:22:12 +0000314 await interaction.editReply({ embeds: [embed], components: [selectMenu, buttons] });
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500315
316 let i: ButtonInteraction | StringSelectMenuInteraction;
317 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000318 i = (await m.awaitMessageComponent({
319 filter: (i) => interaction.user.id === i.user.id && i.message.id === m.id,
320 time: 300000
321 })) as ButtonInteraction | StringSelectMenuInteraction;
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500322 } catch (e) {
323 closed = true;
324 break;
325 }
326
Skyler Greyda16adf2023-03-05 10:22:12 +0000327 if (i.isButton()) {
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500328 await i.deferUpdate();
Skyler Greyda16adf2023-03-05 10:22:12 +0000329 switch (i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000330 case "back": {
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500331 closed = true;
332 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000333 }
334 case "enabled": {
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500335 current.enabled = !current.enabled;
TheCodedProf35e73712023-03-10 17:35:35 -0500336 unsavedChanges = true;
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500337 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000338 }
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500339 }
340 } else {
Skyler Greyda16adf2023-03-05 10:22:12 +0000341 switch (i.values[0]) {
PineaFanb0d0c242023-02-05 10:59:45 +0000342 case "words": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000343 await interaction.editReply({
344 embeds: [
345 new EmojiEmbed()
346 .setTitle("Word Filter")
347 .setDescription("Modal opened. If you can't see it, click back and try again.")
348 .setStatus("Success")
349 .setEmoji("GUILD.SETTINGS.GREEN")
350 ],
351 components: [
352 new ActionRowBuilder<ButtonBuilder>().addComponents(
353 new ButtonBuilder()
354 .setLabel("Back")
355 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
356 .setStyle(ButtonStyle.Primary)
357 .setCustomId("back")
358 )
359 ]
360 });
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500361 const modal = new ModalBuilder()
362 .setTitle("Word Filter")
363 .setCustomId("wordFilter")
364 .addComponents(
Skyler Greyda16adf2023-03-05 10:22:12 +0000365 new ActionRowBuilder<TextInputBuilder>().addComponents(
366 new TextInputBuilder()
367 .setCustomId("wordStrict")
368 .setLabel("Strict Words")
369 .setPlaceholder(
370 "Matches anywhere in the message, including surrounded by other characters"
371 )
372 .setValue(current.words.strict.join(", "))
373 .setStyle(TextInputStyle.Paragraph)
374 .setRequired(false)
375 ),
376 new ActionRowBuilder<TextInputBuilder>().addComponents(
377 new TextInputBuilder()
378 .setCustomId("wordLoose")
379 .setLabel("Loose Words")
380 .setPlaceholder(
381 "Matches only if the word is by itself, surrounded by spaces or punctuation"
382 )
383 .setValue(current.words.loose.join(", "))
384 .setStyle(TextInputStyle.Paragraph)
385 .setRequired(false)
386 )
387 );
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500388
389 await i.showModal(modal);
390 let out;
391 try {
TheCodedProf01cba762023-02-18 15:55:05 -0500392 out = await modalInteractionCollector(m, interaction.user);
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500393 } catch (e) {
394 break;
395 }
396 if (!out) break;
Skyler Greyda16adf2023-03-05 10:22:12 +0000397 if (out.isButton()) break;
398 current.words.strict = out.fields
399 .getTextInputValue("wordStrict")
400 .split(",")
401 .map((s) => s.trim())
402 .filter((s) => s.length > 0);
403 current.words.loose = out.fields
404 .getTextInputValue("wordLoose")
405 .split(",")
406 .map((s) => s.trim())
407 .filter((s) => s.length > 0);
TheCodedProf35e73712023-03-10 17:35:35 -0500408 unsavedChanges = true;
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500409 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000410 }
411 case "allowedUsers": {
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500412 await i.deferUpdate();
Skyler Greyda16adf2023-03-05 10:22:12 +0000413 current.allowed.users = await toSelectMenu(
414 interaction,
415 m,
416 current.allowed.users,
417 "member",
418 "Word Filter"
419 );
TheCodedProf35e73712023-03-10 17:35:35 -0500420 unsavedChanges = true;
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500421 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000422 }
423 case "allowedRoles": {
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500424 await i.deferUpdate();
Skyler Greyda16adf2023-03-05 10:22:12 +0000425 current.allowed.roles = await toSelectMenu(
426 interaction,
427 m,
428 current.allowed.roles,
429 "role",
430 "Word Filter"
431 );
TheCodedProf35e73712023-03-10 17:35:35 -0500432 unsavedChanges = true;
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500433 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000434 }
435 case "allowedChannels": {
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500436 await i.deferUpdate();
Skyler Greyda16adf2023-03-05 10:22:12 +0000437 current.allowed.channels = await toSelectMenu(
438 interaction,
439 m,
440 current.allowed.channels,
441 "channel",
442 "Word Filter"
443 );
TheCodedProf35e73712023-03-10 17:35:35 -0500444 unsavedChanges = true;
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500445 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000446 }
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500447 }
448 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000449 } while (!closed);
TheCodedProf486bca32023-02-02 16:49:44 -0500450 return current;
Skyler Greyda16adf2023-03-05 10:22:12 +0000451};
TheCodedProf8b3da212023-02-02 15:09:55 -0500452
Skyler Greyda16adf2023-03-05 10:22:12 +0000453const inviteMenu = async (
454 interaction: StringSelectMenuInteraction,
455 m: Message,
TheCodedProf35e73712023-03-10 17:35:35 -0500456 unsavedChanges: boolean,
Skyler Greyda16adf2023-03-05 10:22:12 +0000457 current: {
458 enabled: boolean;
459 allowed: { users: string[]; roles: string[]; channels: string[] };
460 }
461): Promise<{
462 enabled: boolean;
463 allowed: { users: string[]; roles: string[]; channels: string[] };
TheCodedProf8b3da212023-02-02 15:09:55 -0500464}> => {
TheCodedProf486bca32023-02-02 16:49:44 -0500465 let closed = false;
466 do {
Skyler Greyda16adf2023-03-05 10:22:12 +0000467 const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
468 new ButtonBuilder()
469 .setCustomId("back")
470 .setLabel("Back")
471 .setStyle(ButtonStyle.Secondary)
472 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji),
473 new ButtonBuilder()
474 .setCustomId("enabled")
475 .setLabel(current.enabled ? "Enabled" : "Disabled")
476 .setStyle(current.enabled ? ButtonStyle.Success : ButtonStyle.Danger)
477 .setEmoji(emojiFromBoolean(current.enabled, "id") as APIMessageComponentEmoji)
478 );
479 const menu = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
480 new StringSelectMenuBuilder()
481 .setCustomId("toEdit")
482 .setPlaceholder("Edit your allow list")
483 .addOptions(
484 new StringSelectMenuOptionBuilder()
485 .setLabel("Users")
486 .setDescription("Users that are allowed to send invites")
487 .setValue("users"),
488 new StringSelectMenuOptionBuilder()
489 .setLabel("Roles")
490 .setDescription("Roles that are allowed to send invites")
491 .setValue("roles"),
492 new StringSelectMenuOptionBuilder()
493 .setLabel("Channels")
494 .setDescription("Channels that anyone is allowed to send invites in")
495 .setValue("channels")
TheCodedProf486bca32023-02-02 16:49:44 -0500496 )
Skyler Greyda16adf2023-03-05 10:22:12 +0000497 .setDisabled(!current.enabled)
498 );
TheCodedProf486bca32023-02-02 16:49:44 -0500499
500 const embed = new EmojiEmbed()
501 .setTitle("Invite Settings")
502 .setDescription(
Skyler Greyda16adf2023-03-05 10:22:12 +0000503 "Automatically deletes invites sent by users (outside of staff members and self promotion channels)" +
504 `\n\n` +
505 `${emojiFromBoolean(current.enabled)} **${current.enabled ? "Enabled" : "Disabled"}**\n\n` +
506 `**Users:** ` +
507 listToAndMore(
508 current.allowed.users.map((user) => `<@${user}>`),
509 5
510 ) +
511 `\n` +
512 `**Roles:** ` +
513 listToAndMore(
514 current.allowed.roles.map((role) => `<@&${role}>`),
515 5
516 ) +
517 `\n` +
518 `**Channels:** ` +
519 listToAndMore(
520 current.allowed.channels.map((channel) => `<#${channel}>`),
521 5
522 )
TheCodedProf486bca32023-02-02 16:49:44 -0500523 )
524 .setStatus("Success")
TheCodedProf35e73712023-03-10 17:35:35 -0500525 .setEmoji("GUILD.SETTINGS.GREEN")
526 .setFooter({
527 text: unsavedChanges ? "No changes made" : "Changes not saved"
528 });
TheCodedProf486bca32023-02-02 16:49:44 -0500529
Skyler Greyda16adf2023-03-05 10:22:12 +0000530 await interaction.editReply({ embeds: [embed], components: [menu, buttons] });
TheCodedProf486bca32023-02-02 16:49:44 -0500531
532 let i: ButtonInteraction | StringSelectMenuInteraction;
533 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000534 i = (await m.awaitMessageComponent({
535 filter: (i) => interaction.user.id === i.user.id && i.message.id === m.id,
536 time: 300000
537 })) as ButtonInteraction | StringSelectMenuInteraction;
TheCodedProf486bca32023-02-02 16:49:44 -0500538 } catch (e) {
539 return current;
540 }
541
Skyler Greyda16adf2023-03-05 10:22:12 +0000542 if (i.isButton()) {
TheCodedProf486bca32023-02-02 16:49:44 -0500543 await i.deferUpdate();
Skyler Greyda16adf2023-03-05 10:22:12 +0000544 switch (i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000545 case "back": {
TheCodedProf486bca32023-02-02 16:49:44 -0500546 closed = true;
547 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000548 }
549 case "enabled": {
TheCodedProf486bca32023-02-02 16:49:44 -0500550 current.enabled = !current.enabled;
TheCodedProf35e73712023-03-10 17:35:35 -0500551 unsavedChanges = true;
TheCodedProf486bca32023-02-02 16:49:44 -0500552 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000553 }
TheCodedProf486bca32023-02-02 16:49:44 -0500554 }
555 } else {
556 await i.deferUpdate();
Skyler Greyda16adf2023-03-05 10:22:12 +0000557 switch (i.values[0]) {
PineaFanb0d0c242023-02-05 10:59:45 +0000558 case "users": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000559 current.allowed.users = await toSelectMenu(
560 interaction,
561 m,
562 current.allowed.users,
563 "member",
564 "Invite Settings"
565 );
TheCodedProf35e73712023-03-10 17:35:35 -0500566 unsavedChanges = true;
TheCodedProf486bca32023-02-02 16:49:44 -0500567 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000568 }
569 case "roles": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000570 current.allowed.roles = await toSelectMenu(
571 interaction,
572 m,
573 current.allowed.roles,
574 "role",
575 "Invite Settings"
576 );
TheCodedProf35e73712023-03-10 17:35:35 -0500577 unsavedChanges = true;
TheCodedProf486bca32023-02-02 16:49:44 -0500578 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000579 }
580 case "channels": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000581 current.allowed.channels = await toSelectMenu(
582 interaction,
583 m,
584 current.allowed.channels,
585 "channel",
586 "Invite Settings"
587 );
TheCodedProf35e73712023-03-10 17:35:35 -0500588 unsavedChanges = true;
TheCodedProf486bca32023-02-02 16:49:44 -0500589 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000590 }
TheCodedProf486bca32023-02-02 16:49:44 -0500591 }
592 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000593 } while (!closed);
TheCodedProf486bca32023-02-02 16:49:44 -0500594 return current;
Skyler Greyda16adf2023-03-05 10:22:12 +0000595};
TheCodedProf8b3da212023-02-02 15:09:55 -0500596
Skyler Greyda16adf2023-03-05 10:22:12 +0000597const mentionMenu = async (
598 interaction: StringSelectMenuInteraction,
599 m: Message,
TheCodedProf35e73712023-03-10 17:35:35 -0500600 unsavedChanges: boolean,
Skyler Greyda16adf2023-03-05 10:22:12 +0000601 current: {
602 mass: number;
603 everyone: boolean;
604 roles: boolean;
605 allowed: {
606 roles: string[];
607 rolesToMention: string[];
608 users: string[];
609 channels: string[];
610 };
TheCodedProf8b3da212023-02-02 15:09:55 -0500611 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000612): Promise<{
613 mass: number;
614 everyone: boolean;
615 roles: boolean;
TheCodedProf8b3da212023-02-02 15:09:55 -0500616 allowed: {
Skyler Greyda16adf2023-03-05 10:22:12 +0000617 roles: string[];
618 rolesToMention: string[];
619 users: string[];
620 channels: string[];
621 };
TheCodedProf8b3da212023-02-02 15:09:55 -0500622}> => {
TheCodedProf486bca32023-02-02 16:49:44 -0500623 let closed = false;
624
625 do {
Skyler Greyda16adf2023-03-05 10:22:12 +0000626 const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
627 new ButtonBuilder()
628 .setCustomId("back")
629 .setLabel("Back")
630 .setStyle(ButtonStyle.Secondary)
631 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji),
632 new ButtonBuilder()
633 .setCustomId("everyone")
634 .setLabel(current.everyone ? "Everyone" : "No one")
635 .setStyle(current.everyone ? ButtonStyle.Success : ButtonStyle.Danger)
636 .setEmoji(emojiFromBoolean(current.everyone, "id") as APIMessageComponentEmoji),
637 new ButtonBuilder()
638 .setCustomId("roles")
639 .setLabel(current.roles ? "Roles" : "No roles")
640 .setStyle(current.roles ? ButtonStyle.Success : ButtonStyle.Danger)
641 .setEmoji(emojiFromBoolean(current.roles, "id") as APIMessageComponentEmoji)
642 );
643 const menu = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
644 new StringSelectMenuBuilder()
645 .setCustomId("toEdit")
646 .setPlaceholder("Edit mention settings")
647 .addOptions(
648 new StringSelectMenuOptionBuilder()
649 .setLabel("Mass Mention Amount")
650 .setDescription("The amount of mentions before the bot will delete the message")
651 .setValue("mass"),
652 new StringSelectMenuOptionBuilder()
653 .setLabel("Roles")
654 .setDescription("Roles that are able to be mentioned")
655 .setValue("roles")
656 )
657 );
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500658
Skyler Greyda16adf2023-03-05 10:22:12 +0000659 const allowedMenu = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
660 new StringSelectMenuBuilder()
661 .setCustomId("allowed")
pineafan9ca34cd2023-03-21 18:45:58 +0000662 .setPlaceholder("Edit allowed list")
Skyler Greyda16adf2023-03-05 10:22:12 +0000663 .addOptions(
664 new StringSelectMenuOptionBuilder()
665 .setLabel("Users")
666 .setDescription("Users that are unaffected by the mention filter")
667 .setValue("users"),
668 new StringSelectMenuOptionBuilder()
669 .setLabel("Roles")
670 .setDescription("Roles that are unaffected by the mention filter")
671 .setValue("roles"),
672 new StringSelectMenuOptionBuilder()
673 .setLabel("Channels")
674 .setDescription("Channels where anyone is unaffected by the mention filter")
675 .setValue("channels")
676 )
677 );
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500678
679 const embed = new EmojiEmbed()
680 .setTitle("Mention Settings")
681 .setDescription(
682 `Log when members mention:\n` +
Skyler Greyda16adf2023-03-05 10:22:12 +0000683 `${emojiFromBoolean(true)} **${current.mass}+ members** in one message\n` +
684 `${emojiFromBoolean(current.everyone)} **Everyone**\n` +
685 `${emojiFromBoolean(current.roles)} **Roles**\n` +
686 (current.allowed.rolesToMention.length > 0
687 ? `> *Except for ${listToAndMore(
pineafan7c2aedd2023-03-27 16:54:52 +0100688 current.allowed.rolesToMention.map((r) => `<@&${r}>`),
689 3
690 )}*\n`
Skyler Greyda16adf2023-03-05 10:22:12 +0000691 : "") +
692 "\n" +
693 `Except if...\n` +
pineafan9ca34cd2023-03-21 18:45:58 +0000694 (current.allowed.users.length > 0
695 ? `> Member is: ${listToAndMore(
pineafan7c2aedd2023-03-27 16:54:52 +0100696 current.allowed.users.map((u) => `<@${u}>`),
697 3
698 )}\n`
pineafan9ca34cd2023-03-21 18:45:58 +0000699 : "") +
700 (current.allowed.roles.length > 0
701 ? `> Member has role: ${listToAndMore(
pineafan7c2aedd2023-03-27 16:54:52 +0100702 current.allowed.roles.map((r) => `<@&${r}>`),
703 3
704 )}\n`
pineafan9ca34cd2023-03-21 18:45:58 +0000705 : "") +
706 (current.allowed.channels.length > 0
707 ? `> In channel: ${listToAndMore(
pineafan7c2aedd2023-03-27 16:54:52 +0100708 current.allowed.channels.map((c) => `<#${c}>`),
709 3
710 )}\n`
pineafan9ca34cd2023-03-21 18:45:58 +0000711 : "") +
pineafan7c2aedd2023-03-27 16:54:52 +0100712 (current.allowed.users.length == 0 ||
713 current.allowed.roles.length == 0 ||
714 current.allowed.channels.length == 0
715 ? "> *No exceptions*\n"
716 : "")
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500717 )
718 .setStatus("Success")
TheCodedProf35e73712023-03-10 17:35:35 -0500719 .setEmoji("GUILD.SETTINGS.GREEN")
720 .setFooter({
721 text: unsavedChanges ? "No changes made" : "Changes not saved"
TheCodedProfca29ebb2023-03-10 17:40:09 -0500722 });
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500723
Skyler Greyda16adf2023-03-05 10:22:12 +0000724 await interaction.editReply({ embeds: [embed], components: [menu, allowedMenu, buttons] });
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500725
726 let i: ButtonInteraction | StringSelectMenuInteraction;
727 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000728 i = (await m.awaitMessageComponent({
729 filter: (i) => interaction.user.id === i.user.id && i.message.id === m.id,
730 time: 300000
731 })) as ButtonInteraction | StringSelectMenuInteraction;
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500732 } catch (e) {
733 closed = true;
734 break;
735 }
736
Skyler Greyda16adf2023-03-05 10:22:12 +0000737 if (i.isButton()) {
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500738 await i.deferUpdate();
739 switch (i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000740 case "back": {
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500741 closed = true;
742 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000743 }
744 case "everyone": {
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500745 current.everyone = !current.everyone;
TheCodedProf35e73712023-03-10 17:35:35 -0500746 unsavedChanges = true;
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500747 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000748 }
749 case "roles": {
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500750 current.roles = !current.roles;
TheCodedProf35e73712023-03-10 17:35:35 -0500751 unsavedChanges = true;
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500752 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000753 }
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500754 }
755 } else {
756 switch (i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000757 case "toEdit": {
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500758 switch (i.values[0]) {
PineaFanb0d0c242023-02-05 10:59:45 +0000759 case "mass": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000760 await interaction.editReply({
761 embeds: [
762 new EmojiEmbed()
763 .setTitle("Word Filter")
764 .setDescription("Modal opened. If you can't see it, click back and try again.")
765 .setStatus("Success")
766 .setEmoji("GUILD.SETTINGS.GREEN")
767 ],
768 components: [
769 new ActionRowBuilder<ButtonBuilder>().addComponents(
770 new ButtonBuilder()
771 .setLabel("Back")
772 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
773 .setStyle(ButtonStyle.Primary)
774 .setCustomId("back")
775 )
776 ]
777 });
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500778 const modal = new ModalBuilder()
779 .setTitle("Mass Mention Amount")
780 .setCustomId("mass")
781 .addComponents(
Skyler Greyda16adf2023-03-05 10:22:12 +0000782 new ActionRowBuilder<TextInputBuilder>().addComponents(
783 new TextInputBuilder()
784 .setCustomId("mass")
785 .setPlaceholder("Amount")
786 .setMinLength(1)
787 .setMaxLength(3)
788 .setStyle(TextInputStyle.Short)
789 )
790 );
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500791 await i.showModal(modal);
792 let out;
793 try {
TheCodedProf01cba762023-02-18 15:55:05 -0500794 out = await modalInteractionCollector(m, interaction.user);
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500795 } catch (e) {
796 break;
797 }
798 if (!out) break;
Skyler Greyda16adf2023-03-05 10:22:12 +0000799 if (out.isButton()) break;
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500800 current.mass = parseInt(out.fields.getTextInputValue("mass"));
TheCodedProf35e73712023-03-10 17:35:35 -0500801 unsavedChanges = true;
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500802 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000803 }
804 case "roles": {
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500805 await i.deferUpdate();
Skyler Greyda16adf2023-03-05 10:22:12 +0000806 current.allowed.rolesToMention = await toSelectMenu(
807 interaction,
808 m,
809 current.allowed.rolesToMention,
810 "role",
811 "Mention Settings"
812 );
TheCodedProf35e73712023-03-10 17:35:35 -0500813 unsavedChanges = true;
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500814 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000815 }
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500816 }
817 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000818 }
819 case "allowed": {
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500820 await i.deferUpdate();
821 switch (i.values[0]) {
PineaFanb0d0c242023-02-05 10:59:45 +0000822 case "users": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000823 current.allowed.users = await toSelectMenu(
824 interaction,
825 m,
826 current.allowed.users,
827 "member",
828 "Mention Settings"
829 );
TheCodedProf35e73712023-03-10 17:35:35 -0500830 unsavedChanges = true;
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500831 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000832 }
833 case "roles": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000834 current.allowed.roles = await toSelectMenu(
835 interaction,
836 m,
837 current.allowed.roles,
838 "role",
839 "Mention Settings"
840 );
TheCodedProf35e73712023-03-10 17:35:35 -0500841 unsavedChanges = true;
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500842 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000843 }
844 case "channels": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000845 current.allowed.channels = await toSelectMenu(
846 interaction,
847 m,
848 current.allowed.channels,
849 "channel",
850 "Mention Settings"
851 );
TheCodedProf35e73712023-03-10 17:35:35 -0500852 unsavedChanges = true;
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500853 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000854 }
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500855 }
856 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000857 }
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500858 }
859 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000860 } while (!closed);
861 return current;
862};
TheCodedProf5b53a8c2023-02-03 15:40:26 -0500863
Skyler Greyda16adf2023-03-05 10:22:12 +0000864const cleanMenu = async (
865 interaction: StringSelectMenuInteraction,
866 m: Message,
TheCodedProf35e73712023-03-10 17:35:35 -0500867 unsavedChanges: boolean,
Skyler Greyda16adf2023-03-05 10:22:12 +0000868 current?: {
869 channels?: string[];
870 allowed?: {
871 roles: string[];
872 users: string[];
873 };
TheCodedProfad0b8202023-02-14 14:27:09 -0500874 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000875): Promise<{
876 channels: string[];
TheCodedProfad0b8202023-02-14 14:27:09 -0500877 allowed: {
Skyler Greyda16adf2023-03-05 10:22:12 +0000878 roles: string[];
879 users: string[];
880 };
TheCodedProfad0b8202023-02-14 14:27:09 -0500881}> => {
882 let closed = false;
Skyler Greyda16adf2023-03-05 10:22:12 +0000883 if (!current) current = { channels: [], allowed: { roles: [], users: [] } };
884 if (!current.channels) current.channels = [];
885 if (!current.allowed) current.allowed = { roles: [], users: [] };
TheCodedProfad0b8202023-02-14 14:27:09 -0500886
Skyler Greyda16adf2023-03-05 10:22:12 +0000887 const channelMenu = new ActionRowBuilder<ChannelSelectMenuBuilder>().addComponents(
888 new ChannelSelectMenuBuilder().setCustomId("toAdd").setPlaceholder("Select a channel")
889 );
TheCodedProfad0b8202023-02-14 14:27:09 -0500890
Skyler Greyda16adf2023-03-05 10:22:12 +0000891 const allowedMenu = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
892 new StringSelectMenuBuilder()
893 .setCustomId("allowed")
894 .setPlaceholder("Edit exceptions")
895 .addOptions(
896 new StringSelectMenuOptionBuilder()
897 .setLabel("Users")
898 .setDescription("Users that are unaffected by the mention filter")
899 .setValue("users"),
900 new StringSelectMenuOptionBuilder()
901 .setLabel("Roles")
902 .setDescription("Roles that are unaffected by the mention filter")
903 .setValue("roles")
904 )
905 );
TheCodedProfad0b8202023-02-14 14:27:09 -0500906
907 do {
Skyler Greyda16adf2023-03-05 10:22:12 +0000908 const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
909 new ButtonBuilder()
910 .setCustomId("back")
911 .setLabel("Back")
912 .setStyle(ButtonStyle.Primary)
913 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
914 );
TheCodedProfad0b8202023-02-14 14:27:09 -0500915
916 const embed = new EmojiEmbed()
917 .setTitle("Clean Settings")
918 .setEmoji("GUILD.SETTINGS.GREEN")
919 .setDescription(
920 `Current clean channels:\n\n` +
Skyler Greyda16adf2023-03-05 10:22:12 +0000921 `${
922 current.channels.length > 0
923 ? listToAndMore(
924 current.channels.map((c) => `<#${c}>`),
925 10
926 )
927 : "None"
928 }\n\n`
TheCodedProfad0b8202023-02-14 14:27:09 -0500929 )
TheCodedProf35e73712023-03-10 17:35:35 -0500930 .setStatus("Success")
931 .setFooter({
932 text: unsavedChanges ? "No changes made" : "Changes not saved"
933 });
TheCodedProfad0b8202023-02-14 14:27:09 -0500934
Skyler Greyda16adf2023-03-05 10:22:12 +0000935 await interaction.editReply({ embeds: [embed], components: [channelMenu, allowedMenu, buttons] });
TheCodedProfad0b8202023-02-14 14:27:09 -0500936
937 let i: ButtonInteraction | ChannelSelectMenuInteraction;
938 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000939 i = (await m.awaitMessageComponent({
940 filter: (i) => interaction.user.id === i.user.id && i.message.id === m.id,
941 time: 300000
942 })) as ButtonInteraction | ChannelSelectMenuInteraction;
TheCodedProfad0b8202023-02-14 14:27:09 -0500943 } catch (e) {
944 closed = true;
945 break;
946 }
947 await i.deferUpdate();
Skyler Greyda16adf2023-03-05 10:22:12 +0000948 if (i.isButton()) {
TheCodedProfad0b8202023-02-14 14:27:09 -0500949 switch (i.customId) {
950 case "back": {
951 closed = true;
952 break;
953 }
954 }
955 } else {
956 switch (i.customId) {
957 case "toAdd": {
TheCodedProf1807fb32023-02-20 14:33:48 -0500958 const channelEmbed = new EmojiEmbed()
TheCodedProfad0b8202023-02-14 14:27:09 -0500959 .setTitle("Clean Settings")
960 .setDescription(`Editing <#${i.values[0]}>`)
961 .setEmoji("GUILD.SETTINGS.GREEN")
Skyler Greyda16adf2023-03-05 10:22:12 +0000962 .setStatus("Success");
963 const channelButtons = new ActionRowBuilder<ButtonBuilder>().addComponents(
964 new ButtonBuilder()
965 .setCustomId("back")
966 .setLabel("Back")
967 .setStyle(ButtonStyle.Primary)
968 .setEmoji(getEmojiByName("CONTROL.LEFT", "id")),
969 new ButtonBuilder()
970 .setCustomId("switch")
971 .setLabel(current.channels.includes(i.values[0]!) ? "Remove" : "Add")
972 .setStyle(
973 current.channels.includes(i.values[0]!) ? ButtonStyle.Danger : ButtonStyle.Success
974 )
975 );
TheCodedProfad0b8202023-02-14 14:27:09 -0500976
Skyler Greyda16adf2023-03-05 10:22:12 +0000977 await i.editReply({ embeds: [channelEmbed], components: [channelButtons] });
TheCodedProfad0b8202023-02-14 14:27:09 -0500978 let j: ButtonInteraction;
979 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000980 j = (await m.awaitMessageComponent({
981 filter: (i) => interaction.user.id === i.user.id && i.message.id === m.id,
982 time: 300000
983 })) as ButtonInteraction;
TheCodedProfad0b8202023-02-14 14:27:09 -0500984 } catch (e) {
985 closed = true;
986 break;
987 }
988 await j.deferUpdate();
989 switch (j.customId) {
990 case "back": {
991 break;
992 }
993 case "switch": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000994 if (current.channels.includes(i.values[0]!)) {
TheCodedProfad0b8202023-02-14 14:27:09 -0500995 current.channels.splice(current.channels.indexOf(i.values[0]!), 1);
996 } else {
997 current.channels.push(i.values[0]!);
998 }
999 }
1000 }
TheCodedProf35e73712023-03-10 17:35:35 -05001001 unsavedChanges = true;
TheCodedProfad0b8202023-02-14 14:27:09 -05001002 break;
1003 }
1004 case "allowed": {
1005 switch (i.values[0]) {
1006 case "users": {
Skyler Greyda16adf2023-03-05 10:22:12 +00001007 current.allowed.users = await toSelectMenu(
1008 interaction,
1009 m,
1010 current.allowed.users,
1011 "member",
1012 "Mention Settings"
1013 );
TheCodedProf35e73712023-03-10 17:35:35 -05001014 unsavedChanges = true;
TheCodedProfad0b8202023-02-14 14:27:09 -05001015 break;
1016 }
1017 case "roles": {
Skyler Greyda16adf2023-03-05 10:22:12 +00001018 current.allowed.roles = await toSelectMenu(
1019 interaction,
1020 m,
1021 current.allowed.roles,
1022 "role",
1023 "Mention Settings"
1024 );
TheCodedProf35e73712023-03-10 17:35:35 -05001025 unsavedChanges = true;
TheCodedProfad0b8202023-02-14 14:27:09 -05001026 break;
1027 }
1028 }
1029 break;
1030 }
1031 }
1032 }
Skyler Greyda16adf2023-03-05 10:22:12 +00001033 } while (!closed);
TheCodedProfad0b8202023-02-14 14:27:09 -05001034
TheCodedProf1807fb32023-02-20 14:33:48 -05001035 return current as {
Skyler Greyda16adf2023-03-05 10:22:12 +00001036 channels: string[];
TheCodedProf1807fb32023-02-20 14:33:48 -05001037 allowed: {
Skyler Greyda16adf2023-03-05 10:22:12 +00001038 roles: string[];
1039 users: string[];
1040 };
TheCodedProf1807fb32023-02-20 14:33:48 -05001041 };
Skyler Greyda16adf2023-03-05 10:22:12 +00001042};
TheCodedProfad0b8202023-02-14 14:27:09 -05001043
TheCodedProf8b3da212023-02-02 15:09:55 -05001044const callback = async (interaction: CommandInteraction): Promise<void> => {
1045 if (!interaction.guild) return;
Skyler Greyda16adf2023-03-05 10:22:12 +00001046 const m = await interaction.reply({ embeds: LoadingEmbed, fetchReply: true, ephemeral: true });
TheCodedProf35e73712023-03-10 17:35:35 -05001047 let config = (await client.database.guilds.read(interaction.guild.id)).filters;
TheCodedProf8b3da212023-02-02 15:09:55 -05001048
1049 let closed = false;
1050
TheCodedProf35e73712023-03-10 17:35:35 -05001051 let current = _.cloneDeep(config);
TheCodedProf8b3da212023-02-02 15:09:55 -05001052 do {
TheCodedProf35e73712023-03-10 17:35:35 -05001053 const button = new ActionRowBuilder<ButtonBuilder>().addComponents(
TheCodedProfca29ebb2023-03-10 17:40:09 -05001054 new ButtonBuilder()
1055 .setCustomId("save")
1056 .setLabel("Save")
1057 .setStyle(ButtonStyle.Success)
1058 .setDisabled(_.isEqual(config, current))
TheCodedProf35e73712023-03-10 17:35:35 -05001059 );
Skyler Greyda16adf2023-03-05 10:22:12 +00001060 const selectMenu = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
1061 new StringSelectMenuBuilder()
1062 .setCustomId("filter")
1063 .setPlaceholder("Select a filter to edit")
1064 .addOptions(
1065 new StringSelectMenuOptionBuilder()
1066 .setLabel("Invites")
1067 .setDescription("Automatically delete messages containing server invites")
1068 .setValue("invites"),
1069 new StringSelectMenuOptionBuilder()
1070 .setLabel("Mentions")
1071 .setDescription("Deletes messages with excessive mentions")
1072 .setValue("mentions"),
1073 new StringSelectMenuOptionBuilder()
1074 .setLabel("Words")
1075 .setDescription("Delete messages containing filtered words")
1076 .setValue("words"),
1077 new StringSelectMenuOptionBuilder()
1078 .setLabel("Malware")
1079 .setDescription("Automatically delete files and links containing malware")
1080 .setValue("malware"),
1081 new StringSelectMenuOptionBuilder()
1082 .setLabel("Images")
1083 .setDescription("Checks performed on images (NSFW, size checking, etc.)")
1084 .setValue("images"),
1085 new StringSelectMenuOptionBuilder()
1086 .setLabel("Clean")
1087 .setDescription("Automatically delete new messages in specific channels")
1088 .setValue("clean")
1089 )
1090 );
TheCodedProf8b3da212023-02-02 15:09:55 -05001091
1092 const embed = new EmojiEmbed()
1093 .setTitle("Automod Settings")
1094 .setDescription(
TheCodedProf486bca32023-02-02 16:49:44 -05001095 `${emojiFromBoolean(config.invite.enabled)} **Invites**\n` +
Skyler Greyda16adf2023-03-05 10:22:12 +00001096 `${emojiFromBoolean(
1097 config.pings.everyone || config.pings.mass > 0 || config.pings.roles
1098 )} **Mentions**\n` +
1099 `${emojiFromBoolean(config.wordFilter.enabled)} **Words**\n` +
1100 `${emojiFromBoolean(config.malware)} **Malware**\n` +
1101 `${emojiFromBoolean(config.images.NSFW || config.images.size)} **Images**\n` +
1102 `${emojiFromBoolean(config.clean.channels.length > 0)} **Clean**\n`
TheCodedProf8b3da212023-02-02 15:09:55 -05001103 )
TheCodedProf486bca32023-02-02 16:49:44 -05001104 .setStatus("Success")
TheCodedProf35e73712023-03-10 17:35:35 -05001105 .setEmoji("GUILD.SETTINGS.GREEN")
1106 .setFooter({
1107 text: _.isEqual(config, current) ? "No changes made" : "Changes not saved"
1108 });
TheCodedProf8b3da212023-02-02 15:09:55 -05001109
Skyler Greyda16adf2023-03-05 10:22:12 +00001110 await interaction.editReply({ embeds: [embed], components: [selectMenu, button] });
TheCodedProf8b3da212023-02-02 15:09:55 -05001111
1112 let i: StringSelectMenuInteraction | ButtonInteraction;
1113 try {
Skyler Greyda16adf2023-03-05 10:22:12 +00001114 i = (await m.awaitMessageComponent({
1115 filter: (i) => i.user.id === interaction.user.id && i.message.id === m.id,
1116 time: 300000
1117 })) as StringSelectMenuInteraction | ButtonInteraction;
TheCodedProf8b3da212023-02-02 15:09:55 -05001118 } catch (e) {
1119 closed = true;
PineaFanb0d0c242023-02-05 10:59:45 +00001120 continue;
TheCodedProf8b3da212023-02-02 15:09:55 -05001121 }
TheCodedProf35e73712023-03-10 17:35:35 -05001122 await i.deferUpdate();
TheCodedProfca29ebb2023-03-10 17:40:09 -05001123 if (i.isButton()) {
TheCodedProf35e73712023-03-10 17:35:35 -05001124 await client.database.guilds.write(interaction.guild.id, { filters: current });
Skyler Grey16ecb172023-03-05 07:30:32 +00001125 await client.memory.forceUpdate(interaction.guild.id);
TheCodedProf35e73712023-03-10 17:35:35 -05001126 config = current;
1127 current = _.cloneDeep(config);
TheCodedProf8b3da212023-02-02 15:09:55 -05001128 } else {
Skyler Greyda16adf2023-03-05 10:22:12 +00001129 switch (i.values[0]) {
PineaFanb0d0c242023-02-05 10:59:45 +00001130 case "invites": {
TheCodedProf35e73712023-03-10 17:35:35 -05001131 config.invite = await inviteMenu(i, m, _.isEqual(config, current), config.invite);
TheCodedProf8b3da212023-02-02 15:09:55 -05001132 break;
PineaFanb0d0c242023-02-05 10:59:45 +00001133 }
1134 case "mentions": {
TheCodedProf35e73712023-03-10 17:35:35 -05001135 config.pings = await mentionMenu(i, m, _.isEqual(config, current), config.pings);
TheCodedProf8b3da212023-02-02 15:09:55 -05001136 break;
PineaFanb0d0c242023-02-05 10:59:45 +00001137 }
1138 case "words": {
TheCodedProf35e73712023-03-10 17:35:35 -05001139 config.wordFilter = await wordMenu(i, m, _.isEqual(config, current), config.wordFilter);
TheCodedProf8b3da212023-02-02 15:09:55 -05001140 break;
PineaFanb0d0c242023-02-05 10:59:45 +00001141 }
1142 case "malware": {
TheCodedProf8b3da212023-02-02 15:09:55 -05001143 config.malware = !config.malware;
1144 break;
PineaFanb0d0c242023-02-05 10:59:45 +00001145 }
1146 case "images": {
TheCodedProf35e73712023-03-10 17:35:35 -05001147 const next = await imageMenu(i, m, _.isEqual(config, current), config.images);
PineaFanb0d0c242023-02-05 10:59:45 +00001148 config.images = next;
TheCodedProf8b3da212023-02-02 15:09:55 -05001149 break;
PineaFanb0d0c242023-02-05 10:59:45 +00001150 }
TheCodedProfad0b8202023-02-14 14:27:09 -05001151 case "clean": {
TheCodedProf35e73712023-03-10 17:35:35 -05001152 const next = await cleanMenu(i, m, _.isEqual(config, current), config.clean);
TheCodedProfad0b8202023-02-14 14:27:09 -05001153 config.clean = next;
1154 break;
1155 }
TheCodedProf8b3da212023-02-02 15:09:55 -05001156 }
1157 }
Skyler Greyda16adf2023-03-05 10:22:12 +00001158 } while (!closed);
1159 await interaction.deleteReply();
TheCodedProf8b3da212023-02-02 15:09:55 -05001160};
1161
1162const check = (interaction: CommandInteraction, _partial: boolean = false) => {
1163 const member = interaction.member as Discord.GuildMember;
1164 if (!member.permissions.has("ManageMessages"))
1165 return "You must have the *Manage Messages* permission to use this command";
1166 return true;
1167};
1168
1169export { command };
1170export { callback };
1171export { check };