completed user/role
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<unknown> => {
-    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<unknown> => {
+    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<UserSelectMenuBuilder>().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<RoleSelectMenuBuilder>()
+        .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<ButtonBuilder>()
+            .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 };