From 4a958a1b224529186a0dda6467c7c7fbde1dbfbe Mon Sep 17 00:00:00 2001 From: TheCodedProf Date: Tue, 14 Feb 2023 13:41:51 -0500 Subject: [PATCH] completed user/role --- TODO | 3 +- src/commands/role/_meta.ts | 8 -- src/commands/role/user.ts | 100 ----------------------- src/commands/user/role.ts | 163 +++++++++++++++++++++++++++++++++++++ 4 files changed, 165 insertions(+), 109 deletions(-) delete mode 100644 src/commands/role/_meta.ts delete mode 100644 src/commands/role/user.ts create mode 100644 src/commands/user/role.ts diff --git a/TODO b/TODO index 91eecea..9217e27 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,5 @@ Role all (?) Server rules verificationRequired on welcome -Role User GUI \ No newline at end of file +Role User GUI +clean channels diff --git a/src/commands/role/_meta.ts b/src/commands/role/_meta.ts deleted file mode 100644 index f546d51..0000000 --- a/src/commands/role/_meta.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { command } from "../../utils/commandRegistration/slashCommandBuilder.js"; - -const name = "role"; -const description = "Change roles for users"; - -const subcommand = await command(name, description, `role`); - -export { name, description, subcommand as command }; diff --git a/src/commands/role/user.ts b/src/commands/role/user.ts deleted file mode 100644 index 4ec7f3e..0000000 --- a/src/commands/role/user.ts +++ /dev/null @@ -1,100 +0,0 @@ -import type { CommandInteraction, GuildMember, Role, User } from "discord.js"; -import type { SlashCommandSubcommandBuilder } from "discord.js"; -import client from "../../utils/client.js"; -import confirmationMessage from "../../utils/confirmationMessage.js"; -import keyValueList from "../../utils/generateKeyValueList.js"; -import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; - -const command = (builder: SlashCommandSubcommandBuilder) => - builder - .setName("user") - .setDescription("Gives or removes a role from someone") - .addUserOption((option) => - option.setName("user").setDescription("The member to give or remove the role from").setRequired(true) - ) - .addRoleOption((option) => - option.setName("role").setDescription("The role to give or remove").setRequired(true) - ) - .addStringOption((option) => - option - .setName("action") - .setDescription("The action to perform") - .setRequired(true) - .addChoices( - {name: "Add", value: "give"}, - {name: "Remove", value: "remove"} - ) - ); - -const callback = async (interaction: CommandInteraction): Promise => { - const { renderUser, renderRole } = client.logger; - const action = interaction.options.get("action")?.value as string; - const role: Role = (await interaction.guild!.roles.fetch(interaction.options.get("role")?.value as string))!; - // TODO:[Modals] Replace this with a modal - const confirmation = await new confirmationMessage(interaction) - .setEmoji("GUILD.ROLES.DELETE") - .setTitle("Role") - .setDescription( - keyValueList({ - user: renderUser(interaction.options.getUser("user")! as User), - role: renderRole(role) - }) + - `\nAre you sure you want to ${ - action === "give" ? "give the role to" : "remove the role from" - } ${interaction.options.getUser("user")}?` - ) - .setColor("Danger") - .setFailedMessage("No changes were made", "Success", "GUILD.ROLES.CREATE") - .send(); - if (confirmation.cancelled || !confirmation.success) return; - try { - const member = interaction.options.getMember("user") as GuildMember; - if ((interaction.options.get("action")?.value as string) === "give") { - member.roles.add(role); - } else { - member.roles.remove(role); - } - } catch (e) { - return await interaction.editReply({ - embeds: [ - new EmojiEmbed() - .setTitle("Role") - .setDescription("Something went wrong and the role could not be added") - .setStatus("Danger") - .setEmoji("CONTROL.BLOCKCROSS") - ], - components: [] - }); - } - return await interaction.editReply({ - embeds: [ - new EmojiEmbed() - .setTitle("Role") - .setDescription(`The role has been ${action === "give" ? "given" : "removed"} successfully`) - .setStatus("Success") - .setEmoji("GUILD.ROLES.CREATE") - ], - components: [] - }); -}; - -const check = (interaction: CommandInteraction, partial: boolean = false) => { - const member = interaction.member as GuildMember; - // Check if the user has manage_roles permission - if (!member.permissions.has("ManageRoles")) return "You do not have the *Manage Roles* permission"; - if (partial) return true; - if (!interaction.guild) return - const me = interaction.guild.members.me!; - const apply = interaction.options.getMember("user") as GuildMember | null; - if (apply === null) return "That member is not in the server"; - // Check if Nucleus has permission to role - if (!me.permissions.has("ManageRoles")) return "I do not have the *Manage Roles* permission"; - // Allow the owner to role anyone - if (member.id === interaction.guild.ownerId) return true; - // Allow role - return true; -}; - -export { command }; -export { callback }; -export { check }; diff --git a/src/commands/user/role.ts b/src/commands/user/role.ts new file mode 100644 index 0000000..19bd3c7 --- /dev/null +++ b/src/commands/user/role.ts @@ -0,0 +1,163 @@ +import { ActionRowBuilder, APIMessageComponentEmoji, ButtonBuilder, ButtonInteraction, ButtonStyle, CommandInteraction, GuildMember, Role, RoleSelectMenuBuilder, RoleSelectMenuInteraction, UserSelectMenuBuilder, UserSelectMenuInteraction } from "discord.js"; +import type { SlashCommandSubcommandBuilder } from "discord.js"; +import client from "../../utils/client.js"; +import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; +import { LoadingEmbed } from "../../utils/defaults.js"; +import getEmojiByName from "../../utils/getEmojiByName.js"; + +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 { renderUser } = client.logger; + +const canEdit = (role: Role, member: GuildMember, me: GuildMember): [string, boolean] => { + if(role.position >= me.roles.highest.position || + role.position >= member.roles.highest.position + ) return [`~~<@&${role.id}>~~`, false]; + return [`<@&${role.id}>`, true]; +}; + +const command = (builder: SlashCommandSubcommandBuilder) => + builder + .setName("role") + .setDescription("Gives or removes a role from someone") + .addUserOption((option) => option.setName("user").setDescription("The user to give or remove the role from")) + +const callback = async (interaction: CommandInteraction): Promise => { + const m = await interaction.reply({ embeds: LoadingEmbed, fetchReply: true, ephemeral: true }); + + let member = interaction.options.getMember("user") as GuildMember | null; + + if(!member) { + let memberEmbed = new EmojiEmbed() + .setTitle("Role") + .setDescription(`Please choose a member to edit the roles of.`) + .setEmoji("GUILD.ROLES.CREATE") + .setStatus("Success"); + let memberChooser = new ActionRowBuilder().addComponents( + new UserSelectMenuBuilder() + .setCustomId("memberChooser") + .setPlaceholder("Select a member") + ); + await interaction.editReply({embeds: [memberEmbed], components: [memberChooser]}); + + const filter = (i: UserSelectMenuInteraction) => i.customId === "memberChooser" && i.user.id === interaction.user.id; + + let i: UserSelectMenuInteraction | null; + try { + i = await m.awaitMessageComponent<5>({ filter, time: 300000}); + } catch (e) { + return; + } + + if(!i) return; + memberEmbed.setDescription(`Editing roles for ${renderUser(i.values[0]!)}`); + await i.deferUpdate(); + await interaction.editReply({ embeds: LoadingEmbed, components: [] }) + member = await interaction.guild?.members.fetch(i.values[0]!)!; + + } + + let closed = false; + let rolesToChange: string[] = []; + const roleAdd = new ActionRowBuilder() + .addComponents( + new RoleSelectMenuBuilder() + .setCustomId("roleAdd") + .setPlaceholder("Select a role to add") + .setMaxValues(25) + ); + + do { + const embed = new EmojiEmbed() + .setTitle("Role") + .setDescription( + `${getEmojiByName("ICONS.EDIT")} Editing roles for <@${member.id}>\n\n` + + `Adding:\n` + + `${listToAndMore(rolesToChange.filter((r) => !member!.roles.cache.has(r)).map((r) => canEdit(interaction.guild?.roles.cache.get(r)!, interaction.member as GuildMember, interaction.guild?.members.me!)[0]) || ["None"], 5)}\n` + + `Removing:\n` + + `${listToAndMore(rolesToChange.filter((r) => member!.roles.cache.has(r)).map((r) => canEdit(interaction.guild?.roles.cache.get(r)!, interaction.member as GuildMember, interaction.guild?.members.me!)[0]) || ["None"], 5)}\n` + ) + .setEmoji("GUILD.ROLES.CREATE") + .setStatus("Success"); + + const buttons = new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId("roleSave") + .setLabel("Apply") + .setEmoji(getEmojiByName("ICONS.SAVE", "id") as APIMessageComponentEmoji) + .setStyle(ButtonStyle.Success), + new ButtonBuilder() + .setCustomId("roleDiscard") + .setLabel("Reset") + .setEmoji(getEmojiByName("CONTROL.CROSS", "id") as APIMessageComponentEmoji) + .setStyle(ButtonStyle.Danger) + ); + + await interaction.editReply({ embeds: [embed], components: [roleAdd, buttons] }); + + let i: RoleSelectMenuInteraction | ButtonInteraction | null; + try { + i = await m.awaitMessageComponent({ filter: (i) => i.user.id === interaction.user.id, time: 300000 }) as RoleSelectMenuInteraction | ButtonInteraction; + } catch (e) { + return; + } + + if(!i) return; + i.deferUpdate(); + if(i.isButton()) { + switch(i.customId) { + case "roleSave": + const roles = rolesToChange.map((r) => interaction.guild?.roles.cache.get(r)!); + await interaction.editReply({ embeds: LoadingEmbed, components: [] }); + let rolesToAdd: Role[] = []; + let rolesToRemove: Role[] = []; + for(const role of roles) { + if(!canEdit(role, interaction.member as GuildMember, interaction.guild?.members.me!)[1]) continue; + if(member.roles.cache.has(role.id)) { + rolesToRemove.push(role); + } else { + rolesToAdd.push(role); + } + } + await member.roles.add(rolesToAdd); + await member.roles.remove(rolesToRemove); + rolesToChange = []; + break; + case "roleDiscard": + rolesToChange = []; + await interaction.editReply({ embeds: LoadingEmbed, components: [] }); + break; + } + } else { + rolesToChange = i.values; + } + + } while (!closed); + +}; + +const check = (interaction: CommandInteraction, partial: boolean = false) => { + const member = interaction.member as GuildMember; + // Check if the user has manage_roles permission + if (!member.permissions.has("ManageRoles")) return "You do not have the *Manage Roles* permission"; + if (partial) return true; + if (!interaction.guild) return + const me = interaction.guild.members.me!; + // Check if Nucleus has permission to role + if (!me.permissions.has("ManageRoles")) return "I do not have the *Manage Roles* permission"; + // Allow the owner to role anyone + if (member.id === interaction.guild.ownerId) return true; + // Allow role + return true; +}; + +export { command }; +export { callback }; +export { check };