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