blob: 743579b9a1a7d5c7ab0953ffc7e038759d7df041 [file] [log] [blame]
Skyler Greyda16adf2023-03-05 10:22:12 +00001import { LinkWarningFooter } from "./../../utils/defaults.js";
pineafan6de4da52023-03-07 20:43:44 +00002import { ActionRowBuilder, ButtonBuilder, CommandInteraction, GuildMember, ButtonStyle, Message, ButtonInteraction, ModalBuilder, TextInputBuilder, TextInputStyle } from "discord.js";
TheCodedProff86ba092023-01-27 17:10:07 -05003import type { SlashCommandSubcommandBuilder } from "discord.js";
pineafan32767212022-03-14 21:27:39 +00004import confirmationMessage from "../../utils/confirmationMessage.js";
pineafan4edb7762022-06-26 19:21:04 +01005import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
pineafan32767212022-03-14 21:27:39 +00006import keyValueList from "../../utils/generateKeyValueList.js";
pineafan63fc5e22022-08-04 22:04:10 +01007import client from "../../utils/client.js";
PineaFane6ba7882023-01-18 20:41:16 +00008import { areTicketsEnabled, create } from "../../actions/createModActionTicket.js";
TheCodedProf94ff6de2023-02-22 17:47:26 -05009import getEmojiByName from "../../utils/getEmojiByName.js";
pineafan32767212022-03-14 21:27:39 +000010
Skyler Greyda16adf2023-03-05 10:22:12 +000011const command = (builder: SlashCommandSubcommandBuilder) =>
12 builder
13 .setName("nick")
14 .setDescription("Changes a users nickname")
15 .addUserOption((option) => option.setName("user").setDescription("The user to change").setRequired(true))
16 .addStringOption((option) =>
17 option.setName("name").setDescription("The name to set | Leave blank to clear").setRequired(false)
18 );
pineafan32767212022-03-14 21:27:39 +000019
pineafan6de4da52023-03-07 20:43:44 +000020const callback = async (interaction: CommandInteraction | ButtonInteraction, member?: GuildMember): Promise<unknown> => {
PineaFane6ba7882023-01-18 20:41:16 +000021 const { log, NucleusColors, entry, renderDelta, renderUser } = client.logger;
pineafan6de4da52023-03-07 20:43:44 +000022 let newNickname;
23 if (!interaction.isButton()) {
24 member = interaction.options.getMember("user") as GuildMember;
25 newNickname = interaction.options.get("name")?.value as string | undefined;
26 }
27 if (!member) return;
pineafan377794f2022-04-18 19:01:01 +010028 // TODO:[Modals] Replace this with a modal
pineafan6de4da52023-03-07 20:43:44 +000029 let notify = false;
pineafan02ba0232022-07-24 22:16:15 +010030 let confirmation;
Skyler Greyad002172022-08-16 18:48:26 +010031 let timedOut = false;
32 let success = false;
PineaFane6ba7882023-01-18 20:41:16 +000033 let createAppealTicket = false;
pineafan6de4da52023-03-07 20:43:44 +000034 let firstRun = !interaction.isButton();
PineaFane6ba7882023-01-18 20:41:16 +000035 do {
pineafan02ba0232022-07-24 22:16:15 +010036 confirmation = await new confirmationMessage(interaction)
37 .setEmoji("PUNISH.NICKNAME.RED")
38 .setTitle("Nickname")
Skyler Grey75ea9172022-08-06 10:22:23 +010039 .setDescription(
40 keyValueList({
pineafan6de4da52023-03-07 20:43:44 +000041 user: renderUser(member.user),
Skyler Grey75ea9172022-08-06 10:22:23 +010042 "new nickname": `${
pineafan6de4da52023-03-07 20:43:44 +000043 newNickname
44 ? newNickname
Skyler Greyda16adf2023-03-05 10:22:12 +000045 : "*No nickname*"
Skyler Grey75ea9172022-08-06 10:22:23 +010046 }`
47 }) +
Skyler Greyda16adf2023-03-05 10:22:12 +000048 `Are you sure you want to ${
pineafan6de4da52023-03-07 20:43:44 +000049 newNickname ? "change" : "clear"
50 } <@!${member.id}>'s nickname?`
Skyler Grey75ea9172022-08-06 10:22:23 +010051 )
pineafan02ba0232022-07-24 22:16:15 +010052 .setColor("Danger")
Skyler Grey75ea9172022-08-06 10:22:23 +010053 .addCustomBoolean(
PineaFane6ba7882023-01-18 20:41:16 +000054 "appeal",
55 "Create appeal ticket",
56 !(await areTicketsEnabled(interaction.guild!.id)),
Skyler Greyda16adf2023-03-05 10:22:12 +000057 async () =>
58 await create(
59 interaction.guild!,
pineafan6de4da52023-03-07 20:43:44 +000060 member!.user,
Skyler Greyda16adf2023-03-05 10:22:12 +000061 interaction.user,
62 "Nickname changed"
63 ),
PineaFane6ba7882023-01-18 20:41:16 +000064 "An appeal ticket will be created",
65 null,
66 "CONTROL.TICKET",
67 createAppealTicket
68 )
69 .addCustomBoolean(
Skyler Grey75ea9172022-08-06 10:22:23 +010070 "notify",
71 "Notify user",
72 false,
73 null,
PineaFane6ba7882023-01-18 20:41:16 +000074 "The user will be sent a DM",
PineaFana34d04b2023-01-03 22:05:42 +000075 null,
Skyler Grey75ea9172022-08-06 10:22:23 +010076 "ICONS.NOTIFY." + (notify ? "ON" : "OFF"),
77 notify
78 )
pineafan6de4da52023-03-07 20:43:44 +000079 .addModal(
80 "Change nickname",
81 "ICONS.EDIT",
82 "modal",
83 newNickname ?? "",
84 new ModalBuilder()
85 .setTitle("Editing nickname")
86 .addComponents(
87 new ActionRowBuilder<TextInputBuilder>().addComponents(
88 new TextInputBuilder()
89 .setCustomId("default")
90 .setLabel("Nickname")
91 .setMaxLength(32)
92 .setRequired(false)
93 .setStyle(TextInputStyle.Short)
94 .setValue(newNickname ? newNickname : " ")
95 )
96 )
97 )
PineaFane6ba7882023-01-18 20:41:16 +000098 .setFailedMessage("No changes were made", "Success", "PUNISH.NICKNAME.GREEN")
99 .send(!firstRun);
100 firstRun = false;
Skyler Greyad002172022-08-16 18:48:26 +0100101 if (confirmation.cancelled) timedOut = true;
PineaFane6ba7882023-01-18 20:41:16 +0000102 else if (confirmation.success !== undefined) success = true;
Skyler Greyad002172022-08-16 18:48:26 +0100103 else if (confirmation.components) {
Skyler Greyda16adf2023-03-05 10:22:12 +0000104 notify = confirmation.components["notify"]!.active;
PineaFane6ba7882023-01-18 20:41:16 +0000105 createAppealTicket = confirmation.components["appeal"]!.active;
pineafan02ba0232022-07-24 22:16:15 +0100106 }
pineafan6de4da52023-03-07 20:43:44 +0000107 if (confirmation.modals) newNickname = confirmation.modals![0]!.value
PineaFane6ba7882023-01-18 20:41:16 +0000108 } while (!timedOut && !success);
109 if (timedOut || !success) return;
110 let dmSent = false;
111 let dmMessage: Message;
112 const config = await client.database.guilds.read(interaction.guild!.id);
Skyler Greyad002172022-08-16 18:48:26 +0100113 try {
114 if (notify) {
PineaFane6ba7882023-01-18 20:41:16 +0000115 const messageData: {
116 embeds: EmojiEmbed[];
117 components: ActionRowBuilder<ButtonBuilder>[];
118 } = {
Skyler Greyad002172022-08-16 18:48:26 +0100119 embeds: [
120 new EmojiEmbed()
121 .setEmoji("PUNISH.NICKNAME.RED")
122 .setTitle("Nickname changed")
123 .setDescription(
Skyler Greyda16adf2023-03-05 10:22:12 +0000124 `Your nickname was ${
pineafan6de4da52023-03-07 20:43:44 +0000125 newNickname ? "changed" : "cleared"
Skyler Greyda16adf2023-03-05 10:22:12 +0000126 } in ${interaction.guild!.name}.` +
pineafan6de4da52023-03-07 20:43:44 +0000127 (newNickname
128 ? `\nIt is now: ${newNickname}`
Skyler Greyad002172022-08-16 18:48:26 +0100129 : "") +
130 "\n\n" +
PineaFane6ba7882023-01-18 20:41:16 +0000131 (createAppealTicket
Skyler Greyda16adf2023-03-05 10:22:12 +0000132 ? `You can appeal this in the ticket created in <#${
133 confirmation.components!["appeal"]!.response
134 }>`
Skyler Greyad002172022-08-16 18:48:26 +0100135 : "")
136 )
137 .setStatus("Danger")
Skyler Greyda16adf2023-03-05 10:22:12 +0000138 ],
139 components: []
PineaFane6ba7882023-01-18 20:41:16 +0000140 };
141 if (config.moderation.nick.text && config.moderation.nick.link) {
Skyler Greyda16adf2023-03-05 10:22:12 +0000142 messageData.embeds[0]!.setFooter(LinkWarningFooter);
143 messageData.components.push(
144 new ActionRowBuilder<ButtonBuilder>().addComponents(
145 new ButtonBuilder()
PineaFane6ba7882023-01-18 20:41:16 +0000146 .setStyle(ButtonStyle.Link)
147 .setLabel(config.moderation.nick.text)
Skyler Greyda16adf2023-03-05 10:22:12 +0000148 .setURL(
149 config.moderation.nick.link.replaceAll(
150 "{id}",
pineafan6de4da52023-03-07 20:43:44 +0000151 member.id
Skyler Greyda16adf2023-03-05 10:22:12 +0000152 )
153 )
154 )
155 );
PineaFane6ba7882023-01-18 20:41:16 +0000156 }
pineafan6de4da52023-03-07 20:43:44 +0000157 dmMessage = await member.send(messageData);
PineaFane6ba7882023-01-18 20:41:16 +0000158 dmSent = true;
Skyler Greyad002172022-08-16 18:48:26 +0100159 }
160 } catch {
PineaFane6ba7882023-01-18 20:41:16 +0000161 dmSent = false;
Skyler Greyad002172022-08-16 18:48:26 +0100162 }
PineaFane6ba7882023-01-18 20:41:16 +0000163 let before: string | null;
Skyler Greyad002172022-08-16 18:48:26 +0100164 try {
PineaFane6ba7882023-01-18 20:41:16 +0000165 before = member.nickname;
pineafan6de4da52023-03-07 20:43:44 +0000166 member.setNickname(newNickname ?? null, "Nucleus Nickname command");
Skyler Greyad002172022-08-16 18:48:26 +0100167 await client.database.history.create(
168 "nickname",
PineaFane6ba7882023-01-18 20:41:16 +0000169 interaction.guild!.id,
Skyler Greyad002172022-08-16 18:48:26 +0100170 member.user,
171 interaction.user,
172 null,
173 before,
pineafan6de4da52023-03-07 20:43:44 +0000174 newNickname
Skyler Greyad002172022-08-16 18:48:26 +0100175 );
Skyler Greyad002172022-08-16 18:48:26 +0100176 } catch {
177 await interaction.editReply({
178 embeds: [
179 new EmojiEmbed()
180 .setEmoji("PUNISH.NICKNAME.RED")
181 .setTitle("Nickname")
182 .setDescription("Something went wrong and the users nickname could not be changed.")
183 .setStatus("Danger")
184 ],
185 components: []
186 });
PineaFane6ba7882023-01-18 20:41:16 +0000187 if (dmSent) await dmMessage!.delete();
Skyler Greyad002172022-08-16 18:48:26 +0100188 return;
189 }
PineaFane6ba7882023-01-18 20:41:16 +0000190 const data = {
191 meta: {
192 type: "memberUpdate",
193 displayName: "Member Updated",
194 calculateType: "guildMemberUpdate",
195 color: NucleusColors.yellow,
196 emoji: "PUNISH.NICKNAME.YELLOW",
TheCodedProf6ec331b2023-02-20 12:13:06 -0500197 timestamp: Date.now()
PineaFane6ba7882023-01-18 20:41:16 +0000198 },
199 list: {
pineafan6de4da52023-03-07 20:43:44 +0000200 member: entry(member.id, renderUser(member.user)),
PineaFane6ba7882023-01-18 20:41:16 +0000201 before: entry(before, before ?? "*No nickname set*"),
pineafan6de4da52023-03-07 20:43:44 +0000202 after: entry(newNickname ?? null, newNickname ?? "*No nickname set*"),
TheCodedProf6ec331b2023-02-20 12:13:06 -0500203 updated: entry(Date.now(), renderDelta(Date.now())),
PineaFane6ba7882023-01-18 20:41:16 +0000204 updatedBy: entry(interaction.user.id, renderUser(interaction.user))
205 },
TheCodedProf94ff6de2023-02-22 17:47:26 -0500206 separate: {
Skyler Greyda16adf2023-03-05 10:22:12 +0000207 end:
208 getEmojiByName("ICONS.NOTIFY." + (notify ? "ON" : "OFF")) +
209 ` The user was ${notify ? "" : "not "}notified`
TheCodedProf94ff6de2023-02-22 17:47:26 -0500210 },
PineaFane6ba7882023-01-18 20:41:16 +0000211 hidden: {
212 guild: interaction.guild!.id
213 }
214 };
215 log(data);
216 const failed = !dmSent && notify;
Skyler Greyad002172022-08-16 18:48:26 +0100217 await interaction.editReply({
218 embeds: [
219 new EmojiEmbed()
220 .setEmoji(`PUNISH.NICKNAME.${failed ? "YELLOW" : "GREEN"}`)
221 .setTitle("Nickname")
222 .setDescription(
223 "The members nickname was changed" +
224 (failed ? ", but was not notified" : "") +
PineaFane6ba7882023-01-18 20:41:16 +0000225 (confirmation.components!["appeal"]!.response !== null
226 ? ` and an appeal ticket was opened in <#${confirmation.components!["appeal"]!.response}>`
Skyler Greyad002172022-08-16 18:48:26 +0100227 : "")
228 )
229 .setStatus(failed ? "Warning" : "Success")
230 ],
231 components: []
232 });
pineafan63fc5e22022-08-04 22:04:10 +0100233};
pineafan32767212022-03-14 21:27:39 +0000234
pineafan6de4da52023-03-07 20:43:44 +0000235const check = async (interaction: CommandInteraction | ButtonInteraction, partial: boolean, target?: GuildMember) => {
Skyler Grey75ea9172022-08-06 10:22:23 +0100236 const member = interaction.member as GuildMember;
TheCodedProff86ba092023-01-27 17:10:07 -0500237 // Check if the user has manage_nicknames permission
238 if (!member.permissions.has("ManageNicknames")) return "You do not have the *Manage Nicknames* permission";
239 if (partial) return true;
PineaFan1dee28f2023-01-16 22:09:07 +0000240 const me = interaction.guild!.members.me!;
pineafan6de4da52023-03-07 20:43:44 +0000241 let apply: GuildMember;
242 if (interaction.isButton()) {
243 apply = target!;
244 } else {
245 apply = interaction.options.getMember("user") as GuildMember;
246 }
pineafan62ce1922022-08-25 20:34:45 +0100247 const memberPos = member.roles.cache.size ? member.roles.highest.position : 0;
248 const mePos = me.roles.cache.size ? me.roles.highest.position : 0;
249 const applyPos = apply.roles.cache.size ? apply.roles.highest.position : 0;
PineaFan1dee28f2023-01-16 22:09:07 +0000250 if (!interaction.guild) return false;
pineafanc1c18792022-08-03 21:41:36 +0100251 // Do not allow any changing of the owner
PineaFan0d06edc2023-01-17 22:10:31 +0000252 if (member.id === interaction.guild.ownerId) return "You cannot change the owner's nickname";
pineafan377794f2022-04-18 19:01:01 +0100253 // Check if Nucleus can change the nickname
TheCodedProf0941da42023-02-18 20:28:04 -0500254 if (!(mePos > applyPos)) return `I do not have a role higher than <@${apply.id}>`;
pineafan377794f2022-04-18 19:01:01 +0100255 // Check if Nucleus has permission to change the nickname
PineaFan0d06edc2023-01-17 22:10:31 +0000256 if (!me.permissions.has("ManageNicknames")) return "I do not have the *Manage Nicknames* permission";
pineafan377794f2022-04-18 19:01:01 +0100257 // Allow the owner to change anyone's nickname
pineafan63fc5e22022-08-04 22:04:10 +0100258 if (member.id === interaction.guild.ownerId) return true;
pineafane625d782022-05-09 18:04:32 +0100259 // Allow changing your own nickname
pineafan63fc5e22022-08-04 22:04:10 +0100260 if (member === apply) return true;
pineafan377794f2022-04-18 19:01:01 +0100261 // Check if the user is below on the role list
TheCodedProf0941da42023-02-18 20:28:04 -0500262 if (!(memberPos > applyPos)) return `You do not have a role higher than <@${apply.id}>`;
pineafan377794f2022-04-18 19:01:01 +0100263 // Allow change
pineafan63fc5e22022-08-04 22:04:10 +0100264 return true;
265};
pineafan32767212022-03-14 21:27:39 +0000266
Skyler Grey75ea9172022-08-06 10:22:23 +0100267export { command, callback, check };
TheCodedProfa112f612023-01-28 18:06:45 -0500268export const metadata = {
Skyler Greyda16adf2023-03-05 10:22:12 +0000269 longDescription:
270 "Changes the nickname of a member. This is the name that shows in the member list and on messages.",
271 premiumOnly: true
272};