blob: 74b6c4110372083d89fc84332df9fc820c622898 [file] [log] [blame]
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";
import listToAndMore from "../../utils/listToAndMore.js";
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) {
const memberEmbed = new EmojiEmbed()
.setTitle("Role")
.setDescription(`Please choose a member to edit the roles of.`)
.setEmoji("GUILD.ROLES.CREATE")
.setStatus("Success");
const 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;
}
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 removing = 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]
);
const adding = 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]
);
const embed = new EmojiEmbed()
.setTitle("Role")
.setDescription(
`${getEmojiByName("ICONS.EDIT")} Editing roles for <@${member.id}>\n\n` +
`Adding:\n` +
`${listToAndMore(adding.length > 0 ? adding : ["None"], 5)}\n` +
`Removing:\n` +
`${listToAndMore(removing.length > 0 ? removing : ["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) {
closed = true;
continue;
}
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: [] });
const rolesToAdd: Role[] = [];
const 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 };