worked on automod settings
diff --git a/src/commands/settings/automod.ts b/src/commands/settings/automod.ts
index b9f2453..a7b6dbc 100644
--- a/src/commands/settings/automod.ts
+++ b/src/commands/settings/automod.ts
@@ -1,17 +1,114 @@
import type Discord from "discord.js";
-import { ActionRowBuilder, APIMessageComponentEmoji, ButtonBuilder, ButtonInteraction, ButtonStyle, CommandInteraction, Message, StringSelectMenuBuilder, StringSelectMenuInteraction, StringSelectMenuOptionBuilder } from "discord.js";
+import { ActionRowBuilder, AnyComponent, AnyComponentBuilder, AnySelectMenuInteraction, APIMessageComponentEmoji, ButtonBuilder, ButtonInteraction, ButtonStyle, ChannelSelectMenuBuilder, ChannelSelectMenuInteraction, CommandInteraction, Guild, GuildMember, GuildTextBasedChannel, MentionableSelectMenuBuilder, Message, Role, RoleSelectMenuBuilder, RoleSelectMenuInteraction, SelectMenuBuilder, StringSelectMenuBuilder, StringSelectMenuInteraction, StringSelectMenuOptionBuilder, UserSelectMenuBuilder, UserSelectMenuInteraction } from "discord.js";
import type { SlashCommandSubcommandBuilder } from "discord.js";
import { LoadingEmbed } from "../../utils/defaults.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import client from "../../utils/client.js";
import getEmojiByName from "../../utils/getEmojiByName.js";
+
const command = (builder: SlashCommandSubcommandBuilder) =>
builder.setName("automod").setDescription("Setting for automatic moderation features");
const emojiFromBoolean = (bool: boolean, id?: string) => bool ? getEmojiByName("CONTROL.TICK", id) : getEmojiByName("CONTROL.CROSS", id);
+const listToAndMore = (list: string[], max: number) => {
+ // PineappleFan, Coded, Mini (and 10 more)
+ if(list.length > max) {
+ return list.slice(0, max).join(", ") + ` (and ${list.length - max} more)`;
+ }
+ return list.join(", ");
+}
+
+const toSelectMenu = async (interaction: StringSelectMenuInteraction, m: Message, ids: string[], type: "member" | "role" | "channel", title: string): Promise<string[]> => {
+
+ const back = new ActionRowBuilder<ButtonBuilder>().addComponents(new ButtonBuilder().setCustomId("back").setLabel("Back").setStyle(ButtonStyle.Secondary).setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji));
+
+ let closed;
+ do {
+ let render: string[] = []
+ let mapped: string[] = [];
+ let menu: UserSelectMenuBuilder | RoleSelectMenuBuilder | ChannelSelectMenuBuilder;
+ switch(type) {
+ case "member":
+ menu = new UserSelectMenuBuilder().setCustomId("user").setPlaceholder("Select users").setMaxValues(25);
+ mapped = await Promise.all(ids.map(async (id) => { return (await client.users.fetch(id).then(user => user.tag)) || "Unknown User" }));
+ render = ids.map(id => client.logger.renderUser(id))
+ break;
+ case "role":
+ menu = new RoleSelectMenuBuilder().setCustomId("role").setPlaceholder("Select roles").setMaxValues(25);
+ mapped = await Promise.all(ids.map(async (id) => { return (await interaction.guild!.roles.fetch(id).then(role => role?.name)) || "Unknown Role" }));
+ render = ids.map(id => client.logger.renderRole(id, interaction.guild!))
+ break;
+ case "channel":
+ menu = new ChannelSelectMenuBuilder().setCustomId("channel").setPlaceholder("Select channels").setMaxValues(25);
+ mapped = await Promise.all(ids.map(async (id) => { return (await interaction.guild!.channels.fetch(id).then(channel => channel?.name)) || "Unknown Channel" }));
+ render = ids.map(id => client.logger.renderChannel(id))
+ break;
+ }
+ const removeOptions = new ActionRowBuilder<StringSelectMenuBuilder>()
+ .addComponents(
+ new StringSelectMenuBuilder()
+ .setCustomId("remove")
+ .setPlaceholder("Remove")
+ .addOptions(mapped.map((name, i) => new StringSelectMenuOptionBuilder().setLabel(name).setValue(ids[i]!)))
+ .setDisabled(ids.length === 0)
+ );
+
+ const embed = new EmojiEmbed()
+ .setTitle(title)
+ .setEmoji(getEmojiByName("GUILD.SETTINGS.GREEN"))
+ .setDescription(`Select ${type}s:\n\nCurrent:\n` + (render.length > 0 ? render.join("\n") : "None"))
+ .setStatus("Success");
+ let components: ActionRowBuilder<
+ StringSelectMenuBuilder |
+ ButtonBuilder |
+ ChannelSelectMenuBuilder |
+ UserSelectMenuBuilder |
+ RoleSelectMenuBuilder
+ >[] = [new ActionRowBuilder<typeof menu>().addComponents(menu)]
+ if(ids.length > 0) components.push(removeOptions);
+ components.push(back);
+
+ await interaction.editReply({embeds: [embed], components: components})
+
+ let i: AnySelectMenuInteraction | ButtonInteraction;
+ try {
+ i = await interaction.channel!.awaitMessageComponent({filter: i => i.user.id === interaction.user.id, time: 300000});
+ } catch(e) {
+ closed = true;
+ break;
+ }
+
+ if(i.isButton()) {
+ await i.deferUpdate();
+ if(i.customId === "back") {
+ closed = true;
+ break;
+ }
+ } else if(i.isStringSelectMenu()) {
+ await i.deferUpdate();
+ if(i.customId === "remove") {
+ ids = ids.filter(id => id !== (i as StringSelectMenuInteraction).values[0]);
+ if(ids.length === 0) {
+ menu.data.disabled = true;
+ }
+ }
+ } else {
+ await i.deferUpdate();
+ if(i.customId === "user") {
+ ids = ids.concat((i as UserSelectMenuInteraction).values);
+ } else if(i.customId === "role") {
+ ids = ids.concat((i as RoleSelectMenuInteraction).values);
+ } else if(i.customId === "channel") {
+ ids = ids.concat((i as ChannelSelectMenuInteraction).values);
+ }
+ }
+
+ } while(!closed)
+ return ids;
+}
const imageMenu = async (interaction: StringSelectMenuInteraction, m: Message, current: {
NSFW: boolean,
@@ -72,23 +169,112 @@
const wordMenu = async (interaction: StringSelectMenuInteraction, m: Message, current: {
enabled: boolean,
words: {strict: string[], loose: string[]},
- allowed: {user: string[], roles: string[], channels: string[]}
+ allowed: {users: string[], roles: string[], channels: string[]}
}): Promise<{
enabled: boolean,
words: {strict: string[], loose: string[]},
- allowed: {user: string[], roles: string[], channels: string[]}
+ allowed: {users: string[], roles: string[], channels: string[]}
}> => {
-
+ let closed = false;
+ do {
+ closed = true;
+ } while(!closed);
+ return current;
}
const inviteMenu = async (interaction: StringSelectMenuInteraction, m: Message, current: {
enabled: boolean,
- allowed: {user: string[], roles: string[], channels: string[]}
+ allowed: {users: string[], roles: string[], channels: string[]}
}): Promise<{
enabled: boolean,
- allowed: {user: string[], roles: string[], channels: string[]}
+ allowed: {users: string[], roles: string[], channels: string[]}
}> => {
+ let closed = false;
+ do {
+ const buttons = new ActionRowBuilder<ButtonBuilder>()
+ .addComponents(
+ new ButtonBuilder()
+ .setCustomId("back")
+ .setLabel("Back")
+ .setStyle(ButtonStyle.Secondary)
+ .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji),
+ new ButtonBuilder()
+ .setCustomId("enabled")
+ .setLabel(current.enabled ? "Enabled" : "Disabled")
+ .setStyle(current.enabled ? ButtonStyle.Success : ButtonStyle.Danger)
+ .setEmoji(emojiFromBoolean(current.enabled, "id") as APIMessageComponentEmoji)
+ );
+ const menu = new ActionRowBuilder<StringSelectMenuBuilder>()
+ .addComponents(
+ new StringSelectMenuBuilder()
+ .setCustomId("toEdit")
+ .setPlaceholder("Edit your allow list")
+ .addOptions(
+ new StringSelectMenuOptionBuilder()
+ .setLabel("Users")
+ .setDescription("Users that are allowed to send invites")
+ .setValue("users"),
+ new StringSelectMenuOptionBuilder()
+ .setLabel("Roles")
+ .setDescription("Roles that are allowed to send invites")
+ .setValue("roles"),
+ new StringSelectMenuOptionBuilder()
+ .setLabel("Channels")
+ .setDescription("Channels that anyone is allowed to send invites in")
+ .setValue("channels")
+ ).setDisabled(!current.enabled)
+ )
+
+ const embed = new EmojiEmbed()
+ .setTitle("Invite Settings")
+ .setDescription(
+ "Automatically deletes invites sent by users (outside of staff members and self promotion channels)" + `\n\n` +
+ `${emojiFromBoolean(current.enabled)} **${current.enabled ? "Enabled" : "Disabled"}**\n\n` +
+ `**Users:** ` + listToAndMore(current.allowed.users.map(user => `> <@${user}>`), 5) + `\n` +
+ `**Roles:** ` + listToAndMore(current.allowed.roles.map(role => `> <@&${role}>`), 5) + `\n` +
+ `**Channels:** ` + listToAndMore(current.allowed.channels.map(channel => `> <#${channel}>`), 5)
+ )
+ .setStatus("Success")
+ .setEmoji("GUILD.SETTINGS.GREEN")
+
+
+ await interaction.editReply({embeds: [embed], components: [buttons, menu]});
+
+ let i: ButtonInteraction | StringSelectMenuInteraction;
+ try {
+ i = await m.awaitMessageComponent({filter: (i) => interaction.user.id === i.user.id && i.message.id === m.id, time: 300000}) as ButtonInteraction | StringSelectMenuInteraction;
+ } catch (e) {
+ return current;
+ }
+
+ if(i.isButton()) {
+ await i.deferUpdate();
+ switch(i.customId) {
+ case "back":
+ closed = true;
+ break;
+ case "enabled":
+ current.enabled = !current.enabled;
+ break;
+ }
+ } else {
+ await i.deferUpdate();
+ switch(i.values[0]) {
+ case "users":
+ current.allowed.users = await toSelectMenu(interaction, m, current.allowed.users, "member", "Invite Settings");
+ break;
+ case "roles":
+ current.allowed.roles = await toSelectMenu(interaction, m, current.allowed.roles, "role", "Invite Settings");
+ break;
+ case "channels":
+ current.allowed.channels = await toSelectMenu(interaction, m, current.allowed.channels, "channel", "Invite Settings");
+ break;
+ }
+ }
+
+ } while(!closed);
+ return current;
}
const mentionMenu = async (interaction: StringSelectMenuInteraction, m: Message, current: {
@@ -112,7 +298,12 @@
channels: string[]
}
}> => {
-
+ let closed = false;
+
+ do {
+ closed = true;
+ } while(!closed);
+ return current
}
const callback = async (interaction: CommandInteraction): Promise<void> => {
@@ -164,12 +355,14 @@
const embed = new EmojiEmbed()
.setTitle("Automod Settings")
.setDescription(
- `${emojiFromBoolean(config.invite.enabled)} **Invites**}\n` +
+ `${emojiFromBoolean(config.invite.enabled)} **Invites**\n` +
`${emojiFromBoolean(config.pings.everyone || config.pings.mass > 0 || config.pings.roles)} **Mentions**\n` +
`${emojiFromBoolean(config.wordFilter.enabled)} **Words**\n` +
`${emojiFromBoolean(config.malware)} **Malware**\n` +
- `${emojiFromBoolean(config.images.NSFW || config.images.size)} **Images**}\n`
+ `${emojiFromBoolean(config.images.NSFW || config.images.size)} **Images**\n`
)
+ .setStatus("Success")
+ .setEmoji("GUILD.SETTINGS.GREEN")
await interaction.editReply({embeds: [embed], components: [selectMenu, button]});
@@ -188,10 +381,16 @@
} else {
switch(i.values[0]) {
case "invites":
+ await i.deferUpdate();
+ config.invite = await inviteMenu(i, m, config.invite);
break;
case "mentions":
+ await i.deferUpdate();
+ config.pings = await mentionMenu(i, m, config.pings);
break;
case "words":
+ await i.deferUpdate();
+ config.wordFilter = await wordMenu(i, m, config.wordFilter);
break;
case "malware":
await i.deferUpdate();
diff --git a/src/utils/log.ts b/src/utils/log.ts
index 184f29a..4eff4e2 100644
--- a/src/utils/log.ts
+++ b/src/utils/log.ts
@@ -33,7 +33,8 @@
if (typeof channel === "string") channel = client.channels.cache.get(channel) as Discord.GuildChannel | Discord.ThreadChannel;
return `${channel.name} [<#${channel.id}>]`;
},
- renderRole(role: Discord.Role) {
+ renderRole(role: Discord.Role | string, guild?: Discord.Guild | string) {
+ if (typeof role === "string") role = (typeof guild === "string" ? client.guilds.cache.get(guild) : guild)!.roles.cache.get(role) as Discord.Role;
return `${role.name} [<@&${role.id}>]`;
},
renderEmoji(emoji: Discord.GuildEmoji) {