blob: 5aa878253557a3e49feee37310cfcd18a1964950 [file] [log] [blame]
TheCodedProf4a958a12023-02-14 13:41:51 -05001import { ActionRowBuilder, APIMessageComponentEmoji, ButtonBuilder, ButtonInteraction, ButtonStyle, CommandInteraction, GuildMember, Role, RoleSelectMenuBuilder, RoleSelectMenuInteraction, UserSelectMenuBuilder, UserSelectMenuInteraction } from "discord.js";
2import type { SlashCommandSubcommandBuilder } from "discord.js";
3import client from "../../utils/client.js";
4import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
5import { LoadingEmbed } from "../../utils/defaults.js";
6import getEmojiByName from "../../utils/getEmojiByName.js";
TheCodedProf1f675042023-02-16 17:01:29 -05007import listToAndMore from "../../utils/listToAndMore.js"
TheCodedProf4a958a12023-02-14 13:41:51 -05008
9const { renderUser } = client.logger;
10
11const canEdit = (role: Role, member: GuildMember, me: GuildMember): [string, boolean] => {
12 if(role.position >= me.roles.highest.position ||
13 role.position >= member.roles.highest.position
14 ) return [`~~<@&${role.id}>~~`, false];
15 return [`<@&${role.id}>`, true];
16};
17
18const command = (builder: SlashCommandSubcommandBuilder) =>
19 builder
20 .setName("role")
21 .setDescription("Gives or removes a role from someone")
22 .addUserOption((option) => option.setName("user").setDescription("The user to give or remove the role from"))
23
24const callback = async (interaction: CommandInteraction): Promise<unknown> => {
25 const m = await interaction.reply({ embeds: LoadingEmbed, fetchReply: true, ephemeral: true });
26
27 let member = interaction.options.getMember("user") as GuildMember | null;
28
29 if(!member) {
30 let memberEmbed = new EmojiEmbed()
31 .setTitle("Role")
32 .setDescription(`Please choose a member to edit the roles of.`)
33 .setEmoji("GUILD.ROLES.CREATE")
34 .setStatus("Success");
35 let memberChooser = new ActionRowBuilder<UserSelectMenuBuilder>().addComponents(
36 new UserSelectMenuBuilder()
37 .setCustomId("memberChooser")
38 .setPlaceholder("Select a member")
39 );
40 await interaction.editReply({embeds: [memberEmbed], components: [memberChooser]});
41
42 const filter = (i: UserSelectMenuInteraction) => i.customId === "memberChooser" && i.user.id === interaction.user.id;
43
44 let i: UserSelectMenuInteraction | null;
45 try {
46 i = await m.awaitMessageComponent<5>({ filter, time: 300000});
47 } catch (e) {
48 return;
49 }
50
51 if(!i) return;
52 memberEmbed.setDescription(`Editing roles for ${renderUser(i.values[0]!)}`);
53 await i.deferUpdate();
54 await interaction.editReply({ embeds: LoadingEmbed, components: [] })
55 member = await interaction.guild?.members.fetch(i.values[0]!)!;
56
57 }
58
59 let closed = false;
60 let rolesToChange: string[] = [];
61 const roleAdd = new ActionRowBuilder<RoleSelectMenuBuilder>()
62 .addComponents(
63 new RoleSelectMenuBuilder()
64 .setCustomId("roleAdd")
65 .setPlaceholder("Select a role to add")
66 .setMaxValues(25)
67 );
68
69 do {
70 const embed = new EmojiEmbed()
71 .setTitle("Role")
72 .setDescription(
73 `${getEmojiByName("ICONS.EDIT")} Editing roles for <@${member.id}>\n\n` +
74 `Adding:\n` +
75 `${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` +
76 `Removing:\n` +
77 `${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`
78 )
79 .setEmoji("GUILD.ROLES.CREATE")
80 .setStatus("Success");
81
82 const buttons = new ActionRowBuilder<ButtonBuilder>()
83 .addComponents(
84 new ButtonBuilder()
85 .setCustomId("roleSave")
86 .setLabel("Apply")
87 .setEmoji(getEmojiByName("ICONS.SAVE", "id") as APIMessageComponentEmoji)
88 .setStyle(ButtonStyle.Success),
89 new ButtonBuilder()
90 .setCustomId("roleDiscard")
91 .setLabel("Reset")
92 .setEmoji(getEmojiByName("CONTROL.CROSS", "id") as APIMessageComponentEmoji)
93 .setStyle(ButtonStyle.Danger)
94 );
95
96 await interaction.editReply({ embeds: [embed], components: [roleAdd, buttons] });
97
98 let i: RoleSelectMenuInteraction | ButtonInteraction | null;
99 try {
100 i = await m.awaitMessageComponent({ filter: (i) => i.user.id === interaction.user.id, time: 300000 }) as RoleSelectMenuInteraction | ButtonInteraction;
101 } catch (e) {
102 return;
103 }
104
105 if(!i) return;
106 i.deferUpdate();
107 if(i.isButton()) {
108 switch(i.customId) {
109 case "roleSave":
110 const roles = rolesToChange.map((r) => interaction.guild?.roles.cache.get(r)!);
111 await interaction.editReply({ embeds: LoadingEmbed, components: [] });
112 let rolesToAdd: Role[] = [];
113 let rolesToRemove: Role[] = [];
114 for(const role of roles) {
115 if(!canEdit(role, interaction.member as GuildMember, interaction.guild?.members.me!)[1]) continue;
116 if(member.roles.cache.has(role.id)) {
117 rolesToRemove.push(role);
118 } else {
119 rolesToAdd.push(role);
120 }
121 }
122 await member.roles.add(rolesToAdd);
123 await member.roles.remove(rolesToRemove);
124 rolesToChange = [];
125 break;
126 case "roleDiscard":
127 rolesToChange = [];
128 await interaction.editReply({ embeds: LoadingEmbed, components: [] });
129 break;
130 }
131 } else {
132 rolesToChange = i.values;
133 }
134
135 } while (!closed);
136
137};
138
139const check = (interaction: CommandInteraction, partial: boolean = false) => {
140 const member = interaction.member as GuildMember;
141 // Check if the user has manage_roles permission
142 if (!member.permissions.has("ManageRoles")) return "You do not have the *Manage Roles* permission";
143 if (partial) return true;
144 if (!interaction.guild) return
145 const me = interaction.guild.members.me!;
146 // Check if Nucleus has permission to role
147 if (!me.permissions.has("ManageRoles")) return "I do not have the *Manage Roles* permission";
148 // Allow the owner to role anyone
149 if (member.id === interaction.guild.ownerId) return true;
150 // Allow role
151 return true;
152};
153
154export { command };
155export { callback };
156export { check };